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
20 changes: 20 additions & 0 deletions .clangd
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# clangd config for cthreads (Cursor / VS Code clangd).
# Paths are relative to this file (repo root), except Vulkan SDK.

CompileFlags:
Add:
- -std=c++17
# Match CMake CTHREADS_GPU=ON so #ifdef CTHREADS_WITH_GPU paths analyze correctly.
- -DCTHREADS_WITH_GPU=1
# Project includes (same as CMake target_include_directories for GPU builds).
- -Isrc/cthreads/cpp/headers
- -Isrc/cthreads/cpp/gpu/headers
# Vulkan headers from local SDK (VULKAN_SDK=C:\VulkanSDK\1.4.357.0).
# Update this -I if you install a newer SDK version.
- -IC:/VulkanSDK/1.4.357.0/Include

Diagnostics:
UnusedIncludes: None

Index:
Background: Build
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ libcthreads_kernels.so
libcthreads_kernels.dylib
# <<< cthreads (auto)

todo.md
todo.md
# demo artifacts
*.gif
24 changes: 24 additions & 0 deletions src/cthreads/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# Enable locally (pick one shell):
# cmd: set CMAKE_ARGS=-DCTHREADS_GPU=ON
# PowerShell: $env:CMAKE_ARGS="-DCTHREADS_GPU=ON"
# either: pip install -e . --config-settings=cmake.define.CTHREADS_GPU=ON
# Wipe build/ if toggling ON/OFF so CMake reconfigures (cached OFF sticks otherwise).
option(CTHREADS_GPU "Build Vulkan GPU support into _ext" OFF)

# --- Python + pybind11 -------------------------------------------------------
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)

Expand Down Expand Up @@ -132,3 +139,20 @@ message(STATUS "Python: ${Python_EXECUTABLE} (${Python_VERSION})")
if(DEFINED SKBUILD_STATE)
message(STATUS "SKBUILD_STATE: ${SKBUILD_STATE}")
endif()

# --- GPU ----

if(CTHREADS_GPU)
find_package(Vulkan REQUIRED) # vulkan sdk for headers/includes
message(STATUS "cthreads GPU: ON (Vulkan)")
target_sources(_ext PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/gpu/impl/context.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/bindings/gpu_module.cpp"
# extend more source when implemented (current stage: GPU-01)
)
target_include_directories(_ext PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/gpu/headers
${Vulkan_INCLUDE_DIRS}
)
target_compile_definitions(_ext PRIVATE CTHREADS_WITH_GPU=1)
endif()
42 changes: 42 additions & 0 deletions src/cthreads/cpp/bindings/gpu_module.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2026 Tobias Karusseit
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

#include "gpu_module.hpp"

#include "../gpu/headers/context.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

void bind_gpu(py::module_& parent) {
py::module_ g = parent.def_submodule(
"gpu",
"Vulkan GPU runtime (loader dynamically loaded at init)"
);

g.def(
"available",
&cthreads::gpu::available,
"True if Vulkan loader + compute device initialized successfully."
);

g.def(
"device_name",
&cthreads::gpu::device_name,
"GPU deviceName from Vulkan; calls init() (may raise)."
);

g.def(
"init",
&cthreads::gpu::init,
"Explicitly initialize Vulkan context (optional; available/device_name also init)."
);

g.def(
"shutdown",
&cthreads::gpu::shutdown,
"Destroy device/instance and unload the Vulkan loader."
);
}
8 changes: 8 additions & 0 deletions src/cthreads/cpp/bindings/gpu_module.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <pybind11/pybind11.h>

namespace py = pybind11;

/** Register ``cthreads._ext.gpu`` (Vulkan context probe API). */
void bind_gpu(py::module_& parent);
9 changes: 9 additions & 0 deletions src/cthreads/cpp/bindings/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#include "../headers/pool/threadPool.hpp"
#include "../headers/shared_host.hpp"

#ifdef CTHREADS_WITH_GPU
#include "gpu_module.hpp"
#endif

#include <cstdint>
#include <atomic>
#include <condition_variable>
Expand Down Expand Up @@ -1255,4 +1259,9 @@ PYBIND11_MODULE(_ext, m) {

bind_linalg(m);
bind_pool(m);

// runs when the user has the gpu capable version
#ifdef CTHREADS_WITH_GPU
bind_gpu(m);
#endif
}
51 changes: 51 additions & 0 deletions src/cthreads/cpp/gpu/headers/context.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once
#include <vulkan/vulkan.h>
#include <string>
#include <cstdint>

namespace cthreads::gpu {

struct Context {
// OS handle to the loader shared library (HMODULE on Windows, void* on Linux).
// Purpose: keep the DLL mapped and FreeLibrary/dlclose on shutdown.
void* loader_module = nullptr;

// Bootstrap entry from the loader. Type: pointer-to-function matching
// VkResult-less GetInstanceProcAddr signature from the headers.
// Usage: resolve almost every other Vulkan function by name string.
// Naming Note: GetPRocAddress => GetFunctionAddress (Proc -> procedure -> function)
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr;
// Global (pre-instance) / instance-level entry points.
// Each is a typed function pointer; assigned in init() via GetInstanceProcAddr.
PFN_vkCreateInstance vkCreateInstance = nullptr;
PFN_vkDestroyInstance vkDestroyInstance = nullptr;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices = nullptr;
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties = nullptr;
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties = nullptr;
PFN_vkCreateDevice vkCreateDevice = nullptr;
PFN_vkDestroyDevice vkDestroyDevice = nullptr;
PFN_vkGetDeviceQueue vkGetDeviceQueue = nullptr;
// Opaque Vulkan handles.
VkInstance instance = VK_NULL_HANDLE; // connection to the loader/app
VkPhysicalDevice physical_device = VK_NULL_HANDLE; // chosen GPU
VkDevice device = VK_NULL_HANDLE; // logical device (opened GPU)
VkQueue queue = VK_NULL_HANDLE; // compute submission port
// Which queue family index we passed to vkCreateDevice (needed for pools).
uint32_t queue_family = 0;
// Human-readable GPU name from VkPhysicalDeviceProperties::deviceName.
std::string device_name;
// True only after init() fully succeeded.
bool ready = false;
};
// Process-wide singleton accessor.
Context& context();
// Create loader + instance + device + queue. Throws on failure.
void init();
// Destroy device/instance; unload loader; clear pointers. Safe to call if not ready.
void shutdown();
// If not ready, try init once; return ready without throwing (for available()).
bool available();
// Requires ready context; returns device_name.
const std::string& device_name();

} // namespace cthreads::gpu
Loading
Loading