Инструменты: preprocess_cl и convert_cl_structs
Ниже описаны два вспомогательных CLI-инструмента, которые упрощают работу с OpenCL-кодом и его интеграцию в C#:
preprocess_cl: препроцессор OpenCL-файлов (разворачивает#include, применяет#define, нормализует код для компиляции)convert_cl_structs: конвертер структур из OpenCL C в эквивалентные C#-структуры для безопасного маршаллинга
Оба инструмента предназначены для использования на этапе подготовки исходников к сборке и помогают добиться стабильной компиляции/совместимости на разных драйверах.
preprocess_cl
Инструмент выполняет предварительную обработку .cl файлов:
- Разворачивает
#include(с учётом каталогов поиска) - Применяет
#define/условную компиляцию (#if,#ifdef,#ifndef) - Удаляет комментарии и/или нормализует формат (опционально)
- Генерирует единый «плоский» файл для передачи компилятору OpenCL
Частые сценарии
Сборка ядра из нескольких файлов:
preprocess_cl -I kernels/include -I shared -D USE_FAST_MATH=1 -o build/add.pre.cl kernels/add.cl
Тестирование разных конфигураций:
# Включаем поддержку double
preprocess_cl -D ENABLE_DOUBLE=1 -o build/compute.pre.cl kernels/compute.cl
# Сборка под CL2.0 (можно вместе с ProgramBuildOptions.SetStandard("CL2.0"))
preprocess_cl -D CL_STD=200 -I kernels -o build/main.pre.cl kernels/main.cl
Ключевые параметры (типичные)
-I <path>: каталог для поиска#include-D NAME=VALUEили-D NAME: определить макрос(ы)-o <file>: путь к выходному «плоскому» файлу--strip-comments: убрать комментарии--keep-line-directives: сохранять#line(если нужно для диагностики)
Рекомендации
- Храните общие хедеры в
kernels/include, чтобы упорядочить-I - Все платформо-специфичные секции оборачивайте в макросы (
#ifdef AMD,#ifdef NVIDIAи т.п.) - Генерируйте «плоский» файл рядом с артефактами сборки (например,
build/*.pre.cl) - Стабилизируйте набор
-Dмакросов черезProgramBuildOptions, чтобы поведение было одинаковым
convert_cl_structs
Инструмент анализирует объявления структур/типов в OpenCL C и генерирует эквивалентные C#-структуры с корректной раскладкой памяти:
- Создаёт
structс[StructLayout(LayoutKind.Sequential, Pack = N)] - Конвертирует поля примитивных типов (например,
int,uint,float,double) - Корректно обрабатывает векторные типы (
float2/3/4) как составные поля - Сохраняет выравнивание и размер для совместимости с OpenCL-кернелами
Конфигурация (YAML/JSON)
Инструмент поддерживает YAML/JSON конфиг и CLI-переопределения.
Пример cl_structs.yml:
input: kernels/types.cl
output:
folder: IgdrasilCompute/Generated
single_file: true
file_name: ClTypes.cs
namespace: IgdrasilEngine.Engine.Runtime.GPU
pack: 4
unsafe_vectors: false
mappings:
size_t: ulong
bool: int
float4:
cs: FVector4
import: IgdrasilEngine.Engine.Math.Vectors
Ключи:
input: путь к.clфайлуoutput.folder: папка для генерацииoutput.single_file: генерировать один файл, а не по файлу на структуруoutput.file_name: имя файла приsingle_filenamespace: пространство имён для C# типовpack: значениеPackв[StructLayout]unsafe_vectors: разворачивать векторы в примитивные компоненты (x/y/z/w)mappings: пользовательские соответствия типов (var_type: cs_typeили объект{ cs, import })
Запуск
С конфигом:
python IgdrasilCompute/Tools/convert_cl_structs.py --config cl_structs.yml
Переопределения через CLI:
python IgdrasilCompute/Tools/convert_cl_structs.py --config cl_structs.yml --pack 8 --unsafe-vectors
Без конфигурации (легаси-режим, совместимость):
python IgdrasilCompute/Tools/convert_cl_structs.py kernels/types.cl IgdrasilCompute/Generated IgdrasilEngine.Engine.Runtime.GPU
Вывод
single_file = true: создаётся один файл, напримерClTypes.cs- иначе: создаётся по одному
.csна каждую структуру (<StructName>.cs)
Парсер OpenCL структур
Поддерживаются объявления:
struct Name { ... };typedef struct { ... } Name;
Поля: массивы и векторы
- Массивы
type name[N]→[MarshalAs(UnmanagedType.ByValArray, SizeConst = N)] public type[] name; - Векторы по умолчанию мапятся на типы движка (например,
FVector4) - При
unsafe_vectors = true— разворачиваются в компоненты:name_x,name_y,name_z,name_w
Соответствия типов (дефолтные правила)
char→byteshort→shortushort→ushortint→intuint→uintlong→longulong→ulongfloat→floatdouble→doublefloat2/3/4,double2/3/4,int2/3/4,long2/3/4→ специализированные типы движка (например,FVector2/3/4) или компоненты приunsafe_vectors
Соответствия типов (правила)
char→byteshort→shortushort→ushortint→intuint→uintlong→long(илиlong64-бит; убедитесь, что целевая платформа соответствует)ulong→ulongfloat→floatdouble→doublefloat2/3/4→ составные поля (float x; float y; ...) или специализированные типы (при--unsafe-vectors)
Рекомендации
- Избегайте
boolв структурах — используйтеint/uint - Явно задавайте
Pack, если структуры используются в разных компиляторах/драйверах - Для больших массивов внутри структур предпочтительнее отдельные буферы (
MemoryObject<T>), а не «встроенные» массивы — упрощает маршаллинг - После генерации проверьте размер структуры через
Marshal.SizeOf<T>()и сравните с ожидаемым размером на стороне OpenCL
Интеграция с IgdrasilCompute
Типовой пайплайн
preprocess_cl → ProgramBuildOptions → Program.FromFile → CreateKernel → SetArgs → Run → Wait → Read
- Используйте
preprocess_cl, чтобы получить «плоский» файл, исключив проблемы с#includeи условной компиляцией - Сгенерируйте C#-структуры через
convert_cl_structs— это упростит инициализацию аргументов (SetArgument) и обеспечит совпадение раскладок - Храните артефакты (
*.pre.cl,*.csот конвертера) в подкаталогеGenerated/build
Пример связки
# 1) Препроцессинг
preprocess_cl -I kernels/include -D USE_FAST_MATH=1 -o build/main.pre.cl kernels/main.cl
# 2) Конвертация структур
convert_cl_structs --input kernels/types.cl --namespace IgdrasilEngine.Engine.Runtime.GPU --output IgdrasilCompute/Generated/ClTypes.cs
В C#:
// Загрузка «плоского» файла
using var program = Program.FromFile("build/main.pre.cl", new ProgramBuildOptions().EnableFastMath(true));
var kernel = program.CreateKernel("process");
// Использование сгенерированных типов
var param = new ClMyParams { iterations = 1024, scale = 3.14f };
kernel.SetArgument(0u, param);
Лучшие практики
- Зафиксируйте версии инструментов и опции в скрипте сборки (батник/PowerShell/MSBuild Target)
- Проверяйте
BuildLogпри любых ошибках компиляции — препроцессинг не заменяет диагностику компилятора - Автотест: сравнивайте
Marshal.SizeOf<T>()с ожидаемым размером из OpenCL для каждой сгенерированной структуры - Не смешивайте старые и новые версии «плоских» файлов — пересобирайте при изменении
#define/#include