Модули для работы с OpenCL и OpenGL

Отдельный репозиторий

Сейчас у паскаля есть только модуль OpenGL, и то, он ужасно кривой и в нём нет большинства функций, включая базовые (как те что для работы с текстурами).

Для начала я занялся OpenCL, раз для него вообще ничего нет.


Структура проекта:

Есть всего 4 модуля:

  • 2 базовых (OpenCL.pas и OpenGL.pas) - они только оборачивают неуправляемый код, не добавляя ничего своего.

  • 2 высокоуровневые обёртки (OpenCLABC.pas и OpenGLABC.pas) - они имеют много ограничений из за высокоуровневости, но позволяют сильно сократить код. По сути, они относятся к OpenCL.pas и OpenGL.pas, как WPFObjects к GraphWPF.


В данный момент OpenGL.pas, OpenCLABC.pas и OpenGLABC.pas недоделаны (последний - не начат).

Названия последних 2 я думаю поменять (только не знаю на что, есть идеи - предлагайте).

И так же другие претензии и идеи - излагайте в этой теме.

OpenCLABC работает!
(если не считать вот этого всего ужаса)


Для сравнения, вот самый простой пример, на OpenCL:

uses OpenCL;
uses System;
uses System.Runtime.InteropServices;

//Описания всех подпрограмм найдёте в справке по OpenCL:
//www.khronos.org/registry/OpenCL/specs/2.2/html/OpenCL_API.html

begin
  var ec: ErrorCode;
  
  // Инициализация
  
  var platform: cl_platform_id;
  cl.GetPlatformIDs(1, @platform, nil).RaiseIfError;
  
  var device: cl_device_id;
  cl.GetDeviceIDs(platform, DeviceTypeFlags.Default, 1, @device, nil).RaiseIfError;
  
  var context := cl.CreateContext(nil, 1, @device, nil, nil, @ec);
  ec.RaiseIfError;
  
  var command_queue := cl.CreateCommandQueue(context, device, CommandQueuePropertyFlags.NONE, ec);
  ec.RaiseIfError;
  
  // Чтение и компиляция .cl файла
  
  {$resource SimpleAddition.cl} // эта строчка засовывает SimpleAddition.cl внутрь .exe, чтоб не надо было таскать его вместе с .exe
  var prog_str := System.IO.StreamReader.Create(GetResourceStream('SimpleAddition.cl')).ReadToEnd;
  var prog := cl.CreateProgramWithSource(
    context,
    1,
    new string[](prog_str),
    new UIntPtr[](new UIntPtr(prog_str.Length)),
    ec
  );
  ec.RaiseIfError;
  
  cl.BuildProgram(prog, 1, @device, nil, nil, nil).RaiseIfError;
  
  // Подготовка и запуск программы на GPU
  
  var kernel := cl.CreateKernel(prog, 'TEST', ec); // Обязательно то же имя что у карнела из .cl файла. И регистр важен!
  ec.RaiseIfError;
  
  var mem := Marshal.AllocHGlobal(40);
  Marshal.Copy(ArrFill(10,1),0,mem,10);
  var memobj := cl.CreateBuffer(context, MemoryFlags.READ_WRITE or MemoryFlags.USE_HOST_PTR, new UIntPtr(40), mem, ec); // USE_HOST_PTR значит что нужно скопировать память из mem в memobj
  ec.RaiseIfError;
  
  cl.SetKernelArg(kernel, 0, new UIntPtr(UIntPtr.Size), memobj).RaiseIfError;
  
  cl.EnqueueNDRangeKernel(command_queue, kernel, 1, nil,new UIntPtr[](new UIntPtr(10)),nil, 0,nil,nil).RaiseIfError;
  
  cl.Finish(command_queue).RaiseIfError;
  
  // Чтение и вывод результата
  
  cl.EnqueueReadBuffer(command_queue, memobj, 1, new UIntPtr(0), new UIntPtr(40), pointer(mem), 0,nil,nil).RaiseIfError;
  
  var res := new integer[10];
  Marshal.Copy(mem,res,0,10);
  res.Println;
  
end.

И он же, на OpenCLABC:

uses OpenCLABC;

//ToDo issue компилятора:
// - #1981

begin
  
  // Чтение и компиляция .cl файла
  
  {$resource SimpleAddition.cl} // эта строчка засовывает SimpleAddition.cl внутрь .exe, чтоб не надо было таскать его вместе с .exe
  var prog := new ProgramCode(Context.Default,
    System.IO.StreamReader.Create(GetResourceStream('SimpleAddition.cl')).ReadToEnd
  );
  
  // Подготовка параметров
  
  var A := new KernelArg(40);
  
  // Подготовка очередей выполнения
  
  var prog_Q :=
    prog['TEST'].NewQueue.Exec(10,
      
      A.NewQueue.WriteData(
        ArrFill(10,1)
      ) as CommandQueue<KernelArg>
      
    ) as CommandQueue<Kernel>;
  
  // Выполнение
  
  Context.Default.SyncInvoke(prog_Q);
  
  // Чтение и вывод результата
  
  A.GetArray&<array of integer>(10).Println;
  
