Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

env:
CARGO_TERM_COLOR: always
RUSTFLAGS: --deny warnings
RUSTDOCFLAGS: --deny warnings
SLANG_TAG: 2025.18.2

jobs:
# Check formatting.
format:
name: Format
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt

- name: Run cargo fmt
run: cargo fmt --all -- --check
setup-slang:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
outputs:
slang-dir: ${{ steps.setup.outputs.slang-dir }} # Pass SLANG_DIR to dependent jobs
slang-cache-key: ${{ steps.setup.outputs.slang-cache-key }} # Pass SLANG_DIR to dependent jobs
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Cache Slang
id: cache-slang
uses: actions/cache/restore@v4 # Restore first
with:
path: |
~/.cache/slang # Matches script's default OUTPUT_DIR
key: slang-v$SLANG_TAG-${{ runner.os }}-${{ runner.arch }}

- name: Setup Slang
id: setup
run: |
echo "version=$SLANG_TAG" >> $GITHUB_OUTPUT # Output for cache key
SLANG_DIR=$(./.github/workflows/download_slang.sh --version $SLANG_TAG | grep '^SLANG_DIR=' | cut -d'=' -f2-)
echo "slang-dir=$SLANG_DIR" >> $GITHUB_OUTPUT # Output for dependents
echo "slang-cache-key=slang-v$SLANG_TAG-${{ runner.os }}-${{ runner.arch }}" >> $GITHUB_OUTPUT
echo "SLANG_DIR=$SLANG_DIR" >> $GITHUB_ENV # For this job if needed

- name: Save Slang Cache
if: steps.cache-slang.outputs.cache-hit != 'true' # Only save on miss
uses: actions/cache/save@v4
with:
path: ~/.cache/slang
key: ${{ steps.setup.outputs.slang-cache-key }}
# Run clippy lints.
clippy:
needs: setup-slang # Depends on setup-slang
name: Clippy
runs-on: ubuntu-latest
env:
SLANG_DIR: ${{ needs.setup-slang.outputs.slang-dir }}
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy

- name: Install dependencies
run: sudo apt-get update; sudo apt-get install --no-install-recommends build-essential curl wget file libssl-dev

- name: Retrieve Cache for Slang
uses: actions/cache/restore@v4
with:
path: ~/.cache/slang
key: ${{ needs.setup-slang.outputs.slang-cache-key }}

- name: Populate target directory from cache
uses: Leafwing-Studios/cargo-cache@v2
with:
sweep-cache: true

- name: Run clippy lints
run: SLANG_DIR=$SLANG_DIR cargo clippy --locked --workspace --all-targets -- --deny warnings

# Check documentation.
doc:
needs: setup-slang # Depends on setup-slang
name: Docs
runs-on: ubuntu-latest
timeout-minutes: 30
env:
SLANG_DIR: ${{ needs.setup-slang.outputs.slang-dir }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Install dependencies
run: sudo apt-get update; sudo apt-get install --no-install-recommends build-essential curl wget file libssl-dev

- name: Retrieve Cache for Slang
uses: actions/cache/restore@v4
with:
path: ~/.cache/slang
key: ${{ needs.setup-slang.outputs.slang-cache-key }}

- name: Populate target directory from cache
uses: Leafwing-Studios/cargo-cache@v2
with:
sweep-cache: true

- name: Check documentation
run: SLANG_DIR=$SLANG_DIR cargo doc --locked --workspace --document-private-items --no-deps
# Testing.
test:
needs: setup-slang # Depends on setup-slang
name: Tests
runs-on: ubuntu-latest
timeout-minutes: 30
env:
SLANG_DIR: ${{ needs.setup-slang.outputs.slang-dir }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
build-essential curl wget file libssl-dev \
libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers

- name: Retrieve Cache for Slang
uses: actions/cache/restore@v4
with:
path: ~/.cache/slang
key: ${{ needs.setup-slang.outputs.slang-cache-key }}

- name: Populate target directory from cache
uses: Leafwing-Studios/cargo-cache@v2
with:
sweep-cache: true
- name: Run Cargo Tests
run: |
SLANG_DIR=$SLANG_DIR LIBGL_ALWAYS_SOFTWARE=1 cargo test --verbose
109 changes: 109 additions & 0 deletions .github/workflows/download_slang.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/bin/bash

# Default values
OS=""
OUTPUT_DIR="$HOME/.cache/slang"
SLANG_VERSION=""
SLANG_TAG=""
ASSET_SUFFIX=""
SLANG_URL_BASE="https://github.com/shader-slang/slang/releases/download"

# Help message
usage() {
echo "Usage: $0 [--os <linux|macos|macos-arm64|windows>] [--output-dir <path>] [--version <version>]"
echo " --os: Target OS (default: auto-detect from current platform)"
echo " --output-dir: Directory to extract Slang (default: ~/.cache/slang)"
echo " --version: Slang version (e.g., 2025.18.2, default: latest)"
echo "Example: $0 --os linux --output-dir /tmp/slang"
}

# Parse arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--os) OS="$2"; shift ;;
--output-dir) export OUTPUT_DIR="$2"; shift ;;
--version) export SLANG_VERSION="$2"; shift ;;
*) usage ; exit 1 ;;
esac
shift
done

