//*****************************************************************************************************\\ // Copyright (©) Cergey Latchenko ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega ) // This code is distributed under the Unlicense // For details see LICENSE file or this: // https://github.com/SunSerega/POCGL/blob/master/LICENSE //*****************************************************************************************************\\ // Copyright (©) Сергей Латченко ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega ) // Этот код распространяется с лицензией Unlicense // Подробнее в файле LICENSE или тут: // https://github.com/SunSerega/POCGL/blob/master/LICENSE //*****************************************************************************************************\\ /// ///Высокоуровневая оболочка модуля OpenCL /// OpenCL и OpenCLABC можно использовать одновременно /// Но контактировать они практически не будут /// ///Если не хватает типа/метода или найдена ошибка - писать сюда: /// https://github.com/SunSerega/POCGL/issues /// ///Справка данного модуля находится в начале его исходника /// Исходники можно открыть Ctrl-кликом на любом имени из модуля /// unit OpenCLABC; {$region Подробное описание OpenCLABC} {$region 1. Основные принципы} {$region 1.0. Разное} // 1.0.1 // // Для большего удобства чтения справки рекомендуется включить сворачивание кода // ( Сервис —> Настройки —> Редактор —> Разрешить сворачивание кода ) // Когда эта опция включена - регионы можно сворачивать нажав на [-] слева // А в контекстном меню (по нажатии ПКМ) можно свернуть все регионы сразу // // 1.0.2 // // В этой справке будут упоминания примеров // Их можно найти в папке "C:\PABCWork.NET\Samples\OpenCL\OpenCLABC\Из справки" // А также в соответствующей папке репозитория на гитхабе: "https://github.com/SunSerega/POCGL/tree/master/Samples/OpenCL/OpenCLABC/Из справки" // // 1.0.3 —— Термины, которые часто путают новички // // CPU — Центральное Процессорное Устройство (процессор) // GPU — Графическое Процессорное Устройство (видеокарта) // // Команда — запрос на выполнение чего-либо. К примеру: // — Запрос на запуск программы на GPU // — Запрос на начало чтения данных из памяти GPU в оперативную память // // !! Называть процедуры и функции командами ошибочно !! // // Подпрограмма — процедура или функция // // Метод — особая подпрограмма, вызываемая через экземпляр // К примеру, метод Context.SyncInvoke выглядит в коде как "cont.SyncInvoke(...)", где cont — переменная типа Context // // Статический метод - особая подпрограмма, вызываемая через тип // К примеру, статический метод Buffer.ValueQueue выглядит в коде как "Buffer.ValueQueue(...)" // // Остальные непонятные термины ищите в справке PascalABC.NET (F1 —> Справка) или в интернете {$endregion 1.0. Разное} {$region 1.1 —— Что такое OpenCLABC} // // OpenCLABC — высокоуровневая оболочка модуля OpenCL // Это значит, что с OpenCLABC можно писать гораздо меньше кода в больших и сложных программах // Однако, такой же уровень микроконтроля как с модулем OpenCL недоступен // Например, напрямую управлять cl_event'ами в OpenCLABC невозможно // Вместо этого надо использовать операции с очередями (как сложение и умножение очередей) // {$endregion 1.1 —— Что такое OpenCLABC} {$region 1.2 —— Контекст (Context)} // // Для отправки команд в GPU необходим контекст (объект типа Context) // Он содержит информацию о том, какое устройство будет использоваться при выполнении программ и хранении содержимого буферов // {$endregion 1.2 —— Контекст (Context)} {$region 1.3 —— Очередь [команд] (CommandQueue)} // // Передавать команды для GPU по одной не эффективно // Гораздо эффективнее передавать несколько команд сразу // Для этого существуют очереди (типы, наследующие от CommandQueue) // Они хранят произвольное количество команд для GPU // А при необходимости также и части кода, выполняемые на CPU // {$endregion 1.3 —— Очередь [команд] (CommandQueue)} {$region 1.4 —— Буфер (Buffer)} // // Программы на GPU не могут использовать оперативную память (без определённых расширений) // Из-за чего для передачи данных в программу и чтения результата нужно выделять память на самом GPU // // Данные об области памяти, выделенной на GPU // И все возможные операции с этой памятью // Доступны через переменные типа Buffer // {$endregion 1.4 —— Буфер (Buffer)} {$region 1.5 —— Контейнер для кода (ProgramCode)} // // Обычные программы невозможно запустить на GPU // Специальные программы для GPU, запускаемые через OpenCL, обычно пишутся на особом языке "OpenCL C" (основанном на языке "C") // Описание "OpenCL C" не входит в данную справку // Одну из последних версий его спецификации можно найти тут: // https://www.khronos.org/registry/OpenCL/specs/2.2/pdf/ // // Код на языке "OpenCL C" хранится в объектах типа ProgramCode // Объекты этого типа используются только как контейнеры // Один объект ProgramCode может содержать любое количествово подпрограмм-кёрнелов // {$endregion 1.5 —— Контейнер для кода (ProgramCode)} {$region 1.6 —— Кёрнел (Kernel)} // // Объект типа Kernel представляет одну подпрограмму-кёрнел // Он хранит код, который можно выполнить на GPU // {$endregion 1.6 —— Кёрнел (Kernel)} {$endregion 1. Основные принципы} {$region 2. Контекст (Context)} // // Создать контекст можно конструктором ("new Context") // Контекст можно и не создавать, используя всюду свойство Context.Default // Изначально этому свойству присваивается контекст, использующий один любой GPU, если таковой есть // или один любой другой девайс, поддерживающий OpenCL, если GPU отсутствует // // Context.Default можно перезаписывать // Это удобно если во всей программе использовать общий контекст // Операции, у которых невозможно указать контекст - всегда используют Context.Default // // Для вызова команд в определённом контексте используется метод Context.BeginInvoke // Он возвращает объект типа Task, через который можно наблюдать за выполнением очереди и ожидать его окончания // Также есть метод Context.SyncInvoke, вызывающий .BeginInvoke и затем метод Task.Wait на полученом объекте // Подробнее в разделе 3.2 // {$endregion 2. Контекст (Context)} {$region 3. Очередь [команд] (CommandQueue)} {$region 3.0 —— Возвращаемое значение очередей} // // У каждого типа-очереди есть свой тип возвращаемого значение // К примеру, так объявляется переменная в которую можно будет сохранить очередь, возвращающую integer: // var Q1: CommandQueue; // // Очереди, созданные из буфера или кёрнела возващают свой буфер/кёрнел соответственно, из которого были созданы // Очереди, созданные с HFQ - значение, которое возвращала оригинальная функция // Очереди, созданные с HPQ - значение типа object (и всегда nil) // Подробнее в примере: "3 – Очередь\3.0\Примеры возвращаемого значения" // // После выполнения очереди метод Context.SyncInvoke возвращает то, что вернула очередь // Если использовать метод Context.BeginInvoke, то возвращаемое значение можно получить через свойство Task.Result // {$endregion 3.0 —— Возвращаемое значение очередей} {$region 3.1. —— Создание очередей} // 3.1.1 —— Создание очередей с командами для GPU // // Самый просто способ создать очередь —— выбрать объект (Kernel или Buffer) // у которого есть что-то, что можно выполнять на GPU (выполнение кёрнела или запись/чтение содержимого буфера) // и вызвать для него метод .NewQueue // Подробнее в примере "3 – Очередь\3.1\Создание очереди из буфера.pas" // // Полученная очередь будет иметь особый тип: KernelCommandQueue/BufferCommandQueue для кёрнела/буфера соответственно // К такой очереди можно добавлять команды, вызывая её методы, имена которых начинаются с ".Add..." // // 3.1.2 —— Создание очередей из подпрограммы, написанной для CPU // // Иногда между командами для GPU надо вставить выполнение обычного кода на CPU // И в большинстве таких случаев придётся разрывать очередь на две части, что плохо // (Одна целая очередь всегда выполнится быстрее двух её частей) // // Для таких случаев существуют глобальные подпрограммы HFQ и HPQ // HFQ — Host Function Queue // HPQ — Host Procedure Queue // Они возвращают очередь, выполняющую код (функцию/процедуру соотвественно) на CPU // // 3.1.3 —— Объединение очередей // // Если сложить две очереди A и B ("var C := A+B") — получится очередь C, в которой сначала выполнится A, а затем B // Очередь C будет считаться выполненной тогда, когда выполнится очередь B // Очередь C будет возвращать то, что вернула очередь B // // Если умножить две очереди A и B ("var C := A*B") — получится очередь C, в которой одновременно начнут выполняться A и B // Очередь C будет считаться выполненной тогда, когда обе очереди (A и B) выполнятся // Очередь C будет возвращать то, что вернула очередь B // // Подробнее в примере "3 – Очередь\3.1\Сложение vs умножение очередей.pas" // // Как и в математике, умножение имеет бОльший приоритет чем сложение // // // Операторы += и *= также применимы к очередям // И как и для чисел - "A += B" работает как "A := A+B" (и так же с *=) // Это значит, что возвращаемые типы очередей A и B должны быть одинаковыми, чтобы к ним можно было применить +=/*= // // // Если нужно сложить много очередей, лучше применять CombineSyncQueue // Если нужно умножить много очередей — CombineAsyncQueue // Эти подпрограммы работают немного быстрее чем сложение и умножение, если вы объединяете больше двух очередей // // Кроме того, CombineSyncQueue и CombineAsyncQueue могут принимать ещё 1 параметр перед очередями // Этот параметр позволяет указать функцию преобразования, которая использует результаты всех входных очередей // Подробнее в примере "3 – Очередь\3.1\Использование результатов всех очередей.pas" // // 3.1.4 —— CommandQueue.ThenConvert (Прикрепление очередей) // // Результат очереди бывает необходимо преобразовать перед дальнейшим использованием // Для этого используется метод .ThenConvert // "q.ThenConvert(func)" работает так же как "q + HFQ(func)", с 1 поправкой: // Функция, которую принимает .ThenConvert принимает результат предыдущей очереди в качестве параметра // Подробнее в примере "3 – Очередь\3.1\Преобразование результата очереди.pas" // // 3.1.5 —— CommandQueue.Cycle (Повторение очередей) // // ToDo // // 3.1.6 —— Неявное создание очередей (Передача по одной команде) // // Передавать команды по одной, когда их несколько — ужасно медленно // Но нередко бывает, что команда всего одна // Или для отладки нужно как-то по простому одноразово вызвать одну команду // // Для таких случаев можно создавать очереди неявно // Это можно сделать вызвав метод переменной типа Buffer/Kernel // // У каждого метода очереди, созданной с .NewQueue есть дублирующий метод в оригинальном объекте // Этот метод создаёт новую очередь, добавляет одну соответствующую команду и выполняет полученную очередь методом Context.SyncInvoke // Подробнее в примере "3 - Очередь\3.1\Код с очередью и без.pas" // // Кроме того, у типа Buffer есть дополнительные методы "Buffer.Get..." // Соответствующих методов у очередей — нет //ToDo возможно, в будущем появятся // Методы ".Get..." создают новый объект типа записи, массива или выделяют область неуправляемой памяти, // читают в полученный объект содержимое буфера и возвращают этот объект // Они также используют неявную очередь (для чтения буфера) // // 3.1.7 — Buffer.ValueQueue (Очередь из размерного значения, то есть записи) // // Статический метод Buffer.ValueQueue создаёт новый буфер // Затем создаёт из него новую очередь // И добавляет в полученную очередь команду записи размерного значения в эту очередь // // Это полезно, если вам, например, нужно передать одним из параметров в кёрнел размер передаваемого массива, так как массивы в C не хранят свой размер // Но этим методом не стоит злоупотреблять, потому что каждый вызов создаёт новый буфер // // Если вы запускаете кёрнел, в который нужно передавать число одним из параметров, лучше создайте буфер один раз // и записывайте в него значение перед каждым вызовом кёрнела, используя .NewQueue.WriteValue // // Если же вам нужна какая-то константа, то можно один раз вызвать Buffer.ValueQueue и далее использовать эту переменную (но не добавлять в неё команды) // {$endregion 3.1. —— Создание очередей} {$region 3.2 —— Выполнение очередей} // // Самый простой способ выполнить очередь — через метод Context.SyncInvoke // Он выполняет очередь и после того, как она завершилась, возвращает то, что вернула эта очередь // // Но Context.SyncInvoke в свою очередь работает через метод Context.BeginInvoke // метод Context.BeginInvoke начинает выполнение очереди и возвращает объект типа Task // // У Task, так же как и у очереди — в <> указывается возвращаемое значение // Через объект типа Task можно: // — Следить за выполнением, через различные свойства типа Task // — Ожидать пока выполнение не закончится методом Task.Wait // — Получать возвращаемое значение после завершения выполнения, свойством Task.Result // // Если при выполнении очереди возникает ошибка, о ней выведет не полную информацию // Чтобы получить достаточно информации для того, чтобы понять что за ошибка возникла, используйте следующую конструкцию: // <-------------------------> // try // // // ваш код, вызывающий ошибку // // except // on e: Exception do writeln(e); // writeln выводит все внутренние исключения, поэтому в нём видно что произошло на самом деле // end; // <-------------------------> // Для данного кода есть стандартный снипет // Чтобы активировать его - напишите "tryo" и нажмите Shift+Space // {$endregion 3.2 —— Выполнение очередей} {$region 3.3 —— Очереди как параметры} // // Почти все параметры всех методов, создающих очереди (в том числе и неявные) // Могут принимать вместо любого параметра очередь // (анализатор кода об этом не говорит, чтобы не было слишком много перегрузок каждого метода) // Но в таком случае, передаваемая очередь должна возвращать то, что принимает параметр // Подробнее в примере "3 – Очередь\3.3\Использование очереди как парамметра.pas" // // Очереди, переданные параметрами, выполняются в непредсказуемом порядке, но подчиняются следующим правилам: // 1. Все очереди-параметры начинают выполняться прямо при вызове Context.BeginInvoke // 2. Все очереди-параметры команды A выполнятся до того как начнёт выполняться сама команда A // // Также, никто не запрещает передавать очередь-параметр в метод, // создающий команду, которая сама является частью другой очереди-параметра // В этом случае действуют все те же правила // {$endregion 3.3 —— Очереди как параметры} {$region 3.4. —— Множественное использование очереди} // 3.4.0 // // Одну и ту же очередь можно использовать несколько раз: // <-------------------------> // var Q1: CommandQueue<...>; // ... // Context.Default.SyncInvoke(Q1); // Context.Default.SyncInvoke(Q1); // <-------------------------> // // Однако, во время выполнения очередь хранит в себе данные о своём состоянии выполнения и результат, когда он уже вычислен // Это значит, что 1 объект очереди нельзя выполнять в 2 местах параллельно // Иначе данные о состоянии и результате перемешаются // То есть, такой код: // <-------------------------> // var Q1: CommandQueue<...>; // ... // Context.Default.SyncInvoke( Q1 * Q1 ); // <-------------------------> // Приведёт к неопределённом поведению // То есть, результат выполнения может быть неправильный, но не при каждом запуске // // Также, в некоторых случаях может быть вызвано исключение QueueDoubleInvokeException, помогающее узнать, где именно возник параллельный вызов // Но не стоит полагаться на вызов этого исключения // // Такую ошибку в коде может быть черезвычайно сложно распознать // Поэтому необходимо внимательно проверять, чтобы одна очередь не выполнялась нескольких местах одновременно // // Однако, если вам всё же надо использовать одну очередь в нескольких местах одновременно - есть 2 способа: // 3.4.1 — Клонирование очередей (.Clone) // // Методом CommandQueue.Clone можно создать полную копию очереди // При этом, если исходная очередь проводила какие то вычисления, // они будут произведены дважды, оригиналом и копией, при вызове обоих // // Клон очереди является полностью независимым объектом // Его можно вызывать не только параллельно с оригиналом // Оригинал и копию можно вызывать даже в двух разных Context.BeginInvoke одновременно // // 3.4.2 — Удлинители для очередей (.Multiusable) // // Вариант выполнять очередь несколько раз, как в случае с .Clone, нередко не подходит, // потому что это удваивает затраты производительности // И некоторые очереди (например, выполнения кёрнелов) могут дать разные результаты, если выполнить их лишний раз // // Если вам нужно использовать результат одной очереди многократно, лучше использовать метод CommandQueue.Multiusable // !! Созданные таким образом очереди НЕ является независимыми объектами !! // // .Multiusable работает словно провод-удлинитель, // если сравнивать возвращаемое значение очереди с розеткой // // Исходная очередь, для которой вызвали .Multiusable, подчиняется всё тем же правилам, что и очередь-параметр // То есть, она начинает выполняться во время вызова Context.BeginInvoke // И она всегда выполнится до того, как начнёт выполняться любая из очередей, которую вернул метод .Multiusable // // Очереди, которые вернул .Multiusable могут быть использованы параллельно: // <-------------------------> // var Q1: CommandQueue<...>; // ... // Qs1 := Q1.Multiusable(3); // создаёт массив из 3 очередей // Context.Default.SyncInvoke( Qs1[0] * Qs1[1] * Qs1[2].ThenConvert(o->...) ); // <-------------------------> // Однако, все очереди, полученные из .Multiusable всё ещё связаны оригинальной очередью // А так как Context.BeginInvoke управляет переключением состояния выполнения очереди // Следующий код два раза запустит очередь Q1, и после этого два раза её завершит: // <-------------------------> // var Q1: CommandQueue<...>; // ... // Qs1 := Q1.Multiusable(2); // Context.Default.BeginInvoke( Qs1[0] ); // Context.Default.BeginInvoke( Qs1[1] ); // <-------------------------> // Из-за чего такой код тоже приведёт к неопределённом поведению // Но вероятность получить QueueDoubleInvokeException в данном случае - ещё меньше // Чтобы исправить такой код - надо объеденить вызовы метода Context.BeginInvoke // {$endregion 3.4. —— Множественное использование очереди} {$endregion 3. Очередь [команд] (CommandQueue)} {$region 4 —— Буфер (Buffer)} // // Буфер создаётся через конструктор ("new Buffer(...)") // Однако если не передать в конструктор контекст, // память на GPU выделяется только при вызове метода Buffer.Init // Исходя из контекста, переданного Buffer.Init, выбирается на каком устройстве будет выделена память // // При выделения памяти содержимое буфера НЕ очищается нулями, а значит содержит мусорные данные // Повторный вызов Buffer.Init перевыделяет память // // Если Buffer.Init НЕ был вызван до первой операции чтения/записи буфера, он будет вызван автоматически // В таком случае в качестве контекста для выделения памяти выбирается тот, для которого был вызван метод Context.BeginInvoke // // Буфер можно удалить методом Buffer.Dispose // Но этот метод только освобождает память на GPU // Если после .Dispose использовать буфер снова, память выделится заново // .Dispose также вызовется автоматически, когда в программе не остаётся ссылок на буфер // {$endregion 4 —— Буфер (Buffer)} {$region 5. Контейнер для кода (ProgramCode)} {$region 5.1. —— Создание ProgramCode} // 5.1.1 — Создание из исходного кода // // Конструктор ProgramCode("new ProgramCode(...)") принимает текст исходников программы на языке OpenCL-C // // Так же как исходники паскаля хранятся в .pas файлах // Исходники OpenCL-C кода обычно хранят в .cl файлах // Однако это не принципиально, потому что код не обязательно должен быть в файле, // Он может быть в любой текстовой форме (в том числе в строке в .pas программе) // Тем не менее, хранение исходников OpenCL-C кода в .cl-файлах упрощает жизнь, потому что тогда их легко найти // // 5.1.2 — Создание из бинарных файлов // // После создания объекта типа ProgramCode из исходников // Можно вызвать метод ProgramCode.SerializeTo, чтобы сохранить код в бинарном и прекомпилированном виде // Это обычно делается отдельной программой (не той же самой, которая будет использовать этот бинарный код) // // После этого основная программа может создать объект ProgramCode // Используя статический метод ProgramCode.DeserializeFrom // // Подробнее в примере "5 – ProgramCode\Прекомпиляция исходников OpenCL-C" // {$endregion 5.1. —— Создание ProgramCode} {$endregion 5. Контейнер для кода (ProgramCode)} {$region 6 —— Кёрнел (Kernel)} // // Кёрнел создаётся через индексное свойтсво ProgramCode: // code['KernelName'] // Где code имеет тип ProgramCode, а KernelName — имя подпрограммы-кёрнела в исходном коде (регистр важен!) // // Кёрнел вызывается методом KernelCommandQueue.Exec // См. пример "1.6 - Кёрнел\Вызов кёрнела.pas" // {$endregion 6 —— Кёрнел (Kernel)} {$endregion Подробное описание OpenCLABC} interface uses OpenCL; uses System; uses System.Threading.Tasks; uses System.Runtime.InteropServices; uses System.Runtime.CompilerServices; {$region ToDo} //=================================== // Обязательно сделать до следующего пула: //ToDo мультиюзбл хаб не должен использоваться для обычных Buffer.NewQueue //ToDo разбить BufferCommandCopy на 2 типа +1 базовый, чтоб не использовать лишнюю очередь //ToDo Написать в справке про implicit, типа способ создать очередь //ToDo Написать в справке про CommandQueueBase //ToDo Написать в справке про AddWait,AddProc,AddQueue вместе //ToDo Написать в справке про WaitFor //=================================== // Запланированное: //ToDo DummyQueue.Multiusable может тупо возвращать себя несколько раз //ToDo система создания описаний через отдельные файлы //ToDo CommandQueue.Cycle(integer) //ToDo CommandQueue.Cycle // бесконечность циклов //ToDo CommandQueue.CycleWhile(***->boolean) // - возможность передать свой обработчик ошибок как Exception->Exception // //Update: // - бесконечный цикл будет больно делать // - чтобы не накапливались Task-и - надо полностью перезапускать очередь // - а значит надо что то вроде пре-запуска, чтобы не терять время между итерациями //ToDo Read/Write для массивов - надо бы иметь возможность указывать отступ в массиве //ToDo Типы Device и Platform //ToDo А связь с OpenCL.pas сделать всему (и буферам и кёрнелам), но более человеческую //ToDo Сделать методы BufferCommandQueue.AddGet // - они особенные, потому что возвращают не BufferCommandQueue, а каждый свою очередь // - полезно, потому что SyncInvoke такой очереди будет возвращать полученное значение //ToDo Интегрировать профайлинг очередей //=================================== // Сделать когда-нибуть: //ToDo У всего, у чего есть Finalize - проверить чтобы было и .Dispose, если надо // - и добавить в справку, про то что этот объект можно удалять //ToDo Пройтись по всем функциям OpenCL, посмотреть функционал каких не доступен из OpenCLABC //ToDo Тесты всех фич модуля //=================================== //ToDo issue компилятора: // - #1981 // - #2048 // - #2067 // - #2068 // - #2118 // - #2119 // - #2120 {$endregion ToDo} type {$region misc class def} CommandQueue = class; Context = class; Buffer = class; Kernel = class; ProgramCode = class; DeviceTypeFlags = OpenCL.DeviceTypeFlags; QueueDoubleInvokeException = class(Exception) public constructor := inherited Create('Нельзя выполнять одну и ту же очередь в 2 местах одновременно. Используйте .Clone или .Multiusable'); end; ///-- __SafeNativQueue = sealed class q: cl_command_queue; constructor := raise new InvalidOperationException; constructor(c: cl_context; dvc: cl_device_id); begin var ec: ErrorCode; self.q := cl.CreateCommandQueue(c, dvc, CommandQueuePropertyFlags.NONE, ec); ec.RaiseIfError; end; protected procedure Finalize; override := cl.ReleaseCommandQueue(self.q).RaiseIfError; end; {$endregion misc class def} {$region CommandQueue} CommandQueueBase = abstract class protected ev, mw_ev: cl_event; protected is_busy: boolean; {$region Queue converters} {$region DummyQueue} public static function operator implicit(o: object): CommandQueueBase; {$endregion DummyQueue} {$region Mutiusable} //ToDo #2120 // ///Создаёт массив из n очередей, каждая из которых возвращает результат данной очереди // ///Каждую полученную очередь можно использовать одновременно с другими, но только в общей очереди // public function Multiusable(n: integer): array of CommandQueueBase; // // ///Создаёт функцию, создающую очередь, которая возвращает результат данной очереди // ///Каждую очередь, созданную полученной функцией, можно использовать одновременно с другими, но только в общей очереди // public function Multiusable: ()->CommandQueueBase; {$endregion Mutiusable} {$region ThenConvert} //ToDo #2118 // ///Создаёт очередь, которая выполнит данную // ///А затем выполнит на CPU функцию f, используя результат данной очереди // public function ThenConvert(f: object->T): CommandQueue := // self.ThenConvert((o,c)->f(o)); // ///Создаёт очередь, которая выполнит данную // ///А затем выполнит на CPU функцию f, используя результат данной очереди и контекст на котором её выполнили // public function ThenConvert(f: (object,Context)->T): CommandQueue; {$endregion ThenConvert} {$region WaitFor} {$endregion WaitFor} {$region [A]SyncQueue} public static function operator+(q1, q2: CommandQueueBase): CommandQueueBase; public static function operator+(q1: CommandQueueBase; q2: CommandQueue): CommandQueue; public static procedure operator+=(var q1: CommandQueueBase; q2: CommandQueueBase) := q1 := q1+q2; public static function operator*(q1, q2: CommandQueueBase): CommandQueueBase; public static function operator*(q1: CommandQueueBase; q2: CommandQueue): CommandQueue; public static procedure operator*=(var q1: CommandQueueBase; q2: CommandQueueBase) := q1 := q1*q2; {$endregion [A]SyncQueue} {$endregion Queue converters} {$region def} protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; abstract; protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); abstract; protected procedure UnInvoke; virtual; begin if self.is_busy then is_busy := false else raise new InvalidOperationException('Ошибка внутри модуля OpenCLABC: совершена попыта завершить не запущенную очередь. Сообщите, пожалуйста, разработчику OpenCLABC'); end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; abstract; {$endregion def} {$region Utils} {$region Misc} protected procedure MakeBusy := lock self do if not self.is_busy then is_busy := true else raise new QueueDoubleInvokeException; protected function GetRes: object; abstract; {$endregion Misc} {$region Event's} protected static procedure WaitAndRelease(ev: cl_event); begin if ev=cl_event.Zero then exit; cl.WaitForEvents(1, @ev).RaiseIfError; cl.ReleaseEvent(ev).RaiseIfError; end; ///evs.Count<>0, не 1 из ивентов не должен быть Zero protected static procedure WaitAndRelease(evs: List); begin cl.WaitForEvents(evs.Count, evs.ToArray).RaiseIfError; foreach var ev in evs do cl.ReleaseEvent(ev).RaiseIfError; end; protected function GetMWEvent(c: cl_context): cl_event; begin lock self do begin if is_busy then begin Result := self.ev; cl.RetainEvent(Result).RaiseIfError; exit; end; if self.mw_ev=cl_event.Zero then begin var ec: ErrorCode; self.mw_ev := cl.CreateUserEvent(c, ec); ec.RaiseIfError; end else cl.RetainEvent(self.mw_ev).RaiseIfError; Result := self.mw_ev; end; end; protected procedure SignalMWEvent; begin if self.mw_ev=cl_event.Zero then exit; cl.SetUserEventStatus(self.mw_ev, CommandExecutionStatus.COMPLETE); self.mw_ev := cl_event.Zero; end; {$endregion Event's} {$region Invoke} protected procedure InvokeNewQ(c: Context; tasks: List); begin var cq: __SafeNativQueue := nil; Invoke(c, cq, cl_event.Zero, tasks); end; {$endregion Invoke} {$region Clone} protected function InternalCloneCached(muhs: Dictionary; cache: Dictionary): CommandQueueBase; begin if cache.TryGetValue(self, Result) then exit; Result := InternalClone(muhs, cache); cache.Add(self, Result); end; {$endregion Clone} {$endregion Utils} end; /// Базовый тип всех очередей команд в OpenCLABC CommandQueue = abstract class(CommandQueueBase) protected res: T; {$region Misc} protected function GetRes: object; override := self.res; ///Создаёт полную копию данной очереди, ///Всех очередей из которых она состоит, ///А так же всех очередей-параметров, использованных в данной очереди public function Clone := self.InternalClone(new Dictionary, new Dictionary) as CommandQueue; {$endregion Misc} {$region Queue converters} {$region DummyQueue} public static function operator implicit(o: T): CommandQueue; {$endregion DummyQueue} {$region Mutiusable} ///Создаёт массив из n очередей, каждая из которых возвращает результат данной очереди ///Каждую полученную очередь можно использовать одновременно с другими, но только в общей очереди public function Multiusable(n: integer): array of CommandQueue; ///Создаёт функцию, создающую очередь, которая возвращает результат данной очереди ///Каждую очередь, созданную полученной функцией, можно использовать одновременно с другими, но только в общей очереди public function Multiusable: ()->CommandQueue; {$endregion Mutiusable} {$region ThenConvert} ///Создаёт очередь, которая выполнит данную ///А затем выполнит на CPU функцию f, используя результат данной очереди public function ThenConvert(f: T->T2): CommandQueue := self.ThenConvert((o,c)->f(o)); ///Создаёт очередь, которая выполнит данную ///А затем выполнит на CPU функцию f, используя результат данной очереди и контекст на котором её выполнили public function ThenConvert(f: (T,Context)->T2): CommandQueue; {$endregion ThenConvert} {$region WaitFor} public function WaitFor(q: CommandQueueBase; allow_q_cloning: boolean := true): CommandQueue; {$endregion WaitFor} {$region [A]SyncQueue} public static function operator+(q1: CommandQueue; q2: CommandQueue): CommandQueue; public static procedure operator+=(var q1: CommandQueue; q2: CommandQueue) := q1 := q1+q2; public static function operator*(q1: CommandQueue; q2: CommandQueue): CommandQueue; public static procedure operator*=(var q1: CommandQueue; q2: CommandQueue) := q1 := q1*q2; {$endregion [A]SyncQueue} {$endregion Queue converters} end; {$endregion CommandQueue} {$region GPUCommand} ///-- GPUCommand = abstract class protected ev: cl_event; {$region Command def} protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; abstract; protected procedure Invoke(o_q: CommandQueue; o: T; c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); abstract; protected procedure UnInvoke; abstract; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; abstract; {$endregion Command def} {$region Utils} protected static procedure WaitAndRelease(ev: cl_event) := CommandQueueBase.WaitAndRelease(ev); ///evs.Count<>0, не 1 из ивентов не должен быть Zero protected static procedure WaitAndRelease(evs: List) := CommandQueueBase.WaitAndRelease(evs); {$endregion Utils} end; ///-- GPUCommandContainer = abstract class(CommandQueue) protected res_q_hub: object; protected last_center_plug: CommandQueueBase; protected commands := new List>; {$region def} protected procedure OnEarlyInit(c: Context); virtual := exit; {$endregion def} {$region Common} protected constructor(o: T) := self.res := o; protected constructor(q: CommandQueue); protected function GetNewResPlug: CommandQueue; protected procedure InternalAddQueue(q: CommandQueueBase); protected procedure InternalAddProc(p: (T,Context)->()); protected procedure InternalAddWait(q: CommandQueueBase; allow_q_cloning: boolean); {$endregion Common} {$region sub implementation} protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override; private procedure CLSignalMWEvent(ev: cl_event; status: CommandExecutionStatus; data: pointer) := self.SignalMWEvent; protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; protected procedure UnInvoke; override; begin // inherited; // не надо, команды уже удалили свои эвенты self.ev := cl_event.Zero; if last_center_plug<>nil then begin last_center_plug.UnInvoke; last_center_plug := nil; end; foreach var comm in commands do comm.UnInvoke; end; {$endregion sub implementation} {$region reintroduce методы} private function Equals(obj: object): boolean; reintroduce := false; private function ToString: string; reintroduce := nil; private function GetType: System.Type; reintroduce := nil; private function GetHashCode: integer; reintroduce := 0; {$endregion reintroduce методы} end; {$endregion GPUCommand} {$region Buffer} ///Особый тип очереди, всегда возвращающий Buffer ///Может быть создан из объекта Buffer или очереди, возвращающей Buffer ///Используется для хранения списка особых команд, применимых только к Buffer BufferCommandQueue = sealed class(GPUCommandContainer) {$region constructor's} ///Создаёт объект BufferCommandQueue, команды которого будут применятся к буферу b public constructor(b: Buffer) := inherited Create(b); ///Создаёт объект BufferCommandQueue, команды которого будут применятся к буферу, который будет результатом q public constructor(q: CommandQueue); {$endregion constructor's} {$region Utils} protected function AddCommand(comm: GPUCommand): BufferCommandQueue; begin self.commands += comm; Result := self; end; protected function GetSizeQ: CommandQueue; public function Clone: BufferCommandQueue := inherited Clone as BufferCommandQueue; {$endregion Utils} {$region Write} ///- function WriteData(ptr: IntPtr): BufferCommandQueue; ///Копирует область оперативной памяти, на которую ссылается ptr, в данный буфер ///Копируется нужное кол-во байт чтобы заполнить весь буфер public function AddWriteData(ptr: CommandQueue): BufferCommandQueue := AddWriteData(ptr, 0,GetSizeQ); ///- function WriteData(ptr: IntPtr; offset, len: integer): BufferCommandQueue; ///Копирует область оперативной памяти, на которую ссылается ptr, в данный буфер ///offset это отступ в буфере, а len - кол-во копируемых байтов public function AddWriteData(ptr: CommandQueue; offset, len: CommandQueue): BufferCommandQueue; ///- function WriteData(ptr: pointer): BufferCommandQueue; ///Копирует область оперативной памяти, на которую ссылается ptr, в данный буфер ///Копируется нужное кол-во байт чтобы заполнить весь буфер public function AddWriteData(ptr: pointer) := AddWriteData(IntPtr(ptr)); ///- function WriteData(ptr: pointer; offset, len: integer): BufferCommandQueue; ///Копирует область оперативной памяти, на которую ссылается ptr, в данный буфер ///offset это отступ в буфере, а len - кол-во копируемых байтов public function AddWriteData(ptr: pointer; offset, len: CommandQueue) := AddWriteData(IntPtr(ptr), offset, len); ///- function WriteArray(a: Array): BufferCommandQueue; ///Копирует содержимое массива в данный буфер ///Копируется нужное кол-во байт чтобы заполнить весь буфер public function AddWriteArray(a: CommandQueue<&Array>): BufferCommandQueue := AddWriteArray(a, 0,GetSizeQ); ///- function WriteArray(a: Array; offset, len: integer): BufferCommandQueue; ///Копирует содержимое массива в данный буфер ///offset это отступ в буфере, а len - кол-во копируемых байтов public function AddWriteArray(a: CommandQueue<&Array>; offset, len: CommandQueue): BufferCommandQueue; ///- function WriteArray(a: Array): BufferCommandQueue; ///Копирует содержимое массива в данный буфер ///Копируется нужное кол-во байт чтобы заполнить весь буфер public function AddWriteArray(a: &Array) := AddWriteArray(CommandQueue&<&Array>(a)); ///- function WriteArray(a: Array; offset, len: integer): BufferCommandQueue; ///Копирует содержимое массива в данный буфер ///offset это отступ в буфере, а len - кол-во копируемых байтов public function AddWriteArray(a: &Array; offset, len: CommandQueue) := AddWriteArray(CommandQueue&<&Array>(a), offset, len); ///- function WriteValue(val: TRecord; offset: integer := 0): BufferCommandQueue; where TRecord: record; ///Записывает значение любого размерного типа в данный буфер ///С отступом в offset байт в буфере public [MethodImpl(MethodImplOptions.AggressiveInlining)] function AddWriteValue(val: TRecord; offset: CommandQueue := 0): BufferCommandQueue; where TRecord: record; ///- function WriteValue(val: TRecord; offset: integer := 0): BufferCommandQueue; where TRecord: record; ///Записывает значение любого размерного типа в данный буфер ///С отступом в offset байт в буфере public function AddWriteValue(val: CommandQueue; offset: CommandQueue := 0): BufferCommandQueue; where TRecord: record; {$endregion Write} {$region Read} ///- function ReadData(ptr: IntPtr): BufferCommandQueue; ///Копирует всё содержимое буффера в область оперативной памяти, на которую указывает ptr public function AddReadData(ptr: CommandQueue): BufferCommandQueue := AddReadData(ptr, 0,GetSizeQ); ///- function ReadData(ptr: IntPtr; offset, len: integer): BufferCommandQueue; ///Копирует len байт, начиная с байта №offset в буфере, в область оперативной памяти, на которую указывает ptr public function AddReadData(ptr: CommandQueue; offset, len: CommandQueue): BufferCommandQueue; ///- function ReadData(ptr: pointer): BufferCommandQueue; ///Копирует всё содержимое буффера в область оперативной памяти, на которую указывает ptr public function AddReadData(ptr: pointer) := AddReadData(IntPtr(ptr)); ///- function ReadData(ptr: pointer; offset, len: integer): BufferCommandQueue; ///Копирует len байт, начиная с байта №offset в буфере, в область оперативной памяти, на которую указывает ptr public function AddReadData(ptr: pointer; offset, len: CommandQueue) := AddReadData(IntPtr(ptr), offset, len); ///- function ReadArray(a: Array): BufferCommandQueue; ///Копирует всё содержимое буффера в содержимое массива public function AddReadArray(a: CommandQueue<&Array>): BufferCommandQueue := AddReadArray(a, 0,GetSizeQ); ///- function ReadArray(a: Array; offset, len: integer): BufferCommandQueue; ///Копирует len байт, начиная с байта №offset в буфере, в содержимое массива public function AddReadArray(a: CommandQueue<&Array>; offset, len: CommandQueue): BufferCommandQueue; ///- function ReadArray(a: Array): BufferCommandQueue; ///Копирует всё содержимое буффера в содержимое массива public function AddReadArray(a: &Array) := AddReadArray(CommandQueue&<&Array>(a)); ///- function ReadArray(a: Array; offset, len: integer): BufferCommandQueue; ///Копирует len байт, начиная с байта №offset в буфере, в содержимое массива public function AddReadArray(a: &Array; offset, len: CommandQueue) := AddReadArray(CommandQueue&<&Array>(a), offset, len); ///- function ReadValue(var val: TRecord; offset: integer := 0): BufferCommandQueue; where TRecord: record; ///Читает значение любого размерного типа из данного буфера ///С отступом в offset байт в буфере public function AddReadValue(var val: TRecord; offset: CommandQueue := 0): BufferCommandQueue; where TRecord: record; begin Result := AddReadData(@val, offset, Marshal.SizeOf&); end; {$endregion Read} {$region Fill} ///- function PatternFill(ptr: IntPtr): BufferCommandQueue; ///Заполняет весь буфер копиями массива байт, длинной pattern_len, ///прочитанным из области оперативной памяти, на которую указывает ptr public function AddFillData(ptr: CommandQueue; pattern_len: CommandQueue): BufferCommandQueue := AddFillData(ptr,pattern_len, 0,GetSizeQ); ///- function PatternFill(ptr: IntPtr; offset, len: integer): BufferCommandQueue; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями массива байт, длинной pattern_len, ///прочитанным из области оперативной памяти, на которую указывает ptr public function AddFillData(ptr: CommandQueue; pattern_len, offset, len: CommandQueue): BufferCommandQueue; ///- function PatternFill(ptr: pointer): BufferCommandQueue; ///Заполняет весь буфер копиями массива байт, длинной pattern_len, ///прочитанным из области оперативной памяти, на которую указывает ptr public function AddFillData(ptr: pointer; pattern_len: CommandQueue) := AddFillData(IntPtr(ptr), pattern_len); ///- function PatternFill(ptr: pointer; offset, len: integer): BufferCommandQueue; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями массива байт, длинной pattern_len, ///прочитанным из области оперативной памяти, на которую указывает ptr public function AddFillData(ptr: pointer; pattern_len, offset, len: CommandQueue) := AddFillData(IntPtr(ptr), pattern_len, offset, len); ///- function PatternFill(a: Array): BufferCommandQueue; ///Заполняет весь буфер копиями содержимого массива public function AddFillArray(a: CommandQueue<&Array>): BufferCommandQueue := AddFillArray(a, 0,GetSizeQ); ///- function PatternFill(a: Array; offset, len: integer): BufferCommandQueue; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями содержимого массива public function AddFillArray(a: CommandQueue<&Array>; offset, len: CommandQueue): BufferCommandQueue; ///- function PatternFill(a: Array): BufferCommandQueue; ///Заполняет весь буфер копиями содержимого массива public function AddFillArray(a: &Array) := AddFillArray(CommandQueue&<&Array>(a)); ///- function PatternFill(a: Array; offset, len: integer): BufferCommandQueue; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями содержимого массива public function AddFillArray(a: &Array; offset, len: CommandQueue) := AddFillArray(CommandQueue&<&Array>(a), offset, len); ///- function PatternFill(val: TRecord): BufferCommandQueue; where TRecord: record; ///Заполняет весь буфер копиями значения любого размерного типа public [MethodImpl(MethodImplOptions.AggressiveInlining)] function AddFillValue(val: TRecord): BufferCommandQueue; where TRecord: record; begin Result := AddFillValue(val, 0,GetSizeQ); end; ///- function PatternFill(val: TRecord; offset, len: integer): BufferCommandQueue; where TRecord: record; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями значения любого размерного типа public [MethodImpl(MethodImplOptions.AggressiveInlining)] function AddFillValue(val: TRecord; offset, len: CommandQueue): BufferCommandQueue; where TRecord: record; ///- function PatternFill(val: TRecord): BufferCommandQueue; where TRecord: record; ///Заполняет весь буфер копиями значения любого размерного типа public function AddFillValue(val: CommandQueue): BufferCommandQueue; where TRecord: record; begin Result := AddFillValue(val, 0,GetSizeQ); end; ///- function PatternFill(val: TRecord; offset, len: integer): BufferCommandQueue; where TRecord: record; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями значения любого размерного типа public function AddFillValue(val: CommandQueue; offset, len: CommandQueue): BufferCommandQueue; where TRecord: record; {$endregion Fill} {$region Copy} ///- function CopyFrom(b: Buffer; from, &to, len: integer): BufferCommandQueue; ///Копирует содержимое буфера b в данный буфер ///from - отступ в буффере b ///to - отступ в данном буффере ///len - кол-во копируемых байт public function AddCopyFrom(b: CommandQueue; from, &to, len: CommandQueue): BufferCommandQueue; ///- function CopyTo(b: Buffer; from, &to, len: integer): BufferCommandQueue; ///Копирует содержимое данного буфера в буфер b ///from - отступ в данном буффере ///to - отступ в буффере b ///len - кол-во копируемых байт public function AddCopyTo (b: CommandQueue; from, &to, len: CommandQueue): BufferCommandQueue; ///- function CopyFrom(b: Buffer): BufferCommandQueue; ///Копирует всё содержимое буфера b в данный буфер public function AddCopyFrom(b: CommandQueue) := AddCopyFrom(b, 0,0, GetSizeQ); ///- function CopyTo(b: Buffer): BufferCommandQueue; ///Копирует всё содержимое данного буфера в буфер b public function AddCopyTo (b: CommandQueue) := AddCopyTo (b, 0,0, GetSizeQ); {$endregion Copy} {$region Non-command add's} public function AddQueue(q: CommandQueueBase): BufferCommandQueue; begin InternalAddQueue(q); Result := self; end; public function AddProc(p: (Buffer,Context)->()): BufferCommandQueue; begin InternalAddProc(p); Result := self; end; public function AddProc(p: Buffer->()) := AddProc((b,c)->p(b)); public function AddWait(q: CommandQueueBase; allow_q_cloning: boolean := true): BufferCommandQueue; begin InternalAddWait(q, allow_q_cloning); Result := self; end; {$endregion Non-command add's} {$region override методы} protected procedure OnEarlyInit(c: Context); override; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override; {$endregion override методы} end; ///Буфер, хранящий своё содержимое в памяти GPU (обычно) ///Используется для передачи данных в Kernel-ы перед их выполнением Buffer = sealed class(IDisposable) private memobj: cl_mem; private sz: UIntPtr; private _parent: Buffer; {$region constructor's} private constructor := raise new System.NotSupportedException; ///Создаён не_инициализированный буфер с размером size байт public constructor(size: UIntPtr) := self.sz := size; ///Создаён не_инициализированный буфер с размером size байт public constructor(size: integer) := Create(new UIntPtr(size)); ///Создаён не_инициализированный буфер с размером size байт public constructor(size: int64) := Create(new UIntPtr(size)); ///Создаён инициализированный в контексте "c" буфер с размером size байт public constructor(size: UIntPtr; c: Context); begin Create(size); Init(c); end; ///Создаён инициализированный в контексте "c" буфер с размером size байт public constructor(size: integer; c: Context) := Create(new UIntPtr(size), c); ///Создаён инициализированный в контексте "c" буфер с размером size байт public constructor(size: int64; c: Context) := Create(new UIntPtr(size), c); ///Создаёт под-буфер размера size и с отступом в данном буфере offset ///Под буфер имеет общую память с оригинальным, но иммеет доступ только к её части public function SubBuff(offset, size: integer): Buffer; ///Инициализирует буфер, выделяя память на девайсе - который связан с данным контекстом public procedure Init(c: Context); {$endregion constructor's} {$region property's} ///Возвращает размер буфера в байтах public property Size: UIntPtr read sz; ///Возвращает размер буфера в байтах public property Size32: UInt32 read sz.ToUInt32; ///Возвращает размер буфера в байтах public property Size64: UInt64 read sz.ToUInt64; ///Если данный буфер был создан функцией SubBuff - возвращает родительский буфер ///Иначе возвращает nil public property Parent: Buffer read _parent; {$endregion property's} {$region Queue's} ///Создаёт новую очередь-обёртку данного буфера ///Которая может хранить множество операций чтения/записи одновременно public function NewQueue := new BufferCommandQueue(self); /// - static function ValueQueue(val: TRecord): BufferCommandQueue; where TRecord: record; ///Создаёт новый буфер того же размера что и val, оборачивает в очередь ///И вызывает у полученной очереди .WriteValue(val) public [MethodImpl(MethodImplOptions.AggressiveInlining)] static function ValueQueue(val: TRecord): BufferCommandQueue; where TRecord: record; begin Result := Buffer.Create(Marshal.SizeOf&) .NewQueue.AddWriteValue(val); end; {$endregion Queue's} {$region Write} ///- function WriteData(ptr: IntPtr): BufferCommandQueue; ///Копирует область оперативной памяти, на которую ссылается ptr, в данный буфер ///Копируется нужное кол-во байт чтобы заполнить весь буфер public function WriteData(ptr: CommandQueue): Buffer; ///- function WriteData(ptr: IntPtr; offset, len: integer): BufferCommandQueue; ///Копирует область оперативной памяти, на которую ссылается ptr, в данный буфер ///offset это отступ в буфере, а len - кол-во копируемых байтов public function WriteData(ptr: CommandQueue; offset, len: CommandQueue): Buffer; ///- function WriteData(ptr: pointer): BufferCommandQueue; ///Копирует область оперативной памяти, на которую ссылается ptr, в данный буфер ///Копируется нужное кол-во байт чтобы заполнить весь буфер public function WriteData(ptr: pointer) := WriteData(IntPtr(ptr)); ///- function WriteData(ptr: pointer; offset, len: integer): BufferCommandQueue; ///Копирует область оперативной памяти, на которую ссылается ptr, в данный буфер ///offset это отступ в буфере, а len - кол-во копируемых байтов public function WriteData(ptr: pointer; offset, len: CommandQueue) := WriteData(IntPtr(ptr), offset, len); ///- function WriteArray(a: Array): BufferCommandQueue; ///Копирует содержимое массива в данный буфер ///Копируется нужное кол-во байт чтобы заполнить весь буфер public function WriteArray(a: CommandQueue<&Array>): Buffer; ///- function WriteArray(a: Array; offset, len: integer): BufferCommandQueue; ///Копирует содержимое массива в данный буфер ///offset это отступ в буфере, а len - кол-во копируемых байтов public function WriteArray(a: CommandQueue<&Array>; offset, len: CommandQueue): Buffer; ///- function WriteArray(a: Array): BufferCommandQueue; ///Копирует содержимое массива в данный буфер ///Копируется нужное кол-во байт чтобы заполнить весь буфер public function WriteArray(a: &Array) := WriteArray(CommandQueue&<&Array>(a)); ///- function WriteArray(a: Array; offset, len: integer): BufferCommandQueue; ///Копирует содержимое массива в данный буфер ///offset это отступ в буфере, а len - кол-во копируемых байтов public function WriteArray(a: &Array; offset, len: CommandQueue) := WriteArray(CommandQueue&<&Array>(a), offset, len); ///- function WriteValue(val: TRecord; offset: integer := 0): BufferCommandQueue; where TRecord: record; ///Записывает значение любого размерного типа в данный буфер ///С отступом в offset байт в буфере public [MethodImpl(MethodImplOptions.AggressiveInlining)] function WriteValue(val: TRecord; offset: CommandQueue := 0): Buffer; where TRecord: record; begin Result := WriteData(@val, offset, Marshal.SizeOf&); end; ///- function WriteValue(val: TRecord; offset: integer := 0): BufferCommandQueue; where TRecord: record; ///Записывает значение любого размерного типа в данный буфер ///С отступом в offset байт в буфере public function WriteValue(val: CommandQueue; offset: CommandQueue := 0): Buffer; where TRecord: record; {$endregion Write} {$region Read} ///- function ReadData(ptr: IntPtr): BufferCommandQueue; ///Копирует всё содержимое буффера в область оперативной памяти, на которую указывает ptr public function ReadData(ptr: CommandQueue): Buffer; ///- function ReadData(ptr: IntPtr; offset, len: integer): BufferCommandQueue; ///Копирует len байт, начиная с байта №offset в буфере, в область оперативной памяти, на которую указывает ptr public function ReadData(ptr: CommandQueue; offset, len: CommandQueue): Buffer; ///- function ReadData(ptr: pointer): BufferCommandQueue; ///Копирует всё содержимое буффера в область оперативной памяти, на которую указывает ptr public function ReadData(ptr: pointer) := ReadData(IntPtr(ptr)); ///- function ReadData(ptr: pointer; offset, len: integer): BufferCommandQueue; ///Копирует len байт, начиная с байта №offset в буфере, в область оперативной памяти, на которую указывает ptr public function ReadData(ptr: pointer; offset, len: CommandQueue) := ReadData(IntPtr(ptr), offset, len); ///- function ReadArray(a: Array): BufferCommandQueue; ///Копирует всё содержимое буффера в содержимое массива public function ReadArray(a: CommandQueue<&Array>): Buffer; ///- function ReadArray(a: Array; offset, len: integer): BufferCommandQueue; ///Копирует len байт, начиная с байта №offset в буфере, в содержимое массива public function ReadArray(a: CommandQueue<&Array>; offset, len: CommandQueue): Buffer; ///- function ReadArray(a: Array): BufferCommandQueue; ///Копирует всё содержимое буффера в содержимое массива public function ReadArray(a: &Array) := ReadArray(CommandQueue&<&Array>(a)); ///- function ReadArray(a: Array; offset, len: integer): BufferCommandQueue; ///Копирует len байт, начиная с байта №offset в буфере, в содержимое массива public function ReadArray(a: &Array; offset, len: CommandQueue) := ReadArray(CommandQueue&<&Array>(a), offset, len); ///- function ReadValue(var val: TRecord; offset: integer := 0): BufferCommandQueue; where TRecord: record; ///Читает значение любого размерного типа из данного буфера ///С отступом в offset байт в буфере public function ReadValue(var val: TRecord; offset: CommandQueue := 0): Buffer; where TRecord: record; begin Result := ReadData(@val, offset, Marshal.SizeOf&); end; {$endregion Read} {$region Fill} ///- function PatternFill(ptr: IntPtr): BufferCommandQueue; ///Заполняет весь буфер копиями массива байт, длинной pattern_len, ///прочитанным из области оперативной памяти, на которую указывает ptr public function FillData(ptr: CommandQueue; pattern_len: CommandQueue): Buffer; ///- function PatternFill(ptr: IntPtr; offset, len: integer): BufferCommandQueue; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями массива байт, длинной pattern_len, ///прочитанным из области оперативной памяти, на которую указывает ptr public function FillData(ptr: CommandQueue; pattern_len, offset, len: CommandQueue): Buffer; ///- function PatternFill(ptr: pointer): BufferCommandQueue; ///Заполняет весь буфер копиями массива байт, длинной pattern_len, ///прочитанным из области оперативной памяти, на которую указывает ptr public function FillData(ptr: pointer; pattern_len: CommandQueue) := FillData(IntPtr(ptr), pattern_len); ///- function PatternFill(ptr: pointer; offset, len: integer): BufferCommandQueue; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями массива байт, длинной pattern_len, ///прочитанным из области оперативной памяти, на которую указывает ptr public function FillData(ptr: pointer; pattern_len, offset, len: CommandQueue) := FillData(IntPtr(ptr), pattern_len, offset, len); ///- function PatternFill(a: Array): BufferCommandQueue; ///Заполняет весь буфер копиями содержимого массива public function FillArray(a: CommandQueue<&Array>): Buffer; ///- function PatternFill(a: Array; offset, len: integer): BufferCommandQueue; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями содержимого массива public function FillArray(a: CommandQueue<&Array>; offset, len: CommandQueue): Buffer; ///- function PatternFill(a: Array): BufferCommandQueue; ///Заполняет весь буфер копиями содержимого массива public function FillArray(a: &Array) := FillArray(CommandQueue&<&Array>(a)); ///- function PatternFill(a: Array; offset, len: integer): BufferCommandQueue; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями содержимого массива public function FillArray(a: &Array; offset, len: CommandQueue) := FillArray(CommandQueue&<&Array>(a), offset, len); ///- function PatternFill(val: TRecord): BufferCommandQueue; where TRecord: record; ///Заполняет весь буфер копиями значения любого размерного типа public [MethodImpl(MethodImplOptions.AggressiveInlining)] function FillValue(val: TRecord): Buffer; where TRecord: record; ///- function PatternFill(val: TRecord; offset, len: integer): BufferCommandQueue; where TRecord: record; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями значения любого размерного типа public [MethodImpl(MethodImplOptions.AggressiveInlining)] function FillValue(val: TRecord; offset, len: CommandQueue): Buffer; where TRecord: record; ///- function PatternFill(val: TRecord): BufferCommandQueue; where TRecord: record; ///Заполняет весь буфер копиями значения любого размерного типа public function FillValue(val: CommandQueue): Buffer; where TRecord: record; ///- function PatternFill(val: TRecord; offset, len: integer): BufferCommandQueue; where TRecord: record; ///Заполняет часть буфера (начиная с байта №offset и длинной len) копиями значения любого размерного типа public function FillValue(val: CommandQueue; offset, len: CommandQueue): Buffer; where TRecord: record; {$endregion Fill} {$region Copy} ///- function CopyFrom(b: Buffer; from, &to, len: integer): BufferCommandQueue; ///Копирует содержимое буфера b в данный буфер ///from - отступ в буффере b ///to - отступ в данном буффере ///len - кол-во копируемых байт public function CopyFrom(b: CommandQueue; from, &to, len: CommandQueue): Buffer; ///- function CopyTo(b: Buffer; from, &to, len: integer): BufferCommandQueue; ///Копирует содержимое данного буфера в буфер b ///from - отступ в данном буффере ///to - отступ в буффере b ///len - кол-во копируемых байт public function CopyTo (b: CommandQueue; from, &to, len: CommandQueue): Buffer; ///- function CopyFrom(b: Buffer): BufferCommandQueue; ///Копирует всё содержимое буфера b в данный буфер public function CopyFrom(b: CommandQueue): Buffer; ///- function CopyTo(b: Buffer): BufferCommandQueue; ///Копирует всё содержимое данного буфера в буфер b public function CopyTo (b: CommandQueue): Buffer; {$endregion Copy} {$region Get} ///- function GetData(offset, len: integer): IntPtr; ///Выделяет неуправляемую область в памяти ///И копирует в неё len байт из данного буфера, начиная с байта №offset ///Обязательно вызовите Marshal.FreeHGlobal на полученном дескрипторе, после использования public function GetData(offset, len: CommandQueue): IntPtr; ///- function GetData: IntPtr; ///Выделяет неуправляемую область в памяти, одинакового размера с данным буфером ///И копирует в неё всё содержимое данного буфера ///Обязательно вызовите Marshal.FreeHGlobal на полученном дескрипторе, после использования public function GetData := GetData(0,integer(self.Size32)); ///- function GetArrayAt(offset: integer; params szs: array of integer): TArray; where TArray: &Array; ///Создаёт новый массив с размерностями szs ///И копирует в него, начиная с байта offset, достаточно байт чтобы заполнить весь массив public function GetArrayAt(offset: CommandQueue; szs: CommandQueue): TArray; where TArray: &Array; ///- function GetArray(params szs: array of integer): TArray; where TArray: &Array; ///Создаёт новый массив с размерностями szs ///И копирует в него достаточно байт чтобы заполнить весь массив public function GetArray(szs: CommandQueue): TArray; where TArray: &Array; begin Result := GetArrayAt&(0, szs); end; ///- function GetArrayAt(offset: integer; params szs: array of integer): TArray; where TArray: &Array; ///Создаёт новый массив с размерностями szs ///И копирует в него, начиная с байта offset, достаточно байт чтобы заполнить весь массив public function GetArrayAt(offset: CommandQueue; params szs: array of CommandQueue): TArray; where TArray: &Array; ///- function GetArray(params szs: array of integer): TArray; where TArray: &Array; ///Создаёт новый массив с размерностями szs ///И копирует в него достаточно байт чтобы заполнить весь массив public function GetArray(params szs: array of integer): TArray; where TArray: &Array; begin Result := GetArrayAt&(0, CommandQueue&(szs)); end; ///- function GetArray1At(offset: integer; length: integer): array of TRecord; where TRecord: record; ///Создаёт новый 1-мерный массив, с length элементами типа TRecord ///И копирует в него, начиная с байта offset, достаточно байт чтобы заполнить весь массив public function GetArray1At(offset, length: CommandQueue): array of TRecord; where TRecord: record; begin Result := GetArrayAt&(offset, length); end; ///- function GetArray1(length: integer): array of TRecord; where TRecord: record; ///Создаёт новый 1-мерный массив, с length элементами типа TRecord ///И копирует в него достаточно байт чтобы заполнить весь массив public function GetArray1(length: CommandQueue): array of TRecord; where TRecord: record; begin Result := GetArrayAt&(0,length); end; ///- function GetArray1: array of TRecord; where TRecord: record; ///Создаёт новый 1-мерный массив, с максимальным кол-вом элементов типа TRecord ///И копирует в него достаточно байт чтобы заполнить весь массив public function GetArray1: array of TRecord; where TRecord: record; begin Result := GetArrayAt&(0, integer(sz.ToUInt32) div Marshal.SizeOf&); end; ///- function GetArray2At(offset: integer; length: integer): array[,] of TRecord; where TRecord: record; ///Создаёт новый 2-мерный массив, с length элементами типа TRecord ///И копирует в него, начиная с байта offset, достаточно байт чтобы заполнить весь массив public function GetArray2At(offset, length1, length2: CommandQueue): array[,] of TRecord; where TRecord: record; begin Result := GetArrayAt&(offset, length1, length2); end; ///- function GetArray2(length: integer): array of TRecord; where TRecord: record; ///Создаёт новый 2-мерный массив, с length элементами типа TRecord ///И копирует в него достаточно байт чтобы заполнить весь массив public function GetArray2(length1, length2: CommandQueue): array[,] of TRecord; where TRecord: record; begin Result := GetArrayAt&(0, length1, length2); end; ///- function GetArray3At(offset: integer; length: integer): array[,,] of TRecord; where TRecord: record; ///Создаёт новый 3-мерный массив, с length элементами типа TRecord ///И копирует в него, начиная с байта offset, достаточно байт чтобы заполнить весь массив public function GetArray3At(offset, length1, length2, length3: CommandQueue): array[,,] of TRecord; where TRecord: record; begin Result := GetArrayAt&(offset, length1, length2, length3); end; ///- function GetArray3(length: integer): array[,,] of TRecord; where TRecord: record; ///Создаёт новый 3-мерный массив, с length элементами типа TRecord ///И копирует в него достаточно байт чтобы заполнить весь массив public function GetArray3(length1, length2, length3: CommandQueue): array[,,] of TRecord; where TRecord: record; begin Result := GetArrayAt&(0, length1, length2, length3); end; ///- function GetValueAt(offset: integer): TRecord; where TRecord: record; ///Читает значение любого размерного типа из данного буфера ///С отступом в offset байт в буфере public [MethodImpl(MethodImplOptions.AggressiveInlining)] function GetValueAt(offset: CommandQueue): TRecord; where TRecord: record; ///- function GetValue: TRecord; where TRecord: record; ///Читает значение любого размерного типа из начала данного буфера public [MethodImpl(MethodImplOptions.AggressiveInlining)] function GetValue: TRecord; where TRecord: record; begin Result := GetValueAt&(0); end; {$endregion Get} ///Высвобождает выделенную на GPU память ///Если такой нету - не делает ничего ///Память будет заново выделена, если снова использовать данный буфер public procedure Dispose := if self.memobj<>cl_mem.Zero then begin cl.ReleaseMemObject(memobj).RaiseIfError; memobj := cl_mem.Zero; end; protected procedure Finalize; override := self.Dispose; end; {$endregion Buffer} {$region Kernel} ///Особый тип очереди, всегда возвращающий Kernel ///Может быть создан из объекта Kernel или очереди, возвращающей Kernel ///Используется для хранения списка особых команд, применимых только к Kernel KernelCommandQueue = sealed class(GPUCommandContainer) {$region constructor's} ///Создаёт объект KernelCommandQueue, команды которого будут применятся к кёрнелу b public constructor(k: Kernel) := inherited Create(k); ///Создаёт объект KernelCommandQueue, команды которого будут применятся к кёрнелу, который будет результатом q public constructor(q: CommandQueue) := inherited Create(q); {$endregion constructor's} {$region Utils} protected function AddCommand(comm: GPUCommand): KernelCommandQueue; begin self.commands += comm; Result := self; end; public function Clone: KernelCommandQueue := inherited Clone as KernelCommandQueue; {$endregion Utils} {$region Exec} public function AddExec(work_szs: array of UIntPtr; params args: array of CommandQueue): KernelCommandQueue; public function AddExec(work_szs: array of integer; params args: array of CommandQueue) := AddExec(work_szs.ConvertAll(sz->new UIntPtr(sz)), args); public function AddExec1(work_sz1: UIntPtr; params args: array of CommandQueue) := AddExec(new UIntPtr[](work_sz1), args); public function AddExec1(work_sz1: integer; params args: array of CommandQueue) := AddExec1(new UIntPtr(work_sz1), args); public function AddExec2(work_sz1, work_sz2: UIntPtr; params args: array of CommandQueue) := AddExec(new UIntPtr[](work_sz1, work_sz2), args); public function AddExec2(work_sz1, work_sz2: integer; params args: array of CommandQueue) := AddExec2(new UIntPtr(work_sz1), new UIntPtr(work_sz2), args); public function AddExec3(work_sz1, work_sz2, work_sz3: UIntPtr; params args: array of CommandQueue) := AddExec(new UIntPtr[](work_sz1, work_sz2, work_sz3), args); public function AddExec3(work_sz1, work_sz2, work_sz3: integer; params args: array of CommandQueue) := AddExec3(new UIntPtr(work_sz1), new UIntPtr(work_sz2), new UIntPtr(work_sz3), args); public function AddExec(work_szs: array of CommandQueue; params args: array of CommandQueue): KernelCommandQueue; public function AddExec(work_szs: array of CommandQueue; params args: array of CommandQueue) := AddExec(work_szs.ConvertAll(sz_q->sz_q.ThenConvert(sz->new UIntPtr(sz))), args); public function AddExec1(work_sz1: CommandQueue; params args: array of CommandQueue) := AddExec(new CommandQueue[](work_sz1), args); public function AddExec1(work_sz1: CommandQueue; params args: array of CommandQueue) := AddExec1(work_sz1.ThenConvert(sz->new UIntPtr(sz)), args); public function AddExec2(work_sz1, work_sz2: CommandQueue; params args: array of CommandQueue) := AddExec(new CommandQueue[](work_sz1, work_sz2), args); public function AddExec2(work_sz1, work_sz2: CommandQueue; params args: array of CommandQueue) := AddExec2(work_sz1.ThenConvert(sz->new UIntPtr(sz)), work_sz2.ThenConvert(sz->new UIntPtr(sz)), args); public function AddExec3(work_sz1, work_sz2, work_sz3: CommandQueue; params args: array of CommandQueue) := AddExec(new CommandQueue[](work_sz1, work_sz2, work_sz3), args); public function AddExec3(work_sz1, work_sz2, work_sz3: CommandQueue; params args: array of CommandQueue) := AddExec3(work_sz1.ThenConvert(sz->new UIntPtr(sz)), work_sz2.ThenConvert(sz->new UIntPtr(sz)), work_sz3.ThenConvert(sz->new UIntPtr(sz)), args); public function AddExec(work_szs: CommandQueue; params args: array of CommandQueue): KernelCommandQueue; public function AddExec(work_szs: CommandQueue; params args: array of CommandQueue): KernelCommandQueue; {$endregion Exec} {$region Non-command add's} public function AddQueue(q: CommandQueueBase): KernelCommandQueue; begin InternalAddQueue(q); Result := self; end; public function AddProc(p: (Kernel,Context)->()): KernelCommandQueue; begin InternalAddProc(p); Result := self; end; public function AddProc(p: Kernel->()) := AddProc((k,c)->p(k)); public function AddWait(q: CommandQueueBase; allow_q_cloning: boolean := true): KernelCommandQueue; begin InternalAddWait(q, allow_q_cloning); Result := self; end; {$endregion Non-command add's} {$region override методы} protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override; {$endregion override методы} end; Kernel = sealed class private _kernel: cl_kernel; {$region constructor's} private constructor := raise new System.NotSupportedException; public constructor(prog: ProgramCode; name: string); {$endregion constructor's} {$region Queue's} public function NewQueue := new KernelCommandQueue(self); {$endregion Queue's} {$region Exec} public function Exec(work_szs: array of UIntPtr; params args: array of CommandQueue): Kernel; public function Exec(work_szs: array of integer; params args: array of CommandQueue) := Exec(work_szs.ConvertAll(sz->new UIntPtr(sz)), args); public function Exec1(work_sz1: UIntPtr; params args: array of CommandQueue) := Exec(new UIntPtr[](work_sz1), args); public function Exec1(work_sz1: integer; params args: array of CommandQueue) := Exec1(new UIntPtr(work_sz1), args); public function Exec2(work_sz1, work_sz2: UIntPtr; params args: array of CommandQueue) := Exec(new UIntPtr[](work_sz1, work_sz2), args); public function Exec2(work_sz1, work_sz2: integer; params args: array of CommandQueue) := Exec2(new UIntPtr(work_sz1), new UIntPtr(work_sz2), args); public function Exec3(work_sz1, work_sz2, work_sz3: UIntPtr; params args: array of CommandQueue) := Exec(new UIntPtr[](work_sz1, work_sz2, work_sz3), args); public function Exec3(work_sz1, work_sz2, work_sz3: integer; params args: array of CommandQueue) := Exec3(new UIntPtr(work_sz1), new UIntPtr(work_sz2), new UIntPtr(work_sz3), args); public function Exec(work_szs: array of CommandQueue; params args: array of CommandQueue): Kernel; public function Exec(work_szs: array of CommandQueue; params args: array of CommandQueue) := Exec(work_szs.ConvertAll(sz_q->sz_q.ThenConvert(sz->new UIntPtr(sz))), args); public function Exec1(work_sz1: CommandQueue; params args: array of CommandQueue) := Exec(new CommandQueue[](work_sz1), args); public function Exec1(work_sz1: CommandQueue; params args: array of CommandQueue) := Exec1(work_sz1.ThenConvert(sz->new UIntPtr(sz)), args); public function Exec2(work_sz1, work_sz2: CommandQueue; params args: array of CommandQueue) := Exec(new CommandQueue[](work_sz1, work_sz2), args); public function Exec2(work_sz1, work_sz2: CommandQueue; params args: array of CommandQueue) := Exec2(work_sz1.ThenConvert(sz->new UIntPtr(sz)), work_sz2.ThenConvert(sz->new UIntPtr(sz)), args); public function Exec3(work_sz1, work_sz2, work_sz3: CommandQueue; params args: array of CommandQueue) := Exec(new CommandQueue[](work_sz1, work_sz2, work_sz3), args); public function Exec3(work_sz1, work_sz2, work_sz3: CommandQueue; params args: array of CommandQueue) := Exec3(work_sz1.ThenConvert(sz->new UIntPtr(sz)), work_sz2.ThenConvert(sz->new UIntPtr(sz)), work_sz3.ThenConvert(sz->new UIntPtr(sz)), args); public function Exec(work_szs: CommandQueue; params args: array of CommandQueue): Kernel; public function Exec(work_szs: CommandQueue; params args: array of CommandQueue): Kernel; {$endregion Exec} protected procedure Finalize; override := cl.ReleaseKernel(self._kernel).RaiseIfError; end; {$endregion Kernel} {$region Context} Context = sealed class private static _platform: cl_platform_id; private static _def_cont: Context; private _device: cl_device_id; private _context: cl_context; private need_finnalize := false; public static property &Default: Context read _def_cont write _def_cont; static constructor := try var ec := cl.GetPlatformIDs(1,@_platform,nil); ec.RaiseIfError; try _def_cont := new Context; except _def_cont := new Context(DeviceTypeFlags.All); // если нету GPU - попытаться хотя бы для чего то его инициализировать end; except on e: Exception do begin {$reference PresentationFramework.dll} System.Windows.MessageBox.Show(e.ToString, 'Не удалось инициализировать OpenCL'); Halt; end; end; /// Инициализирует новый контекст c 1 девайсом типа GPU public constructor := Create(DeviceTypeFlags.GPU); /// Инициализирует новый контекст c 1 девайсом типа dt public constructor(dt: DeviceTypeFlags); begin var ec: ErrorCode; cl.GetDeviceIDs(_platform, dt, 1, @_device, nil).RaiseIfError; _context := cl.CreateContext(nil, 1, @_device, nil, nil, @ec); ec.RaiseIfError; need_finnalize := true; end; /// Создаёт обёртку для дескриптора контекста, полученного модулем OpenCL /// Девайс выбирается первый попавшейся из списка связанных /// Автоматическое удаление контекста не произойдёт при удалении всех ссылок на полученную обёртку /// В отличии от создания нового контекста - контекстом управляет модуль OpenCL а не OpenCLABC public constructor(context: cl_context); begin cl.GetContextInfo(context, ContextInfoType.CL_CONTEXT_DEVICES, new UIntPtr(IntPtr.Size), @_device, nil).RaiseIfError; _context := context; end; /// Создаёт обёртку для дескриптора контекста, полученного модулем OpenCL /// Девайс выбирается с указанным дескриптором, так же полученный из модуля OpenCL /// Автоматическое удаление контекста не произойдёт при удалении всех ссылок на полученную обёртку /// В отличии от создания нового контекста - контекстом управляет модуль OpenCL а не OpenCLABC public constructor(context: cl_context; device: cl_device_id); begin _device := device; _context := context; end; /// Инициализирует все команды в очереди и запускает первые /// Возвращает объект задачи, по которому можно следить за состоянием выполнения очереди public function BeginInvoke(q: CommandQueueBase): Task; begin var tasks := new List( q.GetEstimateTaskCount(new HashSet) ); q.InvokeNewQ(self, tasks); Result := new Task(()-> //ToDo #2048 try while true do begin for var i := tasks.Count-1 downto 0 do if tasks[i].Status <> TaskStatus.Running then begin if tasks[i].Exception<>nil then raise tasks[i].Exception; tasks.RemoveAt(i); end; if tasks.Count=0 then break; Sleep(10); end; CommandQueueBase.WaitAndRelease(q.ev); Result := q.GetRes; finally q.UnInvoke; end); Result.Start; end; /// Инициализирует все команды в очереди и запускает первые /// Возвращает объект задачи, по которому можно следить за состоянием выполнения очереди public function BeginInvoke(q: CommandQueue): Task; begin var tasks := new List( q.GetEstimateTaskCount(new HashSet) ); q.InvokeNewQ(self, tasks); Result := new Task(()-> //ToDo #2048 try while true do begin for var i := tasks.Count-1 downto 0 do if tasks[i].Status <> TaskStatus.Running then begin if tasks[i].Exception<>nil then raise tasks[i].Exception; tasks.RemoveAt(i); end; if tasks.Count=0 then break; Sleep(10); end; CommandQueueBase.WaitAndRelease(q.ev); Result := q.res; finally q.UnInvoke; end); Result.Start; end; /// Выполняет BeginInvoke и ожидает окончания выполнения возвращённой задачи /// Возвращает результат очереди public function SyncInvoke(q: CommandQueueBase): object; begin var tsk := BeginInvoke(q); tsk.Wait; Result := tsk.Result; end; /// Выполняет BeginInvoke и ожидает окончания выполнения возвращённой задачи /// Возвращает результат очереди public function SyncInvoke(q: CommandQueue): T; begin var tsk := BeginInvoke(q); tsk.Wait; Result := tsk.Result; end; protected procedure Finalize; override := if need_finnalize then // если было исключение при инициализации или инициализация произошла из дескриптора cl.ReleaseContext(_context).RaiseIfError; end; {$endregion Context} {$region ProgramCode} ProgramCode = sealed class private _program: cl_program; private cntxt: Context; private constructor := exit; public constructor(c: Context; params files_texts: array of string); begin var ec: ErrorCode; self.cntxt := c; self._program := cl.CreateProgramWithSource(c._context, files_texts.Length, files_texts, files_texts.ConvertAll(s->new UIntPtr(s.Length)), ec); ec.RaiseIfError; cl.BuildProgram(self._program, 1, @c._device, nil,nil,nil).RaiseIfError; end; public constructor(params files_texts: array of string) := Create(Context.Default, files_texts); public property KernelByName[kname: string]: Kernel read new Kernel(self, kname); default; public function GetAllKernels: Dictionary; begin var names_char_len: UIntPtr; cl.GetProgramInfo(_program, ProgramInfoType.NUM_KERNELS, new UIntPtr(UIntPtr.Size), @names_char_len, nil).RaiseIfError; var names_ptr := Marshal.AllocHGlobal(IntPtr(pointer(names_char_len))+1); cl.GetProgramInfo(_program, ProgramInfoType.KERNEL_NAMES, names_char_len, pointer(names_ptr), nil).RaiseIfError; var names := Marshal.PtrToStringAnsi(names_ptr).Split(';'); Marshal.FreeHGlobal(names_ptr); Result := new Dictionary(names.Length); foreach var kname in names do Result[kname] := self[kname]; end; public function Serialize: array of byte; begin var bytes_count: UIntPtr; cl.GetProgramInfo(_program, ProgramInfoType.BINARY_SIZES, new UIntPtr(UIntPtr.Size), @bytes_count, nil).RaiseIfError; var bytes_mem := Marshal.AllocHGlobal(IntPtr(pointer(bytes_count))); cl.GetProgramInfo(_program, ProgramInfoType.BINARIES, new UIntPtr(UIntPtr.Size), @bytes_mem, nil).RaiseIfError; Result := new byte[bytes_count.ToUInt64()]; Marshal.Copy(bytes_mem,Result, 0,Result.Length); Marshal.FreeHGlobal(bytes_mem); end; public procedure SerializeTo(bw: System.IO.BinaryWriter); begin var bts := Serialize; bw.Write(bts.Length); bw.Write(bts); end; public procedure SerializeTo(str: System.IO.Stream) := SerializeTo(new System.IO.BinaryWriter(str)); public static function Deserialize(c: Context; bin: array of byte): ProgramCode; begin var ec: ErrorCode; Result := new ProgramCode; Result.cntxt := c; var gchnd := GCHandle.Alloc(bin, GCHandleType.Pinned); var bin_mem: ^byte := pointer(gchnd.AddrOfPinnedObject); var bin_len := new UIntPtr(bin.Length); Result._program := cl.CreateProgramWithBinary(c._context,1,@c._device, @bin_len, @bin_mem, nil, @ec); ec.RaiseIfError; gchnd.Free; end; public static function DeserializeFrom(c: Context; br: System.IO.BinaryReader): ProgramCode; begin var bin_len := br.ReadInt32; var bin_arr := br.ReadBytes(bin_len); if bin_arr.Length(f: ()->T): CommandQueue; ///Host Procecure Queue ///Создаёт новую очередь, выполняющую процедуру на CPU ///И возвращающую object(nil) function HPQ(p: ()->()): CommandQueue; ///Складывает все очереди qs ///Возвращает очередь, по очереди выполняющую все очереди из qs function CombineSyncQueue(qs: List): CommandQueue; ///Складывает все очереди qs ///Возвращает очередь, по очереди выполняющую все очереди из qs function CombineSyncQueue(qs: List>): CommandQueue; ///Складывает все очереди qs ///Возвращает очередь, по очереди выполняющую все очереди из qs function CombineSyncQueue(params qs: array of CommandQueueBase): CommandQueue; ///Складывает все очереди qs ///Возвращает очередь, по очереди выполняющую все очереди из qs function CombineSyncQueue(params qs: array of CommandQueue): CommandQueue; ///Складывает все очереди qs ///Возвращает очередь, по очереди выполняющую все очереди из qs ///И затем применяет преобразование conv чтобы получить из результатов очередей qs - свой результат function CombineSyncQueue(conv: Func; qs: List): CommandQueue; ///Складывает все очереди qs ///Возвращает очередь, по очереди выполняющую все очереди из qs ///И затем применяет преобразование conv чтобы получить из результатов очередей qs - свой результат function CombineSyncQueue(conv: Func; qs: List>): CommandQueue; ///Складывает все очереди qs ///Возвращает очередь, по очереди выполняющую все очереди из qs ///И затем применяет преобразование conv чтобы получить из результатов очередей qs - свой результат function CombineSyncQueue(conv: Func; params qs: array of CommandQueueBase): CommandQueue; ///Складывает все очереди qs ///Возвращает очередь, по очереди выполняющую все очереди из qs ///И затем применяет преобразование conv чтобы получить из результатов очередей qs - свой результат function CombineSyncQueue(conv: Func; params qs: array of CommandQueue): CommandQueue; ///Умножает все очереди qs ///Возвращает очередь, параллельно выполняющую все очереди из qs function CombineAsyncQueue(qs: List): CommandQueue; ///Умножает все очереди qs ///Возвращает очередь, параллельно выполняющую все очереди из qs function CombineAsyncQueue(qs: List>): CommandQueue; ///Умножает все очереди qs ///Возвращает очередь, параллельно выполняющую все очереди из qs function CombineAsyncQueue(params qs: array of CommandQueueBase): CommandQueue; ///Умножает все очереди qs ///Возвращает очередь, параллельно выполняющую все очереди из qs function CombineAsyncQueue(params qs: array of CommandQueue): CommandQueue; ///Умножает все очереди qs ///Возвращает очередь, параллельно выполняющую все очереди из qs ///И затем применяет преобразование conv чтобы получить из результатов очередей qs - свой результат function CombineAsyncQueue(conv: Func; qs: List): CommandQueue; ///Умножает все очереди qs ///Возвращает очередь, параллельно выполняющую все очереди из qs ///И затем применяет преобразование conv чтобы получить из результатов очередей qs - свой результат function CombineAsyncQueue(conv: Func; qs: List>): CommandQueue; ///Умножает все очереди qs ///Возвращает очередь, параллельно выполняющую все очереди из qs ///И затем применяет conv чтобы получить из результатов очередей qs - свой результат ///И затем применяет преобразование conv чтобы получить из результатов очередей qs - свой результат function CombineAsyncQueue(conv: Func; params qs: array of CommandQueueBase): CommandQueue; ///Умножает все очереди qs ///Возвращает очередь, параллельно выполняющую все очереди из qs ///И затем применяет преобразование conv чтобы получить из результатов очередей qs - свой результат function CombineAsyncQueue(conv: Func; params qs: array of CommandQueue): CommandQueue; {$endregion Сахарные подпрограммы} implementation {$region Utils} type CLGCHandle = sealed class gchnd: GCHandle; constructor(o: object) := gchnd := GCHandle.Alloc(o, GCHandleType.Pinned); property Ptr: IntPtr read gchnd.AddrOfPinnedObject; procedure CLFree(ev: cl_event; status: CommandExecutionStatus; data: pointer) := gchnd.Free; end; {$endregion Utils} {$region CommandQueue} {$region Dummy} type DummyCommandQueue = sealed class(CommandQueue) public constructor(o: T) := self.res := o; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 0; private procedure CLSignalMWEvent(ev: cl_event; status: CommandExecutionStatus; data: pointer) := self.SignalMWEvent; protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin MakeBusy; self.ev := prev_ev; if prev_ev=cl_event.Zero then SignalMWEvent else cl.SetEventCallback(prev_ev, CommandExecutionStatus.COMPLETE, CLSignalMWEvent, nil).RaiseIfError; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new DummyCommandQueue(self.res); end; static function CommandQueueBase.operator implicit(o: object): CommandQueueBase := new DummyCommandQueue(o); static function CommandQueue.operator implicit(o: T): CommandQueue := new DummyCommandQueue(o); {$endregion Dummy} {$region HostFunc} type CommandQueueHostFunc = sealed class(CommandQueue) private f: ()->T; public constructor(f: ()->T) := self.f := f; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1; protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(prev_ev); self.res := self.f(); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueHostFunc(self.f); end; {$endregion HostFunc} {$region Multiusable} type MultiusableCommandQueueNode=class; // invoke_status: // 0 - выполнение не начато // 1 - выполнение начинается // 3 - выполнение прекращается MultiusableCommandQueueHub = class public q: CommandQueueBase; public invoke_status := 0; public invoked_count := 0; public constructor(q: CommandQueueBase) := self.q := q; public procedure OnNodeInvoked(c: Context; cq: __SafeNativQueue; tasks: List); public procedure OnNodeUnInvoked; end; MultiusableCommandQueueNode = sealed class(CommandQueue) public hub: MultiusableCommandQueueHub; public constructor(hub: MultiusableCommandQueueHub) := self.hub := hub; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override; begin Result := 1; if prev_hubs.Add(hub) then Result += hub.q.GetEstimateTaskCount(prev_hubs); end; protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; hub.OnNodeInvoked(c, cq, tasks); self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; var ev_lst := new List(2); if prev_ev<>cl_event.Zero then ev_lst += prev_ev; if hub.q.ev<>cl_event.Zero then ev_lst += hub.q.ev; tasks += Task.Run(()-> begin if ev_lst.Count<>0 then WaitAndRelease(ev_lst); self.res := T(hub.q.GetRes); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end; protected procedure UnInvoke; override; begin inherited; hub.OnNodeUnInvoked; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override; begin var res_hub_o: object; var res_hub: MultiusableCommandQueueHub; if muhs.TryGetValue(self.hub, res_hub_o) then res_hub := MultiusableCommandQueueHub&(res_hub_o) else begin res_hub := new MultiusableCommandQueueHub(self.hub.q.InternalCloneCached(muhs, cache)); muhs.Add(self.hub, res_hub); end; Result := new MultiusableCommandQueueNode(res_hub); end; end; procedure MultiusableCommandQueueHub.OnNodeInvoked(c: Context; cq: __SafeNativQueue; tasks: List); begin case invoke_status of 0: invoke_status := 1; 2: raise new QueueDoubleInvokeException; end; if invoked_count=0 then q.Invoke(c,cq, cl_event.Zero, tasks); if q.ev<>cl_event.Zero then cl.RetainEvent(q.ev).RaiseIfError; invoked_count += 1; end; procedure MultiusableCommandQueueHub.OnNodeUnInvoked; begin case invoke_status of //0: raise new InvalidOperationException('Ошибка внутри модуля OpenCLABC: совершена попыта завершить не запущенную очередь. Сообщите, пожалуйста, разработчику OpenCLABC'); 1: invoke_status := 2; end; invoked_count -= 1; if invoked_count=0 then begin if q.ev<>cl_event.Zero then cl.ReleaseEvent(q.ev).RaiseIfError; invoke_status := 0; q.UnInvoke; end; end; //function CommandQueueBase.Multiusable(n: integer): array of CommandQueueBase; //begin // var hub := new MultiusableCommandQueueHub(self); // Result := ArrGen(n, i-> new MultiusableCommandQueueNode(hub) as CommandQueueBase ); //end; // //function CommandQueueBase.Multiusable: ()->CommandQueueBase; //begin // var hub := new MultiusableCommandQueueHub(self); // Result := ()-> new MultiusableCommandQueueNode(hub); //end; function CommandQueue.Multiusable(n: integer): array of CommandQueue; begin var hub := new MultiusableCommandQueueHub(self); Result := ArrGen(n, i-> new MultiusableCommandQueueNode(hub) as CommandQueue ); end; function CommandQueue.Multiusable: ()->CommandQueue; begin var hub := new MultiusableCommandQueueHub(self); Result := ()-> new MultiusableCommandQueueNode(hub) as CommandQueue; end; {$endregion Multiusable} {$region ThenConvert} type CommandQueueResConvertor = sealed class(CommandQueue) q: CommandQueueBase; f: (T1,Context)->T2; constructor(q: CommandQueueBase; f: (T1,Context)->T2); begin self.q := q; self.f := f; end; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + q.GetEstimateTaskCount(prev_hubs); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; q.Invoke(c, cq, prev_ev, tasks); self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin if q.ev<>cl_event.Zero then WaitAndRelease(q.ev); self.res := self.f(T1(q.GetRes), c); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end; protected procedure UnInvoke; override; begin inherited; q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueResConvertor(self.q.InternalCloneCached(muhs, cache), self.f); end; //function CommandQueueBase.ThenConvert(f: (object,Context)->T) := //new CommandQueueResConvertor(self, f); function CommandQueue.ThenConvert(f: (T,Context)->T2) := new CommandQueueResConvertor(self, f); {$endregion ThenConvert} {$region WaitFor} type CommandQueueWait = sealed class(CommandQueue) public q, wait_source: CommandQueueBase; public allow_source_cloning: boolean; public constructor(q, wait_source: CommandQueueBase; allow_source_cloning: boolean); begin self.q := q; self.wait_source := wait_source; self.allow_source_cloning := allow_source_cloning; end; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 2; protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; var uev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; var ev_lst := new List(2); if prev_ev<>cl_event.Zero then ev_lst += prev_ev; ev_lst += wait_source.GetMWEvent(c._context); tasks += Task.Run(()-> begin cl.WaitForEvents(ev_lst.Count, ev_lst.ToArray).RaiseIfError; cl.SetUserEventStatus(uev, CommandExecutionStatus.COMPLETE).RaiseIfError; end); q.Invoke(c, cq, uev, tasks); self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin cl.WaitForEvents(1,@q.ev).RaiseIfError; self.res := T( q.GetRes() ); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end; protected procedure UnInvoke; override; begin inherited; q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueWait( self.q.InternalCloneCached(muhs, cache), allow_source_cloning? self.wait_source.InternalCloneCached(muhs, cache) : self.wait_source, self.allow_source_cloning ); end; function CommandQueue.WaitFor(q: CommandQueueBase; allow_q_cloning: boolean): CommandQueue := new CommandQueueWait(self, q, allow_q_cloning); {$endregion WaitFor} {$region SyncList} //ToDo лучше всё же хранить массив а не список... И для Async тоже //ToDo базовые не_шаблонные типы, чтоб можно было сделать красивее в CommandQueueBase.operator+ type CommandQueueSyncList = sealed class(CommandQueue) public lst: List; public constructor := lst := new List; public constructor(qs: List) := lst := qs; public constructor(qs: array of CommandQueueBase) := lst := qs.ToList; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + lst.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; foreach var sq in lst do begin sq.Invoke(c, cq, prev_ev, tasks); prev_ev := sq.ev; end; if prev_ev<>cl_event.Zero then begin self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(prev_ev); self.res := T(lst[lst.Count-1].GetRes); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end else begin self.res := T(lst[lst.Count-1].GetRes); self.ev := cl_event.Zero; SignalMWEvent; end; end; protected procedure UnInvoke; override; begin inherited; foreach var q in lst do q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueSyncList(self.lst.ConvertAll(q->q.InternalCloneCached(muhs, cache))); end; CommandQueueTSyncList = sealed class(CommandQueue) public lst: List>; public constructor := lst := new List>; public constructor(qs: List>) := lst := qs; public constructor(qs: array of CommandQueue) := lst := qs.ToList; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + lst.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; foreach var sq in lst do begin sq.Invoke(c, cq, prev_ev, tasks); prev_ev := sq.ev; end; if prev_ev<>cl_event.Zero then begin self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(prev_ev); self.res := lst[lst.Count-1].res; cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end else begin self.res := lst[lst.Count-1].res; self.ev := cl_event.Zero; SignalMWEvent; end; end; protected procedure UnInvoke; override; begin inherited; foreach var q in lst do q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueTSyncList(self.lst.ConvertAll(q->CommandQueue&(q.InternalCloneCached(muhs, cache)))); end; CommandQueueCSyncList = sealed class(CommandQueue) public lst: List; public conv: Func; public constructor := lst := new List; public constructor(qs: List; conv: Func); begin self.lst := qs; self.conv := conv; end; public constructor(qs: array of CommandQueueBase; conv: Func); begin self.lst := qs.ToList; self.conv := conv; end; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + lst.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; foreach var sq in lst do begin sq.Invoke(c, cq, prev_ev, tasks); prev_ev := sq.ev; end; if prev_ev<>cl_event.Zero then begin self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(prev_ev); var a := new object[lst.Count]; for var i := 0 to lst.Count-1 do a[i] := lst[i].GetRes; self.res := conv(a); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end else begin var a := new object[lst.Count]; for var i := 0 to lst.Count-1 do a[i] := lst[i].GetRes; self.res := conv(a); self.ev := cl_event.Zero; SignalMWEvent; end; end; protected procedure UnInvoke; override; begin inherited; foreach var q in lst do q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueCSyncList(self.lst.ConvertAll(q->q.InternalCloneCached(muhs, cache)), conv); end; CommandQueueCTSyncList = sealed class(CommandQueue) public lst: List>; public conv: Func; public constructor := lst := new List>; public constructor(qs: List>; conv: Func); begin self.lst := qs; self.conv := conv; end; public constructor(qs: array of CommandQueue; conv: Func); begin self.lst := qs.ToList; self.conv := conv; end; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + lst.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; foreach var sq in lst do begin sq.Invoke(c, cq, prev_ev, tasks); prev_ev := sq.ev; end; if prev_ev<>cl_event.Zero then begin self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(prev_ev); var a := new T[lst.Count]; for var i := 0 to lst.Count-1 do a[i] := lst[i].res; self.res := conv(a); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end else begin var a := new T[lst.Count]; for var i := 0 to lst.Count-1 do a[i] := lst[i].res; self.res := conv(a); self.ev := cl_event.Zero; SignalMWEvent; end; end; protected procedure UnInvoke; override; begin inherited; foreach var q in lst do q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueCTSyncList(self.lst.ConvertAll(q->CommandQueue&(q.InternalCloneCached(muhs, cache))), conv); end; static function CommandQueue.operator+(q1: CommandQueue; q2: CommandQueue): CommandQueue; begin var ql1 := q1 as CommandQueueSyncList; var ql2 := q2 as CommandQueueSyncList; var qtl1 := q1 as CommandQueueTSyncList; var qtl2 := q2 as CommandQueueTSyncList; if (typeof(T)=typeof(T2)) and (ql1=nil) and (ql2=nil) then begin var res := new CommandQueueTSyncList; if qtl1<>nil then res.lst.AddRange(qtl1.lst.Cast&>) else res.lst += q1 as object as CommandQueue; if qtl2<>nil then res.lst.AddRange(qtl2.lst) else res.lst += q2; Result := res; end else begin var res := new CommandQueueSyncList; if ql1<>nil then res.lst.AddRange(ql1.lst) else if qtl1<>nil then res.lst.AddRange(qtl1.lst.Cast&) else res.lst += q1 as CommandQueueBase; if ql2<>nil then res.lst.AddRange(ql2.lst) else if qtl2<>nil then res.lst.AddRange(qtl2.lst.Cast&) else res.lst += q2 as CommandQueueBase; Result := res; end; end; static function CommandQueueBase.operator+(q1, q2: CommandQueueBase): CommandQueueBase := new CommandQueueSyncList(new CommandQueueBase[](q1,q2)); static function CommandQueueBase.operator+(q1: CommandQueueBase; q2: CommandQueue): CommandQueue := new CommandQueueSyncList(new CommandQueueBase[](q1, q2 as object as CommandQueueBase)); //ToDo #2119 {$endregion SyncList} {$region AsyncList} type CommandQueueAsyncList = sealed class(CommandQueue) public lst: List; public constructor := lst := new List; public constructor(qs: List) := lst := qs; public constructor(qs: array of CommandQueueBase) := lst := qs.ToList; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + lst.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; var evs := new List(lst.Count); foreach var sq in lst do begin var scq: __SafeNativQueue := nil; sq.Invoke(c, scq, prev_ev, tasks); if sq.ev<>cl_event.Zero then evs += sq.ev; end; if evs.Count<>0 then begin cq := nil; self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(evs); self.res := T(lst[lst.Count-1].GetRes); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end else begin self.res := T(lst[lst.Count-1].GetRes); self.ev := cl_event.Zero; SignalMWEvent; end; end; protected procedure UnInvoke; override; begin inherited; foreach var q in lst do q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueAsyncList(self.lst.ConvertAll(q->q.InternalCloneCached(muhs, cache))); end; CommandQueueTAsyncList = sealed class(CommandQueue) public lst: List>; public constructor := lst := new List>; public constructor(qs: List>) := lst := qs; public constructor(qs: array of CommandQueue) := lst := qs.ToList; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + lst.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; var evs := new List(lst.Count); foreach var sq in lst do begin var scq: __SafeNativQueue := nil; sq.Invoke(c, scq, prev_ev, tasks); if sq.ev<>cl_event.Zero then evs += sq.ev; end; if evs.Count<>0 then begin cq := nil; self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(evs); self.res := lst[lst.Count-1].res; cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end else begin self.res := lst[lst.Count-1].res; self.ev := cl_event.Zero; SignalMWEvent; end; end; protected procedure UnInvoke; override; begin inherited; foreach var q in lst do q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueTAsyncList(self.lst.ConvertAll(q->CommandQueue&(q.InternalCloneCached(muhs, cache)))); end; CommandQueueCAsyncList = sealed class(CommandQueue) public lst: List; public conv: Func; public constructor := lst := new List; public constructor(qs: List; conv: Func); begin self.lst := qs; self.conv := conv; end; public constructor(qs: array of CommandQueueBase; conv: Func); begin self.lst := qs.ToList; self.conv := conv; end; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + lst.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; var evs := new List(lst.Count); foreach var sq in lst do begin var scq: __SafeNativQueue := nil; sq.Invoke(c, scq, prev_ev, tasks); if sq.ev<>cl_event.Zero then evs += sq.ev; end; if evs.Count<>0 then begin cq := nil; self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(evs); var a := new object[lst.Count]; for var i := 0 to lst.Count-1 do a[i] := lst[i].GetRes; self.res := conv(a); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end else begin var a := new object[lst.Count]; for var i := 0 to lst.Count-1 do a[i] := lst[i].GetRes; self.res := conv(a); self.ev := cl_event.Zero; SignalMWEvent; end; end; protected procedure UnInvoke; override; begin inherited; foreach var q in lst do q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueCAsyncList(self.lst.ConvertAll(q->q.InternalCloneCached(muhs, cache)), conv); end; CommandQueueCTAsyncList = sealed class(CommandQueue) public lst: List>; public conv: Func; public constructor := lst := new List>; public constructor(qs: List>; conv: Func); begin self.lst := qs; self.conv := conv; end; public constructor(qs: array of CommandQueue; conv: Func); begin self.lst := qs.ToList; self.conv := conv; end; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1 + lst.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)); protected procedure Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; MakeBusy; var evs := new List(lst.Count); foreach var sq in lst do begin var scq: __SafeNativQueue := nil; sq.Invoke(c, scq, prev_ev, tasks); if sq.ev<>cl_event.Zero then evs += sq.ev; end; if evs.Count<>0 then begin cq := nil; self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(evs); var a := new T[lst.Count]; for var i := 0 to lst.Count-1 do a[i] := lst[i].res; self.res := conv(a); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; SignalMWEvent; end); end else begin var a := new T[lst.Count]; for var i := 0 to lst.Count-1 do a[i] := lst[i].res; self.res := conv(a); self.ev := cl_event.Zero; SignalMWEvent; end; end; protected procedure UnInvoke; override; begin inherited; foreach var q in lst do q.UnInvoke; end; protected function InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; override := new CommandQueueCTAsyncList(self.lst.ConvertAll(q->CommandQueue&(q.InternalCloneCached(muhs, cache))), conv); end; static function CommandQueue.operator*(q1: CommandQueue; q2: CommandQueue): CommandQueue; begin var ql1 := q1 as CommandQueueAsyncList; var ql2 := q2 as CommandQueueAsyncList; var qtl1 := q1 as CommandQueueTAsyncList; var qtl2 := q2 as CommandQueueTAsyncList; if (typeof(T)=typeof(T2)) and (ql1=nil) and (ql2=nil) then begin var res := new CommandQueueTAsyncList; if qtl1<>nil then res.lst.AddRange(qtl1.lst.Cast&>) else res.lst += q1 as object as CommandQueue; if qtl2<>nil then res.lst.AddRange(qtl2.lst) else res.lst += q2; Result := res; end else begin var res := new CommandQueueAsyncList; if ql1<>nil then res.lst.AddRange(ql1.lst) else if qtl1<>nil then res.lst.AddRange(qtl1.lst.Cast&) else res.lst += q1 as CommandQueueBase; if ql2<>nil then res.lst.AddRange(ql2.lst) else if qtl2<>nil then res.lst.AddRange(qtl2.lst.Cast&) else res.lst += q2 as CommandQueueBase; Result := res; end; end; static function CommandQueueBase.operator*(q1, q2: CommandQueueBase): CommandQueueBase := new CommandQueueAsyncList(new CommandQueueBase[](q1,q2)); static function CommandQueueBase.operator*(q1: CommandQueueBase; q2: CommandQueue): CommandQueue := new CommandQueueAsyncList(new CommandQueueBase[](q1,q2 as object as CommandQueueBase)); //ToDo #2119 {$endregion AsyncList} {$region GPUCommand} {$region GPUCommandContainer} constructor GPUCommandContainer.Create(q: CommandQueue) := self.res_q_hub := new MultiusableCommandQueueHub(q); function GPUCommandContainer.GetNewResPlug: CommandQueue := new MultiusableCommandQueueNode( MultiusableCommandQueueHub&(res_q_hub) ); function GPUCommandContainer.GetEstimateTaskCount(prev_hubs: HashSet): integer; begin Result := commands.Sum(comm->comm.GetEstimateTaskCount(prev_hubs)); if res_q_hub=nil then exit; Result += commands.Count; // каждая команда вызывает выполнение 1 ноды if prev_hubs.Contains(self.res_q_hub) then exit; // если команда использовала GetSizeQ - хаб уже посчитало Result += MultiusableCommandQueueHub&(self.res_q_hub).q.GetEstimateTaskCount(prev_hubs); end; procedure GPUCommandContainer.Invoke(c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); begin MakeBusy; var new_plug: ()->CommandQueue; if res_q_hub=nil then begin new_plug := ()->nil; last_center_plug := nil; OnEarlyInit(c); end else begin var plug := GetNewResPlug; plug.Invoke(c, cq, prev_ev, tasks); prev_ev := plug.ev; last_center_plug := plug; new_plug := GetNewResPlug; end; foreach var comm in commands do begin comm.Invoke(new_plug, res, c, cq, prev_ev, tasks); prev_ev := comm.ev; end; self.ev := prev_ev; if prev_ev=cl_event.Zero then SignalMWEvent else cl.SetEventCallback(prev_ev, CommandExecutionStatus.COMPLETE, CLSignalMWEvent, nil).RaiseIfError; end; {$endregion GPUCommandContainer} {$region QueueCommand} type QueueCommand = sealed class(GPUCommand) public q: CommandQueueBase; public constructor(q: CommandQueueBase) := self.q := q; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := q.GetEstimateTaskCount(prev_hubs); protected procedure Invoke(o_q: CommandQueue; o: T; c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin q.Invoke(c, cq, prev_ev, tasks); self.ev := q.ev; end; protected procedure UnInvoke; override; begin // inherited; // не надо, q уже удалило свой эвент self.ev := cl_event.Zero; q.UnInvoke; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new QueueCommand(self.q.InternalCloneCached(muhs, cache) as CommandQueue); end; procedure GPUCommandContainer.InternalAddQueue(q: CommandQueueBase) := commands += new QueueCommand(q) as GPUCommand; {$endregion QueueCommand} {$region ProcCommand} type ProcCommand = sealed class(GPUCommand) public p: (T,Context)->(); public last_o_q: CommandQueue; public constructor(p: (T,Context)->()) := self.p := p; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1; protected procedure Invoke(o_q: CommandQueue; o: T; c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; self.last_o_q := o_q; if o_q<>nil then begin o_q.Invoke(c, cq, prev_ev, tasks); prev_ev := o_q.ev; end; self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; tasks += Task.Run(()-> begin WaitAndRelease(prev_ev); self.p( (o_q=nil?o:o_q.res), c ); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; end); end; protected procedure UnInvoke; override := if last_o_q<>nil then begin last_o_q.UnInvoke; last_o_q := nil; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new ProcCommand(self.p); end; procedure GPUCommandContainer.InternalAddProc(p: (T,Context)->()) := commands += new ProcCommand(p) as GPUCommand; {$endregion ProcCommand} {$region WaitCommand} type WaitCommand = sealed class(GPUCommand) public wait_source: CommandQueueBase; public allow_q_cloning: boolean; public constructor(wait_source: CommandQueueBase; allow_q_cloning: boolean); begin self.wait_source := wait_source; self.allow_q_cloning := allow_q_cloning; end; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := 1; protected procedure Invoke(o_q: CommandQueue; o: T; c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin var ec: ErrorCode; self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; var ev_lst := new List(2); if prev_ev<>cl_event.Zero then ev_lst += prev_ev; ev_lst += wait_source.GetMWEvent(c._context); tasks += Task.Run(()-> begin cl.WaitForEvents(ev_lst.Count, ev_lst.ToArray).RaiseIfError; cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; end); end; protected procedure UnInvoke; override := exit; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new WaitCommand( allow_q_cloning? wait_source.InternalCloneCached(muhs, cache) : wait_source, allow_q_cloning ); end; procedure GPUCommandContainer.InternalAddWait(q: CommandQueueBase; allow_q_cloning: boolean) := commands.Add( new WaitCommand(q, allow_q_cloning) ); {$endregion WaitCommand} {$region Special GPUCommand's} type /// От этого наследуют все типы вызывающие cl.Enqueue* методы DirectGPUCommandBase = abstract class(GPUCommand) public last_o_q: CommandQueue; {$region def} protected function GetSubQCount: integer; abstract; protected function EnmrSubQ: sequence of CommandQueueBase; abstract; protected procedure LowerEnqueueSelf(c: Context; cq: cl_command_queue; o: T; ev_c: integer; prev_ev, res_ev: ^cl_event); abstract; {$endregion def} {$region sub implementation} protected procedure Invoke(o_q: CommandQueue; o: T; c: Context; var cq: __SafeNativQueue; prev_ev: cl_event; tasks: List); override; begin self.last_o_q := o_q; if o_q<>nil then begin o_q.Invoke(c, cq, prev_ev, tasks); prev_ev := cl_event.Zero; end; var ev_lst := new List(GetSubQCount); foreach var sq in EnmrSubQ do begin sq.InvokeNewQ(c, tasks); ev_lst += sq.ev; end; ev_lst.RemoveAll(ev->ev=cl_event.Zero); if cq=nil then cq := new __SafeNativQueue(c._context,c._device); // если предыдущий Invoke содержал асинхронный EnqueueSelf или вообще был в другой ветке умноженных очередей if (ev_lst.Count=0) and ((o_q=nil) or (o_q.ev=cl_event.Zero)) then begin var lo := o_q=nil ? o : o_q.res; if prev_ev=cl_event.Zero then LowerEnqueueSelf(c, cq.q, lo, 0, nil, @self.ev) else LowerEnqueueSelf(c, cq.q, lo, 1, @prev_ev, @self.ev); end else begin var ec: ErrorCode; self.ev := cl.CreateUserEvent(c._context, ec); ec.RaiseIfError; var ncq := cq.q; tasks += Task.Run(()-> begin var lo: T; if last_o_q=nil then lo := o else begin WaitAndRelease(last_o_q.ev); lo := last_o_q.res; end; if ev_lst.Count<>0 then WaitAndRelease(ev_lst); var buff_ev: cl_event; if prev_ev=cl_event.Zero then LowerEnqueueSelf(c, ncq, lo, 0, nil, @buff_ev) else LowerEnqueueSelf(c, ncq, lo, 1, @prev_ev, @buff_ev); WaitAndRelease(buff_ev); cl.SetUserEventStatus(self.ev, CommandExecutionStatus.COMPLETE).RaiseIfError; end); cq := nil; // асинхронное EnqueueSelf, далее придётся создать новую очередь end; end; protected function GetEstimateTaskCount(prev_hubs: HashSet): integer; override := EnmrSubQ.Sum(sq->sq.GetEstimateTaskCount(prev_hubs)) + 1; protected procedure UnInvoke; override; begin if last_o_q<>nil then begin last_o_q.UnInvoke; last_o_q := nil; end; foreach var sq in EnmrSubQ do sq.UnInvoke; end; {$endregion sub implementation} end; {$endregion Special GPUCommand's} {$endregion GPUCommand} {$region Buffer} {$region BufferCommandQueue} //ToDo function костыль_BufferCommandQueue_Create(b: Buffer; c: Context): Buffer; begin if b.memobj=cl_mem.Zero then b.Init(c); Result := b; end; constructor BufferCommandQueue.Create(q: CommandQueue) := inherited Create(q.ThenConvert(костыль_BufferCommandQueue_Create)); function BufferCommandQueue.GetSizeQ: CommandQueue := self.res_q_hub=nil?integer(res.sz.ToUInt32) : self.GetNewResPlug().ThenConvert(b->integer(b.sz.ToUInt32)); procedure BufferCommandQueue.OnEarlyInit(c: Context) := if res.memobj=cl_mem.Zero then res.Init(c); function BufferCommandQueue.InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; begin var res := new BufferCommandQueue(self.res); if self.res_q_hub<>nil then begin var hub := MultiusableCommandQueueHub&(self.res_q_hub); res.res_q_hub := new MultiusableCommandQueueHub(CommandQueue&( hub.q.InternalCloneCached(muhs, cache) )); muhs.Add(self.res_q_hub, res.res_q_hub); end; res.commands.Capacity := self.commands.Capacity; foreach var comm in self.commands do res.commands += comm.Clone(muhs, cache); Result := res; end; {$endregion BufferCommandQueue} {$region Misc} type BufferCommandBase = abstract class(DirectGPUCommandBase) protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); abstract; protected procedure LowerEnqueueSelf(c: Context; cq: cl_command_queue; b: Buffer; ev_c: integer; prev_ev, res_ev: ^cl_event); override := EnqueueSelf(c, cq, b.memobj, ev_c, prev_ev, res_ev); end; {$endregion Misc} {$region Write} type BufferCommandWriteData = sealed class(BufferCommandBase) public ptr: CommandQueue; public offset, len: CommandQueue; public constructor(ptr: CommandQueue; offset, len: CommandQueue); begin self.ptr := ptr; self.offset := offset; self.len := len; end; protected function GetSubQCount: integer; override := 3; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield ptr; yield offset; yield len; end; protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override := cl.EnqueueWriteBuffer(cq, mem, 0, new UIntPtr(offset.res), new UIntPtr(len.res), ptr.res, ev_c, prev_ev, res_ev).RaiseIfError; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandWriteData( CommandQueue& (self.ptr .InternalCloneCached(muhs, cache)), CommandQueue&(self.offset.InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; BufferCommandWriteArray = sealed class(BufferCommandBase) public a: CommandQueue<&Array>; public offset, len: CommandQueue; public constructor(a: CommandQueue<&Array>; offset, len: CommandQueue); begin self.a := a; self.offset := offset; self.len := len; end; protected function GetSubQCount: integer; override := 3; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield a; yield offset; yield len; end; protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override; begin var hnd := new CLGCHandle(a.res); cl.EnqueueWriteBuffer(cq, mem, 0, new UIntPtr(offset.res), new UIntPtr(len.res), hnd.Ptr, ev_c, prev_ev, res_ev).RaiseIfError; cl.SetEventCallback(res_ev^, CommandExecutionStatus.COMPLETE, hnd.CLFree, nil).RaiseIfError; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandWriteArray( CommandQueue&<&Array> (self.a .InternalCloneCached(muhs, cache)), CommandQueue&(self.offset.InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; BufferCommandWriteValue = sealed class(BufferCommandBase) public ptr: CommandQueue; public offset, len: CommandQueue; public constructor(ptr: CommandQueue; offset, len: CommandQueue); begin self.ptr := ptr; self.offset := offset; self.len := len; end; protected function GetSubQCount: integer; override := 3; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield ptr; yield offset; yield len; end; protected procedure CLFreeMem(ev: cl_event; status: CommandExecutionStatus; data: pointer) := Marshal.FreeHGlobal(ptr.res); protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override; begin cl.EnqueueWriteBuffer(cq, mem, 0, new UIntPtr(offset.res), new UIntPtr(len.res), ptr.res, ev_c, prev_ev, res_ev).RaiseIfError; cl.SetEventCallback(res_ev^, CommandExecutionStatus.COMPLETE, CLFreeMem, nil).RaiseIfError; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandWriteValue( CommandQueue& (self.ptr .InternalCloneCached(muhs, cache)), CommandQueue&(self.offset.InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; function BufferCommandQueue.AddWriteData(ptr: CommandQueue; offset, len: CommandQueue) := AddCommand(new BufferCommandWriteData(ptr, offset, len)); function BufferCommandQueue.AddWriteArray(a: CommandQueue<&Array>; offset, len: CommandQueue) := AddCommand(new BufferCommandWriteArray(a, offset, len)); function BufferCommandQueue.AddWriteValue(val: TRecord; offset: CommandQueue): BufferCommandQueue; begin var sz := Marshal.SizeOf&; var ptr := Marshal.AllocHGlobal(sz); var typed_ptr: ^TRecord := pointer(ptr); typed_ptr^ := val; Result := AddCommand(new BufferCommandWriteValue(ptr, offset,Marshal.SizeOf&)); end; function BufferCommandQueue.AddWriteValue(val: CommandQueue; offset: CommandQueue) := AddCommand(new BufferCommandWriteValue( val.ThenConvert&(vval-> //ToDo #2067 begin var sz := Marshal.SizeOf&; var ptr := Marshal.AllocHGlobal(sz); var typed_ptr: ^TRecord := pointer(ptr); var костыль_ptr: ^TRecord := pointer(@vval); //ToDo #2068 typed_ptr^ := костыль_ptr^; // := vval Result := ptr; end), offset, Marshal.SizeOf& )); {$endregion Write} {$region Read} type BufferCommandReadData = sealed class(BufferCommandBase) public ptr: CommandQueue; public offset, len: CommandQueue; public constructor(ptr: CommandQueue; offset, len: CommandQueue); begin self.ptr := ptr; self.offset := offset; self.len := len; end; protected function GetSubQCount: integer; override := 3; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield ptr; yield offset; yield len; end; protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override := cl.EnqueueReadBuffer(cq, mem, 0, new UIntPtr(offset.res), new UIntPtr(len.res), ptr.res, ev_c, prev_ev, res_ev).RaiseIfError; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandReadData( CommandQueue& (self.ptr .InternalCloneCached(muhs, cache)), CommandQueue&(self.offset.InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; BufferCommandReadArray = sealed class(BufferCommandBase) public a: CommandQueue<&Array>; public offset, len: CommandQueue; public constructor(a: CommandQueue<&Array>; offset, len: CommandQueue); begin self.a := a; self.offset := offset; self.len := len; end; protected function GetSubQCount: integer; override := 3; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield a; yield offset; yield len; end; protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override; begin var hnd := new CLGCHandle(a.res); cl.EnqueueReadBuffer(cq, mem, 0, new UIntPtr(offset.res), new UIntPtr(len.res), hnd.Ptr, ev_c, prev_ev, res_ev).RaiseIfError; cl.SetEventCallback(res_ev^, CommandExecutionStatus.COMPLETE, hnd.CLFree, nil).RaiseIfError; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandReadArray( CommandQueue&<&Array> (self.a .InternalCloneCached(muhs, cache)), CommandQueue&(self.offset.InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; function BufferCommandQueue.AddReadData(ptr: CommandQueue; offset, len: CommandQueue) := AddCommand(new BufferCommandReadData(ptr, offset, len)); function BufferCommandQueue.AddReadArray(a: CommandQueue<&Array>; offset, len: CommandQueue) := AddCommand(new BufferCommandReadArray(a, offset, len)); {$endregion Read} {$region Fill} type BufferCommandDataFill = sealed class(BufferCommandBase) public ptr: CommandQueue; public pattern_len, offset, len: CommandQueue; public b_q: CommandQueue; public constructor(ptr: CommandQueue; pattern_len, offset, len: CommandQueue); begin self.ptr := ptr; self.pattern_len := pattern_len; self.offset := offset; self.len := len; end; protected function GetSubQCount: integer; override := 4; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield ptr; yield pattern_len; yield offset; yield len; end; protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override := cl.EnqueueFillBuffer(cq, mem, ptr.res, new UIntPtr(pattern_len.res), new UIntPtr(offset.res), new UIntPtr(len.res), ev_c, prev_ev, res_ev).RaiseIfError; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandDataFill( CommandQueue& (self.ptr .InternalCloneCached(muhs, cache)), CommandQueue&(self.pattern_len .InternalCloneCached(muhs, cache)), CommandQueue&(self.offset .InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; BufferCommandArrayFill = sealed class(BufferCommandBase) public a: CommandQueue<&Array>; public offset, len: CommandQueue; public b_q: CommandQueue; public constructor(a: CommandQueue<&Array>; offset, len: CommandQueue); begin self.a := a; self.offset := offset; self.len := len; end; protected function GetSubQCount: integer; override := 3; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield a; yield offset; yield len; end; protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override; begin var hnd := new CLGCHandle(a.res); var pattern_sz := Marshal.SizeOf(a.res.GetType.GetElementType) * a.res.Length; cl.EnqueueFillBuffer(cq, mem, hnd.Ptr, new UIntPtr(pattern_sz), new UIntPtr(offset.res), new UIntPtr(len.res), ev_c, prev_ev, res_ev).RaiseIfError; cl.SetEventCallback(res_ev^, CommandExecutionStatus.COMPLETE, hnd.CLFree, nil).RaiseIfError; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandArrayFill( CommandQueue&<&Array> (self.a .InternalCloneCached(muhs, cache)), CommandQueue&(self.offset .InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; BufferCommandValueFill = sealed class(BufferCommandBase) public ptr: CommandQueue; public pattern_len, offset, len: CommandQueue; public constructor(ptr: CommandQueue; pattern_len, offset, len: CommandQueue); begin self.ptr := ptr; self.pattern_len := pattern_len; self.offset := offset; self.len := len; end; protected function GetSubQCount: integer; override := 4; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield ptr; yield pattern_len; yield offset; yield len; end; protected procedure CLFreeMem(ev: cl_event; status: CommandExecutionStatus; data: pointer) := Marshal.FreeHGlobal(ptr.res); protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override; begin cl.EnqueueFillBuffer(cq, mem, ptr.res, new UIntPtr(pattern_len.res), new UIntPtr(offset.res), new UIntPtr(len.res), ev_c, prev_ev, res_ev).RaiseIfError; cl.SetEventCallback(res_ev^, CommandExecutionStatus.COMPLETE, CLFreeMem, nil).RaiseIfError; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandValueFill( CommandQueue& (self.ptr .InternalCloneCached(muhs, cache)), CommandQueue&(self.pattern_len .InternalCloneCached(muhs, cache)), CommandQueue&(self.offset .InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; function BufferCommandQueue.AddFillData(ptr: CommandQueue; pattern_len, offset, len: CommandQueue) := AddCommand(new BufferCommandDataFill(ptr,pattern_len, offset,len)); function BufferCommandQueue.AddFillArray(a: CommandQueue<&Array>; offset, len: CommandQueue) := AddCommand(new BufferCommandArrayFill(a, offset,len)); function BufferCommandQueue.AddFillValue(val: TRecord; offset, len: CommandQueue): BufferCommandQueue; begin var sz := Marshal.SizeOf&; var ptr := Marshal.AllocHGlobal(sz); var typed_ptr: ^TRecord := pointer(ptr); typed_ptr^ := val; Result := AddCommand(new BufferCommandValueFill(ptr,Marshal.SizeOf&, offset,len)); end; function BufferCommandQueue.AddFillValue(val: CommandQueue; offset, len: CommandQueue) := AddCommand(new BufferCommandValueFill( val.ThenConvert&(vval-> //ToDo #2067 begin var sz := Marshal.SizeOf&; var ptr := Marshal.AllocHGlobal(sz); var typed_ptr: ^TRecord := pointer(ptr); var костыль_ptr: ^TRecord := pointer(@vval); //ToDo #2068 typed_ptr^ := костыль_ptr^; Result := ptr; end), Marshal.SizeOf&, offset, len )); {$endregion Fill} {$region Copy} type BufferCommandCopy = sealed class(BufferCommandBase) public f_buf, t_buf: CommandQueue; public f_pos, t_pos, len: CommandQueue; public constructor(f_buf, t_buf: CommandQueue; f_pos, t_pos, len: CommandQueue); begin self.f_buf := f_buf; self.t_buf := t_buf; self.f_pos := f_pos; self.t_pos := t_pos; self.len := len; end; protected function GetSubQCount: integer; override := 5; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield f_buf; yield t_buf; yield f_pos; yield t_pos; yield len; end; protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; mem: cl_mem; ev_c: integer; prev_ev, res_ev: ^cl_event); override; begin if f_buf.res.memobj=cl_mem.Zero then f_buf.res.Init(c); if t_buf.res.memobj=cl_mem.Zero then t_buf.res.Init(c); cl.EnqueueCopyBuffer(cq, f_buf.res.memobj, t_buf.res.memobj, new UIntPtr(f_pos.res), new UIntPtr(t_pos.res), new UIntPtr(len.res), ev_c, prev_ev, res_ev).RaiseIfError; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new BufferCommandCopy( CommandQueue& (self.f_buf .InternalCloneCached(muhs, cache)), CommandQueue& (self.t_buf .InternalCloneCached(muhs, cache)), CommandQueue&(self.f_pos .InternalCloneCached(muhs, cache)), CommandQueue&(self.t_pos .InternalCloneCached(muhs, cache)), CommandQueue&(self.len .InternalCloneCached(muhs, cache)) ); end; function BufferCommandQueue.AddCopyFrom(b: CommandQueue; from, &to, len: CommandQueue) := AddCommand(new BufferCommandCopy(b,res_q_hub=nil?res:self.GetNewResPlug, from,&to, len)); function BufferCommandQueue.AddCopyTo(b: CommandQueue; from, &to, len: CommandQueue) := AddCommand(new BufferCommandCopy(res_q_hub=nil?res:self.GetNewResPlug,b, &to,from, len)); {$endregion Copy} {$endregion Buffer} {$region Kernel} {$region KernelCommandQueue} function KernelCommandQueue.InternalClone(muhs: Dictionary; cache: Dictionary): CommandQueueBase; begin var res := new KernelCommandQueue(self.res); if self.res_q_hub<>nil then begin var hub := MultiusableCommandQueueHub&(self.res_q_hub); res.res_q_hub := new MultiusableCommandQueueHub(CommandQueue&( hub.q.InternalCloneCached(muhs, cache) )); muhs.Add(self.res_q_hub, res.res_q_hub); end; res.commands.Capacity := self.commands.Capacity; foreach var comm in self.commands do res.commands += comm.Clone(muhs, cache); Result := res; end; {$endregion KernelCommandQueue} {$region Misc} type KernelCommandBase = abstract class(DirectGPUCommandBase) protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; k: cl_kernel; ev_c: integer; prev_ev, res_ev: ^cl_event); abstract; protected procedure LowerEnqueueSelf(c: Context; cq: cl_command_queue; k: Kernel; ev_c: integer; prev_ev, res_ev: ^cl_event); override := EnqueueSelf(c, cq, k._kernel, ev_c, prev_ev, res_ev); end; {$endregion Misc} {$region Exec} type KernelCommandExec = sealed class(KernelCommandBase) public work_szs_q: CommandQueue; public args_q: array of CommandQueue; public constructor(work_szs_q: CommandQueue; args: array of CommandQueue); begin self.work_szs_q := work_szs_q; self.args_q := args; end; protected function GetSubQCount: integer; override := 1 + args_q.Length; protected function EnmrSubQ: sequence of CommandQueueBase; override; begin yield work_szs_q; yield sequence args_q; end; protected procedure EnqueueSelf(c: Context; cq: cl_command_queue; k: cl_kernel; ev_c: integer; prev_ev, res_ev: ^cl_event); override; begin for var i := 0 to args_q.Length-1 do begin if args_q[i].res.memobj=cl_mem.Zero then args_q[i].res.Init(c); cl.SetKernelArg(k, i, new UIntPtr(UIntPtr.Size), args_q[i].res.memobj).RaiseIfError; end; cl.EnqueueNDRangeKernel(cq,k, work_szs_q.res.Length, nil,work_szs_q.res,nil, ev_c,prev_ev,res_ev).RaiseIfError; end; protected function Clone(muhs: Dictionary; cache: Dictionary): GPUCommand; override := new KernelCommandExec( CommandQueue&(self.work_szs_q.InternalCloneCached(muhs, cache)), self.args_q.ConvertAll(q->CommandQueue&(q.InternalCloneCached(muhs, cache))) ); end; function KernelCommandQueue.AddExec(work_szs: array of UIntPtr; params args: array of CommandQueue) := AddCommand(new KernelCommandExec(work_szs, args)); function KernelCommandQueue.AddExec(work_szs: array of CommandQueue; params args: array of CommandQueue) := AddCommand(new KernelCommandExec( CombineAsyncQueue(a->a,work_szs), args )); function KernelCommandQueue.AddExec(work_szs: CommandQueue; params args: array of CommandQueue) := AddCommand(new KernelCommandExec( work_szs, args )); function KernelCommandQueue.AddExec(work_szs: CommandQueue; params args: array of CommandQueue) := AddCommand(new KernelCommandExec( work_szs.ThenConvert(a->a.ConvertAll(sz->new UIntPtr(sz))), args )); {$endregion Exec} {$endregion Kernel} {$endregion CommandQueue} {$region Неявные CommandQueue} {$region Buffer} {$region constructor's} procedure Buffer.Init(c: Context) := lock self do begin var ec: ErrorCode; if self.memobj<>cl_mem.Zero then cl.ReleaseMemObject(self.memobj).RaiseIfError; self.memobj := cl.CreateBuffer(c._context, MemoryFlags.READ_WRITE, self.sz, IntPtr.Zero, ec); ec.RaiseIfError; end; function Buffer.SubBuff(offset, size: integer): Buffer; begin if self.memobj=cl_mem.Zero then Init(Context.Default); Result := new Buffer(size); Result._parent := self; var ec: ErrorCode; var reg := new cl_buffer_region( new UIntPtr( offset ), new UIntPtr( size ) ); Result.memobj := cl.CreateSubBuffer(self.memobj, MemoryFlags.READ_WRITE, BufferCreateType.REGION, pointer(@reg), ec); ec.RaiseIfError; end; {$endregion constructor's} {$region Write} function Buffer.WriteData(ptr: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddWriteData(ptr) as CommandQueue); function Buffer.WriteData(ptr: CommandQueue; offset, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddWriteData(ptr, offset, len) as CommandQueue); function Buffer.WriteArray(a: CommandQueue<&Array>) := Context.Default.SyncInvoke(NewQueue.AddWriteArray(a) as CommandQueue); function Buffer.WriteArray(a: CommandQueue<&Array>; offset, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddWriteArray(a, offset, len) as CommandQueue); function Buffer.WriteValue(val: CommandQueue; offset: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddWriteValue(val, offset) as CommandQueue); {$endregion Write} {$region Read} function Buffer.ReadData(ptr: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddReadData(ptr) as CommandQueue); function Buffer.ReadData(ptr: CommandQueue; offset, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddReadData(ptr, offset, len) as CommandQueue); function Buffer.ReadArray(a: CommandQueue<&Array>) := Context.Default.SyncInvoke(NewQueue.AddReadArray(a) as CommandQueue); function Buffer.ReadArray(a: CommandQueue<&Array>; offset, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddReadArray(a, offset, len) as CommandQueue); {$endregion Read} {$region PatternFill} function Buffer.FillData(ptr: CommandQueue; pattern_len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddFillData(ptr, pattern_len) as CommandQueue); function Buffer.FillData(ptr: CommandQueue; pattern_len, offset, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddFillData(ptr, pattern_len, offset, len) as CommandQueue); function Buffer.FillArray(a: CommandQueue<&Array>) := Context.Default.SyncInvoke(NewQueue.AddFillArray(a) as CommandQueue); function Buffer.FillArray(a: CommandQueue<&Array>; offset, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddFillArray(a, offset, len) as CommandQueue); function Buffer.FillValue(val: TRecord) := Context.Default.SyncInvoke(NewQueue.AddFillValue(val) as CommandQueue); function Buffer.FillValue(val: TRecord; offset, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddFillValue(val, offset, len) as CommandQueue); function Buffer.FillValue(val: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddFillValue(val) as CommandQueue); function Buffer.FillValue(val: CommandQueue; offset, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddFillValue(val, offset, len) as CommandQueue); {$endregion PatternFill} {$region Copy} function Buffer.CopyFrom(b: CommandQueue; from, &to, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddCopyFrom(b, from, &to, len) as CommandQueue); function Buffer.CopyFrom(b: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddCopyFrom(b) as CommandQueue); function Buffer.CopyTo(b: CommandQueue; from, &to, len: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddCopyTo(b, from, &to, len) as CommandQueue); function Buffer.CopyTo(b: CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddCopyTo(b) as CommandQueue); {$endregion Copy} {$region Get} function Buffer.GetData(offset, len: CommandQueue): IntPtr; begin var res: IntPtr; var Qs_len := len.Multiusable(2); var Q_res := Qs_len[0].ThenConvert(len_val-> begin Result := Marshal.AllocHGlobal(len_val); res := Result; end); Context.Default.SyncInvoke( self.NewQueue.AddReadData(Q_res, offset,Qs_len[1]) as CommandQueue ); Result := res; end; function Buffer.GetArrayAt(offset: CommandQueue; szs: CommandQueue): TArray; begin var el_t := typeof(TArray).GetElementType; var res: &Array; var Qs_szs := szs.Multiusable(2); var Q_res := Qs_szs[0].ThenConvert(szs_val-> begin Result := System.Array.CreateInstance( el_t, szs_val ); res := Result; end); var Q_res_len := Qs_szs[1].ThenConvert( szs_val -> Marshal.SizeOf(el_t)*szs_val.Aggregate((i1,i2)->i1*i2) ); Context.Default.SyncInvoke( self.NewQueue .AddReadArray(Q_res, offset, Q_res_len) as CommandQueue ); Result := TArray(res); end; function Buffer.GetArrayAt(offset: CommandQueue; params szs: array of CommandQueue) := GetArrayAt&(offset, CombineAsyncQueue(a->a, szs)); function Buffer.GetValueAt(offset: CommandQueue): TRecord; begin Context.Default.SyncInvoke( self.NewQueue .AddReadValue(Result, offset) as CommandQueue ); end; {$endregion Get} {$endregion Buffer} {$region Kernel} {$region constructor's} constructor Kernel.Create(prog: ProgramCode; name: string); begin var ec: ErrorCode; self._kernel := cl.CreateKernel(prog._program, name, ec); ec.RaiseIfError; end; {$endregion constructor's} {$region Exec} function Kernel.Exec(work_szs: array of UIntPtr; params args: array of CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddExec(work_szs, args) as CommandQueue); function Kernel.Exec(work_szs: array of CommandQueue; params args: array of CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddExec(work_szs, args) as CommandQueue); function Kernel.Exec(work_szs: CommandQueue; params args: array of CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddExec(work_szs, args) as CommandQueue); function Kernel.Exec(work_szs: CommandQueue; params args: array of CommandQueue) := Context.Default.SyncInvoke(NewQueue.AddExec(work_szs, args) as CommandQueue); {$endregion Exec} {$endregion Kernel} {$endregion Неявные CommandQueue} {$region Сахарные подпрограммы} function HFQ(f: ()->T) := new CommandQueueHostFunc(f); function HPQ(p: ()->()) := HFQ&(()-> begin p(); Result := nil; end); function CombineSyncQueue(qs: List) := new CommandQueueSyncList(qs); function CombineSyncQueue(qs: List>) := new CommandQueueTSyncList(qs); function CombineSyncQueue(params qs: array of CommandQueueBase) := new CommandQueueSyncList(qs); function CombineSyncQueue(params qs: array of CommandQueue) := new CommandQueueTSyncList(qs); function CombineSyncQueue(conv: Func; qs: List) := new CommandQueueCSyncList(qs, conv); function CombineSyncQueue(conv: Func; qs: List>) := new CommandQueueCTSyncList(qs, conv); function CombineSyncQueue(conv: Func; params qs: array of CommandQueueBase) := new CommandQueueCSyncList(qs, conv); function CombineSyncQueue(conv: Func; params qs: array of CommandQueue) := new CommandQueueCTSyncList(qs, conv); function CombineAsyncQueue(qs: List): CommandQueue := new CommandQueueSyncList(qs); function CombineAsyncQueue(qs: List>): CommandQueue := new CommandQueueTSyncList(qs); function CombineAsyncQueue(params qs: array of CommandQueueBase): CommandQueue := new CommandQueueSyncList(qs); function CombineAsyncQueue(params qs: array of CommandQueue) := new CommandQueueTAsyncList(qs); function CombineAsyncQueue(conv: Func; qs: List) := new CommandQueueCAsyncList(qs, conv); function CombineAsyncQueue(conv: Func; qs: List>) := new CommandQueueCTAsyncList(qs, conv); function CombineAsyncQueue(conv: Func; params qs: array of CommandQueueBase) := new CommandQueueCAsyncList(qs, conv); function CombineAsyncQueue(conv: Func; params qs: array of CommandQueue) := new CommandQueueCTAsyncList(qs, conv); {$endregion Сахарные подпрограммы} end.