Skip to content
Draft
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
14 changes: 11 additions & 3 deletions .github/actions/setup_base/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ inputs:
python-version:
required: false
description: ''
default: '3.12'
default: '3.13'
emscripten-version:
required: false
description: ''
default: ''

outputs:
cache-dir:
Expand Down Expand Up @@ -244,8 +248,12 @@ runs:
shell: bash
run: |

pip install pyodide-build>=0.28.0
echo "EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)" >> $GITHUB_ENV
pip install pyodide-build==0.30.9
if [[ "${{ inputs.emscripten-version }}" == "" ]]; then
echo "EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)" >> $GITHUB_ENV
else
echo "EMSCRIPTEN_VERSION=${{ inputs.emscripten-version }}" >> $GITHUB_ENV
fi

- name: Setup Emscripten
if: inputs.target-arch == 'wasm32'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build_llvm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ jobs:
###############################

$CCACHE -z
pyodide build scripts/llvm_wasm -o wheelhouse --compression-level 10
PYODIDE_BUILD_EXPORTS=whole_archive pyodide build scripts/llvm_wasm -o wheelhouse --compression-level 10
$CCACHE -s

- name: Upload ccache log
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build_mlir_python_bindings_wheel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ jobs:
run: |

if [[ "${{ matrix.target-arch }}" == "wasm32" ]]; then
pip download mlir-wheel --plat pyodide_2024_0_wasm32 --no-deps --python-version 3.12 -f https://llvm.github.io/eudsl
pip download mlir-wheel --plat pyodide_2025_0_wasm32 --no-deps --python-version 3.13 -f https://llvm.github.io/eudsl
else
pip download mlir-wheel -f https://llvm.github.io/eudsl
fi
Expand Down Expand Up @@ -214,7 +214,7 @@ jobs:
run: |

if [[ "${{ matrix.target-arch }}" == "wasm32" ]]; then
pyodide build "$PWD/projects/mlir-python-bindings-wasm" -o wheelhouse --compression-level 10
PYODIDE_BUILD_EXPORTS=whole_archive pyodide build "$PWD/projects/mlir-python-bindings-wasm" -o wheelhouse --compression-level 10
else
$python3_command -m cibuildwheel "$PWD/projects/mlir-python-bindings" --output-dir wheelhouse
fi
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy_pip_page.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
python-version: '3.13'

- name: Fetch latest WASM wheel
run: |
pip download mlir-python-bindings --plat pyodide_2024_0_wasm32 --no-deps --python-version 3.12 -f https://llvm.github.io/eudsl
pip download mlir-python-bindings --plat pyodide_2025_0_wasm32 --no-deps --python-version 3.13 -f https://llvm.github.io/eudsl
echo "MLIR_PYTHON_WHEEL_NAME=$(ls mlir_python_bindings*)" >> $GITHUB_ENV
pip wheel eudsl-python-extras -f https://llvm.github.io/eudsl --no-deps -w .
echo "EUDSL_PYTHON_EXTRAS_WHEEL_NAME=$(ls eudsl_python_extras*)" >> $GITHUB_ENV
Expand Down
11 changes: 11 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,14 @@
ignore = dirty
shallow = true
branch = main
[submodule "third_party/dawn"]
path = third_party/dawn
url = https://github.com/google/dawn.git
fetchRecurseSubmodules = false
shallow = true
[submodule "third_party/SPIRV-Headers"]
path = third_party/SPIRV-Headers
url = https://github.com/KhronosGroup/SPIRV-Headers.git
[submodule "third_party/SPIRV-Tools"]
path = third_party/SPIRV-Tools
url = https://github.com/KhronosGroup/SPIRV-Tools.git
4 changes: 3 additions & 1 deletion projects/mlir-python-bindings-wasm/build.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/bash

TD="$(cd $(dirname $0) && pwd)"
REPO_ROOT="$(cd $TD/../.. && pwd)"

if ! command -v pyodide >/dev/null 2>&1
then
Expand Down Expand Up @@ -33,4 +35,4 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
# the above doesn't work so you need to run in docker
fi