# Detect OS if not specified
if [[ -z "$OS" ]]; then
case "$(uname -s)" in
Linux*) OS="linux" ;;
Darwin*)
if [[ "$(uname -m)" == "arm64" ]]; then
OS="macos-aarch64"
else
OS="macos"
fi
;;
CYGWIN*|MINGW*|MSYS*) OS="windows" ;;
*) echo "Error: Unable to detect OS. Specify --os (linux, macos, macos-arm64, windows)"; exit 1 ;;
esac
fi

# Determine asset suffix based on OS
case "$OS" in
linux) ASSET_SUFFIX="linux-x86_64.zip" ;;
macos) ASSET_SUFFIX="macos-x86_64.zip" ;;
macos-aarch64) ASSET_SUFFIX="macos-aarch64.zip" ;;
windows) ASSET_SUFFIX="windows-x86_64.zip" ;;
*) echo "Error: Unsupported OS: $OS"; exit 1 ;;
esac

# Get Slang version if not specified
if [[ -z "$SLANG_VERSION" ]]; then
export SLANG_TAG=$(curl -s https://api.github.com/repos/shader-slang/slang/releases/latest | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
export SLANG_VERSION=$(echo "$SLANG_TAG" | sed 's/v//') # e.g., v2025.18.2 -> 2025.18.2
else
export SLANG_TAG="v$SLANG_VERSION"
fi

if [[ -z "$SLANG_VERSION" ]]; then
echo "Error: Could not determine Slang version"
exit 1
fi

# Set up paths
SLANG_DIR="$OUTPUT_DIR/slang-v$SLANG_VERSION-$OS"
ZIP_URL="$SLANG_URL_BASE/$SLANG_TAG/slang-$SLANG_VERSION-$ASSET_SUFFIX"
TEMP_ZIP="/tmp/slang-$SLANG_VERSION.zip"

# Check if Slang is already extracted
if [[ -d "$SLANG_DIR" ]] && [[ -f "$SLANG_DIR/bin/slangc" || -f "$SLANG_DIR/bin/slangc.exe" ]]; then
echo "Using existing Slang at $SLANG_DIR"
echo "SLANG_DIR=$SLANG_DIR"
exit 0
fi

# Download Slang release
echo "Downloading Slang v$SLANG_VERSION for $OS from $ZIP_URL..."
mkdir -p "$OUTPUT_DIR"
curl -L -o "$TEMP_ZIP" "$ZIP_URL" || { echo "Error: Download failed for $ZIP_URL"; exit 1; }

# Extract based on OS
echo "Extracting to $SLANG_DIR..."
if [[ "$OS" == "windows" ]]; then
# Windows: Assume 7z is available (or adjust for PowerShell/Expand-Archive)
7z x "$TEMP_ZIP" -o"$SLANG_DIR" -y > /dev/null || { echo "Error: Extraction failed"; rm -f "$TEMP_ZIP"; exit 1; }
else
# Linux/macOS: Use unzip
unzip -q "$TEMP_ZIP" -d "$SLANG_DIR" || { echo "Error: Extraction failed"; rm -f "$TEMP_ZIP"; exit 1; }
fi

# Clean up
rm -f "$TEMP_ZIP"

# Verify extraction
if [[ ! -f "$SLANG_DIR/bin/slangc" && ! -f "$SLANG_DIR/bin/slangc.exe" ]]; then
echo "Error: Extraction incomplete, slangc not found in $SLANG_DIR/bin"
exit 1
fi

echo "Slang v$SLANG_VERSION extracted to $SLANG_DIR"
echo "SLANG_DIR=$SLANG_DIR"

# For use in calling script
export SLANG_DIR
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# v0.2.0 (27 Oct. 2025)
- Update to slang-hal 0.2.
- Make rank-1 tensors resizeable.
- Fix svd3.slang retuning incorrect results for near-identity matrices.
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "stensor"
authors = ["Sébastien Crozet <sebcrozet@dimforge.com>"]
description = "Cross-platform GPU tensor library with Slang and Rust."
repository = "https://github.com/dimforge/stensor"
version = "0.1.1"
version = "0.2.0"
edition = "2024"
license = "Apache-2.0"

Expand All @@ -12,15 +12,15 @@ cuda = [ "cudarc", "slang-hal/cuda" ]
cublas = [ "slang-hal/cublas" ]

[dependencies]
wgpu = "26"
wgpu = "27"
encase = "0.12"
bytemuck = "1"
nalgebra = { version = "0.34", features = ["encase"] }

cudarc = { version = "0.16", optional = true }

minislang = "0.1"
slang-hal = { version = "0.1", features = ["derive"] }
minislang = "0.2"
slang-hal = { version = "0.2", features = ["derive"] }
include_dir = "0.7"

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ gpu". It aims (but it isn’t there yet) to expose linear algebra operations (in
operations) as well as geometric types (quaternions, similarities, etc.) as Slang shaders and kernels.