end.

Вроде, примеров должно быть достаточно, чтоб понять как работать с OpenCLABC. Если это не так - спрашивайте, не стесняйтесь. Если надо - я добавлю примеров/комментариев.


Из особо интересных фич:

  Context.Default.SyncInvoke(
    
    Calc_C_Q +
    (
      Otp_C_Q *
      (
        Calc_V2_Q +
        Otp_V2_Q
      )
    )
    
  );

(из примера умножения матриц)

Эту фичу я стыбзил из Graph3D.
Сложение очередей - даёт последовательное выполнение. А умножение - паралельное.

После того как посчиталась матрица C - можно сразу и выводить её, и считать вектор V2, поэтому там умножение.


Сейчас, в OpenCLABC реализовано только базовые возможности, я хочу так же добавить тонну сахара, чтоб уж точно горько небыло. У кого есть идеи - пишите!

2 лайка

У меня просьба. Поместите примеры в папку OpenCL. Уберите вот это задвоение [CL,GL].

То есть как? Сделать в корне папку OpenCL и в неё засунуть папки OpenCL и OpenCLABC? Или как?
И - потом так же с OpenGL?

P.S. так и сделал в общем…

Добавил большинство сахара который планировал с самого начала. Теперь, тот пример выше стал ещё короче и проще:

uses OpenCLABC;

//ToDo issue компилятора:
// - #1981

begin
  
  // Чтение и компиляция .cl файла
  
  {$resource SimpleAddition.cl} // эта строчка засовывает SimpleAddition.cl внутрь .exe, чтоб не надо было таскать его вместе с .exe
  var prog := new ProgramCode(Context.Default,
    System.IO.StreamReader.Create(GetResourceStream('SimpleAddition.cl')).ReadToEnd
  );
  
  // Подготовка параметров
  
  var A := new KernelArg(40);
  
  // Выполнение
  
  prog['TEST'].Exec(10,
    
    A.NewQueue.WriteData(
      ArrFill(10,1)
    ) as CommandQueue<KernelArg>
    
  );
  
  // Чтение и вывод результата
  
  A.GetArray&<array of integer>(10).Println;
  
end.

То есть, теперь для простых, синхронных действий - не надо создавать очереди (хотя их всё равно создаст внутри, ибо сам OpenCL синхронно не работает).

Прошу переработать модуль OpenGLABC - он дестабилизирует инсталлят. Возможность описывать ограничения вида T: Array будет закрыта, поскольку она отсутствует в C# и трудно или очень трудно реализуется корректно.

Несмотря на то, что основное обсуждение (скорее срач) уже прошло на гитхабе и нисколько не пошатнуло мнение разработчиков, мне всё же хочется вставить свои 5 копеек.

Ручаться за то, что разрешение System.Array за собой не может повлечь никаких серьёзных последствий я не буду. Но эта конструкция была 15 лет разрешена и проблем не возникало (хотя мало кто серьёзно это всё тестировал). За исключением конкретно этой issue. И у меня вопрос: В чём смысл запрещать?

Разумеется, мы все знаем то, что язык ориентируется на промышленные языки, включая и С#. Здесь я полностью согласен с мнением SunSerega — в C# полно бессмысленных запретов, скорее всего имеющих шаблонные отговорки вроде “поскольку это сложно реализовать и мало где это используется, то и делать мы этого не будем”. Я, столкнувшись с рядом проблем C# отказался переписывать на него свой проект на PascalABC.NET. Так вот, к чему я это. Равняться на C# во всём — затея неплохая, но с оговоркой — Ничто не идеально. Независимо от статуса. Сколько бы разработчиков не трудилось в Microsoft.

Это, знаете, похоже на одну реальную ситуацию в мире. Ведь в России все хотят жить как в Европе, Японии или США. И мы пытаемся копировать всё с них. Ведь они-то уж наверняка знают, как нормально построить экономику, не зря же у этих государств такие высокие показатели ВВП, они точно знают что и как правильно делать. И никто не задумывается о том, какие бредовые вещи они делают. К примеру, возьмём отказ от ядерной энергетики в Японии. Похоронить будущее своего государства, когда самой дешёвой и чистой электроэнергией является атом, а уголь, газ и нефть стремительно кончаются. Из килограмма урана можно получить энергии столько же, сколько из 100 тонн высокосортного угля или 50 тонн нефти. И без влияния на окружающую среду. А они решили, как п(р)одвинутая Европа, перейти на псевдочистые (производства фотоэлементов и аккумуляторов весьма грязны) источники электроэнергии, такие как ветер и солнце. И почти никого не заботит огромная финансовая невыгодность всей этой бредятины. Платить в сотню раз больше за электричество? Сдувать каждый день пыль с солнечных панелей? Молиться ветру? Кто-нибудь хочет, чтобы также было и в России? Лично я — нет.

