From d9ade46471567aa85d8a7594a43626879ac93743 Mon Sep 17 00:00:00 2001 From: Sun Serega Date: Thu, 28 Dec 2023 13:18:22 +0200 Subject: [PATCH] Mandelbrot example +1 --- LastPack.log | 32 +- Log/Release.log | 18 +- Log/Test.log | 10 + Samples/OpenGLABC/Mandelbrot/.gitignore | 6 + Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas | 503 ++++++---- Samples/OpenGLABC/Mandelbrot/0Mandelbrot.td | 69 +- Samples/OpenGLABC/Mandelbrot/Blocks.pas | 931 ++++++++++++++---- Samples/OpenGLABC/Mandelbrot/CameraDef.pas | 48 +- Samples/OpenGLABC/Mandelbrot/FieldTest.bmp | Bin 107371 -> 116318 bytes Samples/OpenGLABC/Mandelbrot/FieldTest.pas | 71 +- Samples/OpenGLABC/Mandelbrot/FieldTest.td | 45 +- .../OpenGLABC/Mandelbrot/GL_CL_Context.pas | 4 +- .../Mandelbrot/MandelbrotSampling.cl | 171 +--- .../OpenGLABC/Mandelbrot/MemoryLayering.pas | 195 ++++ .../OpenGLABC/Mandelbrot/MemoryLayering.td | 6 + .../OpenGLABC/Mandelbrot/PointComponents.pas | 358 +++++-- Samples/OpenGLABC/Mandelbrot/Settings.pas | 133 +-- Samples/OpenGLABC/Mandelbrot/Shaders/Box.geom | 4 +- .../OpenGLABC/Mandelbrot/Shaders/Rainbow.frag | 60 +- Samples/OpenGLABC/Mandelbrot/SheetTransfer.cl | 65 ++ 20 files changed, 2002 insertions(+), 727 deletions(-) create mode 100644 Samples/OpenGLABC/Mandelbrot/.gitignore create mode 100644 Samples/OpenGLABC/Mandelbrot/MemoryLayering.pas create mode 100644 Samples/OpenGLABC/Mandelbrot/MemoryLayering.td create mode 100644 Samples/OpenGLABC/Mandelbrot/SheetTransfer.cl diff --git a/LastPack.log b/LastPack.log index 62d4e19f..ebb12d34 100644 --- a/LastPack.log +++ b/LastPack.log @@ -621,6 +621,12 @@ Tester: Compiling "Samples/OpenCLABC/Кеширование CLProgramCode/Simple Tester: Compiling: OK Tester: Compiling "Samples/OpenGLABC/!Крутящийся треугольник/Крутящийся треугольник.pas" Tester: Compiling: OK +Tester: Compiling "Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas" +Tester: Compiling: OK +Tester: Compiling "Samples/OpenGLABC/Mandelbrot/FieldTest.pas" +Tester: Compiling: OK +Tester: Compiling "Samples/OpenGLABC/Mandelbrot/SamplingTests.pas" +Tester: Compiling: OK Tester: Compiling "Samples/OpenGLABC/Точки на поле/Точки.pas" Tester: Compiling: OK Tester: Switched to platform "NVIDIA CUDA" and using 1 devices @@ -832,6 +838,10 @@ Tester: Executing Test[Samples/OpenCLABC/Кеширование CLProgramCode/Si Tester: Done executing Tester: Executing Test[Samples/OpenCLABC/Кеширование CLProgramCode/SimpleAddition но с кешем] Tester: Done executing +Tester: Executing Test[Samples/OpenGLABC/Mandelbrot/FieldTest] +Tester: Done executing +Tester: Executing Test[Samples/OpenGLABC/Mandelbrot/SamplingTests] +Tester: Done executing Tester: Switched to platform "Intel(R) OpenCL" and using 1 devices Tester: Executing Test[Tests/Exec/CL/ToString] Tester: Done executing @@ -1041,6 +1051,10 @@ Tester: Executing Test[Samples/OpenCLABC/Кеширование CLProgramCode/Si Tester: Done executing Tester: Executing Test[Samples/OpenCLABC/Кеширование CLProgramCode/SimpleAddition но с кешем] Tester: Done executing +Tester: Executing Test[Samples/OpenGLABC/Mandelbrot/FieldTest] +Tester: Done executing +Tester: Executing Test[Samples/OpenGLABC/Mandelbrot/SamplingTests] +Tester: Done executing Tester: Cleanup Tester: Done testing Finished runing Tester @@ -1070,6 +1084,22 @@ Packing sample file "Samples/OpenCLABC/Кеширование CLProgramCode/Simp Packing sample file "Samples/OpenGLABC/Common.pas" Packing sample file "Samples/OpenGLABC/!Крутящийся треугольник/Крутящийся треугольник.pas" Packing sample file "Samples/OpenGLABC/!Крутящийся треугольник/Крутящийся треугольник.vert" +Packing sample file "Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Blocks.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/CameraDef.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/FieldTest.bmp" +Packing sample file "Samples/OpenGLABC/Mandelbrot/FieldTest.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/GL_CL_Context.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/MandelbrotSampling.cl" +Packing sample file "Samples/OpenGLABC/Mandelbrot/MandelbrotSampling.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/MemoryLayering.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/PointComponents.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/SamplingTests.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Settings.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/SheetTransfer.cl" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Shaders/Box.geom" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Shaders/Empty.vert" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Shaders/Rainbow.frag" Packing sample file "Samples/OpenGLABC/Точки на поле/Empty.vert" Packing sample file "Samples/OpenGLABC/Точки на поле/Mandelbrot.frag" Packing sample file "Samples/OpenGLABC/Точки на поле/SinglePointToScreen.geom" @@ -1079,5 +1109,5 @@ Packing sample file "Samples/OpenGLABC/Точки на поле/Минимум Packing sample file "Samples/OpenGLABC/Точки на поле/Спирали.frag" Packing sample file "Samples/OpenGLABC/Точки на поле/Сумма расстояний.frag" Packing sample file "Samples/OpenGLABC/Точки на поле/Точки.pas" -Packed 27 sample files +Packed 43 sample files Done packing diff --git a/Log/Release.log b/Log/Release.log index f1b714f3..dd18bd5f 100644 --- a/Log/Release.log +++ b/Log/Release.log @@ -23,6 +23,22 @@ Packing sample file "Samples/OpenCLABC/Кеширование CLProgramCode/Simp Packing sample file "Samples/OpenGLABC/Common.pas" Packing sample file "Samples/OpenGLABC/!Крутящийся треугольник/Крутящийся треугольник.pas" Packing sample file "Samples/OpenGLABC/!Крутящийся треугольник/Крутящийся треугольник.vert" +Packing sample file "Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Blocks.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/CameraDef.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/FieldTest.bmp" +Packing sample file "Samples/OpenGLABC/Mandelbrot/FieldTest.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/GL_CL_Context.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/MandelbrotSampling.cl" +Packing sample file "Samples/OpenGLABC/Mandelbrot/MandelbrotSampling.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/MemoryLayering.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/PointComponents.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/SamplingTests.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Settings.pas" +Packing sample file "Samples/OpenGLABC/Mandelbrot/SheetTransfer.cl" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Shaders/Box.geom" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Shaders/Empty.vert" +Packing sample file "Samples/OpenGLABC/Mandelbrot/Shaders/Rainbow.frag" Packing sample file "Samples/OpenGLABC/Точки на поле/Empty.vert" Packing sample file "Samples/OpenGLABC/Точки на поле/Mandelbrot.frag" Packing sample file "Samples/OpenGLABC/Точки на поле/SinglePointToScreen.geom" @@ -32,4 +48,4 @@ Packing sample file "Samples/OpenGLABC/Точки на поле/Минимум Packing sample file "Samples/OpenGLABC/Точки на поле/Спирали.frag" Packing sample file "Samples/OpenGLABC/Точки на поле/Сумма расстояний.frag" Packing sample file "Samples/OpenGLABC/Точки на поле/Точки.pas" -Packed 27 sample files +Packed 43 sample files diff --git a/Log/Test.log b/Log/Test.log index 7e6a268d..52a0afdd 100644 --- a/Log/Test.log +++ b/Log/Test.log @@ -229,6 +229,12 @@ Tester: Compiling "Samples/OpenCLABC/Кеширование CLProgramCode/Simple Tester: Compiling: OK Tester: Compiling "Samples/OpenGLABC/!Крутящийся треугольник/Крутящийся треугольник.pas" Tester: Compiling: OK +Tester: Compiling "Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas" +Tester: Compiling: OK +Tester: Compiling "Samples/OpenGLABC/Mandelbrot/FieldTest.pas" +Tester: Compiling: OK +Tester: Compiling "Samples/OpenGLABC/Mandelbrot/SamplingTests.pas" +Tester: Compiling: OK Tester: Compiling "Samples/OpenGLABC/Точки на поле/Точки.pas" Tester: Compiling: OK Tester: Executing Test[Tests/Exec/CL/ToString] @@ -439,6 +445,10 @@ Tester: Executing Test[Samples/OpenCLABC/Кеширование CLProgramCode/Si Tester: Done executing Tester: Executing Test[Samples/OpenCLABC/Кеширование CLProgramCode/SimpleAddition но с кешем] Tester: Done executing +Tester: Executing Test[Samples/OpenGLABC/Mandelbrot/FieldTest] +Tester: Done executing +Tester: Executing Test[Samples/OpenGLABC/Mandelbrot/SamplingTests] +Tester: Done executing Tester: Cleanup Tester: Done testing Finished runing Tester diff --git a/Samples/OpenGLABC/Mandelbrot/.gitignore b/Samples/OpenGLABC/Mandelbrot/.gitignore new file mode 100644 index 00000000..ee813f11 --- /dev/null +++ b/Samples/OpenGLABC/Mandelbrot/.gitignore @@ -0,0 +1,6 @@ + + + +Cache/block_w_pow=*/*.point_block + + diff --git a/Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas b/Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas index 01319ff4..0f7b8637 100644 --- a/Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas +++ b/Samples/OpenGLABC/Mandelbrot/0Mandelbrot.pas @@ -4,22 +4,49 @@ {$apptype windows} // Управление: +// - Escape: Завершение программы (дважды или +Ctrl чтобы отменить сохранение) // - Space: Сбросить положение камеры -// - Ctrl+C: Скопировать положение камеры -// - Ctrl+V: Вставить положение камеры // - Mouse Drag: Быстрое движение камеры // - Arrows: Гладкое движение камеры // - Scroll: Быстрое изменение масштаба // - "+" и "-": Гладкое изменение масштаба +//TODO: +// - Ctrl+C: Скопировать положение камеры (+Shift чтобы добавить комментарий) +// - Ctrl+V: Вставить положение камеры +// - Alt: Вид без копирования информации предыдущих кадров // - Alt+Enter: Полноэкранный режим +// - B: Телепортировать камеру к курсору (Blink) +// --- Пока держат - выводить точку в начале заголовка, а телепортировать когда отпускают -//TODO Доделать ограничение view_bound -// - Сейчас его не возвращает из функции, создающей блоки - -// Константы, которые можно быстро менять +// В модуле Settings находятся все основных константы +// + объяснение логики программы, чтобы понимать зачем эти константы +// Ctrl+тыкните на название модуля в uses чтобы открыть его uses Settings; -uses GL_CL_Context; +//TODO Вывод шагов под курсором чтобы норм дебажить +// - in[123] +// - out[456] +//TODO Выводить отдельно для sheet и для блоков +// - Для этого надо находить номер блока и точки в нём и кидать (x;y) точки в CQ_GetData + +//TODO Alt-режим почему то не работает... +// - Точнее, если масштабировать с ним - начинаются глюки +// - Вообще, он уже и не полезен. Зато полезна была бы возможность сбрасывать sheet вручную + +//TODO Отдельная программа для полной прорисовки кардров с движением камеры от 1 точки (и масштаба) к другой + +//TODO При очень большом приближении край рисунка ведёт себя криво +// - Потому что FirstWordToReal +// - Надо в виде PointComponent считать разницу сначала + +//TODO mouse_grab_move ведёт себя не стабильно (точка которую держат может потихоньку сдвигаться) +// - Надо запоминать camera.pos в начале движения мышкой +// - И затем пересчитывать на каждом кадре относительно него + +//TODO Отдельное окно по нажатию какой-то клавиши с кучей инфы +// - Загруженность памяти (VRAM,RAM,Drive) +// - Скорость обработки блоков (ну и текущее кол-во слов там же) +// - Для начала выводить сколько памяти тратится на sheet-ы uses System; uses System.Windows.Forms; @@ -27,53 +54,16 @@ uses OpenGL; uses OpenGLABC; -//uses OpenCL; //TODO Remove uses OpenCLABC; -uses Common in '../Common'; +uses GL_CL_Context; + +uses Common;// in '../Common'; //TODO Merge uses PointComponents; uses CameraDef; uses Blocks; -//var extra_debug_output := false; - -//TODO Система отдельных блоков, как на гугл картах -// - Запоминать номер кадра последнего использования каждого блока -//TODO Что насчёт рисования кадра? -// - В идеале надо смотреть только на блок нужного масштаба -// - Но если он не полностью нарисован: -// --- Сначала пытаться нарисовать более мелкие блоки -// --- Затем наоборот, более большие (а знач неточные) -// - А что с точками, которые ещё считает? -// --- По сути можно иметь 3 глубины: -// --- 0=не нарисовано -// --- 1=нарисовано недосчитанным -// --- 2=нарисовано конечным -// - Конечный статус наверн будет у очень маленького кол-ва точек... -// - То есть, наверное, нет смысла давать блоку конечный статус - -//TODO + и - для стабильного зума -//TODO * и / для максимального кол-ва шагов (для цвета и расчётов) - -//TODO Множество разных фрагметных шейдеров, для разной раскраски в зависимости от глубины -// - По глубине: -// --- от depth/макс_глубину -// --- от depth/n%1 -// - По цвету (0 и MaxLongWord считать особенными) -// --- hsv2rgb -// --- чёрно-белое -//TODO Максимальную глубину передавать в уже сглаженном виде, чтобы цвета не менялись резко -//TODO Или может собирать фрагментные шейдеры на ходу, из комбинаций кусков кода? - -//TODO Значения z надо хранить с изменяемой точность, исходя из текущего масштаба - -//TODO Отдельное окно по нажатию какой-то клавиши с кучей инфы -// - Загруженность памяти (VRAM,RAM) -// - Скорость обработки блоков (ну и текущее кол-во слов там же) - -//TODO Попробовать вставить цикл в корень MandelbrotBlockStep, чтобы не запускать kernel кучу раз - type UpdateTimingQueue = sealed class private sw := Stopwatch.StartNew; @@ -115,7 +105,7 @@ BoundUniforms = record (xl, yl) := (gl.GetUniformLocation(shader_prog, $'{prefix}_skip_x_last'), gl.GetUniformLocation(shader_prog, $'{prefix}_skip_y_last')); end; - procedure Write(b: BoundDefs); + procedure Write(b: BoundDefs); begin gl.Uniform1f(xf, b.xf); gl.Uniform1f(yf, b.yf); @@ -125,24 +115,101 @@ BoundUniforms = record end; + CLGLArray = sealed class + where T: record; + + public b_gl: gl_buffer; + public b_cl: CLArray; + + public constructor(gl: OpenGL.gl); + begin + gl.CreateBuffers(1, self.b_gl); + b_cl := nil; + end; + + public function EnsureLen(len: integer): boolean; + begin + Result := false; + if (b_cl<>nil) and (b_cl.Length>=len) then exit; + + if b_cl<>nil then + CLMemoryObserver.Current.RemoveMemoryUse(b_cl.ByteSize, b_gl); + gl.NamedBufferData(b_gl, new UIntPtr(len*System.Runtime.InteropServices.Marshal.SizeOf&), IntPtr.Zero, glVertexBufferObjectUsage.STREAM_DRAW); + GL_CL_Context.WrapBuffer(b_gl, b_cl); + if b_cl.Length <> len then + raise new InvalidOperationException; + CLMemoryObserver.Current.AddMemoryUse(b_cl.ByteSize, b_gl); + + Result := true; + end; + + end; + begin var f := new Form; - f.WindowState := FormWindowState.Maximized; + CLMemoryObserver.Current := new TrackingMemoryObserver; // f.ControlBox := false; +// OpenCLABC.eh_debug_otp := nil; + + // Может понадобится увеличить max_VRAM в "Settings.pas", + // для раскрытия на полный экран с 1920х1080 (1080p) и больше пикселей +// f.WindowState := FormWindowState.Maximized; +// f.ClientSize := new System.Drawing.Size(1920,1080); // 1080p, 16:9 +// f.ClientSize := new System.Drawing.Size(1280,720); // 720p, 16:9 + f.ClientSize := new System.Drawing.Size(1440,720); // 720p, 2:1 +// f.ClientSize := new System.Drawing.Size(1072,603); // 603p, 16:9 +// f.ClientSize := new System.Drawing.Size(1206,603); // 603p, 2:1 + f.StartPosition := FormStartPosition.CenterScreen; + + {$region Закрытие} - // Моментальное закрытие при Alt+F4 и Esc - f.Closed += (o,e)->Halt(); f.KeyUp += (o,e)-> case e.KeyCode of - Keys.Escape: Halt; + Keys.Escape: f.Close; + end; + f.Closing += (o,e)-> + begin + if Control.ModifierKeys.HasFlag(Keys.Control) then Halt; + + var shutdown_progress_form := new Form; + shutdown_progress_form.StartPosition := FormStartPosition.CenterScreen; + shutdown_progress_form.FormBorderStyle := FormBorderStyle.None; + shutdown_progress_form.Closing += (o,e)->Halt(); + shutdown_progress_form.KeyUp += (o,e)-> + case e.KeyCode of + Keys.Escape: shutdown_progress_form.Close; + end; + + var progress_bar := new ProgressBar; + shutdown_progress_form.Controls.Add(progress_bar); + shutdown_progress_form.ClientSize := new System.Drawing.Size( + (Screen.PrimaryScreen.WorkingArea.Width * 0.7).Round, + progress_bar.Height + ); + progress_bar.Dock := DockStyle.Fill; + + var progress_t := new Timer; + progress_t.Interval := 10; + progress_t.Tick += (o,e)-> + begin + var (done,total) := BlockUpdater.ShutdownProgress; + progress_bar.Minimum := 0; + progress_bar.Maximum := total; + progress_bar.Value := done; + f.Text := $'Saving to drive: {done/total:P} ({done}/{total})'; + end; + + BlockUpdater.BeginShutdown(shutdown_progress_form.Close); + progress_t.Start; + + shutdown_progress_form.ShowDialog; end; - var camera: CameraPos; + {$endregion Закрытие} var need_resize := false; f.Shown += (o,e)-> begin - camera := new CameraPos(f.ClientSize.Width, f.ClientSize.Height); need_resize := true; f.Resize += (o,e)-> begin @@ -153,35 +220,84 @@ BoundUniforms = record end; end; - //TODO Использовать чтобы выдавать кол-во итераций под курсором - var mouse_pos: Vec2i; + var draw_alt_mode := false; + var mouse_pos := default(Vec2i); + var mouse_captured := true; + var mouse_grab_move := default(Vec2i); + var scale_speed_add := 0; + var camera_reset := false; + var slow_move_dir := default(Vec2i); + var slow_scale_dir1 := 0; + var slow_scale_dir2 := 0; {$region Управление} begin -// var CoordsFromScreen := function(X,Y: integer): Vec2d -> -// begin -// var logic_pos := new Vec2d(X/f.ClientSize.Width-0.5, 0.5-Y/f.ClientSize.Height)*2; -// var pos := new Vec2d(logic_pos.val0*camera.aspect, logic_pos.val1); -// Result := pos*camera.scale + camera.pos; -// end; + f.MouseEnter += (o,e)->(mouse_captured := true); + f.MouseLeave += (o,e)->(mouse_captured := false); - f.MouseWheel += (o,e)-> - begin - //TODO -// var pos := CoordsFromScreen(e.X, e.Y); -// -// var pow := 1 - Sign(e.Delta)*0.1; -// camera.scale := camera.scale * pow; -// -// camera.pos := camera.pos + (pos-CoordsFromScreen(e.X, e.Y)); - end; + f.KeyDown += (o,e)->(draw_alt_mode := e.Alt); + f.KeyUp += (o,e)->(draw_alt_mode := e.Alt); f.KeyDown += (o,e)-> case e.KeyCode of - Keys.Space: camera := new CameraPos(f.Width, f.Height); + Keys.Space: + begin + camera_reset := true; + scale_speed_add := 0; + end; end; - f.MouseMove += (o,e)->(mouse_pos := new Vec2i(e.X,e.Y)); + var mouse_grabbed := false; + f.MouseDown += (o,e)-> + case e.Button of + MouseButtons.Left: mouse_grabbed := true; + end; + f.MouseUp += (o,e)-> + case e.Button of + MouseButtons.Left: mouse_grabbed := false; + end; + + f.MouseWheel += (o,e)->System.Threading.Interlocked.Add(scale_speed_add, e.Delta); + f.MouseMove += (o,e)-> + begin + var n_mouse_pos := new Vec2i(e.X,e.Y); + if mouse_grabbed then + begin + var change := mouse_pos-n_mouse_pos; + System.Threading.Interlocked.Add(mouse_grab_move.val0, change.val0); + System.Threading.Interlocked.Add(mouse_grab_move.val1, change.val1); + end; + mouse_pos := n_mouse_pos; + end; + + var define_slow_control := procedure(key_low, key_high, modifiers: Keys; on_change: integer->())-> + begin + var low_pressed := false; + var high_pressed := false; + var update := procedure->on_change(Ord(high_pressed)-Ord(low_pressed)); + f.KeyDown += (o,e)-> + begin + if e.Modifiers and modifiers <> modifiers then exit; + if e.KeyCode=key_low then low_pressed := true else + if e.KeyCode=key_high then high_pressed := true else + exit; + update; + end; + f.KeyUp += (o,e)-> + begin + if e.Modifiers and modifiers <> modifiers then exit; + if e.KeyCode=key_low then low_pressed := false else + if e.KeyCode=key_high then high_pressed := false else + exit; + update; + end; + end; + + define_slow_control(Keys.Subtract, Keys.Add, Keys.None, x->(slow_scale_dir1:=x)); + define_slow_control(Keys.OemMinus, Keys.Oemplus, Keys.Shift, x->(slow_scale_dir2:=x)); + + define_slow_control(Keys.Left, Keys.Right, Keys.None, x->(slow_move_dir.val0:=x)); + define_slow_control(Keys.Down, Keys.Up, Keys.None, x->(slow_move_dir.val1:=x)); end; {$endregion Управление} @@ -192,19 +308,13 @@ BoundUniforms = record GL_CL_Context.Init(hdc); gl := new OpenGL.gl(pl); - begin - var org_swap_interval := wglSwapControlEXT.Instance.GetSwapIntervalEXT; - var new_swap_interval := Settings.frame_interval; - if extra_debug_output then - $'Swap interval: {org_swap_interval}=>{new_swap_interval}'.Println; - if not wglSwapControlEXT.Instance.SwapIntervalEXT(new_swap_interval) then - raise new System.InvalidOperationException; - end; + // Реализация от NVidia тратит 16мс на вызов clGLSharingKHR.EnqueueAcquireGLObjects (при чём синхронно), если не выключить vsync + // Точнее vsync применяется и к EndFrame, и затем ещё раз к .EnqueueAcquireGLObjects + // Ну, в этой программе vsync не сдался... + if not wglSwapControlEXT.Instance.SwapIntervalEXT(0) then + raise new System.InvalidOperationException; - var sw := Stopwatch.StartNew; - var timings_max_count := 30; - // sw.Elapsed для каждого из последних timings_max_count кадров - var timings := new Queue(timings_max_count); + {$region Общие данные для всех кадров} var s_vert_empty := InitShaderResource('Empty.vert', glShaderType.VERTEX_SHADER); {$resource Shaders/Empty.vert} @@ -216,7 +326,6 @@ BoundUniforms = record var uniform_view_bound, uniform_sheet_bound: BoundUniforms; var uniform_sheet_size: integer; var ssb_sheet: integer; - var uniform_max_steps: integer; var choose_frag_shader := procedure(s_frag: gl_shader)-> begin shader_prog := InitProgram(s_vert_empty, s_geom_box, s_frag); @@ -225,39 +334,16 @@ BoundUniforms = record uniform_sheet_bound := new BoundUniforms(shader_prog, 'sheet'); uniform_sheet_size := gl.GetUniformLocation(shader_prog, 'sheet_size'); ssb_sheet := gl.GetProgramResourceIndex(shader_prog, glProgramInterface.SHADER_STORAGE_BLOCK, 'sheet_block'); - uniform_max_steps := gl.GetUniformLocation(shader_prog, 'max_steps'); end; + //TODO Собственно использовать чтобы менять шейдеры choose_frag_shader(s_frag_rainbow); - var cl_err_buffer := new CLArray(3); - var cl_uc_buffer := new CLValue; + var sheet_draw := new CLGLArray(gl); + var sheet_back := new CLGLArray(gl); + var V_ExtractedCount := new CLValue; - var gl_sheet_buffer: gl_buffer; - gl.CreateBuffers(1, gl_sheet_buffer); - var cl_sheet_buffer: CLArray; - var cl_sheet_states: CLArray; - var curr_sheet_size := -1; - var Q_Init := CQNil; - var ensure_sheet_buffer_size := procedure(w, h: integer)-> - begin - var req_size := w*h; - if req_size<=0 then - raise new InvalidOperationException; - if curr_sheet_size>=req_size then exit; - curr_sheet_size := req_size; - gl.NamedBufferData(gl_sheet_buffer, new UIntPtr(req_size*sizeof(cardinal)), IntPtr.Zero, glVertexBufferObjectUsage.STREAM_DRAW); - GL_CL_Context.WrapBuffer(gl_sheet_buffer, cl_sheet_buffer); - if cl_sheet_buffer.Length <> req_size then - raise new InvalidOperationException; - cl_sheet_states := new CLArray(req_size); - Q_Init := CQNil - + cl_sheet_buffer.MakeCCQ.ThenFillValue(0) - + cl_sheet_states.MakeCCQ.ThenFillValue(0) - + cl_err_buffer.MakeCCQ.ThenFillValue(0) - + cl_uc_buffer.MakeCCQ.ThenWriteValue(0) - + CQNil - ; - end; + var last_render_info := default(BlockLayerRenderInfo?); + var last_render_sheet_w := 0; // Для дебага // var buffer_temp: gl_buffer; @@ -265,63 +351,122 @@ BoundUniforms = record // gl.NamedBufferData(buffer_temp, new IntPtr(3*sizeof(real)), IntPtr.Zero, VertexBufferObjectUsage.DYNAMIC_READ); // gl.BindBufferBase(BufferTarget.SHADER_STORAGE_BUFFER, 1, buffer_temp); - //TODO Calculate on-fly - var max_steps := 256; + var t_full := new UpdateTimingQueue(120); + var t_body := new UpdateTimingQueue(120); - while true do - begin + var camera := new CameraPos(f.ClientSize.Width, f.ClientSize.Height); + var scale_speed := 0.0; + + var frame_time_sw := Stopwatch.StartNew; + var last_frame_time := frame_time_sw.Elapsed; + + {$endregion Общие данные для всех кадров} + + while BlockUpdater.ShutdownProgress=nil do + try var curr_frame_resized := false; - if need_resize then begin var w_size := f.ClientSize; + if need_resize then + begin + gl.Viewport(0,0, w_size.Width,w_size.Height); + curr_frame_resized := true; + camera.Resize(w_size.Width, w_size.Height); + end; + if camera_reset then + begin + camera_reset := false; + camera := new CameraPos(w_size.Width, w_size.Height); + scale_speed := 0; + end; + end; + t_body.Start; + + begin + var next_frame_time := frame_time_sw.Elapsed; + var frame_len := (next_frame_time-last_frame_time).TotalSeconds; + last_frame_time := next_frame_time; - gl.Viewport(0,0, w_size.Width,w_size.Height); - curr_frame_resized := true; + scale_speed += System.Threading.Interlocked.Exchange(scale_speed_add,0) * 0.005; - camera.Resize(w_size.Width, w_size.Height); + camera.Move( + slow_move_dir.val0*10 * frame_len + System.Threading.Interlocked.Exchange(mouse_grab_move.val0, 0), + slow_move_dir.val1*10 * frame_len - System.Threading.Interlocked.Exchange(mouse_grab_move.val1, 0), + mouse_pos.val0, mouse_pos.val1, + scale_speed + frame_len * (slow_scale_dir1+slow_scale_dir2), + mouse_captured + ); + + scale_speed *= 0.5; end; - var render_info := BlockLayer.BlocksForCurrentScale(camera); - var b_cy := render_info.blocks.GetLength(0); - var b_cx := render_info.blocks.GetLength(1); - var render_block_size := Settings.block_w shr render_info.mipmap_lvl; - var render_sheet_w := b_cx * render_block_size; - var render_sheet_h := b_cy * render_block_size; - ensure_sheet_buffer_size(render_sheet_w, render_sheet_h); + camera.FixWordCount; + var render_info := BlockLayer.GetLayer(camera).GetRenderInfo(camera, last_render_info); + BlockUpdater.SetCurrent(render_info.block_area); + last_render_info := render_info; + + {$region Кадр} + gl.Clear(glClearBufferMask.COLOR_BUFFER_BIT); + + var blocks := render_info.block_area.MakeInitedBlocksMatr; + var b_cy := blocks.GetLength(0); + var b_cx := blocks.GetLength(1); + + var render_sheet_w := b_cx * block_w; + var render_sheet_h := b_cy * block_w; + + var need_back_sheet := not draw_alt_mode + and (render_info.last_sheet_diff<>nil) + and not render_info.last_sheet_diff.Value.IsNoChange; - var Q_Steps := CQNil; //TODO Calculate in separate thread - var Q_Extract := CQNil; + var Q_Acquire := CQNil; + var Q_Release := CQNil; + var Q_Init := CQNil; + if need_back_sheet then + begin + Q_Acquire += CQAcquireGL(sheet_draw.b_cl); + Q_Release += CQReleaseGL(sheet_draw.b_cl); + Swap(sheet_back, sheet_draw); + end; + var need_zero_out := sheet_draw.EnsureLen(render_sheet_w * render_sheet_h); + Q_Acquire += CQAcquireGL(sheet_draw.b_cl); + Q_Release += CQReleaseGL(sheet_draw.b_cl); + if need_back_sheet or need_zero_out then + Q_Init += sheet_draw.b_cl.MakeCCQ.ThenFillValue(0).DiscardResult; + if need_back_sheet then + Q_Init += render_info.last_sheet_diff.Value.CQ_CopySheet(sheet_back.b_cl, sheet_draw.b_cl, last_render_sheet_w, render_sheet_w, render_sheet_h); + last_render_sheet_w := render_sheet_w; + + //TODO С текущими очередями OpenCLABC не получится тут использовать имеющуюся очередь + // - Проблема в том что CQ_GetData может выдавать CQNil, если блок создан, но его vram_data не инициализировано + // - Чтобы это решить надо сначала доделать ветвление в OpenCLABC + var Q_Extract := V_ExtractedCount.MakeCCQ.ThenWriteValue(0).DiscardResult; for var b_y := 0 to b_cy-1 do for var b_x := 0 to b_cx-1 do begin - var b := render_info.blocks[b_y, b_x]; - - Q_Steps += b.CQ_MandelbrotBlockStep(max_steps, cl_uc_buffer, cl_err_buffer); - - var sheet_shift := render_block_size * (b_x + b_y*render_sheet_w); - Q_Extract += b.CQ_GetData(render_info.mipmap_lvl - , new ShiftedCLArray(cl_sheet_states, sheet_shift, render_sheet_w) - , new ShiftedCLArray(cl_sheet_buffer, sheet_shift, render_sheet_w) + var b := blocks[b_y, b_x]; + if b=nil then continue; + var sheet_shift := block_w * (b_x + b_y*render_sheet_w); + Q_Extract += b.CQ_GetData( + new ShiftedCLArray(sheet_draw.b_cl, sheet_shift, render_sheet_w), + V_ExtractedCount ); - end; - begin - var cl_err := CLContext.Default.SyncInvoke( - Q_Init + - Q_Steps + - Q_Extract + - cl_err_buffer.MakeCCQ.ThenGetArray - ); - if cl_err[0]<>0 then - $'OpenCL err at [{cl_err[1]},{cl_err[2]}]: {CLCodeExecutionError(cl_err[0])}'.Println; - end; +// var sw := Stopwatch.StartNew; + var extracted_count := CLContext.Default.SyncInvoke( + Q_Acquire + + Q_Init + + Q_Extract + + Q_Release + + V_ExtractedCount.MakeCCQ.ThenGetValue + ); +// Println(sw.Elapsed); uniform_view_bound.Write(render_info.view_bound); uniform_sheet_bound.Write(render_info.sheet_bound); gl.Uniform2i(uniform_sheet_size, render_sheet_w, render_sheet_h); - gl.BindBufferBase(glBufferTarget.SHADER_STORAGE_BUFFER, ssb_sheet, gl_sheet_buffer); - gl.Uniform1f(uniform_max_steps, max_steps); + gl.BindBufferBase(glBufferTarget.SHADER_STORAGE_BUFFER, ssb_sheet, sheet_draw.b_gl); // Для дебага // gl.NamedBufferSubData(buffer_temp, new IntPtr(0*sizeof(real)), new IntPtr(2*sizeof(real)), mouse_pos); @@ -332,37 +477,57 @@ BoundUniforms = record // var temp_data := new real[1]; // gl.GetNamedBufferSubData(buffer_temp, new IntPtr(2*sizeof(real)), new IntPtr(1*sizeof(real)), temp_data); +// gl.BindBufferBase(glBufferTarget.SHADER_STORAGE_BUFFER, ssb_sheet, gl_buffer.Zero); + + {$endregion Кадр} + gl.Finish; //TODO Использовать обмент ивентами OpenCL/OpenGL var err := gl.GetError; if err.IS_ERROR then MessageBox.Show(err.ToString); - gl.Finish; + t_body.Stop; if curr_frame_resized then need_resize := false; - var curr_time := sw.Elapsed; var title_parts := new List; // Для дебага // title_parts += $'temp_data={_ObjectToString(temp_data)}'; - //TODO Оттестировать и убрать - title_parts += $'sheet byte size={curr_sheet_size} (${curr_sheet_size:X})'; +// title_parts += $'mem={CLMemoryObserver.Current.CurrentlyUsedAmount}'; +// title_parts += $'rendering {b_cx} x {b_cy} blocks'; +// title_parts += $'sheet byte size={curr_sheet_size} (${curr_sheet_size:X})'; - if timings.Count=timings_max_count then - begin - var time_diff := curr_time - timings.Dequeue; - var mspf := time_diff.TotalMilliseconds / timings_max_count; - var fps := 1000/mspf; - title_parts += $'{fps:N2} fps'; - title_parts += $'{mspf:N2} mspf'; - end; - timings += curr_time; + t_full.Update; + t_body.Update; + title_parts += $'{t_full.UPS:N2} fps'; + title_parts += $'{t_full.MSPU:N2} full mspf'; +// title_parts += $'{t_body.UPS:N2} body fps'; + title_parts += $'{t_body.MSPU:N2} body mspf'; - title_parts += $'pos=({camera.pos.r}; {camera.pos.i})'; title_parts += $'scale={camera.scale_fine:N3}*2^{camera.scale_pow}'; +// title_parts += $'pos=({camera.pos.r}; {camera.pos.i})'; + + title_parts += $'{1-extracted_count/render_sheet_w/render_sheet_h:00.00%} old'; + + if BlockUpdater.StepInfoStr<>nil then + title_parts += BlockUpdater.StepInfoStr; + +// title_parts += $'RAM: {GC.GetTotalMemory(true)/1024/1024/1024:N5} GC vs {System.Diagnostics.Process.GetCurrentProcess.WorkingSet64/1024/1024/1024:N5} Process'; + + if BlockUpdater.LackingVRAM then + title_parts += $'LACKING VRAM!!!'; - f.Text := title_parts.JoinToString(', '); + f.BeginInvoke(()-> + try + f.Text := title_parts.JoinToString(', '); + except + on e: Exception do + MessageBox.Show(e.ToString); + end); EndFrame; + except + on e: Exception do + Println(e); end; except diff --git a/Samples/OpenGLABC/Mandelbrot/0Mandelbrot.td b/Samples/OpenGLABC/Mandelbrot/0Mandelbrot.td index d84ad68f..a8ed93d6 100644 --- a/Samples/OpenGLABC/Mandelbrot/0Mandelbrot.td +++ b/Samples/OpenGLABC/Mandelbrot/0Mandelbrot.td @@ -1,9 +1,72 @@  -#ExpErr -Compile errors: -[225,27] Blocks.pas: The type 'PointComponent' does not contain a definition for 'FirstWordToReal' +#Delegates +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure +0Mandelbrot.$delegate? = procedure(<>key_low: Keys; <>key_high: Keys; <>modifiers: Keys; <>on_change: integer -> ()) +0Mandelbrot.$delegate? = procedure(<>key_low: Keys; <>key_high: Keys; <>modifiers: Keys; <>on_change: integer -> ()) +0Mandelbrot.$delegate? = procedure(<>s_frag: gl_shader) +0Mandelbrot.$delegate? = procedure(<>s_frag: gl_shader) +0Mandelbrot.$delegate? = procedure(key_low: Keys; key_high: Keys; modifiers: Keys; on_change: integer -> ()) +0Mandelbrot.$delegate? = procedure(s_frag: gl_shader) +Blocks.$delegate? = procedure(<>ml: MemoryLayer) +Blocks.$delegate? = procedure(<>ml: MemoryLayer) +Blocks.$delegate? = procedure(e: Exception) +Blocks.$delegate? = procedure(ml: MemoryLayer) +OpenCL.$delegate? = function(command_queue: cl_command_queue; num_objects: longword; var mem_objects: cl_mem; num_events_in_wait_list: longword; var event_wait_list: cl_event; var event: cl_event): clErrorCode +OpenCL.$delegate? = function(command_queue: cl_command_queue; num_objects: longword; var mem_objects: cl_mem; num_events_in_wait_list: longword; var event_wait_list: cl_event; var event: cl_event): clErrorCode +OpenCL.$delegate? = function(context: cl_context; flags: clMemFlags; bufobj: longword; var errcode_ret: clErrorCode): cl_mem +OpenCL.clCreateContextCallback = procedure(errinfo: string; private_info: System.IntPtr; cb: System.UIntPtr; user_data: System.IntPtr) +OpenCL.clEventCallback = procedure(event: cl_event; event_command_status: clCommandExecutionStatus; user_data: System.IntPtr) +OpenCL.clProgramCallback = procedure(program: cl_program; user_data: System.IntPtr) +OpenCLABC.$delegate? = function(ntv: cl_program; var data: clBool; validate: boolean): clErrorCode +OpenCLABC._GetPropValueFunc = function(ntv: cl_program; var data: T): clErrorCode +OpenCLABC_implementation______.EnqFunc = function(prev_res: T; cq: cl_command_queue; ev_l2: EventList): ValueTuple ()> +OpenCLABC_implementation______.InvokeParamsFunc = function(enq_c: integer; o_const: boolean; g: CLTaskGlobalData; enq_evs: DoubleList; par_err_handlers: DoubleList): ValueTuple> +OpenGL.$delegate? = function(interval: integer): glBool32 +OpenGL.$delegate? = function(program: gl_program; name: System.IntPtr): integer +OpenGL.$delegate? = function(program: gl_program; programInterface: glProgramInterface; name: System.IntPtr): longword +OpenGL.$delegate? = function(type: glShaderType): gl_shader +OpenGL.$delegate? = function: gl_program +OpenGL.$delegate? = function: glErrorCode +OpenGL.$delegate? = procedure +OpenGL.$delegate? = procedure(buffer: gl_buffer; size: System.UIntPtr; data: System.IntPtr; usage: glVertexBufferObjectUsage) +OpenGL.$delegate? = procedure(buffer: gl_buffer; size: System.UIntPtr; var data: byte; usage: glVertexBufferObjectUsage) +OpenGL.$delegate? = procedure(location: integer; v0: integer; v1: integer) +OpenGL.$delegate? = procedure(location: integer; v0: single) +OpenGL.$delegate? = procedure(mask: glClearBufferMask) +OpenGL.$delegate? = procedure(mode: glPrimitiveType; first: integer; count: integer) +OpenGL.$delegate? = procedure(n: integer; buffers: System.IntPtr) +OpenGL.$delegate? = procedure(n: integer; var buffers: gl_buffer) +OpenGL.$delegate? = procedure(program: gl_program) +OpenGL.$delegate? = procedure(program: gl_program) +OpenGL.$delegate? = procedure(program: gl_program) +OpenGL.$delegate? = procedure(program: gl_program; bufSize: integer; length: System.IntPtr; infoLog: System.IntPtr) +OpenGL.$delegate? = procedure(program: gl_program; bufSize: integer; var length: integer; infoLog: System.IntPtr) +OpenGL.$delegate? = procedure(program: gl_program; pname: glProgramProperty; params: System.IntPtr) +OpenGL.$delegate? = procedure(program: gl_program; pname: glProgramProperty; var params: integer) +OpenGL.$delegate? = procedure(program: gl_program; shader: gl_shader) +OpenGL.$delegate? = procedure(shader: gl_shader) +OpenGL.$delegate? = procedure(shader: gl_shader) +OpenGL.$delegate? = procedure(shader: gl_shader; bufSize: integer; length: System.IntPtr; infoLog: System.IntPtr) +OpenGL.$delegate? = procedure(shader: gl_shader; bufSize: integer; var length: integer; infoLog: System.IntPtr) +OpenGL.$delegate? = procedure(shader: gl_shader; count: integer; string: Void*; length: System.IntPtr) +OpenGL.$delegate? = procedure(shader: gl_shader; count: integer; string: Void*; var length: integer) +OpenGL.$delegate? = procedure(shader: gl_shader; count: integer; var string: System.IntPtr; length: System.IntPtr) +OpenGL.$delegate? = procedure(shader: gl_shader; count: integer; var string: System.IntPtr; var length: integer) +OpenGL.$delegate? = procedure(shader: gl_shader; pname: glShaderParameterName; params: System.IntPtr) +OpenGL.$delegate? = procedure(shader: gl_shader; pname: glShaderParameterName; var params: integer) +OpenGL.$delegate? = procedure(target: glBufferTarget; index: longword; buffer: gl_buffer) +OpenGL.$delegate? = procedure(x: integer; y: integer; width: integer; height: integer) +OpenGLABC.RedrawThreadProc = procedure(pl: IGLPlatformLoader; EndFrame: procedure) #ReqModules OpenGL+OpenGLABC diff --git a/Samples/OpenGLABC/Mandelbrot/Blocks.pas b/Samples/OpenGLABC/Mandelbrot/Blocks.pas index e161cd4d..1047f2fc 100644 --- a/Samples/OpenGLABC/Mandelbrot/Blocks.pas +++ b/Samples/OpenGLABC/Mandelbrot/Blocks.pas @@ -8,6 +8,7 @@ uses PointComponents; uses CameraDef; uses MandelbrotSampling; +uses MemoryLayering; type CLCodeExecutionError = MandelbrotSampling.CLCodeExecutionError; @@ -26,272 +27,672 @@ ShiftedCLArray = record end; + BlockLayer = class; // Блок из block_w*block_w точек - PointBlock = sealed class + PointBlock = sealed class(IMemoryLayerData) + private layer: BlockLayer; // Принимает значения -∞..1 - // Длина стороны блока в логическом пространстве = 2**block_scale - private block_scale: integer; - private component_word_count: integer; - private pos00: CLArray; + // Длина стороны блока в логическом пространстве = 2**block_scale_pow + private block_scale_pow: integer; + private pos00: PointPos; - private gpu_data: CLArray; - private gpu_mipmaps_state: CLArray; - private gpu_mipmaps_steps: CLArray; - private gpu_mipmaps_need_update: CLArray; + private vram_pos00: CLArray; + private vram_data: CLArray := nil; - private ram_data: array of byte; + private ram_data: array of cardinal := nil; - public constructor(block_scale: integer; pos00: PointPos); + private drive_cache_file: string := nil; + // Current block format: + // + // version: int32 := cache_format_version + // scale: int32 + // word_count: int32 + // pos00: PointPos + // data: array of uint32 + // + // Also used in Drive_MemoryLayer + private const drive_cache_format_version = 1; + private static last_drive_cache_id := 0; + private static drive_cache_dir_name := $'Cache/block_w_pow={Settings.block_w_pow}'; + private const drive_cache_ext_name = 'point_block'; + private static function AllocDriveCacheFile: string; begin - self.block_scale := block_scale; - if block_scale>=2 then + while true do + begin + var id := System.Threading.Interlocked.Increment(last_drive_cache_id); + Result := $'{drive_cache_dir_name}/{id}.{drive_cache_ext_name}'; + if not FileExists(Result) then break; + end; + end; + + public constructor(layer: BlockLayer; block_scale_pow: integer; pos00: PointPos); + begin + self.layer := layer; + + self.block_scale_pow := block_scale_pow; + if block_scale_pow>Settings.max_block_scale_pow then raise new System.ArgumentOutOfRangeException; - self.component_word_count := pos00.Size; - //TODO Track GPU memory used amount - self.pos00 := new CLArray(pos00.r.Words+pos00.i.Words, CLMemoryUsage.ReadOnly, CLMemoryUsage.None); - - self.gpu_data := new CLArray( block_w*block_w * (2 + component_word_count*2) ); - self.gpu_mipmaps_state := new CLArray(mipmap_total_size); - self.gpu_mipmaps_steps := new CLArray(mipmap_total_size); - self.gpu_mipmaps_need_update := new CLArray(mipmap_total_size); - - //TODO Снять с этого потока выполнения... - CLContext.Default.SyncInvoke( - self.gpu_data.MakeCCQ.ThenFillValue(0) + - self.gpu_mipmaps_state.MakeCCQ.ThenFillValue(0) + - self.gpu_mipmaps_steps.MakeCCQ.ThenFillValue(0) + - self.gpu_mipmaps_need_update.MakeCCQ.ThenFillValue(0) - ); + self.pos00 := pos00; end; private constructor := raise new System.InvalidOperationException; + public static function GetMetaWordCount(component_word_count: integer) := component_word_count*2; + public static function GetDataWordCount(component_word_count: integer) := block_w.Sqr * (2 + component_word_count*2); + public static function GetWordCount(component_word_count: integer) := GetMetaWordCount(component_word_count) + GetDataWordCount(component_word_count); + public static function GetByteSize(component_word_count: integer) := GetWordCount(component_word_count) * sizeof(cardinal); + public function GetByteSize := GetByteSize(self.pos00.Size); + private static CLCodeCache := new System.Collections.Concurrent.ConcurrentDictionary; private static function CLCodeFor(word_c: cardinal) := CLCodeCache.GetOrAdd(word_c, word_c->MandelbrotSampling.CompiledCode(word_c)); - public function CQ_MandelbrotBlockStep(step_repeat_count: CommandQueue; V_UpdateCount: CLValue; A_Err: CLArray): CommandQueueNil := - CLCodeFor(self.component_word_count)['MandelbrotBlockSteps'] - .MakeCCQ.ThenExec2(block_w,block_w - , self.gpu_data - , self.pos00 - , Settings.z_int_bits-1 + -(self.block_scale-Settings.block_w_pow) - , self.gpu_mipmaps_need_update - , step_repeat_count - , V_UpdateCount - , A_Err - ).DiscardResult; - - public function CQ_GetData(target_mipmap_lvl: integer; A_State: ShiftedCLArray; A_Steps: ShiftedCLArray): CommandQueueNil; + + public function CQ_DownGradeToRAM: CommandQueueNil; begin + Result := CQNil; + var l_vram_data := System.Threading.Interlocked.Exchange(self.vram_data, nil); + if l_vram_data=nil then exit; - {$ifdef DEBUG} - if target_mipmap_lvl < -scale_shift then - raise new System.InvalidOperationException; - if target_mipmap_lvl > block_w_pow then - raise new System.InvalidOperationException; - {$endif DEBUG} + vram_pos00.Dispose; + vram_pos00 := nil; + + if ram_data<>nil then raise new System.InvalidOperationException; + var l_ram_data := new cardinal[l_vram_data.Length]; + ram_data := l_ram_data; + + //TODO l_vram_data may still be used by the render thread + // - Maybe, instead of disposing, add to some list that render thread can dispose at the end of frame + //TODO #????: & + Result += l_vram_data.MakeCCQ.ThenReadArray(HFQ&(()->l_ram_data, need_own_thread := false)) + + HPQ(()-> + begin + l_vram_data.Dispose; + //TODO Протестить как теперь тратится память. Должно убрать скачёк при сохранении + // - Нет, проблема ж вообще не в этом + // - На самом деле все l_ram_data выделяются вместе, перед началом выполнения очереди + // - Это очень фиговый дизайн получился... + l_ram_data := nil; + end, need_own_thread := false); + + end; + public function CQ_DownGradeToDrive: CommandQueueNil; + begin + Result := CQ_DownGradeToRAM; + + var l_ram_data := System.Threading.Interlocked.Exchange(self.ram_data, nil); + if l_ram_data=nil then exit; - var code := CLCodeFor(self.component_word_count); - var w := block_w; + if drive_cache_file<>nil then raise new System.InvalidOperationException; + var l_drive_cache_file := AllocDriveCacheFile; + drive_cache_file := l_drive_cache_file; - if target_mipmap_lvl=0 then + Result += HPQ(()-> begin - Result := code['ExtractRawSteps'].MakeCCQ.ThenExec2(w,w - , self.gpu_data - , A_State.a, A_State.shift, A_State.row_len - , A_Steps.a, A_Steps.shift, A_Steps.row_len - ).DiscardResult; - exit; - end; + var need_del := true; + var temp_fname := l_drive_cache_file + '.temp'; + var str := System.IO.File.Create(temp_fname); + try + var bw := new System.IO.BinaryWriter(str); + + bw.Write(drive_cache_format_version); + bw.Write(self.block_scale_pow); + bw.Write(self.pos00.Size); + self.pos00.Save(bw); + + // Much faster than reading/writing one word at a time + // But could be even faster, if stream directly accessed l_ram_data + var bytes := new byte[l_ram_data.Length * sizeof(cardinal)]; + System.Buffer.BlockCopy(l_ram_data,0, bytes,0, bytes.Length); + l_ram_data := nil; + str.Write(bytes, 0, bytes.Length); + + need_del := false; + finally + str.Close; + if need_del then + DeleteFile(temp_fname); + end; + System.IO.File.Move(temp_fname, l_drive_cache_file); + end, need_own_thread := false); + + end; + + public function CQ_UpGradeToRAM: CommandQueueNil; + begin + Result := CQNil; + var l_drive_cache_file := System.Threading.Interlocked.Exchange(drive_cache_file, nil); + if l_drive_cache_file=nil then exit; - w := w shr 1; - Result := code['FixFirstMipMap'].MakeCCQ.ThenExec2(w,w - , self.gpu_data - , self.gpu_mipmaps_state - , self.gpu_mipmaps_steps - , self.gpu_mipmaps_need_update - ).DiscardResult; - var mipmap_shift := w*w; + if ram_data<>nil then raise new System.InvalidOperationException; + var l_ram_data := new cardinal[GetDataWordCount(self.pos00.Size)]; + ram_data := l_ram_data; - for var mipmap_lvl := 2 to target_mipmap_lvl do + Result += HPQ(()-> begin - w := w shr 1; - Result += code['FixMipMap'].MakeCCQ.ThenExec2(w,w - , self.gpu_mipmaps_state - , self.gpu_mipmaps_steps - , self.gpu_mipmaps_need_update - , cardinal(mipmap_shift) - , cardinal(mipmap_lvl) + var str := System.IO.File.OpenRead(l_drive_cache_file); + try + str.Position := 12 + GetMetaWordCount(self.pos00.Size)*sizeof(cardinal); + + var bytes := new byte[l_ram_data.Length * sizeof(cardinal)]; + var read_len := str.Read(bytes, 0, bytes.Length); + if read_len<>bytes.Length then raise new System.InvalidOperationException; + + System.Buffer.BlockCopy(bytes,0, l_ram_data,0, bytes.Length); + finally + str.Close; + DeleteFile(l_drive_cache_file); + end; + end, need_own_thread := false); + + end; + public function CQ_UpGradeToVRAM: CommandQueueNil; + begin + Result := CQ_UpGradeToRAM; + var l_ram_data := System.Threading.Interlocked.Exchange(self.ram_data, nil); + // Init vram buffer even if there is no existing data +// if l_ram_data=nil then exit; +// if vram_data<>nil then raise new System.InvalidOperationException; + // Instead, exit if vram is already inited + if vram_data<>nil then exit; + + if vram_pos00<>nil then raise new System.InvalidOperationException; + vram_pos00 := new CLArray(pos00.r.Words + pos00.i.Words, CLMemoryUsage.ReadOnly, CLMemoryUsage.None); + + var l_vram_data := new CLArray(GetDataWordCount(self.pos00.Size), CLMemoryUsage.ReadWrite, CLMemoryUsage.ReadWrite); + vram_data := l_vram_data; + + Result += if l_ram_data=nil then + l_vram_data.MakeCCQ.ThenFillValue(0).DiscardResult else + l_vram_data.MakeCCQ.ThenWriteArray(l_ram_data).DiscardResult; + + end; + + public function CQ_MandelbrotBlockStep(Q_GetStepRepeatCount: CommandQueue; V_UpdateCount: CLValue; A_Err: CLArray): CommandQueueNil; + begin + Result := CQNil; + if vram_data=nil then exit; + + Result := CLCodeFor(self.pos00.Size)['MandelbrotBlockSteps'] + .MakeCCQ.ThenExec2(block_w,block_w + , vram_data + , vram_pos00 + , Settings.z_int_bits-1 + -(self.block_scale_pow-Settings.block_w_pow) + , Q_GetStepRepeatCount + , V_UpdateCount + , A_Err ).DiscardResult; - mipmap_shift += w*w; - end; - Result += code['ExtractMipMapSteps'].MakeCCQ.ThenExec2(w,w - , self.gpu_mipmaps_state, self.gpu_mipmaps_steps, cardinal(mipmap_shift - w*w) - , A_State.a, A_State.shift, A_State.row_len - , A_Steps.a, A_Steps.shift, A_Steps.row_len - , cardinal(target_mipmap_lvl) - ).DiscardResult; + end; + + public function CQ_GetData(A_Result: ShiftedCLArray; V_ExtractedCount: CLValue): CommandQueueNil; + begin + Result := CQNil; + + var l_vram_data := self.vram_data; + if l_vram_data=nil then exit; + + Result += CLCodeFor(self.pos00.Size)['ExtractSteps'] + .MakeCCQ.ThenExec2(block_w,block_w + , l_vram_data + , A_Result.a, A_Result.shift, A_Result.row_len + , V_ExtractedCount + ).DiscardResult; + + end; + + public function ToString: string; override := + $'block at scale_pow={self.block_scale_pow}, pos={self.pos00}'; + + public procedure Dispose; + begin + pos00 := default(PointPos); + + var l_vram_pos00 := System.Threading.Interlocked.Exchange(self.vram_pos00, nil); + if l_vram_pos00<>nil then l_vram_pos00.Dispose; + + var l_vram_data := System.Threading.Interlocked.Exchange(self.vram_data, nil); + if l_vram_data<>nil then l_vram_data.Dispose; + ram_data := nil; + + var l_drive_cache_file := System.Threading.Interlocked.Exchange(self.drive_cache_file, nil); + if l_drive_cache_file<>nil then DeleteFile(l_drive_cache_file); + + GC.SuppressFinalize(self); end; + protected procedure Finalize; override := Dispose; end; - BoundDefs = record - public xf, yf: single; - public xl, yl: single; + BoundDefs = record + public xf, yf: T; + public xl, yl: T; + + public function Convert(dx, dy: T->T2): BoundDefs; + begin + Result.xf := dx(self.xf); Result.yf := dy(self.yf); + Result.xl := dx(self.xl); Result.yl := dy(self.yl); + end; + public function Convert(d: T->T2): BoundDefs := Convert(d,d); + + public function ToString: string; override := + $'{xf}<=x=>{xl} | {yf}<=y=>{yl}'; + + end; + BlockLayerSubArea = record + private c_min, c_max: PointPos; + private r_block_poss, i_block_poss: array of PointComponent; + private layer: BlockLayer; + + /// Ordered by distance from the center + public function MakeOrderedArr(sel: PointPos->T): array of T; + begin + var rc := r_block_poss.Length; + var ic := i_block_poss.Length; + Result := new T[rc*ic]; + var keys := new int64[Result.Length]; + + var ind := 0; + for var i_ind := 0 to ic-1 do + for var r_ind := 0 to rc-1 do + begin + Result[ind] := sel(new PointPos(r_block_poss[r_ind], i_block_poss[i_ind])); + // Doesn't account for sheet_bound + // But in practice it would hardly matter + keys[ind] := Sqr(r_ind*2-rc) + Sqr(i_ind*2-ic); + ind += 1; + end; + {$ifdef DEBUG} + if ind<>Result.Length then + raise new System.InvalidOperationException; + {$endif DEBUG} + + System.Array.Sort(keys, Result); + end; + + end; + SheetDiff = record + + // All positive (or zero) if new sheet is fully inside old sheet + // All negative (or zero) if old sheet is fully inside new sheet + // Counted in terms of points of new sheet + private bounds_diff: BoundDefs; - public static procedure operator*=(var b: BoundDefs; k: System.ValueTuple); + // Positive if new sheet covers smaller scale than old sheet + private scale_diff: integer; + + {$resource SheetTransfer.cl} + private static sheet_transfer_code_text := System.IO.StreamReader.Create( + System.Reflection.Assembly.GetCallingAssembly.GetManifestResourceStream('SheetTransfer.cl') + ).ReadToEnd; + private static sheet_transfer_code := new CLProgramCode(sheet_transfer_code_text); + + public function IsNoChange := (bounds_diff = default(BoundDefs)) and (scale_diff=0); + + public function CQ_CopySheet(old_sheet, new_sheet: CLArray; old_row_len, new_row_len, new_col_len: integer): CommandQueueNil; begin - var (kx,ky) := k; - b.xf *= kx; b.yf *= ky; - b.xl *= kx; b.yl *= ky; + + // Both count in points of new scale + var old_bounds := self.bounds_diff.Convert(x->(+x).ClampBottom(0)); + var new_bounds := self.bounds_diff.Convert(x->(-x).ClampBottom(0)); + + var scale_k := 1 shl Abs(scale_diff); + + // To continue counting everything in points of new sheet, + // the scale_diff<0 should cause "old_row_len/scale_k", converting it to new points + // But that will either loose precision, or require floating point (or both) + // Same problem is old_shift is always in points of old sheet + // So instead, if scale_diff<0, old_bounds is coverted to points of old sheet +// var old_shift := old_row_len*old_bounds.yf*scale_k + old_bounds.xf * if scale_diff<0 then 1 else scale_k; + var new_shift := new_row_len*new_bounds.yf + new_bounds.xf; + + var w := new_row_len - (new_bounds.xf+new_bounds.xl); + var h := new_col_len - (new_bounds.yf+new_bounds.yl); + + if scale_diff<0 then + begin + var old_shift := (old_row_len*old_bounds.yf + old_bounds.xf)*scale_k; + + Result := sheet_transfer_code['DownScaleSheet'].MakeCCQ + .ThenExec2(w,h + , old_sheet, old_shift, old_row_len + , new_sheet, new_shift, new_row_len + , -scale_diff + ).DiscardResult; + + end else + if scale_diff=0 then + begin + var old_shift := old_row_len*old_bounds.yf + old_bounds.xf; + + Result := sheet_transfer_code['CopySheetRect'].MakeCCQ + .ThenExec2(w,h + , old_sheet, old_shift, old_row_len + , new_sheet, new_shift, new_row_len + ).DiscardResult; + + end else + if scale_diff>0 then + begin + + Result := sheet_transfer_code['UpScaleSheet'].MakeCCQ + .ThenExec2(w,h + , old_sheet, old_bounds.xf, old_bounds.yf, old_row_len + , new_sheet, new_shift, new_row_len + , +scale_diff + ).DiscardResult; + + end else + raise new System.InvalidOperationException; + end; - public static procedure operator/=(var b: BoundDefs; k: System.ValueTuple) := - b *= System.ValueTuple.Create(1/k.Item1, 1/k.Item2); end; BlockLayerRenderInfo = record - public blocks: array[,] of PointBlock; // [y,x] - public mipmap_lvl: integer; // How much of viewport is empty + // (between screen edge and the sheet) // [0;2) and first+last<=2 - public view_bound: BoundDefs; + public view_bound: BoundDefs; - // How much of edge blocks is hidden + // How much of edge blocks is hidden by window edge // [0;1) and first+last<=1 - public sheet_bound: BoundDefs; + public sheet_bound: BoundDefs; + // All coordinates of (partially or fully) visible blocks + public block_area: BlockLayerSubArea; + + // Info needed to render + public last_sheet_diff: SheetDiff?; + + private block_sz_bit_ind: integer; end; // Слой, содержащий кэш уже просчитанных блоков BlockLayer = sealed class - private scale: integer; + private scale_pow: integer; private blocks := new Dictionary; - public constructor(scale: integer); + public constructor(scale_pow: integer) := self.scale_pow := scale_pow; + private constructor := raise new System.InvalidOperationException; + + private static all_layers := new List; + public static function GetLayer(block_scale_pow: integer): BlockLayer; begin - self.scale := scale; + var layer_ind := Settings.max_block_scale_pow - block_scale_pow; + while all_layers.Count<=layer_ind do + all_layers += default(BlockLayer); + Result := all_layers[layer_ind]; + if Result<>nil then exit; + Result := new BlockLayer(block_scale_pow); + all_layers[layer_ind] := Result; end; - private constructor := raise new System.InvalidOperationException; - -// private static all_layers := new BlockLayer[1+max_z_scale_bits_rounded]; -// private static function scale_to_layer_ind(scale: integer) := 1-scale; -// -// public static function TakeBlocks(scale: integer; dx, dy: cardinal): List; -// begin -// var layer_ind := scale_to_layer_ind(scale).Clamp(0, all_layers.Length-1); -// scale := 1-layer_ind; -// -// -// end; -// -// public static procedure Cleanup; -// begin -// var TODO := 0; // Вообще вместо этого надо чистить когда заканчивается место -// end; + public static function GetLayer(camera_pos: CameraPos) := + GetLayer(camera_pos.GetPointScalePow + Settings.block_w_pow); - //TODO Сейчас блоки создаёт с 0 при каждом вызове. Использовать экземпляр типа BlockLayer - public static function BlocksForCurrentScale(camera_pos: CameraPos): BlockLayerRenderInfo; + private function GetBlockAt(pos00: PointPos; can_create: boolean): PointBlock; + begin + if self.blocks.TryGetValue(pos00, Result) then exit; + if not can_create then exit; + Result := new PointBlock(self, self.scale_pow, pos00); + self.blocks.Add(pos00, Result); + end; + public function GetRenderInfo(camera_pos: CameraPos; last_ri: BlockLayerRenderInfo?): BlockLayerRenderInfo; begin Result := default(BlockLayerRenderInfo); + {$ifdef DEBUG} + if self.scale_pow <> camera_pos.GetPointScalePow + Settings.block_w_pow then + raise new System.InvalidOperationException; + {$endif DEBUG} + var word_count := camera_pos.pos.Size; var c_ctr := camera_pos.pos; - //TODO Таким макаром view_size_bit_ind может быть <0, не говоря уже о переполнении сложения .AddLowestBits - // Если сильно отдалить камеру - значения границ не поместятся в c_min/c_max - // - Можно сразу складывать, округлять и ограничивать, всё в 1 операцию - // --- Но это будет 1 очень большая и сложная подпрограмма - // - Можно ограничивать отдельно вывод RoundToLowestBits и т.п. - // --- Но тогда границы блоков неправильно поставит относительно границ экрана - // - Можно ограничивать камеру - // --- Но обработать PointComponent границы вместе с fine+pow масштабом будет сложно... - // --- И очень широкое окно всё равно переполнит PointComponent - // - Можно использовать отдельный алгоритм для границ, в зависимости от того - нужна ли точность PointComponent - // --- Но это дубли кода, сложная проверка какой алгоритм выбрать и возможность дёрганья камеры при переходе от 1 алгоритма к другому - // - Можно менять область рендеринга если камера слишком отдалена, а внутри неё всё равно использовать обычный алгоритм с PointComponent - // --- Но тогда на каждый пиксель придётся очень много точек - //TODO В итоге решил ограничить .pos, чтобы оно за [-2;+2] не выходило - // - Тогда достаточно таки view_skip при большом отдалении делать, и больше ничего - if camera_pos.scale_pow>=1 then begin - if c_ctr.Size<>1 then raise new System.NotImplementedException; var cx := c_ctr.r.FirstWordToReal; var cy := c_ctr.i.FirstWordToReal; + var ar := camera_pos.AspectRatio; + + var visible_space_dy := camera_pos.scale_fine * 2.0**camera_pos.scale_pow; + var visible_space_dx := visible_space_dy * ar; + + // Whole logical space is -2 .. +2 + // Visible logical space is c_ctr-visible_space_d .. c_ctr+visible_space_d - //TODO Масштабировать cx и cy, привести их к диапазону -1..+1 + Result.view_bound.xf := (-2-(cx-visible_space_dx)).ClampBottom(0) / visible_space_dx; + Result.view_bound.xl := ((cx+visible_space_dx)-2).ClampBottom(0) / visible_space_dx; + + Result.view_bound.yf := (-2-(cy-visible_space_dy)).ClampBottom(0) / visible_space_dy; + Result.view_bound.yl := ((cy+visible_space_dy)-2).ClampBottom(0) / visible_space_dy; end; - var view_size_bit_ind := Settings.z_int_bits-1 - camera_pos.scale_pow; - var di := PointComponent.RoundToLowestBits(word_count, view_size_bit_ind, camera_pos.scale_fine); - var dr := PointComponent.RoundToLowestBits(word_count, view_size_bit_ind, camera_pos.scale_fine * camera_pos.AspectRatio); - var c_min := c_ctr.AddLowestBits(-dr,-di); - var c_max := c_ctr.AddLowestBits(+dr,+di); - c_max.SelfFlipIfMinusZero; + var dr := new PointComponentShift(word_count, camera_pos.scale_pow, camera_pos.scale_fine * camera_pos.AspectRatio); + var di := new PointComponentShift(word_count, camera_pos.scale_pow, camera_pos.scale_fine); + Result.block_area.c_min := c_ctr.WithShiftClamp2(-dr,-di, false); + Result.block_area.c_max := c_ctr.WithShiftClamp2(+dr,+di, true); - var (block_scale, main_mipmap_lvl) := camera_pos.GetPointScaleAndMainMipMapLvl; - block_scale += Settings.block_w_pow; - Result.mipmap_lvl := main_mipmap_lvl; - var block_sz_bit_ind := Settings.z_int_bits-1 + -block_scale; - c_min.SelfBlockRound(block_sz_bit_ind, false, Result.sheet_bound.xf, Result.sheet_bound.yf); - c_max.SelfBlockRound(block_sz_bit_ind, true, Result.sheet_bound.xl, Result.sheet_bound.yl); - //TODO Debug error for when bounds are >2 + Result.block_sz_bit_ind := Settings.z_int_bits-1 + -self.scale_pow; + Result.block_area.c_min.SelfBlockRound(Result.block_sz_bit_ind, false, Result.sheet_bound.xf, Result.sheet_bound.yf); + Result.block_area.c_max.SelfBlockRound(Result.block_sz_bit_ind, true, Result.sheet_bound.xl, Result.sheet_bound.yl); - var r_blocks_count := PointComponent.BlocksCount(c_min.r, c_max.r, block_sz_bit_ind); - var i_blocks_count := PointComponent.BlocksCount(c_min.i, c_max.i, block_sz_bit_ind); + Result.block_area.r_block_poss := PointComponent.Range(Result.block_area.c_min.r, Result.block_area.c_max.r, Result.block_sz_bit_ind); + Result.block_area.i_block_poss := PointComponent.Range(Result.block_area.c_min.i, Result.block_area.c_max.i, Result.block_sz_bit_ind); + Result.block_area.layer := self; - Result.sheet_bound /= new System.ValueTuple(r_blocks_count, i_blocks_count); + var kx: single := 1/Result.block_area.r_block_poss.Length; + var ky: single := 1/Result.block_area.i_block_poss.Length; + Result.sheet_bound := Result.sheet_bound.Convert(x->x*kx, y->y*ky); - var pc_rs := new PointComponent[r_blocks_count]; - begin - var pc_r := c_min.r; - for var ri := 0 to r_blocks_count-1 do - begin - pc_rs[ri] := pc_r; - pc_r := pc_r.MakeNextBlockBound(block_sz_bit_ind); - end; - {$ifdef DEBUG} - if pc_r<>c_max.r then - raise new System.InvalidOperationException; - {$endif DEBUG} + if last_ri<>nil then + try + var last_ri_v := last_ri.Value; + var last_sheet_diff: SheetDiff; + + last_sheet_diff.scale_diff := Result.block_sz_bit_ind - last_ri_v.block_sz_bit_ind; + if Abs(last_sheet_diff.scale_diff)>16 then + // Handling such jump in scale would require + // increasing precision in SheetDiff.CQ_CopySheet + // But it's not worth it, just recalculate the sheet + raise new System.OverflowException; + + var point_sz_bit_ind := Result.block_sz_bit_ind + Settings.block_w_pow; + last_sheet_diff.bounds_diff.xf := +PointComponent.BlocksCount(last_ri_v.block_area.c_min.r.WithBlockRound(point_sz_bit_ind, false), Result.block_area.c_min.r, point_sz_bit_ind); + last_sheet_diff.bounds_diff.yf := +PointComponent.BlocksCount(last_ri_v.block_area.c_min.i.WithBlockRound(point_sz_bit_ind, false), Result.block_area.c_min.i, point_sz_bit_ind); + last_sheet_diff.bounds_diff.xl := -PointComponent.BlocksCount(last_ri_v.block_area.c_max.r.WithBlockRound(point_sz_bit_ind, false), Result.block_area.c_max.r, point_sz_bit_ind); + last_sheet_diff.bounds_diff.yl := -PointComponent.BlocksCount(last_ri_v.block_area.c_max.i.WithBlockRound(point_sz_bit_ind, false), Result.block_area.c_max.i, point_sz_bit_ind); + + Result.last_sheet_diff := last_sheet_diff; +// if not last_sheet_diff.IsNoChange then +// begin +// Println($'{c_min} .. {c_max}'); +// Println($'{Result.block_area.r_block_poss.Length} x {Result.block_area.i_block_poss.Length}'); +// Println(last_sheet_diff.bounds_diff); +// end; + except + on e: System.OverflowException do ; end; - Result.blocks := new PointBlock[i_blocks_count, r_blocks_count]; -// $'Need {i_blocks_count} x {r_blocks_count} = {Result.blocks.Length} blocks'.Println; Halt; + end; + + end; + + //TODO Вытащить код для вывода KB и т.п. +// // VRAM/RAM/Drive +// MemoryLayer = sealed class +// private blocks := new List; +// private new_blocks := new List; +// private layer_name: string; +// private max_size: int64; +// private get_curr_size: ()->int64; +// private next := default(MemoryLayer); +// +// public constructor(layer_name: string; max_size: int64; get_curr_size: ()->int64); +// begin +// self.layer_name := layer_name; +// self.max_size := max_size; +// self.get_curr_size := get_curr_size; +// end; +// private constructor := raise new System.InvalidOperationException; +// +//// public procedure Flush; +//// begin +//// blocks.AddRange(new_blocks); +//// new_blocks.Clear; +//// end; +//// public procedure FlushAll := Enmr.ForEach(l->l.Flush); +// +// public function MemoryInfoStr: string; +// begin +// var c1 := real(get_curr_size()); +// var c2 := real(max_size); +// +// var pow := 0; +// var pow_step := 1024; +// var pow_names := |'KB','MB','GB'|; +// while (c1>=pow_step) or (c2>=pow_step) do +// begin +// if pow=pow_names.Length then break; +// pow += 1; +// c1 /= pow_step; +// c2 /= pow_step; +// end; +// +// var pow_name := if pow=0 then nil else ' '+pow_names[pow]; +// Result := $'{layer_name}: {c1}/{c2}{pow_name} ({c1/c2:000.00%})'; +// end; +// public function Enmr: sequence of MemoryLayer; +// begin +// var curr := self; +// repeat +// yield curr; +// curr := curr.next; +// until curr=nil; +// end; +// +// +// +// end; + + VRAM_MemoryLayer = sealed class(MemoryLayer) + + public constructor := inherited Create('VRAM', Settings.max_VRAM); + + protected function GetRealFilledSize: int64; override := CLMemoryObserver.Current.CurrentlyUsedAmount; + + end; + + RAM_MemoryLayer = sealed class(MemoryLayer) + + public constructor := inherited Create('RAM', Settings.max_RAM); + + protected function GetRealFilledSize: int64; override := GC.GetTotalMemory(true); + + end; + + Drive_MemoryLayer = sealed class(MemoryLayer) + + public constructor; + begin + inherited Create('Drive', Settings.max_drive_space); + + var cache_dir := System.IO.Directory.CreateDirectory(PointBlock.drive_cache_dir_name); + + var filled_size := int64(0); + foreach var fi in cache_dir.EnumerateFiles($'*.{PointBlock.drive_cache_ext_name}').OrderByDescending(fi->fi.LastWriteTimeUtc) do begin - var pc_i := c_min.i; - for var ii := 0 to i_blocks_count-1 do - begin - for var ri := 0 to r_blocks_count-1 do - Result.blocks[ii, ri] := new PointBlock(block_scale, new PointPos(pc_rs[ri], pc_i)); - pc_i := pc_i.MakeNextBlockBound(block_sz_bit_ind); + try + var str := fi.OpenRead; + try + var br := new System.IO.BinaryReader(str); + var version := br.ReadInt32; + var scale := br.ReadInt32; + var word_count := br.ReadInt32; + var pos00 := PointPos.Load(br, word_count); + if version<>PointBlock.drive_cache_format_version then + raise new Exception('Unsupported cache format'); + var expected_len := str.Position + PointBlock.GetDataWordCount(word_count)*sizeof(cardinal); + var actual_len := str.Length; + if expected_len <> actual_len then + raise new Exception($'Bad cache file size: {expected_len} vs {actual_len}'); + var layer := BlockLayer.GetLayer(scale); + var bl := new PointBlock(layer, scale, pos00); + bl.drive_cache_file := fi.FullName; + self.TryAdd(bl, bl->raise new Exception($'Not enough allocated disk space to load all the cache')); + layer.blocks.Add(pos00, bl); + filled_size += actual_len; + finally + str.Close; + end; + except + on e: Exception do + begin + $'Failed to load cached block: {fi.FullName}'.Println; + Println(e); + fi.Delete; + end; end; - {$ifdef DEBUG} - if pc_i<>c_max.i then - raise new System.InvalidOperationException; - {$endif DEBUG} end; + self.EndUpdate; // Flush newly added blocks + if filled_size + System.IO.DriveInfo.Create(GetCurrentDir).TotalFreeSpace < Settings.max_drive_space then + raise new System.InvalidOperationException('No enough disk space'); + end; + protected function GetRealFilledSize: int64; override := + System.IO.Directory.CreateDirectory(PointBlock.drive_cache_dir_name) + .EnumerateFiles($'*.{PointBlock.drive_cache_ext_name}').Select(fi->fi.Length).Sum; + end; + // Цикл обработки видимых блоков BlockUpdater = static class - private static output_update_info := false; private static procedure ExceptionToConsole(e: Exception) := Println(e); private static current_ex_handler := ExceptionToConsole; public static procedure SetExHandler(h: Exception->()) := current_ex_handler := h; - private static current_blocks: array[,] of PointBlock; - public static procedure SetCurrent(a: array[,] of PointBlock) := current_blocks := a; + private static current_area := default(System.Tuple); + public static procedure SetCurrent(area: BlockLayerSubArea) := current_area := Tuple.Create(area); + + private static lacking_vram := true; + public static property LackingVRAM: boolean read lacking_vram; + + private static step_info_str := default(string); + public static property StepInfoStr: string read step_info_str; + + private static shutdown_progress: (integer,integer) := nil; + private static on_shutdown_done: Action0 := nil; + public static property ShutdownProgress: (integer,integer) read shutdown_progress; + public static procedure BeginShutdown(on_shutdown_done: Action0); + begin + shutdown_progress := (0,1); + BlockUpdater.on_shutdown_done += on_shutdown_done; + end; + + private static function MatrItemsSortedFromCenter(m: array[,] of T): array of T; + begin + Result := new T[m.Length]; + var keys := new integer[Result.Length]; + + var res_i := 0; + var (c1,c2) := m.Size; + for var i1 := 0 to c1-1 do + for var i2 := 0 to c2-1 do + begin + Result[res_i] := m[i1,i2]; + keys[res_i] := Sqr(i1*2 - c1) + Sqr(i2*2 - c2); + res_i += 1; + end; + + System.Array.Sort(keys, Result); + end; static constructor := System.Threading.Thread.Create(()-> begin @@ -301,57 +702,158 @@ BlockLayerRenderInfo = record var A_Err := new CLArray(3); var err := new cardinal[3]; - var last_blocks: array[,] of PointBlock := nil; + var last_blocks := new HashSet; var Q_StepAll: CommandQueue; - var sw := new Stopwatch; + var step_sw := new Stopwatch; var step_count := 1; + var ml_vram := new VRAM_MemoryLayer; + var ml_ram := new RAM_MemoryLayer; + var ml_drive := new Drive_MemoryLayer; + + var all_mem_layers := new MemoryLayer[]( + ml_vram, ml_ram, ml_drive + ); + while true do try + if shutdown_progress<>nil then + begin + + var unloadable_blocks := new List; + var add_to_unload := procedure(ml: MemoryLayer)-> + begin + foreach var bl in ml.Enmr.Reverse do + if ml_drive.TryAdd(bl, bl->bl.Dispose()) then + unloadable_blocks += bl; + ml_drive.EndUpdate; + end; + add_to_unload( ml_ram); + add_to_unload(ml_vram); + + (ml_ram.Enmr+ml_vram.Enmr).ToArray; + foreach var bl in unloadable_blocks do + ml_drive.TryAdd(bl, bl->bl.Dispose()); + + var Q_ShutDown := CQNil; + foreach var bl in unloadable_blocks index bl_ind do + begin + Q_ShutDown += bl.CQ_DownGradeToDrive; + var shutdown_ind := bl_ind+1; + Q_ShutDown += HPQ(()->(shutdown_progress := (shutdown_ind,unloadable_blocks.Count)), need_own_thread := false); + end; + CLContext.Default.SyncInvoke(Q_ShutDown); + + var on_shutdown_done := on_shutdown_done; + if on_shutdown_done<>nil then on_shutdown_done(); + break; + end; - var blocks := current_blocks; - if blocks=nil then + var area: BlockLayerSubArea; begin - Sleep(1); - continue; + var area_t := current_area; + if area_t=nil then + begin + Sleep(1); + continue; + end; + area := area_t.Item1; end; - if blocks<>last_blocks then + var req_blocks := area.MakeOrderedArr(pos00->area.layer.GetBlockAt(pos00, true)); + if req_blocks.Length=0 then raise new System.NotImplementedException($'0 block area'); + + var block_new_table := new Dictionary(area.r_block_poss.Length * area.i_block_poss.Length); + foreach var bl in req_blocks do block_new_table.Add(bl, true); + foreach var ml in all_mem_layers do + ml.BeginUpdate(block_new_table); + + foreach var bl in req_blocks do + ml_vram.TryAdd(bl, bl-> + ml_ram.TryAdd(bl, bl-> + ml_drive.TryAdd(bl, bl-> + begin + if not bl.layer.blocks.Remove(bl.pos00) then + begin + $'Could not delete {bl}'.Println; + $'Current blocks:'.Println; + foreach var curr_bl in req_blocks do + $'{curr_bl}'.Println; + Println('='*50); + end; + bl.Dispose; + end) + ) + ); + + foreach var ml in all_mem_layers do + ml.EndUpdate; + + lacking_vram := ml_vram.Enmr.Take(req_blocks.Length).Count <> req_blocks.Length; + + begin + var Q_Init := CQNil; + + foreach var bl in ml_drive.Enmr do Q_Init += bl.CQ_DownGradeToDrive; + foreach var bl in ml_ram.Enmr do Q_Init += bl.CQ_DownGradeToRAM; + + foreach var bl in ml_ram.Enmr do Q_Init += bl.CQ_UpGradeToRAM; + foreach var bl in ml_vram.Enmr do Q_Init += bl.CQ_UpGradeToVRAM; + +// var init_sw := Stopwatch.StartNew; + CLContext.Default.SyncInvoke(Q_Init); +// $'Inited in {init_sw.Elapsed}'.Println; + end; + + var update_blocks := req_blocks.Intersect(ml_vram.Enmr).ToArray; + if not last_blocks.SetEquals(update_blocks) then begin + last_blocks.Clear; + last_blocks.UnionWith(update_blocks); + + // After blocks have changed, the update might take far longer + // Reset step count to avoid sudden horrible lag + step_count := 1; + var branches := ArrFill(Settings.max_parallel_blocks, CQNil); - foreach var b: PointBlock in blocks index b_i do - branches[b_i mod branches.Length] += b.CQ_MandelbrotBlockStep(P_StepCount, V_UpdateCount, A_Err); - last_blocks := blocks; + foreach var bl in update_blocks index bl_i do + branches[bl_i mod branches.Length] += bl.CQ_MandelbrotBlockStep(P_StepCount, V_UpdateCount, A_Err); + Q_StepAll := V_UpdateCount.MakeCCQ.ThenWriteValue(0) + A_Err.MakeCCQ.ThenFillValue(0) + CombineAsyncQueue(branches) + A_Err.MakeCCQ.ThenReadArray(err) + - V_UpdateCount.MakeCCQ.ThenGetValue + V_UpdateCount.MakeCCQ.ThenGetValue; + end; - sw.Restart; + {$ifdef DEBUG} + //TODO BlockLayer.all_layers использует из нескольких потоков, надо его блокировать... + var blocks_by_scale := BlockLayer.all_layers.Where(l->l<>nil).SelectMany(l->l.blocks.Values).ToArray; + var blocks_by_memory := all_mem_layers.SelectMany(l->l.Enmr).ToArray; + if not blocks_by_scale.ToHashSet.SetEquals(blocks_by_memory) then + raise new System.InvalidOperationException; + {$endif DEBUG} + + step_sw.Restart; var update_count := CLContext.Default.SyncInvoke(Q_StepAll , P_StepCount.NewSetter(step_count) ); - sw.Stop; + step_sw.Stop; - if output_update_info then - begin - $'Updated {update_count} points'.Println; - $'Updated current {blocks.Length} blocks {step_count} times in {sw.Elapsed}'.Println; - Println('='*30); - end; + step_info_str := $'u={update_count/step_count:N0}/step, {step_count} steps in {step_sw.Elapsed.TotalSeconds:N3}s'; // Тут бы PID контроллер реализовать вообще, потому что // зависимость времени от кол-ва шагов не линейная - // Но пока и так работает... - step_count := (step_count * Settings.target_step_time_seconds/sw.Elapsed.TotalSeconds).Clamp(1,max_steps_at_once).Round; + // Но на практике этого достаточно... + step_count := (step_count * Settings.target_step_time_seconds/step_sw.Elapsed.TotalSeconds).Clamp(1,max_steps_at_once).Round; if err[0]<>0 then - //TODO Надо бы выводить какой-то id блока тоже... + // Надо бы выводить какой-то id блока тоже... // - Достаточно [x,y] индекс. Тут из него можно получить все данные блока + // - Но пока что ошибок на стороне .cl кода (после FieldTest) вообще не видел. Слишком хорошо написал? raise new Exception($'Step err {CLCodeExecutionError(err[0])} at [{err[1]},{err[2]}]'); except @@ -363,4 +865,31 @@ BlockLayerRenderInfo = record end; +// For FieldTest, to go around the inconsistent BlockUpdater +procedure InitAllBlocks(self: BlockLayerSubArea); extensionmethod; +begin + var rc := self.r_block_poss.Length; + var ic := self.i_block_poss.Length; + for var i_ind := 0 to ic-1 do + for var r_ind := 0 to rc-1 do + self.layer.GetBlockAt(new PointPos( + self.r_block_poss[r_ind], self.i_block_poss[i_ind] + ), true); +end; + +function MakeInitedBlocksMatr(self: BlockLayerSubArea): array[,] of PointBlock; extensionmethod; +begin + var rc := self.r_block_poss.Length; + var ic := self.i_block_poss.Length; + Result := new PointBlock[ic, rc]; + for var i_ind := 0 to ic-1 do + for var r_ind := 0 to rc-1 do + // Cannot create blocks here, because they + // would not be marked by memory layers + // Instead BlockUpdater manages all creation and initing + Result[i_ind, r_ind] := self.layer.GetBlockAt(new PointPos( + self.r_block_poss[r_ind], self.i_block_poss[i_ind] + ), false); +end; + end. \ No newline at end of file diff --git a/Samples/OpenGLABC/Mandelbrot/CameraDef.pas b/Samples/OpenGLABC/Mandelbrot/CameraDef.pas index e57c5a7a..a21c6e68 100644 --- a/Samples/OpenGLABC/Mandelbrot/CameraDef.pas +++ b/Samples/OpenGLABC/Mandelbrot/CameraDef.pas @@ -31,38 +31,31 @@ CameraPos = record // By default fit 4x4 of logical space around (0;0) inside the window // Logical space Y is -2..+2, scale=2 self.scale_fine := 2; - // But if dw; - const max_point_scale = Settings.max_block_scale-Settings.block_w_pow; - begin - var point_scale := self.scale_pow + Settings.scale_shift + Floor(Log2(self.scale_fine/dh)); - var main_mipmap_lvl := -scale_shift; - if point_scale > max_point_scale then - begin - main_mipmap_lvl += point_scale-max_point_scale; - point_scale := max_point_scale; - end; - Result := System.ValueTuple.Create(point_scale, main_mipmap_lvl.Clamp(0,block_w)); - end; - private function GetBitCount := Settings.z_int_bits + -GetPointScaleAndMainMipMapLvl.Item1 + Settings.z_extra_precision_bits; + public function GetPointScalePow := + (self.scale_pow + Settings.scale_pow_shift + Floor(Log2(self.scale_fine/dh))) + .ClampTop(Settings.max_block_scale_pow - Settings.block_w_pow); + private function GetBitCount := Settings.z_int_bits + -GetPointScalePow + Settings.z_extra_precision_bits; private function GetWordCount := Ceil(GetBitCount/32).ClampBottom(1); // public function GetPosBitCount := Settings.z_int_bits - (self.scale_pow + Floor(Log2(self.scale_fine/dh))); // public function GetBlockBitCount := Settings.z_int_bits - (self.scale_pow + Settings.scale_shift - Settings.block_w_pow) + Settings.z_extra_precision_bits; - public procedure FixScalePow; + private procedure FixScalePow; begin if (scale_fine>=1) and (scale_fine<2) then exit; @@ -75,10 +68,33 @@ CameraPos = record raise new System.InvalidOperationException; end; + end; + + public procedure FixWordCount; + begin var pos_word_count := GetWordCount; if pos.Size = pos_word_count then exit; pos := pos.WithSize(pos_word_count); + end; + + public procedure Move(move_x,move_y, mouse_x,mouse_y, scale_speed: real; mouse_captured: boolean); + begin + var scale_mlt := 0.9 ** scale_speed; + begin + var shift_mlt := scale_fine * (1 - scale_mlt) * Ord(mouse_captured); + var dx := mouse_x-dw; + var dy := mouse_y-dh; + dx *= +shift_mlt/dw * AspectRatio; + dy *= -shift_mlt/dh; + var word_count := self.pos.Size; + var dr := new PointComponentShift(word_count, self.scale_pow, dx + move_x*scale_fine/dh); + var di := new PointComponentShift(word_count, self.scale_pow, dy + move_y*scale_fine/dh); + self.pos := self.pos.WithShiftClamp2(dr,di, true); + end; + + self.scale_fine *= scale_mlt; + FixScalePow; end; end; diff --git a/Samples/OpenGLABC/Mandelbrot/FieldTest.bmp b/Samples/OpenGLABC/Mandelbrot/FieldTest.bmp index 553d5008e67708e2f666ad4737ab490bc07c2e4e..dabc2687a08b65f6417fe0b6b61ab36ea2441cf5 100644 GIT binary patch literal 116318 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%0DR&p_i`!JQzrZ~9ax%ZyKT=T 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 kc(vg}@5e~Emlv(3Tn{?J-EKS|}f21DjjsO4v literal 107371 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 -var A_Steps := new CLArray(A_State.Length); +var A_Sheet := new CLArray(render_sheet_w*render_sheet_h); var V_UpdateCount := new CLValue(0); +var V_ExtractedCount := new CLValue(0); var A_Err := new CLArray(3); var Q_Init := CQNil - + A_State.MakeCCQ.ThenFillValue(0) - + A_Steps.MakeCCQ.ThenFillValue(0) - + A_Err.MakeCCQ.ThenFillValue(0) + + A_Sheet.MakeCCQ.ThenFillValue(0).DiscardResult + + A_Err.MakeCCQ.ThenFillValue(0).DiscardResult ; var Q_Steps := CQNil; @@ -36,19 +43,18 @@ for var b_y := 0 to b_cy-1 do for var b_x := 0 to b_cx-1 do begin - var b := render_info.blocks[b_y, b_x]; + var b := blocks[b_y, b_x]; // if (b_x, b_y) <> (1,1) then continue; + Q_Init += b.CQ_UpGradeToVRAM; + // Можно распараллелить, заменив += на *= (и в случае Q_Extract) // Но из тестирования - затраты на синхронизацию тут того не стоят //TODO Раз всё последовательно считает - можно по 1 блоку выделять и освобождать сразу... Q_Steps += b.CQ_MandelbrotBlockStep(max_iterations, V_UpdateCount, A_Err); var sheet_shift := render_block_size * (b_x + b_y*render_sheet_w); - Q_Extract += b.CQ_GetData(render_info.mipmap_lvl - , new ShiftedCLArray(A_State, sheet_shift, render_sheet_w) - , new ShiftedCLArray(A_Steps, sheet_shift, render_sheet_w) - ); + Q_Extract += b.CQ_GetData(new ShiftedCLArray(A_Sheet, sheet_shift, render_sheet_w), V_ExtractedCount); end; @@ -59,21 +65,32 @@ Q_Steps + Q_Extract + A_Err.MakeCCQ.ThenReadArray(err_data) + - A_Steps.MakeCCQ.ThenGetArray2(render_sheet_h, render_sheet_w) + V_UpdateCount.MakeCCQ.ThenGetValue.ThenUse(v->Println($'Updates: {v}')) + + V_ExtractedCount.MakeCCQ.ThenGetValue.ThenUse(v->Println($'Extracted: {v}')) + + A_Sheet.MakeCCQ.ThenGetArray2(render_sheet_h, render_sheet_w) ); if more_output then Println(sw.Elapsed); if err_data[0]<>0 then $'Err at [{err_data[1]},{err_data[2]}]: {CLCodeExecutionError(err_data[0])}'.Println; -$'Updates: {V_UpdateCount.GetValue}'.Println; +var sps := -Settings.scale_pow_shift; {$reference System.Drawing.dll} -var bmp := new System.Drawing.Bitmap(render_sheet_w, render_sheet_h); -for var y := 0 to render_sheet_h-1 do - for var x := 0 to render_sheet_w-1 do +var bmp := new System.Drawing.Bitmap(render_sheet_w shr sps, render_sheet_h shr sps); +for var y := 0 to bmp.Height-1 do + for var x := 0 to bmp.Width-1 do begin - var v := Round( (sheet_data[y,x]/max_iterations)**0.5 * 255); + var v_r := 0.0; + for var dy := 0 to 1 shl sps - 1 do + for var dx := 0 to 1 shl sps - 1 do + begin + var sy := y shl sps + dy; + var sx := x shl sps + dx; + v_r += ((sheet_data[sy,sx] and integer.MaxValue)/max_iterations) ** 0.5; + end; + v_r /= 1 shl (sps*2); + var v := Round(v_r * 255); bmp.SetPixel(x, y, System.Drawing.Color.FromArgb(v,v,v)); end; @@ -107,6 +124,14 @@ System.IO.File.Delete(res_fname); System.IO.File.Move(tmp_fname, res_fname); +$'Used a total of {CLMemoryObserver.Current.CurrentlyUsedAmount} VRAM bytes'.Println; +foreach var bl in blocks do + bl.Dispose; +A_Sheet.Dispose; +V_UpdateCount.Dispose; +V_ExtractedCount.Dispose; +A_Err.Dispose; + if more_output and ('[REDIRECTIOMODE]' not in System.Environment.CommandLine) then begin $'Press enter to exit'.Println; diff --git a/Samples/OpenGLABC/Mandelbrot/FieldTest.td b/Samples/OpenGLABC/Mandelbrot/FieldTest.td index b4f528df..dcc57cf8 100644 --- a/Samples/OpenGLABC/Mandelbrot/FieldTest.td +++ b/Samples/OpenGLABC/Mandelbrot/FieldTest.td @@ -1,24 +1,39 @@  -#ExpErr -Compile errors: -[225,27] Blocks.pas: The type 'PointComponent' does not contain a definition for 'FirstWordToReal' +#Delegates +Blocks.$delegate? = procedure(<>ml: MemoryLayer) +Blocks.$delegate? = procedure(<>ml: MemoryLayer) +Blocks.$delegate? = procedure(e: Exception) +Blocks.$delegate? = procedure(ml: MemoryLayer) +FieldTest.$delegate? = function: boolean +FieldTest.$delegate? = function: boolean +FieldTest.$delegate? = function: boolean +OpenCL.clCreateContextCallback = procedure(errinfo: string; private_info: System.IntPtr; cb: System.UIntPtr; user_data: System.IntPtr) +OpenCL.clEventCallback = procedure(event: cl_event; event_command_status: clCommandExecutionStatus; user_data: System.IntPtr) +OpenCL.clProgramCallback = procedure(program: cl_program; user_data: System.IntPtr) +OpenCLABC.$delegate? = function(ntv: cl_program; var data: clBool; validate: boolean): clErrorCode +OpenCLABC._GetPropValueFunc = function(ntv: cl_program; var data: T): clErrorCode +OpenCLABC_implementation______.EnqFunc = function(prev_res: T; cq: cl_command_queue; ev_l2: EventList): ValueTuple ()> +OpenCLABC_implementation______.InvokeParamsFunc = function(enq_c: integer; o_const: boolean; g: CLTaskGlobalData; enq_evs: DoubleList; par_err_handlers: DoubleList): ValueTuple> #ExpExecOtp Updates: 115036204 -[EventDebug]: 118 event's created -[QueueDebug]: 18 queue's created -[ExecDebug]: 48 cache entries created -[QueueResNil]: 334 -[QueueRes]: 119 - QueueResPtr: 1 - QueueResValDirect: 1 - QueueResValDirect: 1 - QueueResValDirect>: 16 - QueueResValDirect>: 1 - QueueResValDirect>: 1 - QueueResValDirect: 98 +Extracted: 4194304 +final = ErrHandlerInitial#0: ExecuteEnqFunc container +(total 1 handlers) +Used a total of 83886228 VRAM bytes +[EventDebug]: 56 event's created +[QueueDebug]: 1 queue's created +[ExecDebug]: 32 cache entries created +[QueueResNil]: 158 +[QueueRes]: 73 + QueueResPtr: 2 + QueueResValDirect: 1 + QueueResValDirect: 1 + QueueResValDirect>: 1 + QueueResValDirect>: 2 + QueueResValDirect: 66 #ReqModules OpenCLABC diff --git a/Samples/OpenGLABC/Mandelbrot/GL_CL_Context.pas b/Samples/OpenGLABC/Mandelbrot/GL_CL_Context.pas index 3472df59..41c782c3 100644 --- a/Samples/OpenGLABC/Mandelbrot/GL_CL_Context.pas +++ b/Samples/OpenGLABC/Mandelbrot/GL_CL_Context.pas @@ -39,7 +39,7 @@ procedure Init(hdc: gdi_device_context); var cl_c := cl.CreateContext(cl_c_props, cl_dvcs.Length, cl_dvcs, nil, IntPtr.Zero, ec); ec.RaiseIfError; - OpenCLABC.CLContext.Default := new CLContext(cl_c); + OpenCLABC.CLContext.Default := new CLContext(cl_c, false); end; procedure WrapBuffer(gl_b: gl_buffer; var cl_a: CLArray); where T: record; @@ -52,7 +52,7 @@ procedure WrapBuffer(gl_b: gl_buffer; var cl_a: CLArray); where T: record; if cl_a<>nil then cl_a.Dispose; - cl_a := new CLArray(cl_b); + cl_a := new CLArray(cl_b, false, false); end; end. \ No newline at end of file diff --git a/Samples/OpenGLABC/Mandelbrot/MandelbrotSampling.cl b/Samples/OpenGLABC/Mandelbrot/MandelbrotSampling.cl index e58034e6..74e97ba5 100644 --- a/Samples/OpenGLABC/Mandelbrot/MandelbrotSampling.cl +++ b/Samples/OpenGLABC/Mandelbrot/MandelbrotSampling.cl @@ -3,6 +3,8 @@ +//TODO Попробовать менять x и y местами, для более эффективного доступа к памяти + //TODO Minimize code dupe, using this: // - https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_C.html#declaring-and-using-a-block @@ -252,7 +254,7 @@ void point_component_sqr(point_component* x, uint* err) { } -// add bit at "bit_pos" to "x" multiple ("mlt"+0.5) times +// Add bit at "bit_pos" to "x" multiple ("mlt"+0.5) times // x += (1/2)^(bit_pos-Z_INT_BITS+1) * (mlt+0.5) void point_component_add_bit_mlt(point_component* x, uint bit_pos, uint mlt, uint* err) { err_cond(mlt>=BLOCK_W, CCEE_OVERFLOW, err); @@ -287,7 +289,7 @@ void point_component_add_bit_mlt(point_component* x, uint bit_pos, uint mlt, uin typedef struct { - // real and imaginary components of complex number + // Real and imaginary components of complex number point_component r, i; } point_pos; @@ -303,6 +305,7 @@ void point_sqr(point_pos* x, uint* err) { // = (x.r + x.i*i)*(x.r + x.i*i) = // = (sqr(x.r) - sqr(x.i)) + 2*(x.r*x.i)*i + // x_i_sqr = sqr(x.i) point_component x_i_sqr = x->i; point_component_sqr(&x_i_sqr, err); @@ -312,7 +315,7 @@ void point_sqr(point_pos* x, uint* err) { // x.r = sqr(x.r) point_component_sqr(&x->r, err); - // x.r -= sqr(x.i) + // x.r -= x_i_sqr x_i_sqr.words[0] ^= SIGN_BIT_MASK; point_component_add(&x->r, x_i_sqr, err); @@ -339,7 +342,7 @@ bool point_too_big(const point_pos x, uint* err) { // Should be >4 instead of >=4 // But we don't account here for bits in lower words // And exactly =4 is a point, impossible to see in an image - // Also checking for =4 would require adding +1 to Z_INT_BITS + // While checking for =4 would require adding +1 to Z_INT_BITS if (r2.words[0] >= Z_V_4) return true; return false; @@ -356,7 +359,6 @@ typedef struct { // block - current block of BLOCK_W*BLOCK_W points // pos00 - the "c" value of block[0,0] point // point_size_bit_pos - index of bit, which, when set alone, results in size of 1 point -// mipmap_need_update - will write 1's where mipmap would need to get recalculated // step_count - number of steps to try at once // update_count - will add 1 every time next z value is computed // err - will set !0 if there was an error during calculation @@ -364,7 +366,6 @@ kernel void MandelbrotBlockSteps( global point_info* block, constant point_pos* pos00, int point_size_bit_pos, - global uchar* mipmap_need_update, int step_count, global volatile uint* update_count, global uint* err @@ -399,159 +400,35 @@ kernel void MandelbrotBlockSteps( } if (!any_step_done) return; point->last_z = z; - - uint dx = x; - uint dy = y; - uint w = BLOCK_W; - for (uint w = BLOCK_W>>1; w > 0; w >>= 1) { - dx >>= 1; - dy >>= 1; - mipmap_need_update[dx + dy*w] = 1; - mipmap_need_update += w*w; - } - } -kernel void ExtractHighScaleSteps( - global point_info* block, uint scale_pow, - global uchar* result_state, uint result_state_shift, uint result_state_row_len, - global uint* result_steps, uint result_steps_shift, uint result_steps_row_len -) { - //TODO -} - -void ExtractSteps( - global uchar* source_state, uint source_state_item_len, uint source_state_row_len, - global uint* source_steps, uint source_steps_item_len, uint source_steps_row_len, - global uchar* result_state, uint result_state_item_len, uint result_state_row_len, - global uint* result_steps, uint result_steps_item_len, uint result_steps_row_len -) { - uint x = get_global_id(0); - uint y = get_global_id(1); - - uchar state = source_state[x*source_state_item_len + y*source_state_row_len]; - global uchar* p_result_state = &result_state[x*result_state_item_len + y*result_state_row_len]; - if (state < *p_result_state) return; - *p_result_state = state; - - uint steps = source_steps[x*source_steps_item_len + y*source_steps_row_len]; - //TODO Некоторые state не установлены, хотя должны быть - // - Пока что закомментировал проверку в FixMipMapLvl - но это плохо конечно... - // - Впрочем не похоже чтобы влияло на производительность - // - Может тогда нафиг мипмапы вообще? - // - С мипмапами или без, это слишком медленно... - // - Надо таки использовать только блоки текущего уровня, но вместо этого ещё брать данные из предыдущего кадра - steps |= SIGN_BIT_MASK * (uint)state; - global uint* p_result_steps = &result_steps[x*result_steps_item_len + y*result_steps_row_len]; - if (steps < *p_result_steps) return; - *p_result_steps = steps; - -} - -kernel void ExtractRawSteps( +kernel void ExtractSteps( global point_info* block, - global uchar* result_state, uint result_state_shift, uint result_state_row_len, - global uint* result_steps, uint result_steps_shift, uint result_steps_row_len -) { - - global uint* p_i_state = &block->state; - global uchar* p_state = (global uchar*)p_i_state; - global uint* p_steps = &block->steps; - - ExtractSteps( - p_state, sizeof(point_info)/sizeof(uchar), sizeof(point_info)/sizeof(uchar) * BLOCK_W, - p_steps, sizeof(point_info)/sizeof( uint), sizeof(point_info)/sizeof( uint) * BLOCK_W, - &result_state[result_state_shift], 1, result_state_row_len, - &result_steps[result_steps_shift], 1, result_steps_row_len - ); - -} - -kernel void ExtractMipMapSteps( - global uchar* mip_map_state, global uint* mip_map_steps, uint mip_map_shift, - global uchar* result_state, uint result_state_shift, uint result_state_row_len, - global uint* result_steps, uint result_steps_shift, uint result_steps_row_len, - uint mip_map_lvl -) { - - ExtractSteps( - &mip_map_state[mip_map_shift], 1, BLOCK_W>>mip_map_lvl, - &mip_map_steps[mip_map_shift], 1, BLOCK_W>>mip_map_lvl, - &result_state[result_state_shift], 1, result_state_row_len, - &result_steps[result_steps_shift], 1, result_steps_row_len - ); - -} - - - -void FixMipMapLvl( - global uchar* source_state, uint source_state_item_len, uint source_state_row_len, - global uint* source_steps, uint source_steps_item_len, uint source_steps_row_len, - global uchar* result_state, uint result_state_item_len, uint result_state_row_len, - global uint* result_steps, uint result_steps_item_len, uint result_steps_row_len, - global uchar* need_update, uint need_update_row_len + global uint* result_data, uint result_shift, uint result_row_len, + global volatile uint* extracted_count ) { uint x = get_global_id(0); uint y = get_global_id(1); - uchar* p_need_update = &need_update[x + y*need_update_row_len]; - if (!*p_need_update) return; - *p_need_update = false; + bool new_done = block[x + y*BLOCK_W].state != 0; + uint new_steps = block[x + y*BLOCK_W].steps & SIGN_BIT_ANTI_MASK; + uint new_data = (new_done ? SIGN_BIT_MASK : 0) ^ new_steps; - global uchar* p_source_state = &source_state[2*source_state_item_len*x + 2*source_state_row_len*y]; - global uchar* p_result_state = &result_state[1*result_state_item_len*x + 1*result_state_row_len*y]; - *p_result_state = p_source_state[0] & p_source_state[source_state_item_len] & p_source_state[source_state_row_len] & p_source_state[source_state_item_len+source_state_row_len]; + global uint* p_result_data = &result_data[result_shift + x + y*result_row_len]; + uint old_data = *p_result_data; + bool old_done = old_data>>31; + uint old_steps = old_data & SIGN_BIT_ANTI_MASK; - global uint* p_source_steps = &source_steps[2*source_steps_item_len*x + 2*source_steps_row_len*y]; - global uint* p_result_steps = &result_steps[1*result_steps_item_len*x + 1*result_steps_row_len*y]; - //*p_result_steps = max(max(p_source_steps[0], p_source_steps[source_steps_item_len]), max(p_source_steps[source_steps_row_len], p_source_steps[source_steps_item_len+source_steps_row_len])); - *p_result_steps = (p_source_steps[0] + p_source_steps[source_steps_item_len] + p_source_steps[source_steps_row_len] + p_source_steps[source_steps_item_len+source_steps_row_len] + 2) / 4; + // Попробовал быстрее избавится от артефактов на границе белой области, но pow значительно замедляет кадр + //old_steps *= pow(0.9, frames_since_sheet_transfer); -} - -kernel void FixFirstMipMap( - global point_info* block, - global uchar* mip_map_state, - global uint* mip_map_steps, - global uchar* need_update -) { - - global uint* p_i_state = &block->state; - global uchar* p_state = (global uchar*)p_i_state; - global uint* p_steps = &block->steps; - - FixMipMapLvl( - p_state, sizeof(point_info)/sizeof(uchar), sizeof(point_info)/sizeof(uchar) * BLOCK_W, - p_steps, sizeof(point_info)/sizeof( uint), sizeof(point_info)/sizeof( uint) * BLOCK_W, - mip_map_state, 1, BLOCK_W>>1, - mip_map_steps, 1, BLOCK_W>>1, - need_update, BLOCK_W>>1 - ); - -} - -kernel void FixMipMap( - global uchar* mip_map_state, - global uint* mip_map_steps, - global uchar* need_update, - uint next_mip_map_shift, - uint next_mip_map_lvl -) { - - uint prev_w = BLOCK_W >> (next_mip_map_lvl-1); - uint next_w = BLOCK_W >> next_mip_map_lvl; - uint prev_mip_map_shift = next_mip_map_shift - prev_w*prev_w; - - FixMipMapLvl( - &mip_map_state[prev_mip_map_shift], 1, prev_w, - &mip_map_steps[prev_mip_map_shift], 1, prev_w, - &mip_map_state[next_mip_map_shift], 1, next_w, - &mip_map_steps[next_mip_map_shift], 1, next_w, - &need_update[next_mip_map_shift], next_w - ); + //if (new_data > old_data) + if (new_done>=old_done || new_steps>=old_steps) { + atomic_inc(extracted_count); + *p_result_data = new_data; + } } diff --git a/Samples/OpenGLABC/Mandelbrot/MemoryLayering.pas b/Samples/OpenGLABC/Mandelbrot/MemoryLayering.pas new file mode 100644 index 00000000..fba03654 --- /dev/null +++ b/Samples/OpenGLABC/Mandelbrot/MemoryLayering.pas @@ -0,0 +1,195 @@ +unit MemoryLayering; + +{$savepcu false} //TODO + +type + MemoryLayerDataList = sealed class(IEnumerable) + private a := new T[16]; + private i1 := 0; + private i2 := 0; + {$ifdef DEBUG} + private list_v := int64(0); + {$endif DEBUG} + + private procedure IncInd(var ind: integer) := + ind := (ind+1) mod a.Length; + + public function IsEmpty := i1=i2; + + public function PeekOldest: T; + begin + {$ifdef DEBUG} + if IsEmpty then raise new System.InvalidOperationException; + {$endif DEBUG} + Result := a[i1]; + end; + public function RemoveOldest: T; + begin + {$ifdef DEBUG} + if IsEmpty then raise new System.InvalidOperationException; + list_v += 1; + {$endif DEBUG} + + Result := a[i1]; + a[i1] := default(T); + IncInd(i1); + + end; + + public procedure TryRemoveEachSingle(valid_remove_table: Dictionary; on_rem: T->()); + begin + {$ifdef DEBUG} + list_v += 1; + {$endif DEBUG} + + var look_i := i1; + var store_i := i1; + while look_i<>i2 do + begin + var o := a[look_i]; + + var valid_remove: boolean; + var need_remove := valid_remove_table.TryGetValue(o, valid_remove); + {$ifdef DEBUG} + if need_remove and not valid_remove then + $'Could not remove from memory: {o}'.Println; +// raise new System.InvalidOperationException; + {$endif DEBUG} + valid_remove_table[o] := false; + + if need_remove then + on_rem(o) else + begin + if look_i<>store_i then + a[store_i] := o; + IncInd(store_i); + end; + + IncInd(look_i); + end; + + self.i2 := store_i; + while look_i<>store_i do + begin + a[store_i] := default(T); + IncInd(store_i); + end; + + end; + + public procedure AddNewest(o: T); + begin + {$ifdef DEBUG} + if o in self then + raise new System.InvalidOperationException; + list_v += 1; + {$endif DEBUG} + a[i2] := o; + i2 := (i2+1) mod a.Length; + if i1<>i2 then exit; + var n_a := new T[a.Length*2]; + for var d := 0 to a.Length-1 - i1 do + n_a[d] := a[i1+d]; + for var d := 0 to i1-1 do + n_a[a.Length-i1+d] := a[d]; + i1 := 0; + i2 := a.Length; + a := n_a; + end; + + private function Enmr: sequence of T; + begin + var enmr_i := self.i1; + {$ifdef DEBUG} + var org_list_v := list_v; + {$endif DEBUG} + while enmr_i<>i2 do + begin + {$ifdef DEBUG} + if org_list_v <> self.list_v then + raise new System.InvalidOperationException; + {$endif DEBUG} + yield a[enmr_i]; + IncInd(enmr_i); + end; + {$ifdef DEBUG} + while enmr_i<>i1 do + begin + if a[enmr_i] <> default(T) then + raise new System.InvalidOperationException; + IncInd(enmr_i); + end; + {$endif DEBUG} + end; + public function GetEnumerator := Enmr.GetEnumerator; + public function System.Collections.IEnumerable.GetEnumerator: System.Collections.IEnumerator := GetEnumerator; + + end; + + IMemoryLayerData = interface + function GetByteSize: int64; + end; + MemoryLayer = abstract class + where TData: IMemoryLayerData; + private l := new MemoryLayerDataList; + private newly_added := new List; + + private _name: string; + + private allowed_size: int64; + private filled_size := int64(0); + protected function GetRealFilledSize: int64; abstract; + + public constructor(name: string; allowed_size: int64); + begin + self._name := name; + self.allowed_size := allowed_size; + end; + private constructor := raise new System.InvalidOperationException; + + public property Name: string read _name; + public function Enmr := l.Enmr; + + public procedure BeginUpdate(item_new_table: Dictionary); + begin + self.filled_size := GetRealFilledSize; + l.TryRemoveEachSingle(item_new_table, data->(self.filled_size -= data.GetByteSize)); + end; + public procedure EndUpdate; + begin + // First added is the most important + // Store it last it's the newest in l + for var i := newly_added.Count-1 downto 0 do + l.AddNewest(newly_added[i]); + newly_added.Clear; + end; + + public function TryAdd(new_data: TData; on_displaced: TData->()): boolean; + begin + Result := false; + var new_data_sz := new_data.GetByteSize; + var max_filled_size := allowed_size-new_data_sz; + + while filled_size > max_filled_size do + begin + if l.IsEmpty then + begin + on_displaced(new_data); + exit; + end; + var old_data := l.PeekOldest; // First only peek, in case on_displaced throws + var old_data_size := old_data.GetByteSize; + on_displaced(old_data); + if old_data <> l.RemoveOldest then + raise new System.InvalidOperationException; + filled_size -= old_data_size; + end; + + newly_added += new_data; + filled_size += new_data_sz; + Result := true; + end; + + end; + +end. \ No newline at end of file diff --git a/Samples/OpenGLABC/Mandelbrot/MemoryLayering.td b/Samples/OpenGLABC/Mandelbrot/MemoryLayering.td new file mode 100644 index 00000000..ab9afd1f --- /dev/null +++ b/Samples/OpenGLABC/Mandelbrot/MemoryLayering.td @@ -0,0 +1,6 @@ + + + +#SkipTest + + diff --git a/Samples/OpenGLABC/Mandelbrot/PointComponents.pas b/Samples/OpenGLABC/Mandelbrot/PointComponents.pas index 951f7ec2..b0ecfbce 100644 --- a/Samples/OpenGLABC/Mandelbrot/PointComponents.pas +++ b/Samples/OpenGLABC/Mandelbrot/PointComponents.pas @@ -6,6 +6,104 @@ type + PointComponentShift = record + private bits: int64; + private word_ind := 0; + private overflow := false; + + public constructor(word_c, pow: integer; shift: real); + // 64 bits of int64 + // -= 1 bit for sign + // -= 1 bit because shift is normalized to [1;2), the "1.xxx" form, with 1 leading int bit + const max_local_pow = 62; + begin + var sign := Sign(shift); + shift *= sign; + self.bits := sign; + if sign=0 then exit; + + if (shift<1) or (shift>=2) then + begin + var extra_pow_r := Log2(shift); + var extra_pow_i := Floor(extra_pow_r); + pow += extra_pow_i; + shift := 2 ** (extra_pow_r-extra_pow_i); + {$ifdef DEBUG} + if (shift<1) or (shift>=2) then + raise new System.InvalidOperationException; + {$endif DEBUG} + end; + + // With pow=0,shift=1 this is the only bit to set + var head_bit_ind := Settings.z_int_bits-1 - pow; + // In ideal case this is on the word bound or within 12 more bits, + // allowing to save all 52 bits of mantissa of "real" + // But if not, we round word_ind down (div) to prev word + // And then rounding to int64, loosing some precision (max 19/52 bits) + var after_tail_bit_ind := head_bit_ind + max_local_pow; + + var word_ind_raw := after_tail_bit_ind div 32; + var word_ind := word_ind_raw.Clamp(0, word_c-1); + self.word_ind := word_ind; + + // Bit shift by max_local_pow if there was no rounding (div) + // If there was, bit shift a bit less + shift *= 2 ** (max_local_pow-1 - (after_tail_bit_ind - (word_ind+1)*32)); + + {$ifdef DEBUG} + //TODO Слишном сонный чтобы доразобраться + // - Вылетает если pow>32 (очень далеко отдалить) +// if (after_tail_bit_ind>=0) and (shift>int64.MaxValue) then +// raise new System.InvalidOperationException; + if (after_tail_bit_indinteger.MaxValue) downto 0 do + begin + sb.Append(carry shr bit_i and 1); + if bit_i=32 then + sb += '|'; + end; + end; + Result := sb.ToString; + end; + + end; + PointComponent = record(System.IEquatable) private _words: array of cardinal; private const sign_bit_mask: cardinal = 1 shl 31; @@ -26,7 +124,7 @@ PointComponent = record(System.IEquatable) var size := c1.Words.Length; {$ifdef DEBUG} if size <> c2.Words.Length then - raise new System.InvalidOperationException; + raise new System.InvalidOperationException($'{c1} vs {c2}'); {$endif DEBUG} Result := false; for var i := size-1 downto 0 do @@ -46,7 +144,7 @@ PointComponent = record(System.IEquatable) Result := Words[l-1]; if l<>1 then Result := Result xor Words[l-2]; - // GetHashCode должно считать быстро + // GetHashCode должно считаться быстро // 64 нижних бита уже достаточно end; @@ -77,6 +175,25 @@ PointComponent = record(System.IEquatable) Result := sb.ToString; end; + public procedure Save(bw: System.IO.BinaryWriter) := + foreach var x in Words do bw.Write(x); + public static function Load(br: System.IO.BinaryReader; word_count: integer): PointComponent; + begin + Result := new PointComponent(word_count); + for var i := 0 to word_count-1 do + Result.WOrds[i] := br.ReadUInt32; + end; + + public function FirstWordToReal: real; + const body_d = 1 shl (32-z_int_bits); + const body_m = 1/body_d; + begin + Result := Words[0] and sign_bit_anti_mask; + Result *= body_m; + if Words[0] and sign_bit_mask <> 0 then + Result := -Result; + end; + public function WithSize(size: integer): PointComponent; begin {$ifdef DEBUG} @@ -88,50 +205,70 @@ PointComponent = record(System.IEquatable) Result.Words[i] := self.Words[i]; end; - public static function RoundToLowestBits(size, bit_ind: integer; x: real): int64; + private procedure HandleMinusZero(expect_minus_zero: boolean); begin - var d_shift := size*32-1-bit_ind; - {$ifdef DEBUG} - if d_shift>=63 then - raise new System.OverflowException; - {$endif DEBUG} - x *= int64(1) shl d_shift; - Result := Convert.ToInt64(x); + if Words[0] <> sign_bit_mask then exit; + for var i := 1 to Words.Length-1 do + if Words[i]<>0 then exit; + if not expect_minus_zero then + raise new System.InvalidOperationException; + Words[0] += sign_bit_mask; end; - public function AddLowestBits(d: int64): PointComponent; + + public function WithShiftClamp2(shift: PointComponentShift; expect_minus_zero: boolean): PointComponent; + const v2: cardinal = 1 shl (33-Settings.z_int_bits); begin + Result := self; + if shift.bits=0 then exit; var size := self.Words.Length; + Result := new PointComponent(size); + + if shift.overflow then + begin + Result.Words[0] := v2; + if shift.bits<0 then + Result.Words[0] += sign_bit_mask; + // All other words are already 0 on init + exit; + end; + + var d := shift.bits; var self_sign := self.Words[0] and sign_bit_mask; var same_sign := (self_sign<>0) = (d<0); if self_sign<>0 then d := -d; - Result := new PointComponent(size); + for var i := size-1 downto shift.word_ind+1 do + Result.Words[i] := self.Words[i]; + var carry := d; - for var i := size-1 downto 1 do + for var i := shift.word_ind downto 1 do begin carry += self.Words[i]; - {$ifdef DEBUG} - if (d<0) <> (carry<0) then - raise new System.OverflowException; - {$endif DEBUG} Result.Words[i] := carry; carry := carry shr 32; end; begin carry += self.Words[0] and sign_bit_anti_mask; - {$ifdef DEBUG} - if (d<0) <> (carry<0) then - raise new System.OverflowException; - {$endif DEBUG} Result.Words[0] := carry xor self_sign; end; + if Abs(carry) shr (33-Settings.z_int_bits) <> 0 then + begin + var res_sign := self_sign; + if not same_sign then + res_sign := sign_bit_mask - res_sign; + Result.Words[0] := v2 xor res_sign; + for var i := 1 to size-1 do + Result.Words[i] := 0; + exit; + end; + if self_sign <> (Result.Words[0] and sign_bit_mask) then begin - if same_sign or (Abs(carry) shr 32 <> 0) then - raise new System.OverflowException; + if same_sign then + raise new System.InvalidOperationException; var compliment := true; for var i := size-1 downto 1 do begin @@ -141,9 +278,10 @@ PointComponent = record(System.IEquatable) Result.Words[0] := sign_bit_mask xor not Result.Words[0] + Ord(compliment); end; + self.HandleMinusZero(expect_minus_zero); end; - // Round to the block bound + // Round self to the block boundry // PointComponent with only bit at block_sz_bit_ind set is the block side length // - Self is set to the bound between blocks // - block_len_outside is set to value in [0;1), indicating how much of the block was outside of initial bounds @@ -217,69 +355,102 @@ PointComponent = record(System.IEquatable) if Words[0] and sign_bit_mask <> self_sign then raise new System.OverflowException; + HandleMinusZero(round_up); + end; - - public procedure SelfFlipIfMinusZero; + public function WithBlockRound(block_sz_bit_ind: integer; round_up: boolean): PointComponent; begin - if Words[0] <> sign_bit_mask then exit; - for var i := 1 to Words.Length-1 do - if Words[i]<>0 then exit; - Words[0] += sign_bit_mask; + Result._words := self.Words.ToArray; + var block_len_outside: single; + Result.SelfBlockRound(block_sz_bit_ind, round_up, block_len_outside); end; - private function BodyWordAs64At(ind: integer): int64 := - if ind=0 then Words[0] and sign_bit_anti_mask else Words[ind]; + private function BodyWordAs64AtOr0(ind: integer): int64 := + if ind=0 then Words[0] and sign_bit_anti_mask else + if ind c2.Words.Length then - raise new System.InvalidOperationException; - {$endif DEBUG} - var word_inner_pos: integer; // 0..31 var word_ind := System.Math.DivRem(block_sz_bit_ind, 32, word_inner_pos); + var lower_bits_mask: int64 := (1 shl (31-word_inner_pos))-1; var c1_sign := c1.Words[0] and sign_bit_mask; var c2_sign := c2.Words[0] and sign_bit_mask; - {$ifdef DEBUG} - if (c1_sign=0) and (c2_sign<>0) then - raise new System.InvalidOperationException; - {$endif DEBUG} var same_sign := c1_sign=c2_sign; - var lower_bits_mask: cardinal := (1 shl (31-word_inner_pos))-1; + for var i := 0 to word_ind-2 do + begin + var word1 := c1.BodyWordAs64AtOr0(i); + var word2 := c2.BodyWordAs64AtOr0(i); + if word1<>word2 then + raise new System.OverflowException; + if not same_sign and (word1<>0) then + raise new System.OverflowException; + end; + + {$ifdef DEBUG} + for var i := word_ind+1 to c1.Words.Length-1 do + if c1.Words[i] <> 0 then raise new System.InvalidOperationException; + for var i := word_ind+1 to c2.Words.Length-1 do + if c2.Words[i] <> 0 then raise new System.InvalidOperationException; + {$endif DEBUG} - var diff := int64(0); + var total_diff := int64(0); if word_ind<>0 then begin - var prev_word1 := c1.BodyWordAs64At(word_ind-1); - var prev_word2 := c2.BodyWordAs64At(word_ind-1); - diff := lower_bits_mask and if same_sign then prev_word2-prev_word1 else prev_word2+prev_word1; + var word1 := c1.BodyWordAs64AtOr0(word_ind-1); + var word2 := c2.BodyWordAs64AtOr0(word_ind-1); + + var diff := if same_sign then word2-word1 else word2+word1; + var diff_sign := Sign(diff); + diff *= diff_sign; + diff := diff and lower_bits_mask; diff := diff shl (1+word_inner_pos); + diff *= diff_sign; + total_diff += diff; + + word1 := word1 and not lower_bits_mask; + word2 := word2 and not lower_bits_mask; + if word1<>word2 then + raise new System.OverflowException; + if not same_sign and (word1<>0) then + raise new System.OverflowException; + + end; + begin + var word1 := c1.BodyWordAs64AtOr0(word_ind); + var word2 := c2.BodyWordAs64AtOr0(word_ind); + + var diff := if same_sign then word2-word1 else word2+word1; + var diff_sign := Sign(diff); + diff *= diff_sign; + diff := diff shr (31-word_inner_pos); + diff *= diff_sign; + total_diff += diff; + {$ifdef DEBUG} - if (prev_word1 and not lower_bits_mask) <> (prev_word2 and not lower_bits_mask) then - raise new System.InvalidOperationException; + if word1 and lower_bits_mask <> 0 then raise new System.InvalidOperationException; + if word2 and lower_bits_mask <> 0 then raise new System.InvalidOperationException; {$endif DEBUG} + end; - var curr_word1 := c1.BodyWordAs64At(word_ind); - var curr_word2 := c2.BodyWordAs64At(word_ind); - {$ifdef DEBUG} - if curr_word1 and lower_bits_mask <> 0 then raise new System.InvalidOperationException; - if curr_word2 and lower_bits_mask <> 0 then raise new System.InvalidOperationException; - {$endif DEBUG} - diff += (if same_sign then curr_word2-curr_word1 else curr_word2+curr_word1) shr (31-word_inner_pos); - Result := diff; + total_diff := if c2_sign=0 then +total_diff else -total_diff; + Result := total_diff; {$ifdef DEBUG} - if int64(Result) <> diff then + if Result <> total_diff then + begin +// BlocksCount(c1, c2, block_sz_bit_ind); + //TODO "diff := lower_bits_mask and ..." даёт неправильное значение для отрицательных diff + // - shr/shl тоже не расчитаны на этот случай + // - Сейчас падает если приблизить на scale_pow=-30 + // - Тестил на мини-мандельбротах в левой, легко-просчитываемой части raise new System.OverflowException; - for var i := 0 to word_ind-2 do - if c1.BodyWordAs64At(i) <> c2.BodyWordAs64At(i) then - raise new System.OverflowException; + end; {$endif DEBUG} end; @@ -290,7 +461,6 @@ PointComponent = record(System.IEquatable) var word_inner_pos: integer; // 0..31 var word_ind := System.Math.DivRem(block_sz_bit_ind, 32, word_inner_pos); - var curr_block_sz_bit: cardinal := 1 shl (31-word_inner_pos); {$ifdef DEBUG} @@ -302,19 +472,19 @@ PointComponent = record(System.IEquatable) {$endif DEBUG} var self_sign := Words[0] and sign_bit_mask; + var i := word_ind; + if self_sign=0 then begin - var i := word_ind; Result.Words[i] := self.Words[i] + curr_block_sz_bit; while (Result.Words[i]=0) and (i<>0) do begin i -= 1; - Result.Words[word_ind] := self.Words[word_ind] + 1; + Result.Words[i] := self.Words[i] + 1; end; end else // Substract instead of adding begin - var i := word_ind; Result.Words[i] := self.Words[i] - curr_block_sz_bit; while (Result.Words[i]>self.Words[i]) and (i<>0) do begin @@ -323,28 +493,72 @@ PointComponent = record(System.IEquatable) end; end; + while i<>0 do + begin + i -= 1; + Result.Words[i] := self.Words[i]; + end; + if Result.Words[0] and sign_bit_mask <> self_sign then raise new System.OverflowException; - Result.SelfFlipIfMinusZero; + Result.HandleMinusZero(true); + end; + + // [c1,c2) range of points + public static function Range(c1, c2: PointComponent; block_sz_bit_ind: integer): array of PointComponent; + begin + Result := new PointComponent[BlocksCount(c1,c2,block_sz_bit_ind)]; + var x := c1; + Result[0] := x; + for var i := 1 to Result.Length-1 do + begin + x := x.MakeNextBlockBound(block_sz_bit_ind); + Result[i] := x; + end; + {$ifdef DEBUG} + if x.MakeNextBlockBound(block_sz_bit_ind)<>c2 then + raise new System.InvalidOperationException; + {$endif DEBUG} end; end; - PointPos = record + PointPos = record(System.IEquatable) + // Real and imaginary components of complex number public r,i: PointComponent; public constructor(r,i: PointComponent) := (self.r,self.i) := (r,i); + public static function operator=(p1, p2: PointPos) := (p1.r=p2.r) and (p1.i=p2.i); + public static function operator<>(p1, p2: PointPos) := not(p1=p2); + public function Equals(other: PointPos) := self=other; + public function Equals(o: object): boolean; override := + (o is PointPos(var other)) and Equals(other); + public function GetHashCode: integer; override := + r.GetHashCode xor i.GetHashCode*668265263; + + public function ToString: string; override := $'({r}; {i})'; + public property Size: integer read r.Words.Length; public function WithSize(size: integer) := new PointPos( r.WithSize(size), i.WithSize(size) ); - public function AddLowestBits(dr,di: int64) := new PointPos( - r.AddLowestBits(dr), - i.AddLowestBits(di) + public procedure Save(bw: System.IO.BinaryWriter); + begin + r.Save(bw); + i.Save(bw); + end; + public static function Load(br: System.IO.BinaryReader; word_count: integer) := new PointPos( + PointComponent.Load(br, word_count), + PointComponent.Load(br, word_count) + ); + + public function WithShiftClamp2(dr, di: PointComponentShift; expect_minus_zero: boolean) := new PointPos( + self.r.WithShiftClamp2(dr, expect_minus_zero), + self.i.WithShiftClamp2(di, expect_minus_zero) ); public procedure SelfBlockRound(block_sz_bit_ind: integer; round_up: boolean; var skip_r: single; var skip_i: single); @@ -353,12 +567,6 @@ PointPos = record self.i.SelfBlockRound(block_sz_bit_ind, round_up, skip_i); end; - public procedure SelfFlipIfMinusZero; - begin - self.r.SelfFlipIfMinusZero; - self.i.SelfFlipIfMinusZero; - end; - end; end. \ No newline at end of file diff --git a/Samples/OpenGLABC/Mandelbrot/Settings.pas b/Samples/OpenGLABC/Mandelbrot/Settings.pas index c1d1319c..0ec4154a 100644 --- a/Samples/OpenGLABC/Mandelbrot/Settings.pas +++ b/Samples/OpenGLABC/Mandelbrot/Settings.pas @@ -2,71 +2,92 @@ {$savepcu false} //TODO -// Всё пространство разделено на блоки этой ширины и высоты -// Если камеру приближают - просчитываются 4 более блока меньшего масштаба: -// Cтолько же точек, но эти точки упакованы ближе друг к другу -// Чем больше блоки - тем быстрее их считает и тем больше использует памяти +// Я разделяю в этой программе 3 вида пространства: +// - Графическое: 1 пиксель экрана это 1х1 графического пространства +// - Логическое: Весь рисунок Mandelbrot помещается в -2..+2 логического пространства (горизонтально и вертикально), независимо от графического масштаба +// - Пространство блока/точек: Каждый блок любого масштаба хранит block_w*block_w точек, каждая из которых просчитывается отдельно + +// Если в блоках слишком мало точек, получается много отдельных обращений к GPU +// А если слишном много - значительная часть просчитываемых точек +// (для которых надо VRAM и время GPU) может быть за пределами экрана const block_w_pow = 9; const block_w = 1 shl block_w_pow; // 512 -// Одновременно может просчитываться max_GPU_inst блоков -// Каждый из видимых блоков нужного масштаба будет по очереди -// пытаться сделать ещё 1 шаг рекуррентной функции "z_next(z) = z*z + c" для каждой точки -// Где "z" это предыдущее значение (изначально 0), а "c" это координата точки -const max_GPU_inst = 5; -// Сколько надо прибавить к масштабу камеры чтобы получить текущий масштаб просчитываемых блоков -// Масштаб это степень двойки -// -1 значит на каждый пиксель экрана придётся хотя бы 4 (2х2) просчитанные точки -const scale_shift = -1; // <=0 -// Блок максимального размера это 2х2 -// Таким образом вся просчитываемая область разбита на минимум 4 блока, по 1 на угол -// При приближении камеры каждый из этих угловых блоков будет далее разбивать на 4 меньших блока -const max_block_scale = 1; // Не менять - эта константа много где неявно задана -// Отрисованные блоки хранит в VRAM (памяти GPU) -const max_VRAM = 1610612736; // 1.5 GB +// Просчитываемые в любой момент блоки хранит в VRAM (личной памяти GPU) +// Для 1080p надо чуть больше 1.25 GB при поверхностном приближении +// (чем глубже - тем больше точность расчётов и тем больше надо памяти) +// Но если VRAM у системы мало (к примеру, браузер много скушал), +// драйвера могут крашнутся при слишком большом использовании VRAM данной программой +// Смотрите в диспетчер задач чтобы выставить сколько вы можете себе позволить +const max_VRAM = 838860800; // 800 MB // Если VRAM заканчивается - старые блоки отправляет в RAM (в обычную оперативную память) -const max_RAM = 4294967296; // 4 GB -// Если в RAM есть место, но отрисованный блок был там долго - тоже удалить -// (иначе его отправит в файл подкачки, а потом, при подгрузке назад - будет лагать) -const RAM_life_span_seconds = 15*60; // 15 минут +// Пока что 0, что значит старые блоки в VRAM уничтожаются как только перестают помещаться +const max_RAM = 0;// 4294967296; // 4 GB +// А если заканчивается и RAM - самые старые блоки отправляет на диск +// Если у вас медленный диск, есть смысл оставить тут 0 +// Но если включать - надо сразу установить max_RAM>=max_VRAM +// Потому что блоки не могут прыгнуть сразу из VRAM на диск или назад +const max_drive_space = 0;// 10737418240; // 10 GB + +// Сторона блока в логическом пространстве это всегда степень двойки (обычно отрицательная степень) +// Если scale_pow_shift=0, размер блоков в логическом пространстве будет выбирать так, +// чтобы на каждый пиксель экрана приходилась хотя бы область из 1x1 точек в соответствующем блоке +// Когда отдаляют достаточно чтобы в пиксель поместилась область из 2x2 точек, масштаб блоков переключается на следующий +// Если scale_pow_shift=-1, на каждый пиксель придётся хотя бы 2x2 точек, и всегда меньше 4x4 +// Значение -1 значительно улучшает визуал, но уже требует в 4 раза больше памяти и производительности +// Желательно дальше не понижать без очень мощного компьютера +// Иначе просчитывать будет только блоки в центре экрана (если закончится VRAM) +const scale_pow_shift = -1; // <=0 +// Блок максимального размера занимает в логическом пространстве область 2х2 +// Таким образом вся просчитываемая область (4х4 вокруг точки 0;0) разбита на минимум 4 блока, по 1 на угол +// При приближении камеры каждый из этих угловых блоков будет далее разбивать на 4 меньших блока +// Не менять, эта константа много где неявно задана, самим алгоритмом +const max_block_scale_pow = 1; -// Чтобы для каждого кадра не пересчитывать -// среднее арифметическое всех точек под каждым пикселем, -// Каждому блоку создаст мипмапы таких размеров: -// -// (block_w/2) * (block_w/2) -// (block_w/4) * (block_w/4) -// ... -// 2 * 2 -// 1 * 1 -const mipmap_total_size = block_w*block_w div 3; +// Каждая точка в каждом блоке хранит текущее состояние +// (z и номер шага) рекуррентной функции "z_next(z) = z*z + c" +// Где "z" это предыдущее значение (изначально 0), а "c" это "x+y*i" +// (комплексное число представляющее координаты точки в логическом пространстве) +// На экране рисует только кол-во шагов этой функции (ещё_считает=белый; 0 шагов=чёрный; остальное радугой) +// А в отдельном от графики потоке выполнения (класс Blocks.BlockUpdater) +// циклически берёт блоки текущего масштаба и просчитывает их на несколько шагов вперёд +// Максимум за 1 итерацию обработки может выполнить max_steps_at_once шагов для всех точек +// Это ограничение на всякий случай, чтобы Blocks.BlockUpdater не застряло на несколько секунд +// (от такого застревания может даже полностью перезапустится графический драйвер) +// Но это и так не должно происходить, потому что количество шагов и так сбрасывается к 1 при смене текущих блоков +const max_steps_at_once = 1024; +// Все блоки можно обрабатывать по-очереди или параллельно +// Чтобы достичь большОй параллельности - надо выделить много ресурсов в +// виде OpenCL.cl_command_queue (личные данные каждого потока выполнения GPU) +// Но если обрабатывать все блоки один за другим, GPU будет повторно оставаться +// без работы на короткие промежутки времени, между обработками блоков +// Поэтому одновременно обрабатывать будет не больше чем max_parallel_blocks блоков +const max_parallel_blocks = 2; +// Чем больше шагов - тем меньшая доля времени будет потрачена на синхронизацию и т.п. между обработками +// Но если обработка блоков использует GPU слишком эффективно, +// она заберёт все ресурсы у системы и графика начнёт лагать +// Поэтому кол-во шагов в 1 обработке меняет в диапазоне 1..max_steps_at_once, +// так чтобы 1 обработка заменяла примерно столько секунд: +const target_step_time_seconds = 0.050; +//TODO Может делать несколько запусков kernel-а подряд, чтобы у потока графики было больше шансов получить нужное ему время GPU... -// Последним шагом считаеться шаг где |z_next|>2 -// На шаге=0 z=0, а значит для точек |c|>2 на шаг 1 уже не переключится -// На шаге>0 |z|<=2, иначе на этот шаг не переключилось бы -// Тогда |z*z+c| максимум будет 2*2+2=6 +// Комплексное число z на каждом шаге представляет как 2 компоненты (PointComponents.pas и point_component в MandelbrotSampling.cl) +// Каждая компонента это число число с фиксированной точкой (fixed-point number) +// Это всё число разделено на несколько слов типа UInt32 (чтобы обрабатывать сразу кучу битов каждой операцией процессора) +// Кол-во слов выбирается в CameraDef.CameraPos.GetWordCount, исходя из нужного кол-ва бит для текущего масштаба +// +// Последним шагом считается шаг где |z_next|>2 +// Модуль комплексной точки "x+y*i" это модуль (длина) вектора (x;y), то есть "Sqrt(x.Sqr+y.Sqr)" +// На шаге=0: z=0, а значит для точек |c|>2 на шаг 1 уже не переключится +// На шаге>0: |z|<=2, иначе на этот шаг не переключилось бы +// Тогда |z*z+c| будет максимум 2*2+2=6 // Таким образом чтобы представить целую часть результата вычисления надо максимум 3 бита -// И затем ещё +1 бит для знака (+ или -) +// И затем ещё +1 бит в самом начале для знака (+ или -) const z_int_bits = 4; // 0..31 -// А точность (кол-во бит) после точки будет масштаб+block_w_pow+z_extra_precision_bits -// 16 доп. бит значит что для заметной ошибки надо минимум столько операций: +// А точность (кол-во бит) после точки будет -масштаб_точки+z_extra_precision_bits +// 16 дополнительных бит значит что для заметной ошибки надо минимум столько операций: // LogN(1+2**-16, 1.5) ~= 26572 +// Каждый шаг выполняет несколько операций, но +16 битов это всё равно очень много const z_extra_precision_bits = 16; // >=0 -// Таким образом кол-во байт на 1 точку: -// > 4 (состояние) + 4 (кол-во шагов) + z_component_size * 2 (действительная и мнимая компоненты "z") -// Где компонента z будет записью из нескольких cardinal (32-битных беззнаковых целых) -// > z_component_size = Trunc( (z_int_bits + -point_scale + z_extra_precision_bits) / 32 )*4 -// Значит объём VRAM на 1 блок: -// > VRAM = (block_w*block_w) * (8 + 2*z_component_size) -// > VRAM/(block_w*block_w) = 8 + 2*z_component_size -// > VRAM/(block_w*block_w) - 8 = 2*z_component_size -// > VRAM/(block_w*block_w)/2 - 4 = z_component_size -const max_z_component_size = max_VRAM/(block_w*block_w)/2 - 4; // 3068 -// Минимальный уровень масштаба, на котором максимум можно нарисовать 1 блок -// На самом деле, если у вас на экране одновременно показывает "n" блоков -// То максимальный масштаб без глюков будет max_z_scale_bits/n -// Но до этого масштаба всё равно будет сложно до-скролить -const max_z_scale_bits_raw = max_z_component_size/4*32 - z_int_bits - block_w_pow - z_extra_precision_bits; // 24515 -const max_z_scale_bits_rounded = Trunc(max_z_component_size/4)*32 - z_int_bits - block_w_pow - z_extra_precision_bits; // 24515 end. \ No newline at end of file diff --git a/Samples/OpenGLABC/Mandelbrot/Shaders/Box.geom b/Samples/OpenGLABC/Mandelbrot/Shaders/Box.geom index b6c61c0c..0d46320d 100644 --- a/Samples/OpenGLABC/Mandelbrot/Shaders/Box.geom +++ b/Samples/OpenGLABC/Mandelbrot/Shaders/Box.geom @@ -5,10 +5,10 @@ uniform float view_skip_x_last, view_skip_y_last; layout(points) in; layout(triangle_strip, max_vertices = 4) out; -out vec2 logic_pos; +out vec2 screen_pos; void SendVertex(float coord1, float dx, float coord2, float dy) { - logic_pos = vec2(coord1, coord2); + screen_pos = vec2(coord1, coord2); gl_Position = vec4(coord1+dx, coord2+dy, 0, 1); EmitVertex(); } diff --git a/Samples/OpenGLABC/Mandelbrot/Shaders/Rainbow.frag b/Samples/OpenGLABC/Mandelbrot/Shaders/Rainbow.frag index fbd2b1b7..e8964099 100644 --- a/Samples/OpenGLABC/Mandelbrot/Shaders/Rainbow.frag +++ b/Samples/OpenGLABC/Mandelbrot/Shaders/Rainbow.frag @@ -1,6 +1,6 @@ #version 460 core -noperspective in vec2 logic_pos; +noperspective in vec2 screen_pos; uniform float sheet_skip_x_frst, sheet_skip_y_frst; uniform float sheet_skip_x_last, sheet_skip_y_last; @@ -17,17 +17,19 @@ layout(binding = 1) buffer temp_otp { } temp; /**/ -out vec3 color; -// All components are in the range [0…1], including hue. -vec3 hsv2rgb(float hue) + +// All 3 components are in the [0…1) range +vec3 hsv2rgb(float hue, float saturation, float value) { - vec3 c = {hue, 0.75, 0.5}; + vec3 c = {hue, saturation, value}; vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } + + struct point_state { bool done; uint steps; @@ -42,11 +44,16 @@ point_state PointAt(ivec2 ind) { ); } + + +out vec3 color_out; + const int n_pts_avg = 5; +const int avg_pts_count = n_pts_avg*n_pts_avg; void main() { - // logic_pos is -1..+1 - vec2 sheet_pos_f = (logic_pos+1f)/2f; + // screen_pos is -1..+1 + vec2 sheet_pos_f = (screen_pos+1f)/2f; sheet_pos_f *= vec2(1f-sheet_skip_x_frst-sheet_skip_x_last, 1f-sheet_skip_y_frst-sheet_skip_y_last); sheet_pos_f += vec2(sheet_skip_x_frst, sheet_skip_y_frst); @@ -54,28 +61,49 @@ void main() { sheet_pos_f *= sheet_size-n_pts_avg; ivec2 sheet_pos_i = ivec2(round(sheet_pos_f)); - float done = 0; - float steps = 0; + //TODO More priority to points at the center? + //TODO That can help, but still doesn't fix cases where colors change too quickly and turn into a mess + // - First actually try how avg colors look, maybe it's not that bad... + // - Well, now I avg all 3 options. Now it's an extreamly expensive anti-aliasing + // - Much better to just draw +4 points in each coordinate and only then avg + // - Find how it's usually done in OpenGL + // - Or maybe make a pixel for every sheet point use that as a texture??? A waste otherwise + + //float done = 0; + //float steps = 0; + vec3 color = vec3(0); for (int dx = 0; dx>scale_change) + ((y+old_shift_y)>>scale_change)*old_row_len]; + global uint* new_ptr = &new_data[new_shift + x + y*new_row_len]; + + *new_ptr = *old_ptr; +} + +// map multiple points of new_data to single point of old_data +// old_shift and old_row_len count in old_data points +kernel void DownScaleSheet( + global uint* old_data, uint old_shift, uint old_row_len, + global uint* new_data, uint new_shift, uint new_row_len, + int scale_change +) { + uint x = get_global_id(0); + uint y = get_global_id(1); + + uint old_scale_shift = 1u << (scale_change-1); + + constant static uint SIGN_BIT_MASK = 1u << 31; + constant static uint SIGN_BIT_ANTI_MASK = ~SIGN_BIT_MASK; + constant static uint avg_c = 2; + constant static uint avg_area = avg_c*avg_c; + + uint done_count = avg_area/2; + uint steps_sum = avg_area/2; + + for (uint dy=0; dy> 31; + steps_sum += old_v & SIGN_BIT_ANTI_MASK; + } + + new_data[new_shift + x + y*new_row_len] = (done_count/avg_area*SIGN_BIT_MASK) ^ (steps_sum/avg_area); +} + +