pyodide build . -o wheelhouse --compression-level 10
PYODIDE_SOURCEMAPS=1 PYODIDE_BUILD_EXPORTS=whole_archive pyodide build $TD -o $REPO_ROOT/wheelhouse --compression-level 10
16 changes: 9 additions & 7 deletions projects/mlir-python-bindings-wasm/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ Discussions = "https://discourse.llvm.org/"
requires = [
"scikit-build-core==0.10.7",
"typing_extensions==4.12.2",
"nanobind>=2.4, <3.0",
# https://github.com/wjakob/nanobind/commit/dd350fe81931a1b362196cb415d188c36422766e#diff-8599263e788c107944d356ce118965942735cfbe16289ccf98ee5f8a33f0e808
# error: static assertion failed due to requirement 'pyobj_name::total_count * sizeof(_object *) == 96'
"nanobind>=2.4, <=2.9.2",
"pybind11>=2.10.0, <=2.13.6",
]
build-backend = "scikit_build_core.build"
Expand Down Expand Up @@ -52,14 +54,16 @@ CMAKE_CXX_COMPILER_LAUNCHER = { env = "CMAKE_CXX_COMPILER_LAUNCHER", default = "
# (and setting CMAKE_BUILD_WITH_INSTALL_RPATH does nothing)
# CMAKE_GENERATOR = { env = "CMAKE_GENERATOR", default = "Ninja" }
CMAKE_PLATFORM_NO_VERSIONED_SONAME = "ON"
CMAKE_CXX_FLAGS = "-sNO_DISABLE_EXCEPTION_CATCHING"
CMAKE_VISIBILITY_INLINES_HIDDEN = "ON"
CMAKE_C_VISIBILITY_PRESET = "hidden"
CMAKE_CXX_VISIBILITY_PRESET = "hidden"
CMAKE_EXE_LINKER_FLAGS = "-sALLOW_TABLE_GROWTH -sASSERTIONS -sNO_DISABLE_EXCEPTION_CATCHING -sWASM_BIGINT"
CMAKE_SHARED_LINKER_FLAGS = "-sALLOW_TABLE_GROWTH -sASSERTIONS -sNO_DISABLE_EXCEPTION_CATCHING -sWASM_BIGINT"
CMAKE_MODULE_LINKER_FLAGS = "-sALLOW_TABLE_GROWTH -sASSERTIONS -sNO_DISABLE_EXCEPTION_CATCHING -sWASM_BIGINT"
CMAKE_EXE_LINKER_FLAGS = "-sALLOW_TABLE_GROWTH -sASSERTIONS -sWASM_BIGINT"
CMAKE_SHARED_LINKER_FLAGS = "-sALLOW_TABLE_GROWTH -sASSERTIONS -sWASM_BIGINT"
CMAKE_MODULE_LINKER_FLAGS = "-sALLOW_TABLE_GROWTH -sASSERTIONS -sWASM_BIGINT"
CMAKE_VERBOSE_MAKEFILE = "ON"
# De-duplicate libraries on link lines based on linker capabilities.
# minimum cmake version is 3.29
CMAKE_POLICY_DEFAULT_CMP0156 = "NEW"

# so that NATIVE doesn't try to get built
LLVM_NATIVE_TOOL_DIR = { env = "LLVM_NATIVE_TOOL_DIR", default = "" }
Expand All @@ -68,5 +72,3 @@ MLIR_LINALG_ODS_YAML_GEN = { env = "MLIR_LINALG_ODS_YAML_GEN", default = "" }
MLIR_TABLEGEN = { env = "MLIR_TABLEGEN", default = "" }

MLIR_ENABLE_BINDINGS_PYTHON = "ON"
MLIR_ENABLE_EXECUTION_ENGINE = "ON"
MLIR_ENABLE_SPIRV_CPU_RUNNER = "ON"
89 changes: 89 additions & 0 deletions projects/mlir-wgpu/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# Copyright (c) 2025.

cmake_minimum_required(VERSION 3.29)
project(mlir-webgpu LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(POLICY CMP0068)
cmake_policy(SET CMP0068 NEW)
set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON)
endif()

if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()

if(POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif()

if(POLICY CMP0091)
cmake_policy(SET CMP0091 NEW)
endif()

if(POLICY CMP0116)
cmake_policy(SET CMP0116 NEW)
endif()

if(POLICY CMP0135)
cmake_policy(SET CMP0116 OLD)
endif()

set(DAWN_FETCH_DEPENDENCIES ON)
set(TINT_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_SPV_READER ON CACHE BOOL "" FORCE)
set(TINT_BUILD_WGSL_READER OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_GLSL_WRITER OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_HLSL_WRITER OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_MSL_WRITER OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_SPV_WRITER OFF CACHE BOOL "" FORCE)
set(TINT_BUILD_WGSL_WRITER ON CACHE BOOL "" FORCE)

add_subdirectory(
"${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/dawn"
EXCLUDE_FROM_ALL
${CMAKE_CURRENT_BINARY_DIR}/dawn
)
if(NOT Dawn_SOURCE_DIR OR NOT EXISTS ${Dawn_SOURCE_DIR})
message(FATAL_ERROR "failed to configure Dawn dependency")
endif()
set(DAWN_SOURCE_DIR ${Dawn_SOURCE_DIR})

set(SPIRV_HEADERS_SKIP_EXAMPLES ON CACHE BOOL "" FORCE)
set(SPIRV_HEADERS_SKIP_INSTALL ON CACHE BOOL "" FORCE)
add_subdirectory(
"${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/SPIRV-Headers"
EXCLUDE_FROM_ALL
${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Headers
)

add_subdirectory(
"${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/SPIRV-Tools"
EXCLUDE_FROM_ALL
${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools
)

# https://github.com/kainino0x/webgpu-cross-platform-demo
add_executable(dawn_app shader_test.cpp)
target_compile_definitions(dawn_app PRIVATE DEMO_USE_ASYNCIFY=1)

if(EMSCRIPTEN)
set_target_properties(dawn_app PROPERTIES SUFFIX ".html")
target_link_libraries(dawn_app PRIVATE emdawnwebgpu_cpp webgpu_glfw)
target_link_options(dawn_app PRIVATE
"-sASYNCIFY=1"
"-sUSE_GLFW=3"
"-sASYNCIFY_STACK_SIZE=65536"
"-sEXPORTED_RUNTIME_METHODS=ccall"
)
else()
target_link_libraries(dawn_app PRIVATE webgpu_dawn webgpu_glfw glfw)
endif()
152 changes: 152 additions & 0 deletions projects/mlir-wgpu/dawn_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#include <iostream>

#include <GLFW/glfw3.h>
#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#endif
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <webgpu/webgpu_glfw.h>

wgpu::Instance instance;
wgpu::Adapter adapter;
wgpu::Device device;
wgpu::RenderPipeline pipeline;

wgpu::Surface surface;
wgpu::TextureFormat format;
const uint32_t kWidth = 512;
const uint32_t kHeight = 512;

void ConfigureSurface() {
wgpu::SurfaceCapabilities capabilities;
surface.GetCapabilities(adapter, &capabilities);
format = capabilities.formats[0];

wgpu::SurfaceConfiguration config{.device = device,
.format = format,
.width = kWidth,
.height = kHeight};
surface.Configure(&config);
}

void Init() {
static const auto kTimedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
wgpu::InstanceDescriptor instanceDesc{.requiredFeatureCount = 1,
.requiredFeatures = &kTimedWaitAny};
instance = wgpu::CreateInstance(&instanceDesc);

wgpu::Future f1 = instance.RequestAdapter(
nullptr, wgpu::CallbackMode::WaitAnyOnly,
[](wgpu::RequestAdapterStatus status, wgpu::Adapter a,
wgpu::StringView message) {
if (status != wgpu::RequestAdapterStatus::Success) {
std::cout << "RequestAdapter: " << message << "\n";
exit(0);
}
adapter = std::move(a);
});
instance.WaitAny(f1, UINT64_MAX);

wgpu::DeviceDescriptor desc{};
desc.SetUncapturedErrorCallback([](const wgpu::Device&,
wgpu::ErrorType errorType,
wgpu::StringView message) {
std::cout << "Error: " << errorType << " - message: " << message << "\n";
});

wgpu::Future f2 = adapter.RequestDevice(
&desc, wgpu::CallbackMode::WaitAnyOnly,
[](wgpu::RequestDeviceStatus status, wgpu::Device d,
wgpu::StringView message) {
if (status != wgpu::RequestDeviceStatus::Success) {
std::cout << "RequestDevice: " << message << "\n";
exit(0);
}
device = std::move(d);
});
instance.WaitAny(f2, UINT64_MAX);
}

const char shaderCode[] = R"(
@vertex fn vertexMain(@builtin(vertex_index) i : u32) ->
@builtin(position) vec4f {
const pos = array(vec2f(0, 1), vec2f(-1, -1), vec2f(1, -1));
return vec4f(pos[i], 0, 1);
}
@fragment fn fragmentMain() -> @location(0) vec4f {
return vec4f(1, 0, 0, 1);
}
)";

void CreateRenderPipeline() {
wgpu::ShaderSourceWGSL wgsl{{.code = shaderCode}};

wgpu::ShaderModuleDescriptor shaderModuleDescriptor{.nextInChain = &wgsl};
wgpu::ShaderModule shaderModule =
device.CreateShaderModule(&shaderModuleDescriptor);

wgpu::ColorTargetState colorTargetState{.format = format};

wgpu::FragmentState fragmentState{
.module = shaderModule, .targetCount = 1, .targets = &colorTargetState};

wgpu::RenderPipelineDescriptor descriptor{.vertex = {.module = shaderModule},
.fragment = &fragmentState};
pipeline = device.CreateRenderPipeline(&descriptor);
}

void Render() {
wgpu::SurfaceTexture surfaceTexture;
surface.GetCurrentTexture(&surfaceTexture);

wgpu::RenderPassColorAttachment attachment{
.view = surfaceTexture.texture.CreateView(),
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store};

wgpu::RenderPassDescriptor renderpass{.colorAttachmentCount = 1,
.colorAttachments = &attachment};

wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderpass);
pass.SetPipeline(pipeline);
pass.Draw(3);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
device.GetQueue().Submit(1, &commands);
}

void InitGraphics() {
ConfigureSurface();
CreateRenderPipeline();
}

void Start() {
if (!glfwInit()) {
return;
}

glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window =
glfwCreateWindow(kWidth, kHeight, "WebGPU window", nullptr, nullptr);
surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);

InitGraphics();

#if defined(__EMSCRIPTEN__)
emscripten_set_main_loop(Render, 0, false);
#else
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
Render();
surface.Present();
instance.ProcessEvents();
}
#endif
}

int main() {
Init();
Start();
}
Loading
Loading