Merge pull request #2389 from SunSerega/POCGL-stables

Обновление POCGL
This commit is contained in:
Ivan Bondarev 2021-01-05 18:49:48 +01:00 committed by GitHub
commit bc73af899e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 25828 additions and 26174 deletions

View file

@ -1,4 +1,5 @@

#pragma OPENCL EXTENSION cl_khr_fp64: enable
__kernel void MatrMltMatr(__global double* A, __global double* B, __global double* C, int W)

View file

@ -1,4 +1,5 @@

#pragma OPENCL EXTENSION cl_khr_fp64: enable
__kernel void MatrMltMatr(__global double* A, __global double* B, __global double* C, int W)

View file

@ -13,11 +13,11 @@ uses OpenGL;
{$apptype windows} // убирает консоль
var gl: OpenGL.gl;
var gl: OpenGL.gl<PlWin>;
{$region Shader}
function InitShader(fname: string; st: ShaderType): ShaderName;
function InitShader(fname: string; st: ShaderType): gl_shader;
begin
Result := gl.CreateShader(st);
@ -61,12 +61,12 @@ end;
{$region Program}
function InitProgram(vertex_shader, fragment_shader: ShaderName): ProgramName;
function InitProgram(vertex_shader, fragment_shader: gl_shader): gl_program;
begin
Result := gl.CreateProgram;
gl.AttachShader(Result, vertex_shader);
if fragment_shader<>ShaderName.Zero then gl.AttachShader(Result, fragment_shader);
if fragment_shader<>gl_shader.Zero then gl.AttachShader(Result, fragment_shader);
gl.LinkProgram(Result);
// всё то же самое что и у шейдеров
@ -113,13 +113,13 @@ begin
// При создании экземпляра OpenGL.gl инициализируются некоторые функции
// Это необходимо для всех функций из OpenGL1.2 и выше, потому что они локальны для контекста OpenGL
gl := new OpenGL.gl;
gl := new OpenGL.gl<PlWin>;
{$endregion Настройка глобальных параметров OpenGL}
{$region Инициализация переменных}
var vertex_pos_buffer: BufferName;
var vertex_pos_buffer: gl_buffer;
gl.CreateBuffers(1, vertex_pos_buffer);
gl.NamedBufferData(
vertex_pos_buffer,
@ -137,7 +137,7 @@ begin
VertexBufferObjectUsage.STATIC_DRAW
);
var vertex_clr_buffer: BufferName;
var vertex_clr_buffer: gl_buffer;
gl.CreateBuffers(1, vertex_clr_buffer);
gl.NamedBufferData(
vertex_clr_buffer,
@ -150,7 +150,7 @@ begin
VertexBufferObjectUsage.STATIC_DRAW
);
var element_buffer: BufferName;
var element_buffer: gl_buffer;
gl.CreateBuffers(1, element_buffer);
gl.NamedBufferData(
element_buffer,
@ -164,7 +164,7 @@ begin
var vertex_shader := InitShader('Rot Triangle 1.vertex.glsl', ShaderType.VERTEX_SHADER);
// var fragment_shader := InitShader('fragment.glsl', ShaderType.FRAGMENT_SHADER);
var sprog := InitProgram(vertex_shader, ShaderName.Zero {fragment_shader});
var sprog := InitProgram(vertex_shader, gl_shader.Zero {fragment_shader});
var uniform_rot_k := gl.GetUniformLocation(sprog, 'rot_k');

View file

@ -916,7 +916,7 @@ window.onload = ()=>{
</div>
<script>on_page_added(document.getElementById("page-1"))</script>
<div id="page-2" page_name="О неуправляемых .dll" hidden=true>
<p>Лучше прочитайте <a href="https://docs.microsoft.com/en-us/dotnet/standard/managed-code">оффициальный документ от microsoft</a>, если хотите точной и подробно информации.</p>
<p>Лучше прочитайте <a href="https://docs.microsoft.com/en-us/dotnet/standard/managed-code">оффициальный документ от microsoft</a>, если хотите точной и подробной информации.</p>
<p>Если в кратце:</p>
<p>Есть платформа .Net, объединяющая много языков программирования. В том числе C# и PascalABC.NET.</p>
<p>.exe и .dll созданные такими языками содержат код в особом виде, позволяющем легко подключать
@ -939,7 +939,7 @@ window.onload = ()=>{
<p>Чтоб чистить подпрограммы было проще - я написал несколько инструментов.
Вся упаковка н.у. модулей находится <a href="https://github.com/SunSerega/POCGL/tree/master/Packing/Template">тут</a>.
Откройте папку <code>OpenCL</code> или <code>OpenGL</code>. Они устроены одинаково, поэтому объяснять можно на любой из них.</p>
<p>Все файлы (кроме .exe и .pdb, которые создаёт паскаль) в
<p>Все файлы (кроме .exe, .pdb и .pcu, которые создаёт паскаль) в
этих папках открываются любым текстовым редактором.</p>
<hr />
<h3>Fixers</h3>
@ -964,7 +964,7 @@ window.onload = ()=>{
<hr />
<p>Чтоб применить фиксеры и посмотреть на что они влияют - вызывайте <code>Pack Most.pas</code>.</p>
<p>Ну а чтоб полностью собрать модули - вызывайте <code>PackAll.exe</code> в корне репозитория.<br />
(или <code>.bat</code> файлы там же, для сборки отдельно 1 модуля)</p>
(или <code>.bat</code> файлы там же, для сборки отдельных компонентов)</p>
</div>
<script>on_page_added(document.getElementById("page-3"))</script>
<script>on_end_folder()</script>
@ -998,12 +998,12 @@ window.onload = ()=>{
<p>Имена объектов - это числа (в OpenGL обычно на 32 бита, в OpenCL - зависит от битности системы).</p>
<p>Чтоб в подпрограмму, принимающую имена объектов определённого типа нельзя было передать имя
объекта неправильного типа - в н.у. модулях для каждого типа объектов описана подобная запись:</p>
<pre><code> BufferName = record
<pre><code> gl_buffer = record
public val: UInt32;
public constructor(val: UInt32) := self.val := val;
public static property Zero: BufferName read default(BufferName);
public static property Zero: gl_buffer read default(gl_buffer);
public static property Size: integer read Marshal.SizeOf&amp;&lt;UInt32&gt;;
public function ToString: string; override := $'BufferName[{val}]';
public function ToString: string; override := $'gl_buffer[{val}]';
end;
</code></pre>
<p>Такой подход не замедляет готовую программу, но позволяет отловить некоторые ошибки на этапе компиляции.</p>
@ -1012,8 +1012,8 @@ window.onload = ()=>{
<p>В обычной ситуации - вы будете взаимодействовать с именами только 3 способами:</p>
<ol>
<li><p>Объявление:</p>
<pre><code>var name: BufferName;
var names: array of BufferName := new BufferName[5];
<pre><code>var name: gl_buffer;
var names: array of gl_buffer := new gl_buffer[5];
</code></pre>
</li>
<li><p>Передача исходным библиотекам:</p>
@ -1022,14 +1022,14 @@ gl.CreateBuffers(names.Length, names);
</code></pre>
</li>
<li><p>Использование статичного свойства <code>.Zero</code>:</p>
<pre><code>procedure MyProc(buff: BufferName);
<pre><code>procedure MyProc(buff: gl_buffer);
begin
...
end;
...
MyProc(BufferName.Zero);
// Это то же самое, но с лишними скобками
MyProc(default(BufferName));
MyProc(gl_buffer.Zero);
// То же самое, но с лишними скобками:
MyProc(default(gl_buffer));
</code></pre>
<p>У настоящих объектов имя никогда не будет нулевым.</p>
<p>Но бывает не мало случаев когда исходные библиотеки могут принимать нулевое имя.<br />
@ -1042,7 +1042,7 @@ MyProc(default(BufferName));
<p>Многие параметры подпрограмм в исходных библиотеках принимают перечисления (enum'ы).</p>
<p>Перечисления, как и имена, это числа. Но в отличии от имён - перечисления принимают заданные заранее константные значения.</p>
<p>В качестве примера:</p>
<pre><code>procedure gl.BeginQuery(target: QueryTarget; id: QueryName);
<pre><code>procedure gl.BeginQuery(target: QueryTarget; id: gl_query)
</code></pre>
<p>Параметр <code>target</code> принимает одно из значений, сгрупированных в записи <code>QueryTarget</code>:</p>
<pre><code> QueryTarget = record
@ -1076,7 +1076,7 @@ MyProc(default(BufferName));
end;
</code></pre>
<p>То есть вызов может выглядеть так:</p>
<pre><code>var name: QueryName;
<pre><code>var name: gl_query;
...
gl.BeginQuery(QueryTarget.VERTICES_SUBMITTED, name);
</code></pre>
@ -1087,15 +1087,10 @@ gl.BeginQuery(QueryTarget.VERTICES_SUBMITTED, name);
<pre><code>procedure gl.Clear(mask: ClearBufferMask);
</code></pre>
<pre><code>gl.Clear(
ClearBufferMask.COLOR_BUFFER_BIT or // очистка поверхности рисования одним цветом
ClearBufferMask.DEPTH_BUFFER_BIT // очистка буфера глубины - обязательна при рисовании 3D
ClearBufferMask.COLOR_BUFFER_BIT + // Очистка поверхности рисования одним цветом
ClearBufferMask.DEPTH_BUFFER_BIT // Очистка буфера глубины - нужна при рисовании 3D
);
</code></pre>
<p><code>or</code>, в данном случае, побитовое, а не логическое.</p>
<p>Выражение<br />
<code>ClearBufferMask.COLOR_BUFFER_BIT or ClearBufferMask.DEPTH_BUFFER_BIT</code><br />
можно читать как &quot;все биты <code>COLOR_BUFFER_BIT</code> + все биты <code>DEPTH_BUFFER_BIT</code>&quot;.</p>
<p>А в случае <code>gl.Clear</code> эта комбинация значит &quot;очистить буфер цвета <strong>и</strong> буфер глубины&quot;</p>
</code></pre>
</div>
<script>on_page_added(document.getElementById("page-6"))</script>
<div id="page-7" page_name="Классы-контейнеры подпрограмм" hidden=true>
@ -1107,12 +1102,18 @@ gl.BeginQuery(QueryTarget.VERTICES_SUBMITTED, name);
</code></pre>
<p>По нажатию точки после <code>cl</code> вам так же покажет список всех функций в ядре OpenCL.</p>
<hr />
<p>С OpenGL немного сложнее: в каждом контексте у каждой подпрограммы может быть свой адрес.
Поэтому подпрограммы надо получать в зависимости от контекста. В модуле <code>OpenGL</code> это реализовано так:</p>
<pre><code>//ToDo создание контекста и привязка его к текущему потоку
<p>С OpenGL немного сложнее:</p>
<ol>
<li>В каждом контексте у каждой подпрограммы может быть свой адрес.</li>
<li>Получение адреса подпрограммы выглядит по-разному на разных платформах.</li>
</ol>
<p>В модуле <code>OpenGL</code> это реализовано так:</p>
<pre><code>//ToDo Создание контекста и привязка его к текущему потоку
// все адреса и экземпляры делегатов создаются в конструкторе
var gl := new OpenGL.gl;
// Все адреса и экземпляры делегатов создаются в конструкторе
// В &quot;&lt;&gt;&quot; указывается имя платформы
// Можете написать &quot;Pl&quot; и нажать Ctrl+пробел, чтоб получить список платформ
var gl := new OpenGL.gl&lt;PlWin&gt;;
while true do
begin
@ -1126,27 +1127,28 @@ end;
</code></pre>
<hr />
<p>У каждого расширения есть свой класс. К примеру, так используется расширение <code>GL_AMD_debug_output</code>:</p>
<pre><code>//ToDo опять же, сначала контекст
<pre><code>//ToDo Опять же, сначала контекст
var glDebugOutputAMD := new OpenGL.glDebugOutputAMD;
var glDebugOutputAMD := new OpenGL.glDebugOutputAMD&lt;PlWin&gt;;
...
glDebugOutputAMD.DebugMessageEnableAMD(...);
</code></pre>
<hr />
<p>В модуле <code>OpenGL</code> так же есть особые классы, <code>wgl</code> и <code>gdi</code>:</p>
<p>В модуле <code>OpenGL</code> так же есть особые классы, <code>wgl</code>, <code>gdi</code> и <code>glx</code>:</p>
<ul>
<li><p><code>gdi</code> содержит несколько методов библиотеки <code>gdi32.dll</code>. На этой библиотеке основано всё в <code>System.Windows.Forms</code>.<br />
Подпрограммы включённые в класс <code>gdi</code> - это то что может понадобиться вам чтоб настроить и подготовить форму для рисования на ней с помощью OpenGL.</p>
Подпрограммы включённые в класс <code>gdi</code> - это то, что может понадобиться вам, чтоб настроить и подготовить форму для рисования на ней с помощью OpenGL.</p>
</li>
<li><p><code>wgl</code> содержит методы для работы с OpenGL на Windows. Для других ОС есть класс <code>glX</code>, но он сейчас выключен из релиза.
Это потому, что у меня нет компьютера, на котором установлена другая ОС, а значит я не могу
заняться тестированием чтоб узнать как правильно объявлять всё в этом классе. Если у вас есть
другая ОС и вы хотите помочь мне разобраться - пожалуйста, свяжитесь со мной (желательно в issue).</p>
<li><p><code>wgl</code> содержит методы для подключения OpenGL к окну Windows.</p>
</li>
<li><p><code>glx</code> содержит методы для подключения OpenGL к окну XWindow.</p>
</li>
</ul>
<p>Оба этих класса работают как класс <code>cl</code>, то есть им <strong>не</strong> надо создавать экземпляр.</p>
<hr />
<p>И последнее что вам надо знать об этих классах: Если вы получаете <code>NullReferenceException</code>:</p>
<p>И последнее что вам надо знать об этих классах:<br />
Если вы получаете <code>NullReferenceException</code>,
при попытке вызова функции из инициализируемых классов, как <code>gl</code>:</p>
<ol>
<li><p>Скорее всего вы попытались вызвать подпрограмму, которой нет в реализации библиотеки на вашем компьютере.<br />
Проверьте версию библиотеки, или, если это подпрограмма из расширения - проверьте существование у вас этого расширения.</p>
@ -1270,25 +1272,29 @@ v2.Println;
v2.SqrLength.Sqrt.Println; // Обязательно будет 1
</code></pre>
<h3>Cross</h3>
<p>Статичный метод <code>.Cross</code> возвращает произведение векторов (Cross product, не путать со скалярным произведением).
<p>Статичные методы <code>.Cross[CW,CCW]</code> возвращают векторное произведение двух
3-х мерных векторов (&quot;Cross product&quot;, не путать со скалярным произведением).<br />
Векторное произведение - это вектор, перпендикулярный обоим входным векторам и имеющий длину,
равную площади параллелограмма, образованного входными векторами.</p>
<p>Работает только для 3-х мерных векторов.</p>
<p>И не работает для векторов с элементами-беззнаковыми целыми.<br />
(потому что даёт переполнение на практически любых входных значениях. Если найдёте нормальное применение - напишите в issue).</p>
<p>Не работает для векторов с элементами-беззнаковыми_целыми, потому что даёт переполнение
на практически любых входных значениях. Если найдёте нормальное применение - напишите в issue.</p>
<p>В математике произведение векторов может вернуть один из двух противоположных друг-другу векторов,
в зависимости от ориентации системы координат. В модуле <code>OpenGL</code> это решено следующим образом:</p>
<pre><code>var c := Vec3d.Cross(a, b);
</code></pre>
<p>В данном случае, вектор <code>c</code> будет такой, чтоб соблюдалось следующее условие:</p>
<ul>
<li>Если поставить начала всех 3 векторов в точку (0;0;0), а камеру в точку (a+b+c) и направить
в точку (0;0;0) - вектора a, b и c будут расположены по часовой стрелке вокруг центра экрана.</li>
<li><p><code>Vec3d.CrossCW(new Vec3d(1,0,0), new Vec3d(0,1,0)) = new Vec3d(0,0,1)</code>.</p>
</li>
<li><p><code>Vec3d.CrossCCW(a,b) = -Vec3d.CrossCW(a,b)</code>;</p>
</li>
</ul>
<p>Если надо чтоб было против часовой стрелки - можно использовать CCW (counter clock wise, против часовой стрелки) версию <code>.Cross</code>:</p>
<pre><code>var c := Vec3d.CrossCCW(a,b);
<h3>Ввод с клавиатуры</h3>
<p>Статичные методы <code>Read</code> и <code>Readln</code> создают новый вектор из элементов, прочитанных из стандартного ввода:</p>
<pre><code>// Прочитать 2 числа из ввода
Vec2d.Read('Введите 2 координаты:').Println;
// Прочитать 2 числа из ввода
// и затем пропустить всё до конца строки
Vec2d.Readln.Println;
</code></pre>
<p>(ну или можно использовать <code>-Vec3d.Cross(a,b)</code>, это даст тот же результат но с 1 лишней операцией)</p>
<h3>Превращение в строку</h3>
<pre><code>var v1: Vec4d;
...
@ -1438,6 +1444,19 @@ m.Transpose.Println; // Выводит:
<code>v*m</code> это то же самое что <code>m.Transpose*v</code>.</p>
<h3>Умножение 2 матриц</h3>
<p><code>m1*m2</code> - это математическое умножение матриц <code>m1</code> и <code>m2</code>.</p>
<h3>Ввод с клавиатуры</h3>
<p>Статичные методы <code>Read[,ln][Rows,Cols]</code> создают новую матрицу из элементов, прочитанных из стандартного ввода:</p>
<pre><code>// Прочитать 3*4=12 элементов из ввода
// и сохранить в новую матрицу по строкам
Mtr3x4d.ReadRows('Введите 12 элементов матрицы:').Println;
// Прочитать 4 элемета из ввода, переходя на
// следущую строку ввода после чтения каждого столбца
Mtr2d.ReadlnCols(
'Введите столбцы матрицы...',
col -&gt; $'Столбец #{col}:'
).Println;
</code></pre>
<h3>Превращение в строку</h3>
<p>Как и у векторов - матрицы можно выводить и превращать в строку</p>
<pre><code>var m: Mtr4d;
@ -1814,10 +1833,16 @@ begin
d.DynamicInvoke(5); // вообще .DynamicInvoke это очень медленно
var p: integer-&gt;();
// var p: Action&lt;integer&gt;; // такое же объявление как на предыдущей строчке, но в другом стиле
// Такое же объявление как на предыдущей строчке, но в другом стиле
// var p: Action&lt;integer&gt;;
// И ещё один стиль. Этот особенный, потому что
// он неявно создаёт новый тип делегата
// var p: procedure(i: integer);
p := p1;
p(5); // типизированные делегаты можно вызывать быстрее и проще, так же как обычные подпрограммы
// Типизированные делегаты можно вызывать быстрее и проще,
// так же как обычные подпрограммы
p(5);
end.
</code></pre>
@ -1851,7 +1876,7 @@ end.
<ol>
<li><p><code>cl.SetEventCallback</code> вызывает свой коллбек тогда, когда посчитает нужным (но обычно после того как вызов <code>cl.SetEventCallback</code> завершился);</p>
</li>
<li><p>Делегаты - это классы. И на самом деле кроме самого адреса хранят ещё много всякой информации (как типы параметров);</p>
<li><p>Делегаты - это классы.</p>
</li>
<li><p>Сборщик мусора распоряжается памятью классов и удаляет их, тоже когда посчитает нужным.</p>
</li>
@ -1862,9 +1887,9 @@ end.
<p>Если сборщик мусора удалит делегат, а затем .dll попытается его вызвать -
это приведёт или к ошибке доступа, или к моментальному беззвучному вылету.</p>
<p>Чтоб запретить сборщику мусора удалять делегать - нужно создать <code>GCHandle</code>, привязанный к нему.<br />
Но в отличии от массивов - <code>GCHandleType.Pinned</code> не нужно, потому что сборщик мусора может перемещать
в памяти данные о параметрах делегата, но не сам адрес (а он единственное что передаётся в .dll).
Это потому что адрес хранится в виде указателя на неуправляемый код.</p>
Но в отличии от массивов - <code>GCHandleType.Pinned</code> не нужно, потому что сборщик мусора
не может перемещать адрес исполняемого кода (а он единственное что передаётся в .dll).
Это потому, что этот адрес хранится в виде указателя на неуправляемый код.</p>
<pre><code>uses System.Runtime.InteropServices;
uses System;
uses OpenCL;
@ -1877,8 +1902,8 @@ begin
Writeln($'{ev} перешёл в состояние {st}');
// в данном случае освобождать GCHandle станет можно тогда, когда делегат 1 раз выполнится,
// а значит очень удобно поставить освобождение в конец самого делегата
// В данном случае освобождать GCHandle станет можно тогда, когда делегат 1 раз выполнится,
// А значит очень удобно поставить освобождение в конец самого делегата
gc_hnd.Free;
end;
gc_hnd := GCHandle.Alloc(cb);

View file

@ -1012,7 +1012,7 @@ end.
Неявные очереди всегда используют <code>Context.Default</code>.</p>
<p>Изначально этому свойству присваивается контекст, использующий GPU, если оно имеется,
или любое другое устройство, поддерживающее OpenCL, если GPU отсутствует.</p>
<p>Если устройств, поддерживающих <code>OpenCL</code>, нет - <code>Context.Default</code> будет <code>nil</code>.<br />
<p>Если устройств поддерживающих <code>OpenCL</code> нет - <code>Context.Default</code> будет <code>nil</code>.<br />
Однако такая ситуация теоретически невозможна, потому что OpenCL поддерживается
всеми современными устройствами, занимающимися выводом изображения на экран.<br />
Если <code>Context.Default = nil</code> - переустановите графические драйверы.</p>
@ -1025,8 +1025,8 @@ end.
<script>on_start_folder("Context", document.getElementById("page-6"))</script>
<script>on_end_folder()</script>
<div id="page-7" page_name="" hidden=true>
<p>Программам, выпоняемым на GPU, неудобно использовать оперативную память.
Поэтому обычно надо выделять память на самОм GPU.<br />
<p>Программам, выпоняемые на GPU, не удобно использовать оперативную память.
Поэтому обычно надо выделять память на самом GPU.<br />
Объекты типа <code>Buffer</code> представляют такую область памяти.</p>
<hr />
<p>Буфер создаётся конструктором (<code>new Buffer(...)</code>).</p>
@ -1034,13 +1034,13 @@ end.
Если вызвать <code>Buffer.Init</code> 2 раза - память освободится и выделится заново.</p>
<p>Когда на GPU выделяется память - она <strong>НЕ</strong> очищается нулями, а значит содержит мусорные данные.</p>
<p>Если метод <code>Buffer.Init</code> не был вызван до первой операции чтения/записи - он будет вызван автоматически.
В таком случае в качестве контекста, в котором выделяется память, берётся тот,
В таком случае в качестве контекста в котором выделяется память берётся тот,
на котором вызвали <code>.BeginInvoke</code>, который запустил команду чтения/записи буфера.</p>
<p>Память на GPU можно моментально освободить, вызвав метод <code>Buffer.Dispose</code>.
Но если снова использовать буфер, у которого освободили память, - память выделится заново.</p>
Но если снова использовать буфер, у которого освободили память - память выделится заново.</p>
<p>Если сборщик мусора удаляет объект типа <code>Buffer</code> - <code>.Dispose</code> вызывается автоматически.</p>
<hr />
<p>Но кроме выделения памяти GPU - OpenCL также позволяет выделять память внутри другого буфера.<br />
<p>Но кроме выделения памяти GPU - OpenCL так же позволяет выделять память внутри другого буфера.<br />
Для этого используется тип <code>SubBuffer</code>:</p>
<pre><code>uses OpenCLABC;
@ -1074,7 +1074,7 @@ var k := code['KernelName'];
</code></pre>
<p>Тут <code>'KernelName'</code> — имя подпрограммы-kernel'а в исходном коде (регистр важен!).</p>
<hr />
<p>Также можно получить список всех kernel'ов объекта <code>ProgramCode</code>, методом <code>ProgramCode.GetAllKernels</code>:</p>
<p>Так же можно получить список всех kernel'ов объекта <code>ProgramCode</code>, методом <code>ProgramCode.GetAllKernels</code>:</p>
<pre><code>var code: ProgramCode;
...
var ks := code.GetAllKernels;
@ -1230,7 +1230,7 @@ end.
Но потом вы добавите что то в совсем другой части программы, или запустите её на
другом компьютере и она вдруг начнёт выводить мусорные значения, или падать с ошибками
вроде <code>AccessViolationException</code>, которые совершенно не объясняют в чём проблема.</p>
<p>Если очень интересно, откуда берутся эти ограничения, начните с применения .Net декомпилятора к .exe файлам,
<p>Если очень интересно откуда берутся эти ограничения - начните с применения .Net декомпилятора к .exe файлам,
получаемым при компиляции программ с данной страницы. Что останется непонятно - можете спрашивать в issue.</p>
</div>
<script>on_start_folder("Kernel", document.getElementById("page-8"))</script>
@ -1373,7 +1373,7 @@ except
on e: Exception do Writeln(e);
end;
</code></pre>
<p>Для этого кода есть стандартный снипет. Чтобы активировать его, напишите <code>tryo</code> и нажмите Shift+Пробел.</p>
<p>Для этого кода есть стандартный снипет. Чтобы активировать его - напишите <code>tryo</code> и нажмите Shift+Пробел.</p>
</div>
<script>on_start_folder("Выполнение очередей", document.getElementById("page-14"))</script>
<script>on_end_folder()</script>

View file

@ -878,7 +878,7 @@ window.onload = ()=>{
<div id="page-1" page_name="" hidden=true>
<p><code>ToDo</code> модуль <code>OpenGLABC</code> пока что не существует. Когда то он появится, но пока что есть только это сообщение.</p>
<p>Если хотите понять, было ли это сообщение оставлено тут 10 лет назад и забыто - вот дата, когда автор сообщения последний раз прочитал его:<br />
03.08.2020</p>
2021.01.05</p>
</div>
<script>on_start_folder("GL ABC", document.getElementById("page-1"))</script>
<script>on_end_folder()</script>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff