pascalabcnet/TestSuite/CompilationSamples/OpenCLABC.pas
Mikhalkovich Stanislav bf70ff8450 fix #2140
OpenCLABC - немного изменен текст чтобы погасить ошибку наследования от недоописанного класса

PT4AExam.dll

GraphWPF - Save-Load поправлена буферизация
2020-05-20 21:43:00 +03:00

3930 lines
135 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//*****************************************************************************************************\\
// 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;
interface
uses OpenCL;
uses System;
uses System.Threading;
uses System.Threading.Tasks;
uses System.Runtime.InteropServices;
uses System.Runtime.CompilerServices;
{$region ToDo}
//===================================
// Обязательно сделать до следующего пула:
//ToDo Тесты всех фич модуля
//ToDo И в каждом сделать по несколько выполнений, на случай плавающий ошибок
//===================================
// Запланированное:
//ToDo Раздел справки про оптимизацию
// - почему 1 очередь быстрее 2 её кусков
//ToDo Очередь-обработчик ошибок
// - сделать легко, надо только вставить свой промежуточный CLTaskBase
// - единственное - для Wait очереди надо хранить так же оригинальный таск
//ToDo Если в предыдущей очереди исключение - остановить выполнение
// - это не критично, но иначе будет выводить кучу лишних ошибок
//ToDo cl.SetKernelArg из нескольких потоков одновременно - предусмотреть
//ToDo Синхронные (с припиской Fast) варианты всего работающего по принципу HostQueue
//ToDo и асинхронные умнее запускать - помнить значение, указывающее можно ли выполнить их синхронно
//ToDo исправить десериализацию ProgramCode
//ToDo когда partial классы начнут нормально себя вести - использовать их чтоб переместить все "__*" классы в implementation
//ToDo CommmandQueueBase.ToString для дебага
// - так же дублирующий protected метод (tabs: integer; index: Dictionary<CommandQueueBase,integer>)
//ToDo CommandQueue.Cycle(integer)
//ToDo CommandQueue.Cycle // бесконечность циклов
//ToDo CommandQueue.CycleWhile(***->boolean)
// - Возможность передать свой обработчик ошибок как Exception->Exception
//ToDo В продолжение Cycle: Однако всё ещё остаётся проблема - как сделать ветвление?
// - И если уже делать - стоит сделать и метод CQ.ThenIf(res->boolean; if_true, if_false: CQ)
// - Ивенты должны выполнится, иначе останутся GCHandle которые не освободили
// - Наверное надо что то типа cl.SetUserEventStatus(ErrorCode.MyCustomQueueCancelCode)
// - Протестировать как это работает с юзер-ивентами и с маркерами из таких юзер-ивентов
// - Вроде бы маркер и cl.WaitForEvents имеют свой код ошибки, когда 1 из ивентов неправильный
//ToDo Read/Write для массивов - надо бы иметь возможность указывать отступ в массиве
//ToDo Типы Device и Platform
//ToDo А связь с OpenCL.pas сделать всему (и буферам и кёрнелам), но более человеческую
//ToDo Сделать методы BufferCommandQueue.AddGet
// - они особенные, потому что возвращают не BufferCommandQueue, а каждый свою очередь
// - полезно, потому что SyncInvoke такой очереди будет возвращать полученное значение
//ToDo Интегрировать профайлинг очередей
//===================================
// Сделать когда-нибуть:
//ToDo У всего, у чего есть .Finalize - проверить чтобы было и .Dispose, если надо
// - и добавить в справку, про то что этот объект можно удалять
// - из .Dispose можно блокировать .Finalize
//ToDo Пройтись по всем функциям OpenCL, посмотреть функционал каких не доступен из OpenCLABC
// - у Kernel.Exec несколько параметров не используются. Стоит использовать
//===================================
//ToDo issue компилятора:
// - #1981
// - #2118
// - #2120
// - #2145
// - #2150
// - #2173
{$endregion ToDo}
{$region Debug}
{ $define DebugMode}
{$ifdef DebugMode}
{$endif DebugMode}
{$endregion Debug}
type
{$region pre def}
CommandQueueBase = class;
CommandQueue<T> = class;
CLTaskBase = class;
CLTask<T> = class;
Buffer = class;
Kernel = class;
Context = class;
ProgramCode = class;
DeviceType = OpenCL.DeviceType;
{$endregion pre def}
{$region hidden utils}
__NativUtils = static class
static function CopyToUnm<TRecord>(a: TRecord): IntPtr; where TRecord: record;
begin
Result := Marshal.AllocHGlobal(Marshal.SizeOf&<TRecord>);
var res: ^TRecord := pointer(Result);
res^ := a;
end;
static function AsPtr<T>(p: pointer): ^T := p;
static function GCHndAlloc(o: object) :=
CopyToUnm(GCHandle.Alloc(o));
static procedure GCHndFree(gc_hnd_ptr: IntPtr);
begin
AsPtr&<GCHandle>(pointer(gc_hnd_ptr))^.Free;
Marshal.FreeHGlobal(gc_hnd_ptr);
end;
end;
__EventList = sealed class
private evs: array of cl_event;
private count := 0;
public constructor := exit;
public constructor(count: integer) :=
self.evs := count=0 ? nil : new cl_event[count];
public property Item[i: integer]: cl_event read evs[i]; default;
public static function operator implicit(ev: cl_event): __EventList;
begin
if ev=cl_event.Zero then
Result := new __EventList else
begin
Result := new __EventList(1);
Result += ev;
end;
end;
public constructor(params evs: array of cl_event);
begin
self.evs := evs;
self.count := evs.Length;
end;
public static procedure operator+=(l: __EventList; ev: cl_event);
begin
l.evs[l.count] := ev;
l.count += 1;
end;
public static procedure operator+=(l: __EventList; ev: __EventList);
begin
for var i := 0 to ev.count-1 do
l += ev[i];
end;
public static function operator+(l1,l2: __EventList): __EventList;
begin
Result := new __EventList(l1.count+l2.count);
Result += l1;
Result += l2;
end;
public static function Combine(params evs: array of __EventList): __EventList;
begin
Result := new __EventList(evs.Sum(ev->ev.count));
foreach var ev in evs do
Result += ev;
end;
public procedure Retain :=
for var i := 0 to count-1 do
cl.RetainEvent(evs[i]).RaiseIfError;
public procedure Release :=
for var i := 0 to count-1 do
cl.ReleaseEvent(evs[i]).RaiseIfError;
public static procedure AttachCallback(ev: cl_event; cb: EventCallback);
public static procedure AttachCallback(ev: cl_event; cb: EventCallback; tsk: CLTaskBase);
///cb должен иметь глобальный try и вызывать "state.RaiseIfError" и "__NativUtils.GCHndFree(data)",
///А "cl.ReleaseEvent" если и вызывать - то только на результате вызова AttachCallback
public function AttachCallback(cb: EventCallback; c: Context; var cq: cl_command_queue): cl_event;
end;
__IQueueRes = interface
function ResBase: object;
function ResFBase: ()->object;
function EvBase: __EventList;
//ToDo #2150
//TODo #2173
// function ResFBase<T2>(f2: object->T2): ()->T2;
function IsF: boolean;
function GetBase: object;
function WaitAndGetBase: object;
function AttachCallbackBase(cb: EventCallback; c: Context; var cq: cl_command_queue): __IQueueRes;
end;
__QueueRes<T> = record(__IQueueRes)
res: T;
res_f: ()->T;
ev: __EventList;
function ResBase: object := self.res;
function ResFBase: ()->object := ()->self.res_f();
function EvBase: __EventList := self.ev;
// function __IQueueRes.ResFBase<T2>(f2: object->T2): ()->T2 := ()->f2(self.res_f());
function IsF: boolean := self.res_f<>nil;
function Get: T := res_f=nil ? res : res_f();
function GetBase: object := Get();
function WaitAndGet: T;
begin
if ev.count<>0 then
begin
cl.WaitForEvents(ev.count,ev.evs).RaiseIfError;
ev.Release;
end;
Result := res_f=nil ? res : res_f();
end;
function WaitAndGetBase: object := WaitAndGet();
function LazyQuickTransform<T2>(f: T->T2): __QueueRes<T2>;
begin
Result.ev := self.ev;
if self.res_f<>nil then
begin
var f0 := self.res_f;
Result.res_f := ()->f(f0());
end else
if self.ev.count=0 then
Result.res := f(self.res) else
begin
var r0 := self.res;
Result.res_f := ()->f(r0);
end;
end;
function AttachCallback(cb: EventCallback; c: Context; var cq: cl_command_queue): __QueueRes<T>;
begin
Result.res := self.res;
Result.res_f := self.res_f;
Result.ev := self.ev.AttachCallback(cb, c, cq);
end;
function AttachCallbackBase(cb: EventCallback; c: Context; var cq: cl_command_queue): __IQueueRes := AttachCallback(cb, c, cq);
end;
__MWEventContainer = sealed class // MW = Multi Wait
curr_evs := new Queue<cl_event>;
cached: integer;
end;
{$endregion hidden utils}
{$region CommandQueue's}
{$region CommandQueue}
CommandQueueBase = abstract class
{$region Queue converters}
{$region ConstQueue}
public static function operator implicit(o: object): CommandQueueBase;
{$endregion ConstQueue}
{$region Cast}
public function Cast<T>: CommandQueue<T>;
{$endregion Cast}
{$region ThenConvert}
//ToDo #2118
// ///Создаёт очередь, которая выполнит данную
// ///А затем выполнит на CPU функцию f, используя результат данной очереди
// public function ThenConvert<T>(f: object->T): CommandQueue<T>;
// ///Создаёт очередь, которая выполнит данную
// ///А затем выполнит на CPU функцию f, используя результат данной очереди и контекст на котором её выполнили
// public function ThenConvert<T>(f: (object,Context)->T): CommandQueue<T>;
{$endregion ThenConvert}
{$region [A]SyncQueue}
public static function operator+(q1, q2: CommandQueueBase): CommandQueueBase;
public static function operator+<T>(q1: CommandQueueBase; q2: CommandQueue<T>): CommandQueue<T>;
public static procedure operator+=(var q1: CommandQueueBase; q2: CommandQueueBase) := q1 := q1+q2;
public static function operator*(q1, q2: CommandQueueBase): CommandQueueBase;
public static function operator*<T>(q1: CommandQueueBase; q2: CommandQueue<T>): CommandQueue<T>;
public static procedure operator*=(var q1: CommandQueueBase; q2: CommandQueueBase) := q1 := q1*q2;
{$endregion [A]SyncQueue}
{$region Mutiusable}
//ToDo #2120
// ///Создаёт массив из n очередей, каждая из которых возвращает результат данной очереди
// ///Каждую полученную очередь можно использовать одновременно с другими, но только в общей очереди
// public function Multiusable(n: integer): array of CommandQueueBase;
//
// ///Создаёт функцию, создающую очередь, которая возвращает результат данной очереди
// ///Каждую очередь, созданную полученной функцией, можно использовать одновременно с другими, но только в общей очереди
// public function Multiusable: ()->CommandQueueBase;
{$endregion Mutiusable}
{$region ThenWait}
public function ThenWaitFor(q: CommandQueueBase): CommandQueueBase := ThenWaitForAll(q);
public function ThenWaitForAll(qs: sequence of CommandQueueBase): CommandQueueBase := CreateWaitWrapperBase(qs, true);
public function ThenWaitForAll(params qs: array of CommandQueueBase) := ThenWaitForAll(qs.AsEnumerable);
public function ThenWaitForAny(qs: sequence of CommandQueueBase): CommandQueueBase := CreateWaitWrapperBase(qs, false);
public function ThenWaitForAny(params qs: array of CommandQueueBase) := ThenWaitForAny(qs.AsEnumerable);
{$endregion ThenWait}
{$endregion Queue converters}
{$region Invoke}
protected function InvokeBase(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __IQueueRes; abstract;
protected function InvokeNewQBase(tsk: CLTaskBase; c: Context): __IQueueRes;
{$endregion Invoke}
{$region Utils}
{$region MW}
private waiters_c := 0;
protected function IsWaitable := waiters_c<>0;
protected procedure MakeWaitable := lock self do waiters_c += 1;
protected procedure UnMakeWaitable := lock self do waiters_c -= 1;
/// добавляет tsk в качестве ключа для всех ожидаемых очередей
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); abstract;
private mw_evs := new Dictionary<CLTaskBase, __MWEventContainer>;
protected procedure RegisterWaiterTask(tsk: CLTaskBase);
protected function GetMWEvent(tsk: CLTaskBase; c: Context): cl_event;
protected procedure SignalMWEvent(tsk: CLTaskBase);
{$endregion MW}
{$region Misc}
protected static function CreateUserEvent(c: Context): cl_event;
protected function CreateWaitWrapperBase(qs: sequence of CommandQueueBase; all: boolean): CommandQueueBase; abstract;
{$endregion Misc}
{$endregion Utils}
end;
CommandQueue<T> = abstract class(CommandQueueBase)
{$region Queue converters}
{$region ConstQueue}
public static function operator implicit(o: T): CommandQueue<T>;
{$endregion ConstQueue}
{$region ThenConvert}
public function ThenConvert<T2>(f: T->T2): CommandQueue<T2>;
public function ThenConvert<T2>(f: (T,Context)->T2): CommandQueue<T2>;
{$endregion ThenConvert}
{$region [A]SyncQueue}
public static procedure operator+=(var q1: CommandQueue<T>; q2: CommandQueue<T>) := q1 := q1+q2;
public static procedure operator*=(var q1: CommandQueue<T>; q2: CommandQueue<T>) := q1 := q1*q2;
{$endregion [A]SyncQueue}
{$region Mutiusable}
public function Multiusable: ()->CommandQueue<T>;
{$endregion Mutiusable}
{$region ThenWait}
public function ThenWaitFor(q: CommandQueueBase): CommandQueue<T> := ThenWaitForAll(q);
public function ThenWaitForAll(qs: sequence of CommandQueueBase): CommandQueue<T> := CreateWaitWrapper(qs, true);
public function ThenWaitForAll(params qs: array of CommandQueueBase) := ThenWaitForAll(qs.AsEnumerable);
public function ThenWaitForAny(qs: sequence of CommandQueueBase): CommandQueue<T> := CreateWaitWrapper(qs, false);
public function ThenWaitForAny(params qs: array of CommandQueueBase) := ThenWaitForAny(qs.AsEnumerable);
protected function CreateWaitWrapper(qs: sequence of CommandQueueBase; all: boolean): CommandQueue<T>;
protected function CreateWaitWrapperBase(qs: sequence of CommandQueueBase; all: boolean): CommandQueueBase; override :=
CreateWaitWrapper(qs, all);
{$endregion ThenWait}
{$endregion Queue converters}
{$region Invoke}
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; abstract;
protected function InvokeBase(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __IQueueRes; override :=
Invoke(tsk, c, cq, prev_ev);
protected function InvokeNewQ(tsk: CLTaskBase; c: Context): __QueueRes<T>;
{$endregion Invoke}
end;
{$endregion CommandQueue}
{$region CLTask}
CLTaskBase = abstract class
protected err_lst := new List<Exception>; // Список исключений, вызванных при выполнении
protected mu_res := new Dictionary<object, __IQueueRes>; // Результаты хабов от .Multiusable
protected wh := new ManualResetEvent(false);
protected wh_lock := new object;
{$region Utils}
protected procedure AddErr(e: Exception) :=
lock err_lst do err_lst += e;
protected procedure AddErr(err: ErrorCode) :=
if err.IS_ERROR then AddErr(new OpenCLException(err.ToString));
protected procedure AddErr(st: CommandExecutionStatus) :=
if st.IS_ERROR then AddErr(new OpenCLException(ErrorCode.Create(st).ToString));
/// Возвращает True если очередь уже завершилась
protected function AddEventHandler<T>(var ev: T; cb: T): boolean; where T: Delegate;
begin
lock wh_lock do
Result := wh.WaitOne(0);
if not Result then
ev := Delegate.Combine(ev, cb) as T;
end;
{$endregion Utils}
{$region def}
protected function QBase: CommandQueueBase; abstract;
protected function QResBase: object; abstract;
{$endregion def}
{$region events}
private EvDone: Action<CLTaskBase>;
private EvComplete: Action<CLTaskBase, object>;
private EvError: Action<CLTaskBase, array of Exception>;
public procedure WhenDone(cb: Action<CLTaskBase>) :=
if AddEventHandler(EvDone, cb) then cb(self);
public procedure WhenComplete(cb: Action<CLTaskBase, object>) :=
if AddEventHandler(EvComplete, cb) then cb(self, QResBase);
public procedure WhenError(cb: Action<CLTaskBase, array of Exception>) :=
if AddEventHandler(EvError, cb) then lock err_lst do cb(self, err_lst.ToArray);
{$endregion events}
public property OrgQueue: CommandQueueBase read QBase;
public procedure Wait;
begin
wh.WaitOne;
lock err_lst do if err_lst.Count<>0 then raise new AggregateException(
Format(
'%task:errors%',
err_lst
),
err_lst
);
end;
public function WaitRes: object;
begin
Wait;
Result := QResBase;
end;
end;
CLTask<T> = sealed class(CLTaskBase)
private q: CommandQueue<T>;
private q_res: T;
{$region def}
protected function QBase: CommandQueueBase; override := q;
protected function QResBase: object; override := q_res;
{$endregion def}
{$region event's}
private EvDone: Action<CLTask<T>>;
private EvComplete: Action<CLTask<T>, T>;
private EvError: Action<CLTask<T>, array of Exception>;
public procedure WhenDone(cb: Action<CLTask<T>>); reintroduce :=
if AddEventHandler(EvDone, cb) then cb(self);
public procedure WhenComplete(cb: Action<CLTask<T>, T>); reintroduce :=
if AddEventHandler(EvComplete, cb) then cb(self, q_res);
public procedure WhenError(cb: Action<CLTask<T>, array of Exception>); reintroduce :=
if AddEventHandler(EvError, cb) then lock err_lst do cb(self, err_lst.ToArray);
{$endregion event's}
protected constructor(q: CommandQueue<T>; c: Context);
begin
self.q := q;
q.RegisterWaitables(self, new HashSet<object>);
var cq := cl_command_queue.Zero;
var res := q.Invoke(self, c, cq, new __EventList);
// mu выполняют лишний .Retain, чтоб ивент не удалился пока очередь ещё запускается
foreach var qr in mu_res.Values do
qr.EvBase.Release;
mu_res := nil;
var ev := res.ev;
if ev.count=0 then
begin
if cq<>cl_command_queue.Zero then raise new NotImplementedException; // не должно произойти никогда
OnQDone( res.Get() );
end else
cl.ReleaseEvent(
ev.AttachCallback((ev,st,data)->
begin
self.AddErr( st );
if cq<>cl_command_queue.Zero then
Task.Run(()->self.AddErr( cl.ReleaseCommandQueue(cq) ));
OnQDone( res.Get() );
__NativUtils.GCHndFree(data);
end, c, cq)
).RaiseIfError;
end;
private procedure OnQDone(res: T) :=
try
self.q_res := res;
var lb_EvDone: Action<CLTaskBase>;
var lb_EvComplete: Action<CLTaskBase, object>;
var lb_EvError: Action<CLTaskBase, array of Exception>;
var l_EvDone: Action<CLTask<T>>;
var l_EvComplete: Action<CLTask<T>, T>;
var l_EvError: Action<CLTask<T>, array of Exception>;
lock wh_lock do
begin
lb_EvDone := inherited EvDone;
lb_EvComplete := inherited EvComplete;
lb_EvError := inherited EvError;
l_EvDone := EvDone;
l_EvComplete := EvComplete;
l_EvError := EvError;
wh.Set;
end;
try
if lb_EvDone<>nil then lb_EvDone(self);
if l_EvDone<>nil then l_EvDone(self);
except
on e: Exception do AddErr(e);
end;
try
if lb_EvComplete<>nil then lb_EvComplete(self, res);
if l_EvComplete<>nil then l_EvComplete(self, res);
except
on e: Exception do AddErr(e);
end;
if (lb_EvError<>nil) or (l_EvError<>nil) then
begin
var err_arr: array of Exception;
lock err_lst do err_arr := err_lst.ToArray;
if lb_EvError<>nil then lb_EvError(self, err_arr);
if l_EvError<>nil then l_EvError(self, err_arr);
end;
except
on e: Exception do
begin
AddErr(e);
wh.Set;
end;
end;
public property OrgQueue: CommandQueue<T> read q;
public function WaitRes: T; reintroduce;
begin
Wait;
Result := self.q_res;
end;
end;
__CLTaskResLess = sealed class(CLTaskBase)
private q: CommandQueueBase;
private q_res: object;
protected function QBase: CommandQueueBase; override := q;
protected function QResBase: object; override := q_res;
protected constructor(q: CommandQueueBase; c: Context);
begin
self.q := q;
q.RegisterWaitables(self, new HashSet<object>);
var cq := cl_command_queue.Zero;
var res := q.InvokeBase(self, c, cq, new __EventList);
// mu выполняют лишний .Retain, чтоб ивент не удалился пока очередь ещё запускается
foreach var qr in mu_res.Values do
qr.EvBase.Release;
mu_res := nil;
var ev := res.EvBase;
if ev.count=0 then
begin
if cq<>cl_command_queue.Zero then raise new NotImplementedException; // не должно произойти никогда
OnQDone( res.GetBase() );
end else
cl.ReleaseEvent(
ev.AttachCallback((ev,st,data)->
begin
self.AddErr( st );
if cq<>cl_command_queue.Zero then
Task.Run(()->self.AddErr( cl.ReleaseCommandQueue(cq) ));
OnQDone( res.GetBase() );
__NativUtils.GCHndFree(data);
end, c, cq)
).RaiseIfError;
end;
private procedure OnQDone(res: object) :=
try
self.q_res := res;
var lb_EvDone: Action<CLTaskBase>;
var lb_EvComplete: Action<CLTaskBase, object>;
var lb_EvError: Action<CLTaskBase, array of Exception>;
lock wh_lock do
begin
lb_EvDone := inherited EvDone;
lb_EvComplete := inherited EvComplete;
lb_EvError := inherited EvError;
wh.Set;
end;
try
if lb_EvDone<>nil then lb_EvDone(self);
except
on e: Exception do AddErr(e);
end;
try
if lb_EvComplete<>nil then lb_EvComplete(self, res);
except
on e: Exception do AddErr(e);
end;
if lb_EvError<>nil then
begin
var err_arr: array of Exception;
lock err_lst do err_arr := err_lst.ToArray;
lb_EvError(self, err_arr);
end;
except
on e: Exception do
begin
AddErr(e);
wh.Set;
end;
end;
end;
{$endregion CLTask}
{$region ContainerQueue}
// очередь, выполняющая незначитальный объём своей работы, но запускающая под-очереди
__ContainerQueue<T> = abstract class(CommandQueue<T>)
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; abstract;
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override;
begin
Result := InvokeSubQs(tsk, c, cq, prev_ev);
if self.IsWaitable then
if Result.ev.count=0 then
self.SignalMWEvent(tsk) else
Result := Result.AttachCallback((ev,st,data)->
begin
tsk.AddErr( st );
self.SignalMWEvent(tsk);
__NativUtils.GCHndFree(data);
end, c, cq);
end;
end;
{$endregion ContainerQueue}
{$region HostQueue}
// очередь, выполняющая какую то работу на CPU, всегда в отдельном потоке
__HostQueue<TInp,TRes> = abstract class(CommandQueue<TRes>)
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<TInp>; abstract;
protected function ExecFunc(o: TInp; c: Context): TRes; abstract;
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<TRes>; override;
begin
var prev_res := InvokeSubQs(tsk, c, cq, prev_ev);
var uev := CreateUserEvent(c);
Result.ev := uev;
var res: TRes;
Result.res_f := ()->res;
Thread.Create(()->
begin
try
res := ExecFunc(prev_res.WaitAndGet(), c);
if self.IsWaitable then self.SignalMWEvent(tsk);
except
on e: Exception do tsk.AddErr(e);
end;
tsk.AddErr( cl.SetUserEventStatus(uev, CommandExecutionStatus.COMPLETE) );
end).Start;
end;
end;
{$endregion HostQueue}
{$region ConstQueue}
IConstQueue = interface
function GetConstVal: Object;
end;
ConstQueue<T> = sealed class(CommandQueue<T>, IConstQueue)
private res: T;
public constructor(o: T) :=
self.res := o;
public function GetConstVal: object := self.res;
public property Val: T read self.res;
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override;
begin
if self.IsWaitable then
begin
if prev_ev.count=0 then
SignalMWEvent(tsk) else
prev_ev := prev_ev.AttachCallback((ev,st,data)->
begin
tsk.AddErr( st );
self.SignalMWEvent(tsk);
__NativUtils.GCHndFree(data);
end, c, cq);
end;
Result.ev := prev_ev;
Result.res := self.res;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override := exit;
end;
{$endregion ConstQueue}
{$endregion CommandQueue's}
{$region GPUCommand}
__GPUCommand<T> = abstract class
protected function InvokeObj(o: T; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; abstract;
protected function InvokeQueue(o_q: ()->CommandQueue<T>; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; abstract;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); abstract;
end;
__GPUCommandContainer<T> = class;
__GPUCommandContainerBody<T> = abstract class
private cc: __GPUCommandContainer<T>;
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; abstract;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); abstract;
end;
__GPUCommandContainer<T> = abstract class(__ContainerQueue<T>)
protected body: __GPUCommandContainerBody<T>;
protected commands := new List<__GPUCommand<T>>;
{$region def}
protected procedure OnEarlyInit(c: Context); virtual := exit;
{$endregion def}
{$region Common}
protected constructor(o: T);
protected constructor(q: CommandQueue<T>);
protected procedure InternalAddQueue(q: CommandQueueBase);
protected procedure InternalAddProc(p: T->());
protected procedure InternalAddProc(p: (T,Context)->());
protected procedure InternalAddWaitAll(qs: sequence of CommandQueueBase);
protected procedure InternalAddWaitAny(qs: sequence of CommandQueueBase);
{$endregion Common}
{$region sub implementation}
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override :=
body.Invoke(tsk, c, cq, prev_ev);
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
body.RegisterWaitables(tsk, prev_hubs);
foreach var comm in commands do comm.RegisterWaitables(tsk, prev_hubs);
end;
{$endregion sub implementation}
{$region reintroduce методы}
//ToDo #2145
public function Equals(obj: object): boolean; reintroduce := inherited Equals(obj);
public function ToString: string; reintroduce := inherited ToString();
public function GetType: System.Type; reintroduce := inherited GetType();
public function GetHashCode: integer; reintroduce := inherited GetHashCode();
{$endregion reintroduce методы}
end;
{$endregion GPUCommand}
{$region Buffer}
BufferCommandQueue = class;
Buffer = sealed class(IDisposable)
private memobj: cl_mem;
private sz: UIntPtr;
private _parent: Buffer;
{$region constructor's}
private constructor := raise new System.NotSupportedException;
public constructor(size: UIntPtr) := self.sz := size;
public constructor(size: integer) := Create(new UIntPtr(size));
public constructor(size: int64) := Create(new UIntPtr(size));
public constructor(size: UIntPtr; c: Context);
begin
Create(size);
Init(c);
end;
public constructor(size: integer; c: Context) := Create(new UIntPtr(size), c);
public constructor(size: int64; c: Context) := Create(new UIntPtr(size), c);
public function SubBuff(offset, size: integer): Buffer;
public procedure Init(c: Context);
public procedure Dispose :=
if self.memobj<>cl_mem.Zero then
begin
GC.RemoveMemoryPressure(Size64);
cl.ReleaseMemObject(memobj).RaiseIfError;
memobj := cl_mem.Zero;
end;
protected procedure Finalize; override :=
self.Dispose;
{$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;
public property Parent: Buffer read _parent;
{$endregion property's}
{$region Queue's}
public function NewQueue: BufferCommandQueue;
{$endregion Queue's}
{$region Write}
public function WriteData(ptr: CommandQueue<IntPtr>): Buffer;
public function WriteData(ptr: CommandQueue<IntPtr>; offset, len: CommandQueue<integer>): Buffer;
public function WriteData(ptr: pointer) := WriteData(IntPtr(ptr));
public function WriteData(ptr: pointer; offset, len: CommandQueue<integer>) := WriteData(IntPtr(ptr), offset, len);
public function WriteArray(a: CommandQueue<&Array>): Buffer;
public function WriteArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>): Buffer;
public function WriteArray(a: &Array) := WriteArray(CommandQueue&<&Array>(a));
public function WriteArray(a: &Array; offset, len: CommandQueue<integer>) := WriteArray(CommandQueue&<&Array>(a), offset, len);
public [MethodImpl(MethodImplOptions.AggressiveInlining)] function WriteValue<TRecord>(val: TRecord; offset: CommandQueue<integer> := 0): Buffer; where TRecord: record;
begin Result := WriteData(@val, offset, Marshal.SizeOf&<TRecord>); end;
public function WriteValue<TRecord>(val: CommandQueue<TRecord>; offset: CommandQueue<integer> := 0): Buffer; where TRecord: record;
{$endregion Write}
{$region Read}
public function ReadData(ptr: CommandQueue<IntPtr>): Buffer;
public function ReadData(ptr: CommandQueue<IntPtr>; offset, len: CommandQueue<integer>): Buffer;
public function ReadData(ptr: pointer) := ReadData(IntPtr(ptr));
public function ReadData(ptr: pointer; offset, len: CommandQueue<integer>) := ReadData(IntPtr(ptr), offset, len);
public function ReadArray(a: CommandQueue<&Array>): Buffer;
public function ReadArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>): Buffer;
public function ReadArray(a: &Array) := ReadArray(CommandQueue&<&Array>(a));
public function ReadArray(a: &Array; offset, len: CommandQueue<integer>) := ReadArray(CommandQueue&<&Array>(a), offset, len);
public function ReadValue<TRecord>(var val: TRecord; offset: CommandQueue<integer> := 0): Buffer; where TRecord: record;
begin
Result := ReadData(@val, offset, Marshal.SizeOf&<TRecord>);
end;
{$endregion Read}
{$region Fill}
public function FillData(ptr: CommandQueue<IntPtr>; pattern_len: CommandQueue<integer>): Buffer;
public function FillData(ptr: CommandQueue<IntPtr>; pattern_len, offset, len: CommandQueue<integer>): Buffer;
public function FillData(ptr: pointer; pattern_len: CommandQueue<integer>) := FillData(IntPtr(ptr), pattern_len);
public function FillData(ptr: pointer; pattern_len, offset, len: CommandQueue<integer>) := FillData(IntPtr(ptr), pattern_len, offset, len);
public function FillArray(a: CommandQueue<&Array>): Buffer;
public function FillArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>): Buffer;
public function FillArray(a: &Array) := FillArray(CommandQueue&<&Array>(a));
public function FillArray(a: &Array; offset, len: CommandQueue<integer>) := FillArray(CommandQueue&<&Array>(a), offset, len);
public [MethodImpl(MethodImplOptions.AggressiveInlining)] function FillValue<TRecord>(val: TRecord): Buffer; where TRecord: record;
public [MethodImpl(MethodImplOptions.AggressiveInlining)] function FillValue<TRecord>(val: TRecord; offset, len: CommandQueue<integer>): Buffer; where TRecord: record;
public function FillValue<TRecord>(val: CommandQueue<TRecord>): Buffer; where TRecord: record;
public function FillValue<TRecord>(val: CommandQueue<TRecord>; offset, len: CommandQueue<integer>): Buffer; where TRecord: record;
{$endregion Fill}
{$region Copy}
public function CopyFrom(b: CommandQueue<Buffer>; from, &to, len: CommandQueue<integer>): Buffer;
public function CopyTo (b: CommandQueue<Buffer>; from, &to, len: CommandQueue<integer>): Buffer;
public function CopyFrom(b: CommandQueue<Buffer>): Buffer;
public function CopyTo (b: CommandQueue<Buffer>): Buffer;
{$endregion Copy}
{$region Get}
public function GetData(offset, len: CommandQueue<integer>): IntPtr;
public function GetData := GetData(0,integer(self.Size32));
public function GetArrayAt<TArray>(offset: CommandQueue<integer>; szs: CommandQueue<array of integer>): TArray; where TArray: &Array;
public function GetArray<TArray>(szs: CommandQueue<array of integer>): TArray; where TArray: &Array;
begin Result := GetArrayAt&<TArray>(0, szs); end;
public function GetArrayAt<TArray>(offset: CommandQueue<integer>; params szs: array of CommandQueue<integer>): TArray; where TArray: &Array;
public function GetArray<TArray>(params szs: array of integer): TArray; where TArray: &Array;
begin Result := GetArrayAt&<TArray>(0, CommandQueue&<array of integer>(szs)); end;
public function GetArray1At<TRecord>(offset, length: CommandQueue<integer>): array of TRecord; where TRecord: record;
begin Result := GetArrayAt&<array of TRecord>(offset, length); end;
public function GetArray1<TRecord>(length: CommandQueue<integer>): array of TRecord; where TRecord: record;
begin Result := GetArrayAt&<array of TRecord>(0,length); end;
public function GetArray1<TRecord>: array of TRecord; where TRecord: record;
begin Result := GetArrayAt&<array of TRecord>(0, integer(sz.ToUInt32) div Marshal.SizeOf&<TRecord>); end;
public function GetArray2At<TRecord>(offset, length1, length2: CommandQueue<integer>): array[,] of TRecord; where TRecord: record;
begin Result := GetArrayAt&<array[,] of TRecord>(offset, length1, length2); end;
public function GetArray2<TRecord>(length1, length2: CommandQueue<integer>): array[,] of TRecord; where TRecord: record;
begin Result := GetArrayAt&<array[,] of TRecord>(0, length1, length2); end;
public function GetArray3At<TRecord>(offset, length1, length2, length3: CommandQueue<integer>): array[,,] of TRecord; where TRecord: record;
begin Result := GetArrayAt&<array[,,] of TRecord>(offset, length1, length2, length3); end;
public function GetArray3<TRecord>(length1, length2, length3: CommandQueue<integer>): array[,,] of TRecord; where TRecord: record;
begin Result := GetArrayAt&<array[,,] of TRecord>(0, length1, length2, length3); end;
public [MethodImpl(MethodImplOptions.AggressiveInlining)] function GetValueAt<TRecord>(offset: CommandQueue<integer>): TRecord; where TRecord: record;
public [MethodImpl(MethodImplOptions.AggressiveInlining)] function GetValue<TRecord>: TRecord; where TRecord: record;
begin Result := GetValueAt&<TRecord>(0); end;
{$endregion Get}
end;
BufferCommandQueue = sealed class(__GPUCommandContainer<Buffer>)
{$region constructor's}
public constructor(b: Buffer) := inherited;
public constructor(q: CommandQueue<Buffer>);
{$endregion constructor's}
{$region Utils}
protected function AddCommand(comm: __GPUCommand<Buffer>): BufferCommandQueue;
begin
self.commands += comm;
Result := self;
end;
protected function GetSizeQ: CommandQueue<integer>;
{$endregion Utils}
{$region Write}
public function AddWriteData(ptr: CommandQueue<IntPtr>): BufferCommandQueue := AddWriteData(ptr, 0,GetSizeQ);
public function AddWriteData(ptr: CommandQueue<IntPtr>; offset, len: CommandQueue<integer>): BufferCommandQueue;
public function AddWriteData(ptr: pointer) := AddWriteData(IntPtr(ptr));
public function AddWriteData(ptr: pointer; offset, len: CommandQueue<integer>) := AddWriteData(IntPtr(ptr), offset, len);
public function AddWriteArray(a: CommandQueue<&Array>): BufferCommandQueue := AddWriteArray(a, 0,GetSizeQ);
public function AddWriteArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>): BufferCommandQueue;
public function AddWriteArray(a: &Array) := AddWriteArray(CommandQueue&<&Array>(a));
public function AddWriteArray(a: &Array; offset, len: CommandQueue<integer>) := AddWriteArray(CommandQueue&<&Array>(a), offset, len);
public [MethodImpl(MethodImplOptions.AggressiveInlining)] function AddWriteValue<TRecord>(val: TRecord; offset: CommandQueue<integer> := 0): BufferCommandQueue; where TRecord: record;
public function AddWriteValue<TRecord>(val: CommandQueue<TRecord>; offset: CommandQueue<integer> := 0): BufferCommandQueue; where TRecord: record;
{$endregion Write}
{$region Read}
public function AddReadData(ptr: CommandQueue<IntPtr>): BufferCommandQueue := AddReadData(ptr, 0,GetSizeQ);
public function AddReadData(ptr: CommandQueue<IntPtr>; offset, len: CommandQueue<integer>): BufferCommandQueue;
public function AddReadData(ptr: pointer) := AddReadData(IntPtr(ptr));
public function AddReadData(ptr: pointer; offset, len: CommandQueue<integer>) := AddReadData(IntPtr(ptr), offset, len);
public function AddReadArray(a: CommandQueue<&Array>): BufferCommandQueue := AddReadArray(a, 0,GetSizeQ);
public function AddReadArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>): BufferCommandQueue;
public function AddReadArray(a: &Array) := AddReadArray(CommandQueue&<&Array>(a));
public function AddReadArray(a: &Array; offset, len: CommandQueue<integer>) := AddReadArray(CommandQueue&<&Array>(a), offset, len);
public function AddReadValue<TRecord>(var val: TRecord; offset: CommandQueue<integer> := 0): BufferCommandQueue; where TRecord: record;
begin
Result := AddReadData(@val, offset, Marshal.SizeOf&<TRecord>);
end;
{$endregion Read}
{$region Fill}
public function AddFillData(ptr: CommandQueue<IntPtr>; pattern_len: CommandQueue<integer>): BufferCommandQueue := AddFillData(ptr,pattern_len, 0,GetSizeQ);
public function AddFillData(ptr: CommandQueue<IntPtr>; pattern_len, offset, len: CommandQueue<integer>): BufferCommandQueue;
public function AddFillData(ptr: pointer; pattern_len: CommandQueue<integer>) := AddFillData(IntPtr(ptr), pattern_len);
public function AddFillData(ptr: pointer; pattern_len, offset, len: CommandQueue<integer>) := AddFillData(IntPtr(ptr), pattern_len, offset, len);
public function AddFillArray(a: CommandQueue<&Array>): BufferCommandQueue := AddFillArray(a, 0,GetSizeQ);
public function AddFillArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>): BufferCommandQueue;
public function AddFillArray(a: &Array) := AddFillArray(CommandQueue&<&Array>(a));
public function AddFillArray(a: &Array; offset, len: CommandQueue<integer>) := AddFillArray(CommandQueue&<&Array>(a), offset, len);
public [MethodImpl(MethodImplOptions.AggressiveInlining)] function AddFillValue<TRecord>(val: TRecord): BufferCommandQueue; where TRecord: record;
begin Result := AddFillValue(val, 0,GetSizeQ); end;
public [MethodImpl(MethodImplOptions.AggressiveInlining)] function AddFillValue<TRecord>(val: TRecord; offset, len: CommandQueue<integer>): BufferCommandQueue; where TRecord: record;
public function AddFillValue<TRecord>(val: CommandQueue<TRecord>): BufferCommandQueue; where TRecord: record;
begin Result := AddFillValue(val, 0,GetSizeQ); end;
public function AddFillValue<TRecord>(val: CommandQueue<TRecord>; offset, len: CommandQueue<integer>): BufferCommandQueue; where TRecord: record;
{$endregion Fill}
{$region Copy}
public function AddCopyFrom(b: CommandQueue<Buffer>; from, &to, len: CommandQueue<integer>): BufferCommandQueue;
public function AddCopyTo (b: CommandQueue<Buffer>; from, &to, len: CommandQueue<integer>): BufferCommandQueue;
public function AddCopyFrom(b: CommandQueue<Buffer>) := AddCopyFrom(b, 0,0, GetSizeQ);
public function AddCopyTo (b: CommandQueue<Buffer>) := 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->()): BufferCommandQueue;
begin
InternalAddProc(p);
Result := self;
end;
public function AddWaitAll(qs: sequence of CommandQueueBase): BufferCommandQueue;
begin
InternalAddWaitAll(qs);
Result := self;
end;
public function AddWaitAny(qs: sequence of CommandQueueBase): BufferCommandQueue;
begin
InternalAddWaitAny(qs);
Result := self;
end;
public function AddWaitAll(params qs: array of CommandQueueBase) := AddWaitAll(qs.AsEnumerable);
public function AddWaitAny(params qs: array of CommandQueueBase) := AddWaitAny(qs.AsEnumerable);
public function AddWait(q: CommandQueueBase) := AddWaitAll(q);
{$endregion Non-command add's}
end;
{$endregion Buffer}
{$region Kernel}
KernelCommandQueue = class;
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: KernelCommandQueue;
{$endregion Queue's}
{$region Exec}
public function Exec(work_szs: array of UIntPtr; params args: array of CommandQueue<Buffer>): Kernel;
public function Exec(work_szs: array of integer; params args: array of CommandQueue<Buffer>) :=
Exec(work_szs.ConvertAll(sz->new UIntPtr(sz)), args);
public function Exec1(work_sz1: UIntPtr; params args: array of CommandQueue<Buffer>) := Exec(new UIntPtr[](work_sz1), args);
public function Exec1(work_sz1: integer; params args: array of CommandQueue<Buffer>) := Exec1(new UIntPtr(work_sz1), args);
public function Exec2(work_sz1, work_sz2: UIntPtr; params args: array of CommandQueue<Buffer>) := Exec(new UIntPtr[](work_sz1, work_sz2), args);
public function Exec2(work_sz1, work_sz2: integer; params args: array of CommandQueue<Buffer>) := 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<Buffer>) := 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<Buffer>) := Exec3(new UIntPtr(work_sz1), new UIntPtr(work_sz2), new UIntPtr(work_sz3), args);
public function Exec(work_szs: array of CommandQueue<UIntPtr>; params args: array of CommandQueue<Buffer>): Kernel;
public function Exec(work_szs: array of CommandQueue<integer>; params args: array of CommandQueue<Buffer>) :=
Exec(work_szs.ConvertAll(sz_q->sz_q.ThenConvert(sz->new UIntPtr(sz))), args);
public function Exec1(work_sz1: CommandQueue<UIntPtr>; params args: array of CommandQueue<Buffer>) := Exec(new CommandQueue<UIntPtr>[](work_sz1), args);
public function Exec1(work_sz1: CommandQueue<integer>; params args: array of CommandQueue<Buffer>) := Exec1(work_sz1.ThenConvert(sz->new UIntPtr(sz)), args);
public function Exec2(work_sz1, work_sz2: CommandQueue<UIntPtr>; params args: array of CommandQueue<Buffer>) := Exec(new CommandQueue<UIntPtr>[](work_sz1, work_sz2), args);
public function Exec2(work_sz1, work_sz2: CommandQueue<integer>; params args: array of CommandQueue<Buffer>) := 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<UIntPtr>; params args: array of CommandQueue<Buffer>) := Exec(new CommandQueue<UIntPtr>[](work_sz1, work_sz2, work_sz3), args);
public function Exec3(work_sz1, work_sz2, work_sz3: CommandQueue<integer>; params args: array of CommandQueue<Buffer>) := 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<array of UIntPtr>; params args: array of CommandQueue<Buffer>): Kernel;
public function Exec(work_szs: CommandQueue<array of integer>; params args: array of CommandQueue<Buffer>): Kernel;
{$endregion Exec}
protected procedure Finalize; override :=
cl.ReleaseKernel(self._kernel).RaiseIfError;
end;
KernelCommandQueue = sealed class(__GPUCommandContainer<Kernel>)
{$region constructor's}
public constructor(k: Kernel) := inherited;
public constructor(q: CommandQueue<Kernel>) := inherited;
{$endregion constructor's}
{$region Utils}
protected function AddCommand(comm: __GPUCommand<Kernel>): KernelCommandQueue;
begin
self.commands += comm;
Result := self;
end;
{$endregion Utils}
{$region Exec}
public function AddExec(work_szs: array of UIntPtr; params args: array of CommandQueue<Buffer>): KernelCommandQueue;
public function AddExec(work_szs: array of integer; params args: array of CommandQueue<Buffer>) :=
AddExec(work_szs.ConvertAll(sz->new UIntPtr(sz)), args);
public function AddExec1(work_sz1: UIntPtr; params args: array of CommandQueue<Buffer>) := AddExec(new UIntPtr[](work_sz1), args);
public function AddExec1(work_sz1: integer; params args: array of CommandQueue<Buffer>) := AddExec1(new UIntPtr(work_sz1), args);
public function AddExec2(work_sz1, work_sz2: UIntPtr; params args: array of CommandQueue<Buffer>) := AddExec(new UIntPtr[](work_sz1, work_sz2), args);
public function AddExec2(work_sz1, work_sz2: integer; params args: array of CommandQueue<Buffer>) := 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<Buffer>) := 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<Buffer>) := AddExec3(new UIntPtr(work_sz1), new UIntPtr(work_sz2), new UIntPtr(work_sz3), args);
public function AddExec(work_szs: array of CommandQueue<UIntPtr>; params args: array of CommandQueue<Buffer>): KernelCommandQueue;
public function AddExec(work_szs: array of CommandQueue<integer>; params args: array of CommandQueue<Buffer>) :=
AddExec(work_szs.ConvertAll(sz_q->sz_q.ThenConvert(sz->new UIntPtr(sz))), args);
public function AddExec1(work_sz1: CommandQueue<UIntPtr>; params args: array of CommandQueue<Buffer>) := AddExec(new CommandQueue<UIntPtr>[](work_sz1), args);
public function AddExec1(work_sz1: CommandQueue<integer>; params args: array of CommandQueue<Buffer>) := AddExec1(work_sz1.ThenConvert(sz->new UIntPtr(sz)), args);
public function AddExec2(work_sz1, work_sz2: CommandQueue<UIntPtr>; params args: array of CommandQueue<Buffer>) := AddExec(new CommandQueue<UIntPtr>[](work_sz1, work_sz2), args);
public function AddExec2(work_sz1, work_sz2: CommandQueue<integer>; params args: array of CommandQueue<Buffer>) := 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<UIntPtr>; params args: array of CommandQueue<Buffer>) := AddExec(new CommandQueue<UIntPtr>[](work_sz1, work_sz2, work_sz3), args);
public function AddExec3(work_sz1, work_sz2, work_sz3: CommandQueue<integer>; params args: array of CommandQueue<Buffer>) := 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<array of UIntPtr>; params args: array of CommandQueue<Buffer>): KernelCommandQueue;
public function AddExec(work_szs: CommandQueue<array of integer>; params args: array of CommandQueue<Buffer>): 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->()): KernelCommandQueue;
begin
InternalAddProc(p);
Result := self;
end;
public function AddWaitAll(qs: sequence of CommandQueueBase): KernelCommandQueue;
begin
InternalAddWaitAll(qs);
Result := self;
end;
public function AddWaitAny(qs: sequence of CommandQueueBase): KernelCommandQueue;
begin
InternalAddWaitAny(qs);
Result := self;
end;
public function AddWaitAll(params qs: array of CommandQueueBase) := AddWaitAll(qs.AsEnumerable);
public function AddWaitAny(params qs: array of CommandQueueBase) := AddWaitAny(qs.AsEnumerable);
public function AddWait(q: CommandQueueBase) := AddWaitAll(q);
{$endregion Non-command add's}
end;
{$endregion Kernel}
{$region Context}
Context = sealed class
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
_def_cont := new Context;
except
try
_def_cont := new Context(DeviceType.DEVICE_TYPE_ALL); // если нету GPU - попытаться хотя бы для чего то инициализировать
except
_def_cont := nil;
end;
end;
public constructor := Create(DeviceType.DEVICE_TYPE_GPU);
public constructor(dt: DeviceType);
begin
var ec: ErrorCode;
var _platform: cl_platform_id;
cl.GetPlatformIDs(1, _platform, IntPtr.Zero).RaiseIfError;
cl.GetDeviceIDs(_platform, dt, 1, _device, IntPtr.Zero).RaiseIfError;
_context := cl.CreateContext(IntPtr.Zero, 1, _device, nil, IntPtr.Zero, ec);
ec.RaiseIfError;
need_finnalize := true;
end;
public constructor(context: cl_context);
begin
cl.GetContextInfo(context, ContextInfo.CONTEXT_DEVICES, new UIntPtr(IntPtr.Size), _device, IntPtr.Zero).RaiseIfError;
_context := context;
end;
public constructor(context: cl_context; device: cl_device_id);
begin
_device := device;
_context := context;
end;
public function BeginInvoke<T>(q: CommandQueue<T>) := new CLTask<T>(q, self);
public function BeginInvoke(q: CommandQueueBase): CLTaskBase := new __CLTaskResLess(q, self);
public function SyncInvoke<T>(q: CommandQueue<T>) := BeginInvoke(q).WaitRes();
public function SyncInvoke(q: CommandQueueBase) := BeginInvoke(q).WaitRes();
protected procedure Finalize; override :=
if need_finnalize then // если было исключение при инициализации или инициализация произошла из дескриптора
cl.ReleaseContext(_context).RaiseIfError;
end;
{$endregion Context}
{$region ProgramCode}
ProgramCode = sealed class
private _program: cl_program;
{$region constructor's}
private constructor := exit;
public constructor(c: Context; params files_texts: array of string);
begin
var ec: ErrorCode;
self._program := cl.CreateProgramWithSource(c._context, files_texts.Length, files_texts, nil, ec);
ec.RaiseIfError;
ec := cl.BuildProgram(self._program, 1,c._device, nil, nil,IntPtr.Zero);
if ec=ErrorCode.BUILD_PROGRAM_FAILURE then
begin
var sz: UIntPtr;
cl.GetProgramBuildInfo(self._program, c._device, ProgramBuildInfo.PROGRAM_BUILD_LOG, UIntPtr.Zero,IntPtr.Zero,sz).RaiseIfError;
var str_ptr := Marshal.AllocHGlobal(IntPtr(pointer(sz)));
cl.GetProgramBuildInfo(self._program, c._device, ProgramBuildInfo.PROGRAM_BUILD_LOG, sz,str_ptr,IntPtr.Zero).RaiseIfError;
var str := Marshal.PtrToStringAnsi(str_ptr);
Marshal.FreeHGlobal(str_ptr);
raise new OpenCLException(str);
end else
ec.RaiseIfError;
end;
public constructor(params files_texts: array of string) :=
Create(Context.Default, files_texts);
{$endregion constructor's}
{$region GetKernel}
public property KernelByName[kname: string]: Kernel read new Kernel(self, kname); default;
public function GetAllKernels: Dictionary<string, Kernel>;
begin
// Можно и "cl.CreateKernelsInProgram", но тогда имена придётся получать отдельно, а они нужны
var names_char_len: UIntPtr;
cl.GetProgramInfo(_program, ProgramInfo.PROGRAM_KERNEL_NAMES, UIntPtr.Zero,IntPtr.Zero, names_char_len).RaiseIfError;
var names_ptr := Marshal.AllocHGlobal(IntPtr(pointer(names_char_len)));
cl.GetProgramInfo(_program, ProgramInfo.PROGRAM_KERNEL_NAMES, names_char_len,names_ptr, IntPtr.Zero).RaiseIfError;
var names := Marshal.PtrToStringAnsi(names_ptr).Split(';');
Marshal.FreeHGlobal(names_ptr);
Result := new Dictionary<string, Kernel>(names.Length);
foreach var kname in names do
Result[kname] := self[kname];
end;
{$endregion GetKernel}
{$region Serialize}
public function Serialize: array of byte;
begin
var bytes_count: UIntPtr;
cl.GetProgramInfo(_program, ProgramInfo.PROGRAM_BINARY_SIZES, new UIntPtr(UIntPtr.Size),bytes_count, IntPtr.Zero).RaiseIfError;
Result := new byte[bytes_count.ToUInt64];
cl.GetProgramInfo(_program, ProgramInfo.PROGRAM_BINARIES, bytes_count,Result[0], IntPtr.Zero).RaiseIfError;
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));
{$endregion Serialize}
{$region Deserialize}
public static function Deserialize(c: Context; bin: array of byte): ProgramCode;
begin
Result := new ProgramCode;
var bin_len := new UIntPtr(bin.Length);
var bin_arr: array of array of byte;
SetLength(bin_arr,1);
bin_arr[0] := bin;
var ec: ErrorCode;
Result._program := cl.CreateProgramWithBinary(c._context,1,c._device, bin_len,bin_arr, IntPtr.Zero,ec);
ec.RaiseIfError;
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<bin_len then raise new System.IO.EndOfStreamException;
Result := Deserialize(c, bin_arr);
end;
public static function DeserializeFrom(c: Context; str: System.IO.Stream) :=
DeserializeFrom(c, new System.IO.BinaryReader(str));
{$endregion Deserialize}
protected procedure Finalize; override :=
cl.ReleaseProgram(_program).RaiseIfError;
end;
{$endregion ProgramCode}
{$region Сахарные подпрограммы}
{$region HostExec}
function HFQ<T>(f: ()->T): CommandQueue<T>;
function HFQ<T>(f: Context->T): CommandQueue<T>;
function HPQ(p: ()->()): CommandQueueBase;
function HPQ(p: Context->()): CommandQueueBase;
{$endregion HostExec}
{$region CombineQueues}
{$region Sync}
{$region NonConv}
function CombineSyncQueueBase(qs: sequence of CommandQueueBase): CommandQueueBase;
function CombineSyncQueueBase(params qs: array of CommandQueueBase): CommandQueueBase;
function CombineSyncQueue<T>(qs: sequence of CommandQueueBase): CommandQueue<T>;
function CombineSyncQueue<T>(params qs: array of CommandQueueBase): CommandQueue<T>;
function CombineSyncQueue<T>(qs: sequence of CommandQueue<T>): CommandQueue<T>;
function CombineSyncQueue<T>(params qs: array of CommandQueue<T>): CommandQueue<T>;
{$endregion NonConv}
{$region Conv}
{$region NonContext}
function CombineSyncQueue<TRes>(conv: Func<array of object, TRes>; qs: sequence of CommandQueueBase): CommandQueue<TRes>;
function CombineSyncQueue<TRes>(conv: Func<array of object, TRes>; params qs: array of CommandQueueBase): CommandQueue<TRes>;
function CombineSyncQueue<TInp,TRes>(conv: Func<array of TInp, TRes>; qs: sequence of CommandQueue<TInp>): CommandQueue<TRes>;
function CombineSyncQueue<TInp,TRes>(conv: Func<array of TInp, TRes>; params qs: array of CommandQueue<TInp>): CommandQueue<TRes>;
{$endregion NonContext}
{$region Context}
function CombineSyncQueue<TRes>(conv: Func<array of object, Context, TRes>; qs: sequence of CommandQueueBase): CommandQueue<TRes>;
function CombineSyncQueue<TRes>(conv: Func<array of object, Context, TRes>; params qs: array of CommandQueueBase): CommandQueue<TRes>;
function CombineSyncQueue<TInp,TRes>(conv: Func<array of TInp, Context, TRes>; qs: sequence of CommandQueue<TInp>): CommandQueue<TRes>;
function CombineSyncQueue<TInp,TRes>(conv: Func<array of TInp, Context, TRes>; params qs: array of CommandQueue<TInp>): CommandQueue<TRes>;
{$endregion Context}
{$endregion Conv}
{$endregion Sync}
{$region Async}
{$region NonConv}
function CombineAsyncQueueBase(qs: sequence of CommandQueueBase): CommandQueueBase;
function CombineAsyncQueueBase(params qs: array of CommandQueueBase): CommandQueueBase;
function CombineAsyncQueue<T>(qs: sequence of CommandQueueBase): CommandQueue<T>;
function CombineAsyncQueue<T>(params qs: array of CommandQueueBase): CommandQueue<T>;
function CombineAsyncQueue<T>(qs: sequence of CommandQueue<T>): CommandQueue<T>;
function CombineAsyncQueue<T>(params qs: array of CommandQueue<T>): CommandQueue<T>;
{$endregion NonConv}
{$region Conv}
{$region NonContext}
function CombineAsyncQueue<TRes>(conv: Func<array of object, TRes>; qs: sequence of CommandQueueBase): CommandQueue<TRes>;
function CombineAsyncQueue<TRes>(conv: Func<array of object, TRes>; params qs: array of CommandQueueBase): CommandQueue<TRes>;
function CombineAsyncQueue<TInp,TRes>(conv: Func<array of TInp, TRes>; qs: sequence of CommandQueue<TInp>): CommandQueue<TRes>;
function CombineAsyncQueue<TInp,TRes>(conv: Func<array of TInp, TRes>; params qs: array of CommandQueue<TInp>): CommandQueue<TRes>;
{$endregion NonContext}
{$region Context}
function CombineAsyncQueue<TRes>(conv: Func<array of object, Context, TRes>; qs: sequence of CommandQueueBase): CommandQueue<TRes>;
function CombineAsyncQueue<TRes>(conv: Func<array of object, Context, TRes>; params qs: array of CommandQueueBase): CommandQueue<TRes>;
function CombineAsyncQueue<TInp,TRes>(conv: Func<array of TInp, Context, TRes>; qs: sequence of CommandQueue<TInp>): CommandQueue<TRes>;
function CombineAsyncQueue<TInp,TRes>(conv: Func<array of TInp, Context, TRes>; params qs: array of CommandQueue<TInp>): CommandQueue<TRes>;
{$endregion Context}
{$endregion Conv}
{$endregion Async}
{$endregion CombineQueues}
{$region Wait}
function WaitFor(q: CommandQueueBase): CommandQueueBase;
function WaitForAll(qs: sequence of CommandQueueBase): CommandQueueBase;
function WaitForAll(params qs: array of CommandQueueBase): CommandQueueBase;
function WaitForAny(qs: sequence of CommandQueueBase): CommandQueueBase;
function WaitForAny(params qs: array of CommandQueueBase): CommandQueueBase;
{$endregion Wait}
{$endregion Сахарные подпрограммы}
implementation
function Buffer.NewQueue: BufferCommandQueue;
begin
Result := new BufferCommandQueue(self);
end;
function Kernel.NewQueue := new KernelCommandQueue(self);
{$region Misc}
{$region CommandQueue}
function CommandQueueBase.InvokeNewQBase(tsk: CLTaskBase; c: Context): __IQueueRes;
begin
var cq := cl_command_queue.Zero;
Result := InvokeBase(tsk, c, cq, new __EventList);
var CQFree: Action := ()->tsk.AddErr( cl.ReleaseCommandQueue(cq) );
if Result.EvBase.count=0 then
if cq<>cl_command_queue.Zero then Task.Run(CQFree) else
Result := Result.AttachCallbackBase((ev,st,data)->
begin
tsk.AddErr( st );
if cq<>cl_command_queue.Zero then Task.Run(CQFree);
__NativUtils.GCHndFree(data);
end, c, cq);
end;
function CommandQueue<T>.InvokeNewQ(tsk: CLTaskBase; c: Context): __QueueRes<T>;
begin
var cq := cl_command_queue.Zero;
Result := Invoke(tsk, c, cq, new __EventList);
var CQFree: Action := ()->tsk.AddErr( cl.ReleaseCommandQueue(cq) );
if Result.ev.count=0 then
if cq<>cl_command_queue.Zero then Task.Run(CQFree) else
Result := Result.AttachCallback((ev,st,data)->
begin
tsk.AddErr( st );
if cq<>cl_command_queue.Zero then Task.Run(CQFree);
__NativUtils.GCHndFree(data);
end, c, cq);
end;
procedure CommandQueueBase.RegisterWaiterTask(tsk: CLTaskBase) :=
lock mw_evs do if not mw_evs.ContainsKey(tsk) then
begin
mw_evs.Add(tsk, new __MWEventContainer);
tsk.WhenDone(tsk->mw_evs.Remove(tsk));
end;
function CommandQueueBase.GetMWEvent(tsk: CLTaskBase; c: Context): cl_event;
begin
var cont: __MWEventContainer;
lock mw_evs do cont := mw_evs[tsk];
lock cont do
if cont.cached<>0 then
cont.cached -= 1 else
begin
Result := CreateUserEvent(c);
cont.curr_evs.Enqueue(Result);
end;
end;
procedure CommandQueueBase.SignalMWEvent(tsk: CLTaskBase);
begin
var conts: array of __MWEventContainer;
lock mw_evs do conts := mw_evs.Values.ToArray;
for var i := 0 to conts.Length-1 do
begin
var cont := conts[i];
lock cont do
if cont.curr_evs.Count=0 then
cont.cached += 1 else
begin
var ev := cont.curr_evs.Dequeue;
tsk.AddErr( cl.SetUserEventStatus(ev, CommandExecutionStatus.COMPLETE) );
end;
end;
end;
static function CommandQueueBase.CreateUserEvent(c: Context): cl_event;
begin
var ec: ErrorCode;
Result := cl.CreateUserEvent(c._context, ec);
ec.RaiseIfError;
end;
{$endregion CommandQueue}
static procedure __EventList.AttachCallback(ev: cl_event; cb: EventCallback) :=
cl.SetEventCallback(ev, CommandExecutionStatus.COMPLETE, cb, __NativUtils.GCHndAlloc(cb)).RaiseIfError;
static procedure __EventList.AttachCallback(ev: cl_event; cb: EventCallback; tsk: CLTaskBase) :=
tsk.AddErr( cl.SetEventCallback(ev, CommandExecutionStatus.COMPLETE, cb, __NativUtils.GCHndAlloc(cb)) );
function __EventList.AttachCallback(cb: EventCallback; c: Context; var cq: cl_command_queue): cl_event;
begin
var ev: cl_event;
if self.count>1 then
begin
if cq=cl_command_queue.Zero then
begin
var ec: ErrorCode;
cq := cl.CreateCommandQueueWithProperties(c._context,c._device, IntPtr.Zero, ec);
ec.RaiseIfError;
end;
cl.EnqueueMarkerWithWaitList(cq, self.count, self.evs, ev).RaiseIfError;
self.Release;
end else
ev := self[0];
AttachCallback(ev, cb);
Result := ev;
end;
function LazyQuickTransformBase<T2>(self: __IQueueRes; f: object->T2): __QueueRes<T2>; extensionmethod;
begin
Result.ev := self.EvBase;
if self.IsF then
Result.res_f := ()->f( self.ResFBase()() ) else //ToDo #2150, #2173
if self.EvBase.count=0 then
Result.res := f( self.ResBase() ) else
Result.res_f := ()->f( self.ResBase() );
end;
{$endregion Misc}
{$region CommandQueue}
{$region ConstQueue}
static function CommandQueueBase.operator implicit(o: object): CommandQueueBase :=
new ConstQueue<object>(o);
static function CommandQueue<T>.operator implicit(o: T): CommandQueue<T> :=
new ConstQueue<T>(o);
{$endregion ConstQueue}
{$region Cast}
type
CastQueue<T> = sealed class(__ContainerQueue<T>)
private q: CommandQueueBase;
public constructor(q: CommandQueueBase) := self.q := q;
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override :=
__QueueRes&<T>( q.InvokeBase(tsk, c, cq, prev_ev).LazyQuickTransformBase(o->T(o)) );
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
q.RegisterWaitables(tsk, prev_hubs);
end;
function CommandQueueBase.Cast<T>: CommandQueue<T>;
begin
Result := self as CommandQueue<T>;
if Result=nil then Result := new CastQueue<T>(self);
end;
{$endregion Cast}
{$region HostFunc}
type
CommandQueueHostFuncBase<T> = abstract class(__HostQueue<object,T>)
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<object>; override;
begin
Result.ev := prev_ev;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override := exit;
end;
CommandQueueHostFunc<T> = sealed class(CommandQueueHostFuncBase<T>)
private f: ()->T;
public constructor(f: ()->T) :=
self.f := f;
protected function ExecFunc(o: object; c: Context): T; override := f();
end;
CommandQueueHostFuncC<T> = sealed class(CommandQueueHostFuncBase<T>)
private f: Context->T;
public constructor(f: Context->T) :=
self.f := f;
protected function ExecFunc(o: object; c: Context): T; override := f(c);
end;
CommandQueueHostProc = sealed class(CommandQueueHostFuncBase<object>)
private p: ()->();
public constructor(p: ()->()) :=
self.p := p;
protected function ExecFunc(o: object; c: Context): object; override;
begin
p();
Result := nil;
end;
end;
CommandQueueHostProcС = sealed class(CommandQueueHostFuncBase<object>)
private p: Context->();
public constructor(p: Context->()) :=
self.p := p;
protected function ExecFunc(o: object; c: Context): object; override;
begin
p(c);
Result := nil;
end;
end;
function HFQ<T>(f: ()->T) :=
new CommandQueueHostFunc<T>(f);
function HFQ<T>(f: Context->T) :=
new CommandQueueHostFuncC<T>(f);
function HPQ(p: ()->()) :=
new CommandQueueHostProc(p);
function HPQ(p: Context->()) :=
new CommandQueueHostProcС(p);
{$endregion HostFunc}
{$region ThenConvert}
type
CommandQueueThenConvertBase<TInp,TRes> = abstract class(__HostQueue<TInp, TRes>)
q: CommandQueue<TInp>;
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<TInp>; override :=
q.Invoke(tsk, c, cq, prev_ev);
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
q.RegisterWaitables(tsk, prev_hubs);
end;
CommandQueueThenConvert<TInp,TRes> = sealed class(CommandQueueThenConvertBase<TInp,TRes>)
private f: TInp->TRes;
constructor(q: CommandQueue<TInp>; f: TInp->TRes);
begin
self.q := q;
self.f := f;
end;
protected function ExecFunc(o: TInp; c: Context): TRes; override := f(o);
end;
CommandQueueThenConvertC<TInp,TRes> = sealed class(CommandQueueThenConvertBase<TInp,TRes>)
private f: (TInp,Context)->TRes;
constructor(q: CommandQueue<TInp>; f: (TInp,Context)->TRes);
begin
self.q := q;
self.f := f;
end;
protected function ExecFunc(o: TInp; c: Context): TRes; override := f(o, c);
end;
//function CommandQueueBase.ThenConvert<T>(f: object->T) :=
//self.Cast&<object>.ThenConvert(f);
//function CommandQueueBase.ThenConvert<T>(f: (object,Context)->T) :=
//self.Cast&<object>.ThenConvert(f);
function CommandQueue<T>.ThenConvert<T2>(f: T->T2): CommandQueue<T2>;
begin
var scq := self as ConstQueue<T>;
if scq=nil then
Result := new CommandQueueThenConvert<T,T2>(self, f) else
Result := new CommandQueueHostFunc<T2>(()->f(scq.res));
end;
function CommandQueue<T>.ThenConvert<T2>(f: (T,Context)->T2): CommandQueue<T2>;
begin
var scq := self as ConstQueue<T>;
if scq=nil then
Result := new CommandQueueThenConvertC<T,T2>(self, f) else
Result := new CommandQueueHostFuncC<T2>(c->f(scq.res, c));
end;
{$endregion ThenConvert}
{$region Multiusable}
type
MultiusableCommandQueueHub<T> = sealed class
public q: CommandQueue<T>;
public constructor(q: CommandQueue<T>) := self.q := q;
public function OnNodeInvoked(tsk: CLTaskBase; c: Context): __QueueRes<T>;
begin
var res_o: __IQueueRes;
if tsk.mu_res.TryGetValue(self, res_o) then
Result := __QueueRes&<T>( res_o ) else
begin
Result := self.q.InvokeNewQ(tsk, c);
tsk.mu_res.Add(self, Result);
end;
Result.ev.Retain;
end;
public function MakeNode: CommandQueue<T>;
end;
MultiusableCommandQueueNode<T> = sealed class(__ContainerQueue<T>)
public hub: MultiusableCommandQueueHub<T>;
public constructor(hub: MultiusableCommandQueueHub<T>) := self.hub := hub;
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override;
begin
Result := hub.OnNodeInvoked(tsk, c);
Result.ev := prev_ev + Result.ev;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
if prev_hubs.Add(hub) then hub.q.RegisterWaitables(tsk, prev_hubs);
end;
function MultiusableCommandQueueHub<T>.MakeNode :=
new MultiusableCommandQueueNode<T>(self);
function CommandQueue<T>.Multiusable: ()->CommandQueue<T>;
begin
var hub := new MultiusableCommandQueueHub<T>(self);
Result := hub.MakeNode;
end;
{$endregion Multiusable}
{$region Sync/Async Base}
type
IQueueArray = interface
function GetQS: sequence of CommandQueueBase;
end;
{$region Sync}
SimpleQueueArray<T> = abstract class(__ContainerQueue<T>, IQueueArray)
private qs: array of CommandQueueBase;
public function GetQS: sequence of CommandQueueBase := qs;
public constructor(qs: array of CommandQueueBase) := self.qs := qs;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
foreach var q in qs do q.RegisterWaitables(tsk, prev_hubs);
end;
{$endregion Sync}
{$region Async}
HQAExecutor<T> = abstract class //ToDo #2150
/// синхронно или асинхронно запускает очереди qs и возвращает общий для них ивент в _prev_ev
protected function WorkOn(qs: array of CommandQueue<T>; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var _prev_ev: __EventList): array of __QueueRes<T>; abstract;
end;
HQAExecutorSync<T> = sealed class(HQAExecutor<T>)
protected function WorkOn(qs: array of CommandQueue<T>; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var _prev_ev: __EventList): array of __QueueRes<T>; override;
begin
var prev_ev := _prev_ev;
Result := new __QueueRes<T>[qs.Length];
for var i := 0 to qs.Length-1 do
begin
var r := qs[i].Invoke(tsk, c, cq, prev_ev);
prev_ev := r.ev;
Result[i] := r;
end;
_prev_ev := prev_ev;
end;
end;
HQAExecutorAsync<T> = sealed class(HQAExecutor<T>)
protected function WorkOn(qs: array of CommandQueue<T>; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var _prev_ev: __EventList): array of __QueueRes<T>; override;
begin
var prev_ev := _prev_ev;
Result := new __QueueRes<T>[qs.Length];
var evs := new __EventList[qs.Length];
var count := 0;
for var i := 0 to qs.Length-1 do
begin
var ncq := cl_command_queue.Zero;
prev_ev.Retain;
var res := qs[i].Invoke(tsk, c, ncq, prev_ev);
Result[i].res := res.res;
Result[i].res_f := res.res_f;
var CQFree: Action := ()->tsk.AddErr( cl.ReleaseCommandQueue(ncq) );
if res.ev.count=0 then
begin
if ncq<>cl_command_queue.Zero then
Task.Run(CQFree);
end else
res := res.AttachCallback((_ev,_st,_data)->
begin
tsk.AddErr( _st );
if ncq<>cl_command_queue.Zero then Task.Run(CQFree);
__NativUtils.GCHndFree(_data);
end, c, ncq);
evs[i] := res.ev;
count += res.ev.count;
end;
prev_ev.Release;
prev_ev := new __EventList(count);
foreach var ev in evs do
prev_ev += ev;
_prev_ev := prev_ev;
end;
end;
HostQueueArrayBase<TInp,TRes> = abstract class(__HostQueue<array of TInp, TRes>, IQueueArray)
private qs: array of CommandQueue<TInp>;
private executor: HQAExecutor<TInp>;
protected procedure InitExecutor(is_sync: boolean) :=
self.executor := is_sync ? new HQAExecutorSync<TInp> as HQAExecutor<TInp> : new HQAExecutorAsync<TInp>;
public function GetQS: sequence of CommandQueueBase := qs.Cast&<CommandQueueBase>;
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<array of TInp>; override;
begin
var res := executor.WorkOn(qs, tsk, c, cq, prev_ev);
if res.Any(qr->qr.res_f<>nil) then
begin
var res_ref := res;
Result.res_f := ()->
begin
var res := new TInp[res_ref.Length];
for var i := 0 to res_ref.Length-1 do
res[i] := res_ref[i].Get();
Result := res;
end;
end else
begin
Result.res := new TInp[res.Length];
for var i := 0 to res.Length-1 do
Result.res[i] := res[i].res;
end;
Result.ev := prev_ev;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
foreach var q in qs do q.RegisterWaitables(tsk, prev_hubs);
end;
HostQueueArray<TInp,TRes> = sealed class(HostQueueArrayBase<TInp,TRes>)
private conv: Func<array of TInp, TRes>;
protected constructor(qs: array of CommandQueue<TInp>; conv: Func<array of TInp, TRes>; is_sync: boolean);
begin
self.qs := qs;
self.conv := conv;
InitExecutor(is_sync);
end;
protected function ExecFunc(a: array of TInp; c: Context): TRes; override := conv(a);
end;
HostQueueArrayC<TInp,TRes> = sealed class(HostQueueArrayBase<TInp,TRes>)
private conv: Func<array of TInp, Context, TRes>;
protected constructor(qs: array of CommandQueue<TInp>; conv: Func<array of TInp, Context, TRes>; is_sync: boolean);
begin
self.qs := qs;
self.conv := conv;
InitExecutor(is_sync);
end;
protected function ExecFunc(a: array of TInp; c: Context): TRes; override := conv(a, c);
end;
{$endregion Async}
function FlattenQueueArray<T>(inp: sequence of CommandQueueBase): array of CommandQueueBase; where T: IQueueArray;
begin
var enmr := inp.GetEnumerator;
if not enmr.MoveNext then raise new InvalidOperationException('%FlattenQueueArray:inp empty%');
var res := new List<CommandQueueBase>;
while true do
begin
var curr := enmr.Current;
var next := enmr.MoveNext;
if not (curr is IConstQueue) or not next then
if curr as object is T(var sqa) then //ToDo #2146
res.AddRange(sqa.GetQS) else
res += curr;
if not next then break;
end;
Result := res.ToArray;
end;
{$endregion Sync/Async Base}
{$region SyncArray}
type
CommandQueueSyncArray<T> = sealed class(SimpleQueueArray<T>)
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override;
begin
for var i := 0 to qs.Length-2 do
prev_ev := qs[i].InvokeBase(tsk, c, cq, prev_ev).EvBase;
Result := (qs[qs.Length-1] as CommandQueue<T>).Invoke(tsk, c, cq, prev_ev);
end;
end;
static function CommandQueueBase.operator+(q1, q2: CommandQueueBase): CommandQueueBase :=
CombineSyncQueueBase(q1,q2);
static function CommandQueueBase.operator+<T>(q1: CommandQueueBase; q2: CommandQueue<T>): CommandQueue<T> :=
CombineSyncQueue&<T>(q1,q2);
{$region NonConv}
function __CombineSyncQueue<T>(qss: sequence of CommandQueueBase): CommandQueue<T>;
begin
var qs := FlattenQueueArray&<CommandQueueSyncArray<T>>(qss);
qs[qs.Length-1] := qs[qs.Length-1].Cast&<T>;
if qs.Length=1 then
Result := qs[0] else
Result := new CommandQueueSyncArray<T>(qs);
end;
function CombineSyncQueueBase(qs: sequence of CommandQueueBase) :=
__CombineSyncQueue&<object>(qs);
function CombineSyncQueueBase(params qs: array of CommandQueueBase) :=
__CombineSyncQueue&<object>(qs);
function CombineSyncQueue<T>(qs: sequence of CommandQueueBase) :=
__CombineSyncQueue&<T>(qs);
function CombineSyncQueue<T>(params qs: array of CommandQueueBase) :=
__CombineSyncQueue&<T>(qs);
function CombineSyncQueue<T>(qs: sequence of CommandQueue<T>) :=
__CombineSyncQueue&<T>(qs.Cast&<CommandQueueBase>);
function CombineSyncQueue<T>(params qs: array of CommandQueue<T>) :=
__CombineSyncQueue&<T>(qs.Cast&<CommandQueueBase>);
{$endregion NonConv}
{$region Conv}
{$region NoContext}
function CombineSyncQueue<TRes>(conv: Func<array of object, TRes>; qs: sequence of CommandQueueBase) :=
new HostQueueArray<object,TRes>(qs.Select(q->q.Cast&<object>).ToArray, conv, true);
function CombineSyncQueue<TRes>(conv: Func<array of object, TRes>; params qs: array of CommandQueueBase) :=
new HostQueueArray<object,TRes>(qs.ConvertAll(q->q.Cast&<object>), conv, true);
function CombineSyncQueue<TInp,TRes>(conv: Func<array of TInp, TRes>; qs: sequence of CommandQueue<TInp>) :=
new HostQueueArray<TInp,TRes>(qs.ToArray, conv, true);
function CombineSyncQueue<TInp,TRes>(conv: Func<array of TInp, TRes>; params qs: array of CommandQueue<TInp>) :=
new HostQueueArray<TInp,TRes>(qs.ToArray, conv, true);
{$endregion NoContext}
{$region Context}
function CombineSyncQueue<TRes>(conv: Func<array of object, Context, TRes>; qs: sequence of CommandQueueBase) :=
new HostQueueArrayC<object,TRes>(qs.Select(q->q.Cast&<object>).ToArray, conv, true);
function CombineSyncQueue<TRes>(conv: Func<array of object, Context, TRes>; params qs: array of CommandQueueBase) :=
new HostQueueArrayC<object,TRes>(qs.ConvertAll(q->q.Cast&<object>), conv, true);
function CombineSyncQueue<TInp,TRes>(conv: Func<array of TInp, Context, TRes>; qs: sequence of CommandQueue<TInp>) :=
new HostQueueArrayC<TInp,TRes>(qs.ToArray, conv, true);
function CombineSyncQueue<TInp,TRes>(conv: Func<array of TInp, Context, TRes>; params qs: array of CommandQueue<TInp>) :=
new HostQueueArrayC<TInp,TRes>(qs.ToArray, conv, true);
{$endregion Context}
{$endregion Conv}
{$endregion SyncArray}
{$region AsyncArray}
type
CommandQueueAsyncArray<T> = sealed class(SimpleQueueArray<T>)
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override;
begin
var evs := new __EventList[qs.Length-1];
var count := 0;
for var i := 0 to qs.Length-2 do
begin
var ncq := cl_command_queue.Zero;
prev_ev.Retain;
var ev := qs[i].InvokeBase(tsk, c, ncq, prev_ev).EvBase;
var CQFree: Action := ()->tsk.AddErr( cl.ReleaseCommandQueue(ncq) );
if ev.count=0 then
if ncq<>cl_command_queue.Zero then Task.Run(CQFree) else
ev := ev.AttachCallback((_ev,_st,_data)->
begin
tsk.AddErr( _st );
if ncq<>cl_command_queue.Zero then Task.Run(CQFree);
__NativUtils.GCHndFree(_data);
end, c, ncq);
evs[i] := ev;
count += ev.count;
end;
// ничего страшного что 1 из веток использует внешний cq, пока только 1. Так даже эффективнее
prev_ev.Retain;
Result := (qs[qs.Length-1] as CommandQueue<T>).Invoke(tsk, c, cq, prev_ev);
var res_ev := Result.ev;
prev_ev.Release;
Result.ev := new __EventList(count+res_ev.count);
foreach var ev in evs do Result.ev += ev;
Result.ev += res_ev;
end;
end;
static function CommandQueueBase.operator*(q1, q2: CommandQueueBase): CommandQueueBase :=
CombineAsyncQueueBase(q1,q2);
static function CommandQueueBase.operator*<T>(q1: CommandQueueBase; q2: CommandQueue<T>): CommandQueue<T> :=
CombineAsyncQueue&<T>(q1,q2);
{$region NonConv}
function __CombineAsyncQueue<T>(qss: sequence of CommandQueueBase): CommandQueue<T>;
begin
var qs := FlattenQueueArray&<CommandQueueAsyncArray<T>>(qss);
qs[qs.Length-1] := qs[qs.Length-1].Cast&<T>;
if qs.Length=1 then
Result := qs[0] else
Result := new CommandQueueAsyncArray<T>(qs);
end;
function CombineAsyncQueueBase(qs: sequence of CommandQueueBase) :=
__CombineAsyncQueue&<object>(qs);
function CombineAsyncQueueBase(params qs: array of CommandQueueBase) :=
__CombineAsyncQueue&<object>(qs);
function CombineAsyncQueue<T>(qs: sequence of CommandQueueBase) :=
__CombineAsyncQueue&<T>(qs);
function CombineAsyncQueue<T>(params qs: array of CommandQueueBase) :=
__CombineAsyncQueue&<T>(qs);
function CombineAsyncQueue<T>(qs: sequence of CommandQueue<T>) :=
__CombineAsyncQueue&<T>(qs.Cast&<CommandQueueBase>);
function CombineAsyncQueue<T>(params qs: array of CommandQueue<T>) :=
__CombineAsyncQueue&<T>(qs.Cast&<CommandQueueBase>);
{$endregion NonConv}
{$region Conv}
{$region NoContext}
function CombineAsyncQueue<TRes>(conv: Func<array of object, TRes>; qs: sequence of CommandQueueBase) :=
new HostQueueArray<object,TRes>(qs.Select(q->q.Cast&<object>).ToArray, conv, false);
function CombineAsyncQueue<TRes>(conv: Func<array of object, TRes>; params qs: array of CommandQueueBase) :=
new HostQueueArray<object,TRes>(qs.ConvertAll(q->q.Cast&<object>), conv, false);
function CombineAsyncQueue<TInp,TRes>(conv: Func<array of TInp, TRes>; qs: sequence of CommandQueue<TInp>) :=
new HostQueueArray<TInp,TRes>(qs.ToArray, conv, false);
function CombineAsyncQueue<TInp,TRes>(conv: Func<array of TInp, TRes>; params qs: array of CommandQueue<TInp>) :=
new HostQueueArray<TInp,TRes>(qs.ToArray, conv, false);
{$endregion NoContext}
{$region Context}
function CombineAsyncQueue<TRes>(conv: Func<array of object, Context, TRes>; qs: sequence of CommandQueueBase) :=
new HostQueueArrayC<object,TRes>(qs.Select(q->q.Cast&<object>).ToArray, conv, false);
function CombineAsyncQueue<TRes>(conv: Func<array of object, Context, TRes>; params qs: array of CommandQueueBase) :=
new HostQueueArrayC<object,TRes>(qs.ConvertAll(q->q.Cast&<object>), conv, false);
function CombineAsyncQueue<TInp,TRes>(conv: Func<array of TInp, Context, TRes>; qs: sequence of CommandQueue<TInp>) :=
new HostQueueArrayC<TInp,TRes>(qs.ToArray, conv, false);
function CombineAsyncQueue<TInp,TRes>(conv: Func<array of TInp, Context, TRes>; params qs: array of CommandQueue<TInp>) :=
new HostQueueArrayC<TInp,TRes>(qs.ToArray, conv, false);
{$endregion Context}
{$endregion Conv}
{$endregion AsyncArray}
{$region Wait}
type
WCQWaiter = abstract class
private waitables: array of CommandQueueBase;
public constructor(waitables: array of CommandQueueBase);
begin
foreach var q in waitables do q.MakeWaitable;
self.waitables := waitables;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase) :=
foreach var q in waitables do q.RegisterWaiterTask(tsk);
public function GetWaitEv(tsk: CLTaskBase; c: Context): __EventList; abstract;
protected procedure Finalize; override :=
foreach var q in waitables do q.UnMakeWaitable;
end;
WCQWaiterAll = sealed class(WCQWaiter)
public function GetWaitEv(tsk: CLTaskBase; c: Context): __EventList; override;
begin
Result := new __EventList(waitables.Length);
foreach var q in waitables do
begin
var ev := q.GetMWEvent(tsk, c);
if ev=cl_event.Zero then continue;
Result += ev;
end;
end;
end;
WCQWaiterAny = sealed class(WCQWaiter)
public function GetWaitEv(tsk: CLTaskBase; c: Context): __EventList; override;
begin
var uev := CommandQueueBase.CreateUserEvent(c);
var done := false;
var any_ev := false;
var lo := new object;
foreach var q in waitables do
begin
var ev := q.GetMWEvent(tsk, c);
if ev=cl_event.Zero then continue;
any_ev := true;
__EventList.AttachCallback(ev, (ev,st,data)->
begin
tsk.AddErr( st );
lock lo do
if not done then
begin
tsk.AddErr( cl.SetUserEventStatus(uev, CommandExecutionStatus.COMPLETE) );
done := true;
end;
__NativUtils.GCHndFree(data);
end);
cl.ReleaseEvent(ev).RaiseIfError;
end;
if any_ev then
Result := uev else
begin
cl.ReleaseEvent(uev).RaiseIfError;
Result := new __EventList;
end;
end;
end;
CommandQueueWaitFor = sealed class(__ContainerQueue<object>)
public waiter: WCQWaiter;
public constructor(waiter: WCQWaiter) :=
self.waiter := waiter;
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<object>; override;
begin
Result.ev := waiter.GetWaitEv(tsk, c);
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
waiter.RegisterWaitables(tsk);
end;
CommandQueueThenWaitFor<T> = sealed class(__ContainerQueue<T>)
public waiter: WCQWaiter;
public q: CommandQueue<T>;
public constructor(waiter: WCQWaiter; q: CommandQueue<T>);
begin
self.waiter := waiter;
self.q := q;
end;
protected function InvokeSubQs(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override;
begin
Result := q.Invoke(tsk, c, cq, prev_ev);
Result.ev := waiter.GetWaitEv(tsk, c) + Result.ev;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
waiter.RegisterWaitables(tsk);
q.RegisterWaitables(tsk, prev_hubs);
end;
end;
function WaitFor(q: CommandQueueBase) := WaitForAll(q);
function WaitForAll(params qs: array of CommandQueueBase) := WaitForAll(qs.AsEnumerable);
function WaitForAll(qs: sequence of CommandQueueBase) :=
new CommandQueueWaitFor(
new WCQWaiterAll(
qs.ToArray
)
);
function WaitForAny(params qs: array of CommandQueueBase) := WaitForAny(qs.AsEnumerable);
function WaitForAny(qs: sequence of CommandQueueBase) :=
new CommandQueueWaitFor(
new WCQWaiterAny(
qs.ToArray
)
);
function CommandQueue<T>.CreateWaitWrapper(qs: sequence of CommandQueueBase; all: boolean): CommandQueue<T> :=
new CommandQueueThenWaitFor<T>(
all?
new WCQWaiterAll(qs.ToArray) as WCQWaiter :
new WCQWaiterAny(qs.ToArray),
self
);
{$endregion Wait}
{$region GPUCommand}
{$region GPUCommandContainer}
type
__CCBObj<T> = sealed class(__GPUCommandContainerBody<T>)
public o: T;
public constructor(o: T; cc: __GPUCommandContainer<T>);
begin
self.o := o;
self.cc := cc;
end;
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override;
begin
var res := self.o;
if res as object is Buffer(var b) then
if b.memobj=cl_mem.Zero then
b.Init(c);
foreach var comm in cc.commands do
prev_ev := comm.InvokeObj(res, tsk, c, cq, prev_ev);
Result.ev := prev_ev;
Result.res := res;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override := exit;
end;
__CCBQueue<T> = sealed class(__GPUCommandContainerBody<T>)
public hub: MultiusableCommandQueueHub<T>;
public constructor(q: CommandQueue<T>; cc: __GPUCommandContainer<T>);
begin
self.hub := new MultiusableCommandQueueHub<T>(q);
self.cc := cc;
end;
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __QueueRes<T>; override;
begin
var new_plug: ()->CommandQueue<T> := hub.MakeNode;
foreach var comm in cc.commands do
prev_ev := comm.InvokeQueue(new_plug, tsk, c, cq, prev_ev);
Result := new_plug().Invoke(tsk, c, cq, prev_ev);
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
hub.q.RegisterWaitables(tsk, prev_hubs);
end;
constructor __GPUCommandContainer<T>.Create(o: T) :=
self.body := new __CCBObj<T>(o, self);
constructor __GPUCommandContainer<T>.Create(q: CommandQueue<T>) :=
if q is ConstQueue<T>(var cq) then
self.body := new __CCBObj<T>(cq.Val, self) else
self.body := new __CCBQueue<T>(q, self);
function BufferCommandQueue.GetSizeQ: CommandQueue<integer>;
begin
var ob := self.body as __CCBObj<Buffer>;
if ob<>nil then
Result := integer(ob.o.Size32) else
Result := (self.body as __CCBQueue<Buffer>).hub.MakeNode.ThenConvert(b->integer(b.Size32));
end;
{$endregion GPUCommandContainer}
{$region QueueCommand}
type
QueueCommand<T> = sealed class(__GPUCommand<T>)
public q: CommandQueueBase;
public constructor(q: CommandQueueBase) :=
self.q := q;
protected function InvokeObj(o: T; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; override :=
q.InvokeBase(tsk, c, cq, prev_ev).EvBase;
protected function InvokeQueue(o_q: ()->CommandQueue<T>; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; override :=
q.InvokeBase(tsk, c, cq, prev_ev).EvBase;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
q.RegisterWaitables(tsk, prev_hubs);
end;
procedure __GPUCommandContainer<T>.InternalAddQueue(q: CommandQueueBase) :=
commands.Add( new QueueCommand<T>(q) );
{$endregion QueueCommand}
{$region ProcCommand}
type
ProcCommandBase<T> = abstract class(__GPUCommand<T>)
protected procedure ExecProc(c: Context; o: T); abstract;
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_res: __QueueRes<T>): cl_event;
begin
var uev := CommandQueueBase.CreateUserEvent(c);
Thread.Create(()->
begin
try
self.ExecProc(c, prev_res.WaitAndGet);
except
on e: Exception do tsk.AddErr(e);
end;
tsk.AddErr( cl.SetUserEventStatus(uev, CommandExecutionStatus.COMPLETE) );
end).Start;
Result := uev;
end;
protected function InvokeObj(o: T; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; override;
begin
var prev_res: __QueueRes<T>;
prev_res.res := o;
prev_res.ev := prev_ev;
Result := Invoke(tsk, c, cq, prev_res);
end;
protected function InvokeQueue(o_q: ()->CommandQueue<T>; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; override :=
Invoke(tsk, c, cq, o_q().Invoke(tsk, c, cq, prev_ev));
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override := exit;
end;
ProcCommand<T> = sealed class(ProcCommandBase<T>)
public p: T->();
public constructor(p: T->()) := self.p := p;
protected procedure ExecProc(c: Context; o: T); override := p(o);
end;
ProcCommandC<T> = sealed class(ProcCommandBase<T>)
public p: (T,Context)->();
public constructor(p: (T,Context)->()) := self.p := p;
protected procedure ExecProc(c: Context; o: T); override := p(o, c);
end;
procedure __GPUCommandContainer<T>.InternalAddProc(p: T->()) :=
commands.Add( new ProcCommand<T>(p) );
procedure __GPUCommandContainer<T>.InternalAddProc(p: (T,Context)->()) :=
commands.Add( new ProcCommandC<T>(p) );
{$endregion ProcCommand}
{$region WaitCommand}
type
WaitCommand<T> = sealed class(__GPUCommand<T>)
public waiter: WCQWaiter;
public constructor(waiter: WCQWaiter) :=
self.waiter := waiter;
protected function InvokeObj(o: T; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; override :=
waiter.GetWaitEv(tsk, c) + prev_ev;
protected function InvokeQueue(o_q: ()->CommandQueue<T>; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; override :=
waiter.GetWaitEv(tsk, c) + prev_ev;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override :=
waiter.RegisterWaitables(tsk);
end;
procedure __GPUCommandContainer<T>.InternalAddWaitAll(qs: sequence of CommandQueueBase) :=
commands.Add(new WaitCommand<T>( new WCQWaiterAll(qs.ToArray) ));
procedure __GPUCommandContainer<T>.InternalAddWaitAny(qs: sequence of CommandQueueBase) :=
commands.Add(new WaitCommand<T>( new WCQWaiterAny(qs.ToArray) ));
{$endregion WaitCommand}
{$region EnqueueableGPUCommand}
type
EnqueueableGPUCommand<T> = abstract class(__GPUCommand<T>)
protected allow_sync_enq := true;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (T, Context, cl_command_queue, __EventList)->cl_event; abstract;
protected procedure FixCQ(c: Context; var cq: cl_command_queue) :=
if cq=cl_command_queue.Zero then
begin
var ec: ErrorCode;
cq := cl.CreateCommandQueueWithProperties(c._context,c._device, IntPtr.Zero, ec);
ec.RaiseIfError;
end;
protected function Invoke(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList; o_res: __QueueRes<T>): __EventList;
begin
var enq_f := InvokeParams(tsk, c, cq, o_res.ev);
if allow_sync_enq and (o_res.ev.count=0) then
Result := enq_f(o_res.Get, c, cq, prev_ev) else
begin
var uev := CommandQueueBase.CreateUserEvent(c);
// Асинхронное Enqueue, придётся пересоздать cq
var lcq := cq;
cq := cl_command_queue.Zero;
if not allow_sync_enq then
Thread.Create(()->
begin
try
if prev_ev.count<>0 then
begin
cl.WaitForEvents(prev_ev.count,prev_ev.evs).RaiseIfError;
prev_ev.Release;
end;
enq_f(o_res.WaitAndGet, c, lcq, nil);
except
on e: Exception do tsk.AddErr(e);
end;
tsk.AddErr( cl.SetUserEventStatus(uev,CommandExecutionStatus.COMPLETE) );
tsk.AddErr( cl.ReleaseCommandQueue(lcq) );
end).Start else
begin
var set_complete: EventCallback := (ev,st,data)->
begin
tsk.AddErr( st );
Task.Run(()->tsk.AddErr( cl.ReleaseCommandQueue(lcq) ));
tsk.AddErr( cl.SetUserEventStatus(uev, CommandExecutionStatus.COMPLETE) );
__NativUtils.GCHndFree(data);
end;
cl.ReleaseEvent(o_res.ev.AttachCallback((ev,st,data)->
begin
tsk.AddErr( st );
__EventList.AttachCallback(
enq_f(o_res.Get, c, lcq, prev_ev),
set_complete,
tsk
);
__NativUtils.GCHndFree(data);
end, c, lcq)).RaiseIfError;
end;
Result := uev;
end;
end;
protected function InvokeObj(o: T; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; override;
begin
var o_res: __QueueRes<T>;
o_res.res := o;
o_res.ev := new __EventList;
Result := Invoke(tsk, c, cq, prev_ev, o_res);
end;
protected function InvokeQueue(o_q: ()->CommandQueue<T>; tsk: CLTaskBase; c: Context; var cq: cl_command_queue; prev_ev: __EventList): __EventList; override :=
Invoke(tsk, c, cq, new __EventList, o_q().Invoke(tsk, c, cq, prev_ev));
end;
{$endregion DirectGPUCommandBase}
{$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<Buffer>) :=
inherited Create(q.ThenConvert(костыль_BufferCommandQueue_Create));
{$endregion BufferCommandQueue}
{$region Write}
type
BufferCommandWriteData = sealed class(EnqueueableGPUCommand<Buffer>)
public ptr_q: CommandQueue<IntPtr>;
public offset_q, len_q: CommandQueue<integer>;
public constructor(ptr_q: CommandQueue<IntPtr>; offset_q, len_q: CommandQueue<integer>);
begin
self.ptr_q := ptr_q;
self.offset_q := offset_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var ptr := ptr_q .Invoke(tsk, c, cq, new __EventList);
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, ptr.ev, offset.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
cl.EnqueueWriteBuffer(
l_cq, b.memobj, Bool.NON_BLOCKING,
new UIntPtr(offset.Get), new UIntPtr(len.Get),
ptr.Get,
prev_ev.count,prev_ev.evs,res_ev
).RaiseIfError;
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
ptr_q .RegisterWaitables(tsk, prev_hubs);
offset_q.RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
BufferCommandWriteArray = sealed class(EnqueueableGPUCommand<Buffer>)
public a_q: CommandQueue<&Array>;
public offset_q, len_q: CommandQueue<integer>;
public constructor(a_q: CommandQueue<&Array>; offset_q, len_q: CommandQueue<integer>);
begin
self.allow_sync_enq := false;
self.a_q := a_q;
self.offset_q := offset_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var a := a_q .Invoke(tsk, c, cq, new __EventList);
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, a.ev, offset.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
cl.EnqueueWriteBuffer(
l_cq, b.memobj, Bool.BLOCKING,
new UIntPtr(offset.WaitAndGet), new UIntPtr(len.WaitAndGet),
Marshal.UnsafeAddrOfPinnedArrayElement(a.WaitAndGet,0),
0,IntPtr.Zero,IntPtr.Zero
).RaiseIfError;
Result := cl_event.Zero;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
a_q .RegisterWaitables(tsk, prev_hubs);
offset_q.RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
BufferCommandWriteValue<T> = sealed class(EnqueueableGPUCommand<Buffer>) where T: record;
public val: IntPtr;
public offset_q: CommandQueue<integer>;
public constructor(val: T; offset_q: CommandQueue<integer>);
begin
self.val := __NativUtils.CopyToUnm(val);
self.offset_q := offset_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, offset.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
cl.EnqueueWriteBuffer(
l_cq, b.memobj, Bool.NON_BLOCKING,
new UIntPtr(offset.Get), new UIntPtr(Marshal.SizeOf&<T>),
self.val,
prev_ev.count,prev_ev.evs,res_ev
).RaiseIfError;
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
offset_q.RegisterWaitables(tsk, prev_hubs);
end;
protected procedure Finalize; override :=
Marshal.FreeHGlobal(self.val);
end;
BufferCommandWriteValueQ<T> = sealed class(EnqueueableGPUCommand<Buffer>) where T: record;
public val_q: CommandQueue<T>;
public offset_q: CommandQueue<integer>;
public constructor(val_q: CommandQueue<T>; offset_q: CommandQueue<integer>);
begin
self.val_q := val_q;
self.offset_q := offset_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var val := val_q .Invoke(tsk, c, cq, new __EventList);
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, val.ev, offset.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
var val_ptr := __NativUtils.CopyToUnm(val.Get());
cl.EnqueueWriteBuffer(
l_cq, b.memobj, Bool.NON_BLOCKING,
new UIntPtr(offset.Get), new UIntPtr(Marshal.SizeOf&<T>),
val_ptr,
prev_ev.count,prev_ev.evs,res_ev
).RaiseIfError;
__EventList.AttachCallback(res_ev, (ev,st,data)->
begin
tsk.AddErr(st);
Marshal.FreeHGlobal(val_ptr);
__NativUtils.GCHndFree(data);
end);
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
val_q .RegisterWaitables(tsk, prev_hubs);
offset_q.RegisterWaitables(tsk, prev_hubs);
end;
end;
function BufferCommandQueue.AddWriteData(ptr: CommandQueue<IntPtr>; offset, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandWriteData(ptr, offset, len));
function BufferCommandQueue.AddWriteArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandWriteArray(a, offset, len));
function BufferCommandQueue.AddWriteValue<TRecord>(val: TRecord; offset: CommandQueue<integer>) :=
AddCommand(new BufferCommandWriteValue<TRecord>(val, offset));
function BufferCommandQueue.AddWriteValue<TRecord>(val: CommandQueue<TRecord>; offset: CommandQueue<integer>) :=
AddCommand(new BufferCommandWriteValueQ<TRecord>(val, offset));
{$endregion Write}
{$region Read}
type
BufferCommandReadData = sealed class(EnqueueableGPUCommand<Buffer>)
public ptr_q: CommandQueue<IntPtr>;
public offset_q, len_q: CommandQueue<integer>;
public constructor(ptr_q: CommandQueue<IntPtr>; offset_q, len_q: CommandQueue<integer>);
begin
self.ptr_q := ptr_q;
self.offset_q := offset_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var ptr := ptr_q .Invoke(tsk, c, cq, new __EventList);
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, ptr.ev, offset.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
cl.EnqueueReadBuffer(
l_cq, b.memobj, Bool.NON_BLOCKING,
new UIntPtr(offset.Get), new UIntPtr(len.Get),
ptr.Get,
prev_ev.count,prev_ev.evs,res_ev
).RaiseIfError;
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
ptr_q .RegisterWaitables(tsk, prev_hubs);
offset_q.RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
BufferCommandReadArray = sealed class(EnqueueableGPUCommand<Buffer>)
public a_q: CommandQueue<&Array>;
public offset_q, len_q: CommandQueue<integer>;
public constructor(a_q: CommandQueue<&Array>; offset_q, len_q: CommandQueue<integer>);
begin
self.allow_sync_enq := false;
self.a_q := a_q;
self.offset_q := offset_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var a := a_q .Invoke(tsk, c, cq, new __EventList);
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, a.ev, offset.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
cl.EnqueueReadBuffer(
l_cq, b.memobj, Bool.BLOCKING,
new UIntPtr(offset.WaitAndGet), new UIntPtr(len.WaitAndGet),
Marshal.UnsafeAddrOfPinnedArrayElement(a.WaitAndGet,0),
0,IntPtr.Zero,IntPtr.Zero
).RaiseIfError;
Result := cl_event.Zero;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
a_q .RegisterWaitables(tsk, prev_hubs);
offset_q.RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
function BufferCommandQueue.AddReadData(ptr: CommandQueue<IntPtr>; offset, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandReadData(ptr, offset, len));
function BufferCommandQueue.AddReadArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandReadArray(a, offset, len));
{$endregion Read}
{$region Fill}
type
BufferCommandDataFill = sealed class(EnqueueableGPUCommand<Buffer>)
public ptr_q: CommandQueue<IntPtr>;
public pattern_len_q, offset_q, len_q: CommandQueue<integer>;
public constructor(ptr: CommandQueue<IntPtr>; pattern_len_q, offset_q, len_q: CommandQueue<integer>);
begin
self.ptr_q := ptr_q;
self.pattern_len_q := pattern_len_q;
self.offset_q := offset_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var ptr := ptr_q .Invoke(tsk, c, cq, new __EventList);
var pattern_len := pattern_len_q.Invoke(tsk, c, cq, new __EventList);
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, ptr.ev, pattern_len.ev, offset.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
cl.EnqueueFillBuffer(
l_cq, b.memobj,
ptr.Get, new UIntPtr(pattern_len.Get),
new UIntPtr(offset.Get), new UIntPtr(len.Get),
prev_ev.count, prev_ev.evs, res_ev
).RaiseIfError;
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
ptr_q .RegisterWaitables(tsk, prev_hubs);
pattern_len_q .RegisterWaitables(tsk, prev_hubs);
offset_q .RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
BufferCommandArrayFill = sealed class(EnqueueableGPUCommand<Buffer>)
public a_q: CommandQueue<&Array>;
public offset_q, len_q: CommandQueue<integer>;
public constructor(a_q: CommandQueue<&Array>; offset_q, len_q: CommandQueue<integer>);
begin
self.allow_sync_enq := false;
self.a_q := a_q;
self.offset_q := offset_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var a := a_q .Invoke(tsk, c, cq, new __EventList);
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, a.ev, offset.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
var la := a.WaitAndGet;
// Синхронного Fill нету, поэтому между cl.Enqueue и cl.WaitForEvents сборщик мусора может сломать указатель
// Остаётся только закреплять, хоть так и не любой тип массива пропустит
var a_hnd := GCHandle.Alloc(la, GCHandleType.Pinned);
cl.EnqueueFillBuffer(
l_cq, b.memobj,
a_hnd.AddrOfPinnedObject, new UIntPtr(System.Buffer.ByteLength(la)),
new UIntPtr(offset.WaitAndGet), new UIntPtr(len.WaitAndGet),
prev_ev.count,prev_ev.evs,res_ev
).RaiseIfError;
cl.WaitForEvents(1,res_ev).RaiseIfError;
cl.ReleaseEvent(res_ev).RaiseIfError;
a_hnd.Free;
Result := cl_event.Zero;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
a_q .RegisterWaitables(tsk, prev_hubs);
offset_q.RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
BufferCommandValueFill<T> = sealed class(EnqueueableGPUCommand<Buffer>) where T: record;
public val: IntPtr;
public offset_q, len_q: CommandQueue<integer>;
public constructor(val: T; offset_q, len_q: CommandQueue<integer>);
begin
self.val := __NativUtils.CopyToUnm(val);
self.offset_q := offset_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, offset.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
cl.EnqueueFillBuffer(
l_cq, b.memobj,
val, new UIntPtr(Marshal.SizeOf&<T>),
new UIntPtr(offset.Get), new UIntPtr(len.Get),
prev_ev.count,prev_ev.evs,res_ev
).RaiseIfError;
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
offset_q.RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
protected procedure Finalize; override :=
Marshal.FreeHGlobal(self.val);
end;
BufferCommandValueFillQ<T> = sealed class(EnqueueableGPUCommand<Buffer>) where T: record;
public val_q: CommandQueue<T>;
public offset_q, len_q: CommandQueue<integer>;
public constructor(val_q: CommandQueue<T>; offset_q, len_q: CommandQueue<integer>);
begin
self.val_q := val_q;
self.offset_q := offset_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var val := val_q .Invoke(tsk, c, cq, new __EventList);
var offset := offset_q .Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, val.ev, offset.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
var val_ptr := __NativUtils.CopyToUnm(val.Get());
cl.EnqueueFillBuffer(
l_cq, b.memobj,
val_ptr, new UIntPtr(Marshal.SizeOf&<T>),
new UIntPtr(offset.Get), new UIntPtr(len.Get),
prev_ev.count,prev_ev.evs,res_ev
).RaiseIfError;
__EventList.AttachCallback(res_ev, (ev,st,data)->
begin
tsk.AddErr(st);
Marshal.FreeHGlobal(val_ptr);
__NativUtils.GCHndFree(data);
end);
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
val_q .RegisterWaitables(tsk, prev_hubs);
offset_q.RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
function BufferCommandQueue.AddFillData(ptr: CommandQueue<IntPtr>; pattern_len, offset, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandDataFill(ptr,pattern_len, offset,len));
function BufferCommandQueue.AddFillArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandArrayFill(a, offset,len));
function BufferCommandQueue.AddFillValue<TRecord>(val: TRecord; offset, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandValueFill<TRecord>(val, offset, len));
function BufferCommandQueue.AddFillValue<TRecord>(val: CommandQueue<TRecord>; offset, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandValueFillQ<TRecord>(val, offset, len));
{$endregion Fill}
{$region Copy}
type
BufferCommandCopyFrom = sealed class(EnqueueableGPUCommand<Buffer>)
public buf_q: CommandQueue<Buffer>;
public f_pos_q, t_pos_q, len_q: CommandQueue<integer>;
public constructor(buf_q: CommandQueue<Buffer>; f_pos_q, t_pos_q, len_q: CommandQueue<integer>);
begin
self.buf_q := buf_q;
self.f_pos_q := f_pos_q;
self.t_pos_q := t_pos_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var buf := buf_q .Invoke(tsk, c, cq, new __EventList);
var f_pos := f_pos_q.Invoke(tsk, c, cq, new __EventList);
var t_pos := t_pos_q.Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, buf.ev, f_pos.ev, t_pos.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
var b2 := buf.Get;
if b2.memobj=cl_mem.Zero then b2.Init(l_c);
cl.EnqueueCopyBuffer(
l_cq, b2.memobj,b.memobj,
new UIntPtr(f_pos.Get), new UIntPtr(t_pos.Get),
new UIntPtr(len.Get),
prev_ev.count,prev_ev.evs, res_ev
).RaiseIfError;
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
buf_q .RegisterWaitables(tsk, prev_hubs);
f_pos_q .RegisterWaitables(tsk, prev_hubs);
t_pos_q .RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
BufferCommandCopyTo = sealed class(EnqueueableGPUCommand<Buffer>)
public buf_q: CommandQueue<Buffer>;
public f_pos_q, t_pos_q, len_q: CommandQueue<integer>;
public constructor(buf_q: CommandQueue<Buffer>; f_pos_q, t_pos_q, len_q: CommandQueue<integer>);
begin
self.buf_q := buf_q;
self.f_pos_q := f_pos_q;
self.t_pos_q := t_pos_q;
self.len_q := len_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Buffer, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var buf := buf_q .Invoke(tsk, c, cq, new __EventList);
var f_pos := f_pos_q.Invoke(tsk, c, cq, new __EventList);
var t_pos := t_pos_q.Invoke(tsk, c, cq, new __EventList);
var len := len_q .Invoke(tsk, c, cq, new __EventList);
ev_res := __EventList.Combine(ev_res, buf.ev, f_pos.ev, t_pos.ev, len.ev);
FixCQ(c, cq);
Result := (b, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
var b2 := buf.Get;
if b2.memobj=cl_mem.Zero then b2.Init(l_c);
cl.EnqueueCopyBuffer(
l_cq, b.memobj,b2.memobj,
new UIntPtr(f_pos.Get), new UIntPtr(t_pos.Get),
new UIntPtr(len.Get),
prev_ev.count,prev_ev.evs, res_ev
).RaiseIfError;
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
buf_q .RegisterWaitables(tsk, prev_hubs);
f_pos_q .RegisterWaitables(tsk, prev_hubs);
t_pos_q .RegisterWaitables(tsk, prev_hubs);
len_q .RegisterWaitables(tsk, prev_hubs);
end;
end;
function BufferCommandQueue.AddCopyFrom(b: CommandQueue<Buffer>; from, &to, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandCopyFrom(b, from,&to, len));
function BufferCommandQueue.AddCopyTo(b: CommandQueue<Buffer>; from, &to, len: CommandQueue<integer>) :=
AddCommand(new BufferCommandCopyTo(b, &to,from, len));
{$endregion Copy}
{$endregion Buffer}
{$region Kernel}
{$region Exec}
type
KernelCommandExec = sealed class(EnqueueableGPUCommand<Kernel>)
public work_szs_q: CommandQueue<array of UIntPtr>;
public args_q: array of CommandQueue<Buffer>;
public constructor(work_szs_q: CommandQueue<array of UIntPtr>; args_q: array of CommandQueue<Buffer>);
begin
self.work_szs_q := work_szs_q;
self.args_q := args_q;
end;
protected function InvokeParams(tsk: CLTaskBase; c: Context; var cq: cl_command_queue; var ev_res: __EventList): (Kernel, Context, cl_command_queue, __EventList)->cl_event; override;
begin
var work_szs := work_szs_q.Invoke(tsk, c, cq, new __EventList);
var count := ev_res.count + work_szs.ev.count;
var args := new __QueueRes<Buffer>[args_q.Length];
for var i := 0 to args.Length-1 do
begin
var arg := args_q[i].Invoke(tsk, c, cq, new __EventList);
count += arg.ev.count;
args[i] := arg;
end;
var ev := new __EventList(count);
ev += ev_res;
ev += work_szs.ev;
for var i := 0 to args.Length-1 do
ev += args[i].ev;
ev_res := ev;
FixCQ(c, cq);
Result := (k, l_c, l_cq, prev_ev)->
begin
var res_ev: cl_event;
var work_szs_res := work_szs.Get;
for var i := 0 to args.Length-1 do
begin
var b := args[i].Get;
if b.memobj=cl_mem.Zero then b.Init(l_c);
cl.SetKernelArg(k._kernel, i, new UIntPtr(UIntPtr.Size), b.memobj).RaiseIfError;
end;
cl.EnqueueNDRangeKernel(
l_cq,k._kernel,
work_szs_res.Length,
nil,work_szs_res,nil,
prev_ev.count,prev_ev.evs,res_ev
).RaiseIfError;
Result := res_ev;
end;
end;
protected procedure RegisterWaitables(tsk: CLTaskBase; prev_hubs: HashSet<object>); override;
begin
work_szs_q.RegisterWaitables(tsk, prev_hubs);
foreach var arg in args_q do
arg.RegisterWaitables(tsk, prev_hubs);
end;
end;
function KernelCommandQueue.AddExec(work_szs: array of UIntPtr; params args: array of CommandQueue<Buffer>) :=
AddCommand(new KernelCommandExec(work_szs, args));
function KernelCommandQueue.AddExec(work_szs: array of CommandQueue<UIntPtr>; params args: array of CommandQueue<Buffer>) :=
AddCommand(new KernelCommandExec(
CombineAsyncQueue(a->a,work_szs),
args
));
function KernelCommandQueue.AddExec(work_szs: CommandQueue<array of UIntPtr>; params args: array of CommandQueue<Buffer>) :=
AddCommand(new KernelCommandExec(
work_szs,
args
));
function KernelCommandQueue.AddExec(work_szs: CommandQueue<array of integer>; params args: array of CommandQueue<Buffer>) :=
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
if self.memobj<>cl_mem.Zero then Dispose;
GC.AddMemoryPressure(Size64);
var ec: ErrorCode;
self.memobj := cl.CreateBuffer(c._context, MemFlags.MEM_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, MemFlags.MEM_READ_WRITE, BufferCreateType.BUFFER_CREATE_TYPE_REGION,reg, ec);
ec.RaiseIfError;
end;
{$endregion constructor's}
{$region Write}
function Buffer.WriteData(ptr: CommandQueue<IntPtr>) :=
Context.Default.SyncInvoke(NewQueue.AddWriteData(ptr) as CommandQueue<Buffer>);
function Buffer.WriteData(ptr: CommandQueue<IntPtr>; offset, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddWriteData(ptr, offset, len) as CommandQueue<Buffer>);
function Buffer.WriteArray(a: CommandQueue<&Array>) :=
Context.Default.SyncInvoke(NewQueue.AddWriteArray(a) as CommandQueue<Buffer>);
function Buffer.WriteArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddWriteArray(a, offset, len) as CommandQueue<Buffer>);
function Buffer.WriteValue<TRecord>(val: CommandQueue<TRecord>; offset: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddWriteValue(val, offset) as CommandQueue<Buffer>);
{$endregion Write}
{$region Read}
function Buffer.ReadData(ptr: CommandQueue<IntPtr>) :=
Context.Default.SyncInvoke(NewQueue.AddReadData(ptr) as CommandQueue<Buffer>);
function Buffer.ReadData(ptr: CommandQueue<IntPtr>; offset, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddReadData(ptr, offset, len) as CommandQueue<Buffer>);
function Buffer.ReadArray(a: CommandQueue<&Array>) :=
Context.Default.SyncInvoke(NewQueue.AddReadArray(a) as CommandQueue<Buffer>);
function Buffer.ReadArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddReadArray(a, offset, len) as CommandQueue<Buffer>);
{$endregion Read}
{$region PatternFill}
function Buffer.FillData(ptr: CommandQueue<IntPtr>; pattern_len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddFillData(ptr, pattern_len) as CommandQueue<Buffer>);
function Buffer.FillData(ptr: CommandQueue<IntPtr>; pattern_len, offset, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddFillData(ptr, pattern_len, offset, len) as CommandQueue<Buffer>);
function Buffer.FillArray(a: CommandQueue<&Array>) :=
Context.Default.SyncInvoke(NewQueue.AddFillArray(a) as CommandQueue<Buffer>);
function Buffer.FillArray(a: CommandQueue<&Array>; offset, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddFillArray(a, offset, len) as CommandQueue<Buffer>);
function Buffer.FillValue<TRecord>(val: TRecord) :=
Context.Default.SyncInvoke(NewQueue.AddFillValue(val) as CommandQueue<Buffer>);
function Buffer.FillValue<TRecord>(val: TRecord; offset, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddFillValue(val, offset, len) as CommandQueue<Buffer>);
function Buffer.FillValue<TRecord>(val: CommandQueue<TRecord>) :=
Context.Default.SyncInvoke(NewQueue.AddFillValue(val) as CommandQueue<Buffer>);
function Buffer.FillValue<TRecord>(val: CommandQueue<TRecord>; offset, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddFillValue(val, offset, len) as CommandQueue<Buffer>);
{$endregion PatternFill}
{$region Copy}
function Buffer.CopyFrom(b: CommandQueue<Buffer>; from, &to, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddCopyFrom(b, from, &to, len) as CommandQueue<Buffer>);
function Buffer.CopyFrom(b: CommandQueue<Buffer>) :=
Context.Default.SyncInvoke(NewQueue.AddCopyFrom(b) as CommandQueue<Buffer>);
function Buffer.CopyTo(b: CommandQueue<Buffer>; from, &to, len: CommandQueue<integer>) :=
Context.Default.SyncInvoke(NewQueue.AddCopyTo(b, from, &to, len) as CommandQueue<Buffer>);
function Buffer.CopyTo(b: CommandQueue<Buffer>) :=
Context.Default.SyncInvoke(NewQueue.AddCopyTo(b) as CommandQueue<Buffer>);
{$endregion Copy}
{$region Get}
function Buffer.GetData(offset, len: CommandQueue<integer>): IntPtr;
begin
var res: IntPtr;
var Qs_len: ()->CommandQueue<integer>;
if len is ConstQueue<integer> then
Qs_len := ()->len else
if len is MultiusableCommandQueueNode<integer>(var mcqn) then
Qs_len := mcqn.hub.MakeNode else
Qs_len := len.Multiusable;
var Q_res := Qs_len().ThenConvert(len_val->
begin
Result := Marshal.AllocHGlobal(len_val);
res := Result;
end);
Context.Default.SyncInvoke(
self.NewQueue.AddReadData(Q_res, offset,Qs_len()) as CommandQueue<Buffer>
);
Result := res;
end;
function Buffer.GetArrayAt<TArray>(offset: CommandQueue<integer>; szs: CommandQueue<array of integer>): TArray;
begin
var el_t := typeof(TArray).GetElementType;
if szs is ConstQueue<array of integer>(var const_szs) then
begin
var res := System.Array.CreateInstance(
el_t,
const_szs.res
);
self.ReadArray(res, 0,Marshal.SizeOf(el_t)*res.Length);
Result := TArray(res);
end else
begin
var Qs_szs: ()->CommandQueue<array of integer>;
if szs is ConstQueue<array of integer> then
Qs_szs := ()->szs else
if szs is MultiusableCommandQueueNode<array of integer>(var mcqn) then
Qs_szs := mcqn.hub.MakeNode else
Qs_szs := szs.Multiusable;
var Qs_a := Qs_szs().ThenConvert(szs_val->
System.Array.CreateInstance(
el_t,
szs_val
)).Multiusable;
var Q_a := Qs_a();
var Q_a_len := Qs_szs().ThenConvert( szs_val -> Marshal.SizeOf(el_t)*szs_val.Aggregate((i1,i2)->i1*i2) );
var Q_res := Qs_a().Cast&<TArray>;
Result := Context.Default.SyncInvoke(
self.NewQueue.AddReadArray(Q_a, offset, Q_a_len)
as CommandQueue<Buffer> +
Q_res
);
end;
end;
function Buffer.GetArrayAt<TArray>(offset: CommandQueue<integer>; params szs: array of CommandQueue<integer>) :=
szs.All(q->q is ConstQueue<integer>) ?
GetArrayAt&<TArray>(offset, CommandQueue&<array of integer>( szs.ConvertAll(q->ConstQueue&<integer>(q).res) )) :
GetArrayAt&<TArray>(offset, CombineAsyncQueue(a->a, szs));
function Buffer.GetValueAt<TRecord>(offset: CommandQueue<integer>): TRecord;
begin
Context.Default.SyncInvoke(
self.NewQueue
.AddReadValue(Result, offset) as CommandQueue<Buffer>
);
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<Buffer>) :=
Context.Default.SyncInvoke(NewQueue.AddExec(work_szs, args) as CommandQueue<Kernel>);
function Kernel.Exec(work_szs: array of CommandQueue<UIntPtr>; params args: array of CommandQueue<Buffer>) :=
Context.Default.SyncInvoke(NewQueue.AddExec(work_szs, args) as CommandQueue<Kernel>);
function Kernel.Exec(work_szs: CommandQueue<array of UIntPtr>; params args: array of CommandQueue<Buffer>) :=
Context.Default.SyncInvoke(NewQueue.AddExec(work_szs, args) as CommandQueue<Kernel>);
function Kernel.Exec(work_szs: CommandQueue<array of integer>; params args: array of CommandQueue<Buffer>) :=
Context.Default.SyncInvoke(NewQueue.AddExec(work_szs, args) as CommandQueue<Kernel>);
{$endregion Exec}
{$endregion Kernel}
{$endregion Неявные CommandQueue}
end.