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
10 changes: 6 additions & 4 deletions .github/workflows/pip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ name: Build PyPI package

on:
push:
branches: [ main, build/pip-packaging ]
branches: [ main, release/*.x ]
release:
types: [ created ]
types: [ published ]

env:
# TODO: detect this from repo somehow: https://github.com/halide/Halide/issues/8406
Expand Down Expand Up @@ -253,7 +253,9 @@ jobs:
- uses: pypa/gh-action-pypi-publish@release/v1
if: github.event_name == 'push' && github.ref_name == 'main'
with:
repository_url: https://test.pypi.org/legacy/
repository-url: https://test.pypi.org/legacy/

- uses: pypa/gh-action-pypi-publish@release/v1
if: github.event_name == 'release' && github.event.action == 'published'
if: >
github.event_name == 'release' && github.event.action == 'published' ||
github.event_name == 'push' && (github.ref_name == 'release/19.x' || github.ref_name == 'v19.0.0')
124 changes: 124 additions & 0 deletions apps/external_vk_demo/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
cmake_minimum_required(VERSION 3.16)
project(external_vk_demo)

enable_testing()

# Set up language settings
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)

# Define Halide installation path
# I've compiled it with the changes like the following:
# cmake -G Ninja -S . -B build \
# -DCMAKE_BUILD_TYPE=Debug \
# -DCMAKE_OSX_ARCHITECTURES=arm64 \
# -DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \
# -DLLVM_DIR="$(brew --prefix llvm@19)/lib/cmake/llvm" \
# -DHalide_WASM_BACKEND=wabt \
# -DWITH_PYTHON_BINDINGS=OFF \
# -DWITH_TUTORIALS=OFF \
# -DBUILD_SHARED_LIBS=OFF \
# -DHalide_BUNDLE_STATIC=ON
# Then build:
# cmake --build build -j32
# Then install:
# cmake --install build --prefix "$PWD/install"
set(HALIDE_INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../install)

# Create our own Halide targets exactly like the Bazel BUILD file

# Halide runtime headers (equivalent to ":runtime")
add_library(Halide_Runtime INTERFACE)
target_include_directories(Halide_Runtime INTERFACE ${HALIDE_INSTALL_DIR}/include)

# Halide static library (equivalent to ":lib_halide_static")
add_library(Halide_Static STATIC IMPORTED)
# FIX paths they are hardcoded to my machine
set_target_properties(Halide_Static PROPERTIES
IMPORTED_LOCATION ${HALIDE_INSTALL_DIR}/lib/libHalide.a
INTERFACE_INCLUDE_DIRECTORIES ${HALIDE_INSTALL_DIR}/include
INTERFACE_LINK_LIBRARIES "-L/opt/homebrew/opt/llvm@19/lib;-lLLVM-19;-L/opt/homebrew/Cellar/wabt/1.0.37/lib;-lwabt;-lwasm-rt-impl;-L/opt/homebrew/Cellar/lld@19/19.1.7/lib;-llldWasm;-llldCommon;-framework Foundation;-framework CoreFoundation;-framework CoreGraphics;-framework CoreVideo;-framework IOKit;-framework AppKit;-lz;-ldl;-lpthread;-lcurses;-lffi"
)
target_link_libraries(Halide_Static INTERFACE Halide_Runtime)

# Halide GenGen library (equivalent to ":gengen")
add_library(Halide_GenGen STATIC IMPORTED)
set_target_properties(Halide_GenGen PROPERTIES
IMPORTED_LOCATION ${HALIDE_INSTALL_DIR}/lib/libHalide_GenGen.a
INTERFACE_INCLUDE_DIRECTORIES "${HALIDE_INSTALL_DIR}/include;${HALIDE_INSTALL_DIR}/share/Halide/tools"
# Force load the GenGen library (equivalent to alwayslink = True)
INTERFACE_LINK_LIBRARIES "-Wl,-force_load,${HALIDE_INSTALL_DIR}/lib/libHalide_GenGen.a"
)
target_link_libraries(Halide_GenGen INTERFACE Halide_Static)

# Find Vulkan
find_package(Vulkan REQUIRED)

# Generator executable - create it manually like Bazel does
add_executable(convert_generator convert_generator.cc)
target_link_libraries(convert_generator
PRIVATE
Halide_GenGen
Vulkan::Vulkan)

# Manual AOT compilation using custom command (following Makefile pattern)
# Note: Requires Vulkan library to be in your path. Set environment before building:
# On macOS: export DYLD_LIBRARY_PATH=/path/to/vulkan/lib:$DYLD_LIBRARY_PATH
# On Linux: export LD_LIBRARY_PATH=/path/to/vulkan/lib:$LD_LIBRARY_PATH
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/convert_generator.a ${CMAKE_CURRENT_BINARY_DIR}/convert_generator.h
COMMAND $<TARGET_FILE:convert_generator> -g convert_generator -o ${CMAKE_CURRENT_BINARY_DIR} target=host-vulkan-vk_int8
DEPENDS convert_generator
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating Vulkan AOT library (ensure Vulkan is in library path)"
)

# Create custom target and library from generated files
add_custom_target(convert_aot_files DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/convert_generator.a ${CMAKE_CURRENT_BINARY_DIR}/convert_generator.h)
add_library(convert_aot STATIC IMPORTED)
set_target_properties(convert_aot PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/convert_generator.a)
add_dependencies(convert_aot convert_aot_files)
target_link_libraries(convert_aot INTERFACE Vulkan::Vulkan Halide_Static)

# External Halide override library
add_library(external_halide_override
external_halide_override.cpp
external_halide_override.h)
target_link_libraries(external_halide_override
PRIVATE
convert_aot
Halide_Static
Halide_GenGen
Vulkan::Vulkan)

# Vulkan app library
add_library(vulkan_app
vulkan_app.cpp
vulkan_app.h)
target_include_directories(vulkan_app PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${HALIDE_INSTALL_DIR}/share/tools)
add_dependencies(vulkan_app convert_aot_files) # Ensure generated header exists
target_link_libraries(vulkan_app
PRIVATE
Halide_Static
Vulkan::Vulkan
external_halide_override)

# Simple test executable to verify image loading
add_executable(demo_main demo_main.cpp)
target_include_directories(demo_main PRIVATE ${HALIDE_INSTALL_DIR}/share/tools)
target_link_libraries(demo_main
PRIVATE
vulkan_app
external_halide_override
convert_aot
Halide_Static
Halide_GenGen
Vulkan::Vulkan)

# Did not try the following, only used MacOS, on Windows we will prob fail as we override WEAK
if (NOT WIN32)
target_link_libraries(external_halide_override PRIVATE dl pthread)
target_link_libraries(vulkan_app PRIVATE dl pthread)
target_link_libraries(demo_main PRIVATE dl pthread)
endif ()
36 changes: 36 additions & 0 deletions apps/external_vk_demo/convert_generator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "Halide.h"

using namespace Halide;

// RGB to Grayscale conversion generator for AOT compilation
class ConvertGenerator : public Halide::Generator<ConvertGenerator> {
public:
Input<Buffer<uint8_t, 3>> input{"input"};
Output<Buffer<uint8_t, 2>> output{"output"};

void generate() {
Var x("x"), y("y");
input.dim(0).set_stride(3);
input.dim(2).set_stride(1);
input.dim(2).set_bounds(0, 3);

// RGB to Grayscale conversion using standard luminance formula
output(x, y) = cast<uint8_t>(
0.299f * cast<float>(input(x, y, 0)) + // Red
0.587f * cast<float>(input(x, y, 1)) + // Green
0.114f * cast<float>(input(x, y, 2)) // Blue
);
// Schedule for target
// dumb scheduling
if (get_target().has_feature(Target::Vulkan)) {
// GPU scheduling for Vulkan
Var xi("xi"), yi("yi");
output.gpu_tile(x, y, xi, yi, 16, 16);
} else {
// CPU scheduling
output.vectorize(x, 8);
}
}
};

HALIDE_REGISTER_GENERATOR(ConvertGenerator, convert_generator)
83 changes: 83 additions & 0 deletions apps/external_vk_demo/demo_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include "vulkan_app.h"

#include <iostream>

int main() {
std::cout << "External Vulkan Demo\n";

// Test synthetic image generation first
auto img = loadTestImage();

std::cout << "Image loaded successfully!\n";
std::cout << "Dimensions: " << img.width() << "x" << img.height() << "x"
<< img.channels() << std::endl;

// Check some pixel values to verify the image is properly loaded
std::cout << "Sample pixel values at (10,10): ";
std::cout << "R=" << static_cast<int>(img(10, 10, 0)) << " ";
std::cout << "G=" << static_cast<int>(img(10, 10, 1)) << " ";
std::cout << "B=" << static_cast<int>(img(10, 10, 2)) << std::endl;

// Try to initialize Vulkan context
std::cout << "\nTesting Vulkan context initialization...\n";
if (!initializeVulkanContext()) {
std::cout << "Vulkan not available - skipping VkBuffer allocation test\n";
std::cout << "Image loading test passed!\n";
return 0;
}

// Test VkBuffer allocation
std::cout << "Testing VkBuffer allocation...\n";
if (!allocateVkBuffersForImage(img)) {
std::cerr << "Failed to allocate VkBuffers\n";
cleanupVulkan();
return -1;
}

// Test VkBuffer wrapping with Halide
std::cout << "\nTesting VkBuffer wrapping with Halide...\n";
auto vk_input = wrapVkBufferInput(img);
if (!vk_input.raw_buffer() || !vk_input.raw_buffer()->device_interface) {
std::cerr << "Failed to wrap input VkBuffer\n";
cleanupVulkan();
return -1;
}

auto vk_output = wrapVkBufferOutput(img);
if (!vk_output.raw_buffer() || !vk_output.raw_buffer()->device_interface) {
std::cerr << "Failed to wrap output VkBuffer\n";
cleanupVulkan();
return -1;
}

std::cout << "Successfully created wrapped Halide buffers:\n";
std::cout << " Input: " << vk_input.width() << "x" << vk_input.height()
<< "x" << vk_input.channels() << "\n";
std::cout << " Output: " << vk_output.width() << "x" << vk_output.height()
<< "\n";

// Copy host image data to wrapped VkBuffer input
std::cout << "\nCopying host image data to VkBuffer...\n";
if (!copyHostDataToVkBuffer(img, vk_input)) {
std::cerr << "Failed to copy host data to VkBuffer\n";
cleanupVulkan();
return -1;
}
std::cout << "Successfully copied host image data to VkBuffer!\n";

// Execute RGB to grayscale conversion using wrapped buffers
std::cout << "\nExecuting RGB to grayscale conversion...\n";
if (!executeConversionWithWrappedBuffers(vk_input, vk_output)) {
std::cerr << "Failed to execute conversion with wrapped buffers\n";
cleanupVulkan();
return -1;
}
std::cout << "Successfully executed RGB to grayscale conversion!\n";

std::cout << "\nAll tests passed!\n";

// Cleanup
cleanupVulkan();
std::cout << "Cleaned up Vulkan resources\n";
return 0;
}
Loading