> **Warning**
**stensor** is still very incomplete and under heavy development and is lacking many features.
> **stensor** is still very incomplete and under heavy development and is lacking many features.

See also the README of [slang-hal](https://github.com/dimforge/slang-hal/blob/main/README.md) for information on
supported platforms.

### Using Slang

In order to compile and run any slang project, be sure to define the `SLANG_DIR` environment variable:
1. Download the Slang compiler libraries for your platform: https://github.com/shader-slang/slang/releases/tag/v2025.16
1. Download the Slang compiler libraries for your platform: <https://github.com/shader-slang/slang/releases/tag/v2025.16>
2. Unzip the downloaded directory, and use its path as value to the `SLANG_DIR` environment variable: `SLANG_DIR=/path/to/slang`.
Note that the variable must point to the root of the slang installation (i.e. the directory that contains `bin` and `lib`).
We recommend adding that as a system-wide environment variables so that it also becomes available to your IDE.
1 change: 0 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,3 @@ pub fn main() {
slang.compile_all(target, "../shaders", "./src/autogen", &[]);
}
}

7 changes: 3 additions & 4 deletions examples/gemm_bench.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use approx::assert_relative_eq;
use indexmap::IndexMap;
use minislang::SlangCompiler;
use nalgebra::DMatrix;
use slang_hal::Shader;
use slang_hal::backend::WebGpu;
use slang_hal::backend::{Backend, Encoder};
use slang_hal::shapes::ViewShapeBuffers;
use slang_hal::tensor::{GpuTensor, TensorBuilder};
use stensor::linalg::{Gemm, GemmVariant};
use stensor::shapes::ViewShapeBuffers;
use stensor::tensor::GpuTensor;
use wgpu::{BufferUsages, Features, Limits};

#[async_std::main]
Expand Down Expand Up @@ -105,7 +104,7 @@ async fn run_gemm<B: Backend>(
drop(pass); // Ensure the pass is ended before the encoder is borrowed again.

backend.submit(encoder)?;
backend.synchronize();
backend.synchronize()?;
timing[i] = t0.elapsed().as_secs_f32();
backend
.slow_read_buffer(result.buffer(), gpu_result.as_mut_slice())
Expand Down
6 changes: 4 additions & 2 deletions shaders/stensor/geometry/svd3.slang
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ public struct Svd3 {

// Constants used for calculation of givens quaternions
static const float GAMMA = 5.828427124; // sqrt(8)+3;
static const float CSTAR = 0.923879532; // cos(pi/8)
static const float SSTAR = 0.3826834323; // sin(p/8)
static const float CSTAR = 1.0; // TODO: using no-identity values (below) breaks the SVD for near-identity matrices.
static const float SSTAR = 0.0; // TODO: using no-identity values (below) breaks the SVD for near-identity matrices.
//static const float CSTAR = 0.923879532; // cos(pi/8)
//static const float SSTAR = 0.3826834323; // sin(p/8)
// Threshold value
static const float SVD_EPSILON = 1e-6;
// Iteration counts for Jacobi Eigenanalysis and reciprocal square root functions, influence precision
Expand Down
2 changes: 0 additions & 2 deletions shaders/stensor/linalg/gemm.slang
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ void gemm_fast(
) {
let local_id = local_id.y;

out[0] = 1.0;

for (var k = 0u; k < shape_m2.ncols; k += 4u) {
var sum = float4x4(0.0);

Expand Down
Loading
Loading