Очень отвлечённый пример. Но, возможно, также может выйти и с запретом Array в where. Налицо просто плохая реализация фичи в C#. И да, если вам уж настолько не хватает времени заняться починкой данной issue, то этим может заняться @Sun_Serega. В конце концов, сейчас ему эта фича и нужна.

2 лайка

Я уже как раз искал причину того бага. Лишний box добавляется в вызове на строке 4946 в NetGenerator.cs:

value.obj.visit(this); // obj это IExpressionNode

Eсли его убрать - box пропадает. Но, разумеется, не всё так просто, в месте с ним - пропадает и ldloca.s, то есть загрузка адреса переменной, что уже убирать нельзя. А значит - надо смотреть внутрь этого вызова, и поставить какое то условие перед .Emit(OpCodes.Box, ...)

Но - это виртуальный вызов, ещё и из другой библиотеки… Поэтому нужна отладка. Тупая пересборка всего компилятора + метод тыка - тут уже не прокатят.

Если вам на столько не хватает времени - я готов взять всё связанное с where на себя. Но для этого - я уже много раз просил, посвятите меня в использования отладчика в вашем проекте! Или скажите как вы сами ищите баги.

1 лайк

Когда возникают проблемы, связанные с кем-то написанной сторонней библиотекой - это неприятность, касающаяся того или иного количества пользователей этой библиотеки. И разработчики могут позволить себе в той или иной степени дистанцироваться от такой проблемы. Но когда сторонние разработчики забираются в код ядра проекта, это может преподнести неприятный сюрприз всем пользователям. Причем, во внешне вроде бы не имеющем отношение к внесенным изменениям месте. Сейчас за баги отвечает руководитель проекта, хорошо знающий свою команду и уверенный в ней. Сможет ли он настолько быть уверенным в сторонних разработчиках, допуская их в “святую святых”?

Не сторонней. Компилятор состоит из нескольких библиотек, и вот на той строчке 1 из библиотек вызывает метод интерфейса, который объявлен в другой, а реализуется вообще в третьей библиотеке.

Разработчики всегда могут быть уверены в автоматических тестах, без проведения которых я не залью пулл.

Копировать Запад не нужно,ибо это и есть мировой срач. Но и делать свой срач тоже не нужно. Срач срачем не выбивают. Русские долго запрягают, поэтому не нужно суеты. Спокойствие, только спокойствие. Если не помогает, балуйтесь плюшками (мягкий вариант).

Тем временем, спустя 101 костыль - я кое как запустил отладку. И - вот оно. Вот это тот самый лишний box:

Это точно он, ибо в той подпрограмме больше нет box-ов, и я удостоверился что точка останова попадется только 1 раз:

Сейчас разберусь надо ли там условие или он вообще лишний…

1 лайк
1 лайк

@Admin, пожалуйста, перенесите всё, начиная с этого сообщения - сюда, т.к. это оффтоп.

Кстати, в итоге оказалось что where T: Array было вообще не при чём. Этот код вообще падал:

type
  t0 = abstract class
    function f0: integer; abstract;
  end;
  t1 = class(t0)
    i: integer;
    function f0: integer; override := i;
    constructor(i: integer) := self.i := i;
  end;

function f1<T>: T;
where T: t0;
begin
  
  Result := T(t0(
    new t1(10)
  ));
  
  Result.f0.Println;
  
end;

begin
  
  f1&<t1>;
  
//  readln;
end.

Проблема была та же, и в моём пуле она тоже исправилась.

1 лайк

Да, код падает. Найдёте минимальный падающий код?

  1. Это и есть минимальный падающий.
  2. Я уже исправил это тут:

Я же говорю, это и есть на самом деле минимальный код к той issue в которой вы решили убрать where T: Array. И where T: Array к этой проблеме вообще не имеет никакого отношения.

И, ещё раз, если оно отмоталось куда то:
Я готов взятся за все issue связанные с where (а может и некоторые другие), если это поможет разгрузить вас так чтоб вы не делали запретов от нехватки времени на исправление.

Поэтому пожалуйста, не запрещайте where T: Array. Оно действительно полезно, а если оно что то сломает - я сам исправлю.

1 лайк

А, да, увидел pull request. Мы посоветуемся с ibond.

Если дело было не в ограничении where T: Array, то запрещать нечего.

Два дня поиска информации в Интернете - откуда такое ограничение в C# - к успеху меня не привели. Не вижу в нем ничего сильно криминального.

2 лайка
1 лайк

Видимо, надо заменить модуль OpenGL в комплекте Паскаля