Друзья! Хорошие новости! 🔥 Я реализовал параллелизм: API для вызова пользовательских функций в многопоточном режиме 🤩 . Т.е. параллельный for, одномерный и двумерный. В частности, API будет вызывать вашу функцию для каждой строки экрана, для группы из нескольких пикселей (например, для 64-х) или для каждого пикселя в отдельности.
Самый медленный вариант (как можно догадаться) — использование попиксельных вызовов, т.к. он связан в большим кол-вом накладных расходов на вычисления и вызов функции. На моём компьютере такой режим позволяет достигать 1537 fps для FullHD (против 1112 fps для однопоточного режима) без учёта вывода на экран. Но это для отрисовки банального XOR-паттерна. Для таких простых операций использовать попиксельный режим неэффективно. Построчный или 64-пиксельный работает гораздо быстрее (до 6521 fps). Однако, если добавить в код холостой цикл на 1000 итераций, разница в попиксельном режиме будет куда существеннее: 32,34 fps в многопоточном коде против 2,21 fps в однопоточном (ускорение в 14,639 раз). Т.е. для сложных алгоритмов (реймаршинг, фракталы) такой режим можно использовать.
Первый алгоритм for, который я сделал 2 дня назад (в таблице он обозначен "первая реализация"), работал ощутимо медленнее, т.к. был сделан совсем по-простому. Сегодня я его оптимизировал в 2.7-100 раз для однопиксельных режимов и до 4.5 раз для 64-пиксельных режимов работы. Но это, конечно, далеко не финальная оптимизация, там есть ещё над чем работать😎
Так что друзья, можно сказать, что идея себя оправдала, параллелизм позволяет добиться существенного ускорения (у меня вышло до 16,7 раз для 20 потоков, из которых реальных ядер 12, остальные 8 — гипертреды)🥳
Больше подробностей о тестровании скорости — в таблице (смотрите верхнюю половину, т.к. он учитывает только вычислительную часть, без вывода на экран, который вносит существенную лепту в скорость работы).
Самый медленный вариант (как можно догадаться) — использование попиксельных вызовов, т.к. он связан в большим кол-вом накладных расходов на вычисления и вызов функции. На моём компьютере такой режим позволяет достигать 1537 fps для FullHD (против 1112 fps для однопоточного режима) без учёта вывода на экран. Но это для отрисовки банального XOR-паттерна. Для таких простых операций использовать попиксельный режим неэффективно. Построчный или 64-пиксельный работает гораздо быстрее (до 6521 fps). Однако, если добавить в код холостой цикл на 1000 итераций, разница в попиксельном режиме будет куда существеннее: 32,34 fps в многопоточном коде против 2,21 fps в однопоточном (ускорение в 14,639 раз). Т.е. для сложных алгоритмов (реймаршинг, фракталы) такой режим можно использовать.
Первый алгоритм for, который я сделал 2 дня назад (в таблице он обозначен "первая реализация"), работал ощутимо медленнее, т.к. был сделан совсем по-простому. Сегодня я его оптимизировал в 2.7-100 раз для однопиксельных режимов и до 4.5 раз для 64-пиксельных режимов работы. Но это, конечно, далеко не финальная оптимизация, там есть ещё над чем работать
Так что друзья, можно сказать, что идея себя оправдала, параллелизм позволяет добиться существенного ускорения (у меня вышло до 16,7 раз для 20 потоков, из которых реальных ядер 12, остальные 8 — гипертреды)
Больше подробностей о тестровании скорости — в таблице (смотрите верхнюю половину, т.к. он учитывает только вычислительную часть, без вывода на экран, который вносит существенную лепту в скорость работы).
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Ребята, кто может протестить скорость на своих машинах? Я пришлю файлы, нужно будет запустить BAT-ник и подождать несколько минут. А потом прислать мне результат (TXT-шники).
Поскольку многопоточность работает (и как мы видим по тестам, существенно ускоряет код), уже вполне можно писать интры, причём в хайрез и с использованием SIMD 😉
А я продолжаю думать о названии.
1️⃣ Помимо Para\\e/ и...
2️⃣ loom (главное, чтоб не было стойких ассоциаций с Java) появились ещё идеи.
3️⃣ artefact (что-то типа Advanced RunTime Environment For Art Code Threads, хорошо шарящие в инглише поправьте, плиз, если звучит странно).
4️⃣ Идея о F!RE трансформировалась в firebird (тем более, что я хочу называть версии видами птиц). Перевод с элементами фана: Fast Intro Runtime Environment (Brilliant Idea of Russian Developer)😄 . Смущают только возможные ассоциации с БД и, не дай бог, торпедой или ещё чем-то.
5️⃣ Ну и ещё интересное, на мой взгляд, имя — Para||ex (Parallel Environment of eXecution(?)). На Parallax аббревиатуру не придумал, но Parallex более оригинальное название.
6️⃣ Банальное: remi (Runtime Environment for Multithreaded Intros).
Что думаете? Об этих названиях. Ну и может, тоже идей подкинете?🤔
А я продолжаю думать о названии.
1️⃣ Помимо Para\\e/ и...
2️⃣ loom (главное, чтоб не было стойких ассоциаций с Java) появились ещё идеи.
3️⃣ artefact (что-то типа Advanced RunTime Environment For Art Code Threads, хорошо шарящие в инглише поправьте, плиз, если звучит странно).
4️⃣ Идея о F!RE трансформировалась в firebird (тем более, что я хочу называть версии видами птиц). Перевод с элементами фана: Fast Intro Runtime Environment (Brilliant Idea of Russian Developer)
5️⃣ Ну и ещё интересное, на мой взгляд, имя — Para||ex (Parallel Environment of eXecution(?)). На Parallax аббревиатуру не придумал, но Parallex более оригинальное название.
6️⃣ Банальное: remi (Runtime Environment for Multithreaded Intros).
Что думаете? Об этих названиях. Ну и может, тоже идей подкинете?
Please open Telegram to view this post
VIEW IN TELEGRAM
⏸ Название определено (думаю, все уже заметили), больше мучить этим вас не буду 😃
Ссылки в телеге изменены, но старые тоже работают в режиме переадресации.
⏸ Хотел написать интру для Lovebyte 2023 под Para||elix, но не успел. Жаль, конечно. Но с другой стороны, есть возможность доработать и потом показать более полноценную версию.
⏸ Заметил, что legacy FPU работает пипец как медленно в сравнении со скалярным SSE/AVX (с векторным, думаю, будет такая же картина). По крайней мере, на Alder Lake. На Sandy Bridge замечал разницу, но не настолько большую. Вообще, странно, Instruction Tables (Agner Fog) показывает незначительные различия. В общем, чтобы говорить об этом более предметно, нужно потестить более обстоятельно. И на AMD в том числе.
Плюс вылезли вопросы сохранения FPU и SIMD-регистров (ибо некоторые API типа
⏸ Также задумался о 64-битной версии (на будущее). Раньше, конечно, тоже думал, но эта мысль казалось не столь интересной. По сути, интры получаются не шибко больше в размере, зато кол-во регистров удваивается (как общего назначения, так и SIMD). Но сложнее организовать API-вызовы по похожей схеме (т.е.
⏸ Исправил баг: при использовании многопоточных функций интра висла через несколько секунд или минут. Причина была неочевидна, но чисто интуитивно решил положить
⏸ При старте интры, а также при вызове функций потоков (к примеру, для каждого пикселя) в регистр
Но, наверное, я расширю эту область до 64КБ (18МБ для 256+32 потоков — не так уж и много). Может, урежу стек (для каждого потока) до 512КБ (вместо дефолтного 1МБ) или даже до 256КБ (к чему такой большой?)🤔
⏸ Поменял значение регистра😀
Объясняю суть. При таком значении
✅ Первые 2 варианта (1x, 2x) можно зарезервировать для API (таким образом, кол-во функций увеличивается с 255 до 511 — с учётом возможных расширений, пусть будет).
✅ Следующий (3x) — для сервисных данных (инфа о процессоре, событиях (юзер нажал Esc), опциях, настройках видео и аудио, коэффициенты для приведения координат к диапазону 0..1, FP-координаты центра и т.д.). Причём, если раньше под них выделялось 128 байт, то сейчас можно 256 (128 оказалось мало).
✅ Ещё один (5x) — для хранения констант (0, 0.5, 1, π, 2π, e, √2 и т.д., включая векторные по 16 float'ов для SIMD вплоть до AVX-512).
✅ И последний
Ссылки в телеге изменены, но старые тоже работают в режиме переадресации.
⏸ Хотел написать интру для Lovebyte 2023 под Para||elix, но не успел. Жаль, конечно. Но с другой стороны, есть возможность доработать и потом показать более полноценную версию.
⏸ Заметил, что legacy FPU работает пипец как медленно в сравнении со скалярным SSE/AVX (с векторным, думаю, будет такая же картина). По крайней мере, на Alder Lake. На Sandy Bridge замечал разницу, но не настолько большую. Вообще, странно, Instruction Tables (Agner Fog) показывает незначительные различия. В общем, чтобы говорить об этом более предметно, нужно потестить более обстоятельно. И на AMD в том числе.
Плюс вылезли вопросы сохранения FPU и SIMD-регистров (ибо некоторые API типа
@GetDurationFloat
— получение времени с момента старта, могут их портить).⏸ Также задумался о 64-битной версии (на будущее). Раньше, конечно, тоже думал, но эта мысль казалось не столь интересной. По сути, интры получаются не шибко больше в размере, зато кол-во регистров удваивается (как общего назначения, так и SIMD). Но сложнее организовать API-вызовы по похожей схеме (т.е.
call [ebp+fn]
, где fn — это любое число, сделать уже не получится). В общем, поживём — увидим. До этого ещё далеко.⏸ Исправил баг: при использовании многопоточных функций интра висла через несколько секунд или минут. Причина была неочевидна, но чисто интуитивно решил положить
notify_all
внутрь залоченного мьютекса (раньше он был снаружи) — баг пропал :)⏸ При старте интры, а также при вызове функций потоков (к примеру, для каждого пикселя) в регистр
ebx
теперь записывается выровненный по границе 256 байт адрес 4КБ буфера в TLS, т.е. потоко-локальной области. Это оказалось офигенно удобно. Т.е. вы можете хранить локальные переменные не только в стеке, но и в TLS, используя адресацию вида [ebx+n]
. Потокобезопасно! Скажу по секрету, что можно адресовать не только в плюс, но и в минус (до 128 байт), т.е. у вас есть не 4КБ, а даже 4096+128 байт :)Но, наверное, я расширю эту область до 64КБ (18МБ для 256+32 потоков — не так уж и много). Может, урежу стек (для каждого потока) до 512КБ (вместо дефолтного 1МБ) или даже до 256КБ (к чему такой большой?)
⏸ Поменял значение регистра
ebp
при старте интры. На нём завязана таблица API, а также зона сервисных данных. Раньше было ebp = 0x08000000
(напомню, что код грузится по адресу 0x10000000
(ebp*2
), фрейм буфер — 0x40000000
(ebp*8
)). Сейчас сделал ebp = 0x01C71C80
. Это же гораздо лучше, согласны? Объясняю суть. При таком значении
ebp
можно использовать не только [ebp]
, но и [ebp+ebp]
, [ebp+ebp*2]
, [ebp+ebp*4]
, и [ebp+ebp*8]
, т.е. целых 5 вариантов почти за ту же цену (но первый всё же дешевле на байт) :)[ebp+ebp*8]
(9x) — это адресация 0x10000080
(теперь понятно почему 0x01C71C80
?) Т.е. добавляя байтовую константу (-128..127) можно иметь доступ к первым 256 байтам кода (а сделав, скажем, lea edx,[ebp+ebp*8+127]
— к смещениям 127..382 через edx
). Тоже удобно.Please open Telegram to view this post
VIEW IN TELEGRAM
🟰 При внесении этих изменений (
Если будут проблемы, поменяю😏
С одной стороны, мне не хотелось выносить всё в DLL, но потом я решил, что это даже хорошо. Почему? Об этом в следующем абзаце.
⏸ Пытаясь сварганить интру, я подумал, что было бы здорово иметь возможность написать прототип на C/C++, заодно и потестить финальную скорость. И тут я понял, что вынос основного кода в DLL — это то, что нужно. Сделать прототип можно двумя способами:
🅰️ Создав DLL с экспортируемой функцией типа
🅱️ Создав EXE, который будет вызывать функции DLL среды (в первом варианте это тоже будет, конечно, только здесь нужно будет дополнительно вызывать функцию инициализации среды, передавая параметры командной строки).
За это я ещё пока не брался, конечно же.
⏸ Сделал возможность указывать только ширину экрана с автоматическим подбором высоты, исходя из соотношения сторон текущего видеорежима. Также сделал возможность устанавливать режим экрана по умолчанию (это либо режим, установленный при запуске, либо заданный параметром командной строки). А также половину разрешения этого режима, треть и четверть по каждой из сторон. Причём, при использовании этой возможности зачастую можно сэкономить 3 байта кода.
⏸ Ещё я решил, что нужно сделать API-функцию
🟰 Наверняка, я сделал что-то ещё, но не столь существенное, поэтому забыл написать об этом.
Но самый прикол в том, что файл TODO растёт быстрее, чем я успеваю что-то делать (а посвящать проекту слишком много времени я тоже не могу).
❓ Ещё один вопрос меня терзает. Это неиспользуемые параметры. Сейчас некоторые функции могут принимать переменное число параметров (2-3 или от 1 до 4-х, например). Кол-во параметров определяется отдельными битами первого. Так вот, вся проблема в том, что в конечном счёте любой API-вызов сводится к вызову функции C++, где кол-во параметров не может быть переменным (не надо про variadic — там свои заморочки). Поэтому промежуточный код (который в случае фиксированного кол-во параметров сводится к
В случае сложных схем (где число опциональных параметров 3 и более, либо где параметр зависит от значения нескольких битов), код получается довольно громоздким (не один десяток инструкций). Да, ещё не нужно забывать, что выходов из таких функций должно быть тоже несколько, например,
На ум приходит только следующее: использовать для опциональных параметров регистры или TLS. Первый вариант расходует драгоценные регистры. Второй требует сохранять🧐
ebp=0x01C71C80
) я столкнулся с проблемой. Зарезервировать такие малые адреса не удавалось, т.к. они были уже заняты после инициализации DLL-ок типа USER32, MSVCRT и т.п. Пришлось выносить весь основной код в DLL и делать отдельно EXE-загрузчик, который резервирует нужные адреса и потом уже загружает основной код (динамически — LoadLibrary
). Это сработало. Но требует тестирования на разных версиях винды.Если будут проблемы, поменяю
ebp
на 0x03330000
(2x = 0x06660000, 3x = 0x09990000, 5x = 0x0FFF0000), забив на фичу с [ebp+ebp*8]
С одной стороны, мне не хотелось выносить всё в DLL, но потом я решил, что это даже хорошо. Почему? Об этом в следующем абзаце.
⏸ Пытаясь сварганить интру, я подумал, что было бы здорово иметь возможность написать прототип на C/C++, заодно и потестить финальную скорость. И тут я понял, что вынос основного кода в DLL — это то, что нужно. Сделать прототип можно двумя способами:
🅰️ Создав DLL с экспортируемой функцией типа
ParallelixEntry
, передавая такую DLL в качестве параметра при запуске среды (вместо plx-файла — да, это расширение файлов с интрами, запоминайте).🅱️ Создав EXE, который будет вызывать функции DLL среды (в первом варианте это тоже будет, конечно, только здесь нужно будет дополнительно вызывать функцию инициализации среды, передавая параметры командной строки).
За это я ещё пока не брался, конечно же.
⏸ Сделал возможность указывать только ширину экрана с автоматическим подбором высоты, исходя из соотношения сторон текущего видеорежима. Также сделал возможность устанавливать режим экрана по умолчанию (это либо режим, установленный при запуске, либо заданный параметром командной строки). А также половину разрешения этого режима, треть и четверть по каждой из сторон. Причём, при использовании этой возможности зачастую можно сэкономить 3 байта кода.
⏸ Ещё я решил, что нужно сделать API-функцию
@InitVideoOrDisplayFrame
, которая будет при первом вызове устанавливать видеорежим, а при повторном — выводить изображение. Экономия на лишнем вызове (@InitVideo
и @DisplayFrame
тоже останутся, не переживайте).🟰 Наверняка, я сделал что-то ещё, но не столь существенное, поэтому забыл написать об этом.
Но самый прикол в том, что файл TODO растёт быстрее, чем я успеваю что-то делать (а посвящать проекту слишком много времени я тоже не могу).
jmp
) проверяет биты, записывает в стек либо реальный параметр, либо пустышку, а затем делает вызов stdcall-функции C++ (с уже фиксированным числом параметров).В случае сложных схем (где число опциональных параметров 3 и более, либо где параметр зависит от значения нескольких битов), код получается довольно громоздким (не один десяток инструкций). Да, ещё не нужно забывать, что выходов из таких функций должно быть тоже несколько, например,
ret 4
, ret 8
, ret 12
, ret 16
:). Мне это не очень нравится. Даже не из-за того, что это замедляет вызов (всё-таки основной код таких функций несоизмеримо более громоздкий), а из-за какой-то костыльности.На ум приходит только следующее: использовать для опциональных параметров регистры или TLS. Первый вариант расходует драгоценные регистры. Второй требует сохранять
ebx
для адресации области памяти TLS (ну и запись в память требует больше байтов, чем простой push
). Короче, варианты ещё хуже. Подкиньте идей :). Мне всё-таки хочется оставить вариант stdcall
, но придумать какую-то магию чтобы передать вызов функциям C++ с меньшими усилиями Please open Telegram to view this post
VIEW IN TELEGRAM
Добрался до кода. Сделал рефакторинг. Раньше среда была в классе с одним экземпляром (Синглтон, по сути) в глобальной переменной. Но смысла в этом не было, т.к. это лишний указатель в некоторых случаях, а два экземпляра никогда не будет (две интры не запустишь в одном адресном пространстве). Плюс это создавало неудобства в плане доступа к приватным членам.
API тоже был завернут в класс со статическими функциями, но это тоже так себе идея.
В итоге перенёс и то, и другое в отдельные namespace'ы (с вложенными namespace'ами вроде internal и пр.). Для API сделал отдельно функции для экспорта в DLL, отдельно для вызова из интр. И отдельно с основным кодом (которые вызываются из тех двух). Всё с одинаковыми именами, только namespace'ы разные. Так удобнее, ИМХО 😁
Попробовал загрузить DLL'ку извне на Си, плюсах, Delphi и из асма. Всё работает✌️
Также добавил ещё одну DLL'ку: parallelix_memory для резервирования адресов, если вдруг кто-то захочет сделать обёртку (свой лоадер, так скажем). Х/з зачем, но пусть будет. Заодно потестил кое-что :)
API тоже был завернут в класс со статическими функциями, но это тоже так себе идея.
В итоге перенёс и то, и другое в отдельные namespace'ы (с вложенными namespace'ами вроде internal и пр.). Для API сделал отдельно функции для экспорта в DLL, отдельно для вызова из интр. И отдельно с основным кодом (которые вызываются из тех двух). Всё с одинаковыми именами, только namespace'ы разные. Так удобнее, ИМХО 😁
Попробовал загрузить DLL'ку извне на Си, плюсах, Delphi и из асма. Всё работает
Также добавил ещё одну DLL'ку: parallelix_memory для резервирования адресов, если вдруг кто-то захочет сделать обёртку (свой лоадер, так скажем). Х/з зачем, но пусть будет. Заодно потестил кое-что :)
Please open Telegram to view this post
VIEW IN TELEGRAM
Прототипирование на ЯВУ (языках высокого уровня) работает! 🙂 💪
Правда, ещё не сделана поддержка многопоточных функций (там ABI вызова немного другой будет), но это задача несложная — просто добавить несколько альтернативных веток в уже существующие функции.
Короче говоря, вот такой код уже корректно отрабатывает и выдаёт ту же картинку, что и асмовский.
При этом, не знаю в чём прикол, но прототип выдаёт немного больший fps, чем интра на асме😂
Примерно 720 vs 640.
P.S.
Ну и в целом над заголовочниками нужно ещё покумекать...
Правда, ещё не сделана поддержка многопоточных функций (там ABI вызова немного другой будет), но это задача несложная — просто добавить несколько альтернативных веток в уже существующие функции.
Короче говоря, вот такой код уже корректно отрабатывает и выдаёт ту же картинку, что и асмовский.
[ вырезано, см. ниже ]
Код использует DLL-ку платформы (функции ParallelixMainExW
, InitVideo
, DisplayFrame
).При этом, не знаю в чём прикол, но прототип выдаёт немного больший fps, чем интра на асме
Примерно 720 vs 640.
P.S.
InitVideoOptions::ColorMode::palette_8bpp
, конечно, выглядит немного страшновато, но я подумаю над этим. Может, сделаю макросы, чтобы можно было писать в духе ivColorMode(palette_8bpp)
, ну или алиасы типа using ivColorMode = api::InitVideoOptions::ColorMode;
(чтобы писать ivColorMode::palette_8bpp
).Ну и в целом над заголовочниками нужно ещё покумекать...
Please open Telegram to view this post
VIEW IN TELEGRAM
Нет, с картинкой смотреть на код невозможно (слишком узко) :)
#include "config.h"
#include "api_impl.h"
extern "C" int __stdcall ParallelixMainExW(void*, const wchar_t*);
void __stdcall intro(ServiceData* service_data)
{
using namespace api;
exported::InitVideo({ InitVideoOptions::ColorMode::palette_8bpp, VideoMode::default_full, 0,
InitVideoOptions::use_video_mode, InitVideoOptions::LineAlignment::align_default });
for (int i = 0; ; ++i) {
uint8_t* pixel = service_data->frame_address.pixel8bpp;
for (int y = 0; y <= service_data->current_mode.size.max_y; ++y) {
for (int x = 0; x <= service_data->current_mode.size.max_x; ++x) {
*pixel++ = static_cast<uint8_t>(((x ^ y) + i) >> 4);
}
}
exported::DisplayFrame({ true });
}
}
int main()
{
ParallelixMainExW(intro, L". -kc -fr");
}
Многопоточный код в прототипах теперь тоже работает 🔥
Прототип немного быстрее.
По линиям (1250 vs 1200 fps):
По 1 пикселю (800 vs 780 fps):
По 64 пикселя (1180 vs 1125 fps):
Прототип немного быстрее.
По линиям (1250 vs 1200 fps):
#include "config.h"
#include "api_impl.h"
extern "C" int __stdcall ParallelixMainExW(void*, const wchar_t*);
void __stdcall draw_pixel(uint32_t thread_index, uint32_t y, uint32_t x, uint32_t group_size, PixelPointer pixel_addr, uint32_t spec_value, ServiceData* service_data)
{
for (uint32_t limit = x + group_size; x < limit; ++x) {
*pixel_addr.pixel8bpp++ = static_cast<uint8_t>(((x ^ y) + spec_value) >> 4);
}
}
void __stdcall intro(ServiceData* service_data)
{
using namespace api;
exported::InitVideo({ InitVideoOptions::ColorMode::palette_8bpp, VideoMode::default_full, 0,
InitVideoOptions::use_video_mode, InitVideoOptions::LineAlignment::align_default });
for (int i = 0; ; ++i) {
// 15 = group by lines
exported::ParallelPixels({ 15, ParallelPixelsOptions::ImageSizeType::screen_resolution, true }, draw_pixel, {}, i);
exported::DisplayFrame({ true });
}
}
int main()
{
ParallelixMainExW(intro, L". -kc -fr");
}
По 1 пикселю (800 vs 780 fps):
#include "config.h"
#include "api_impl.h"
extern "C" int __stdcall ParallelixMainExW(void*, const wchar_t*);
void __stdcall draw_pixel(uint32_t thread_index, uint32_t y, uint32_t x, uint32_t group_size, PixelPointer pixel_addr, uint32_t spec_value, ServiceData* service_data)
{
*pixel_addr.pixel8bpp = static_cast<uint8_t>(((x ^ y) + spec_value) >> 4);
}
void __stdcall intro(ServiceData* service_data)
{
using namespace api;
exported::InitVideo({ InitVideoOptions::ColorMode::palette_8bpp, VideoMode::default_full, 0,
InitVideoOptions::use_video_mode, InitVideoOptions::LineAlignment::align_default });
for (int i = 0; ; ++i) {
// 0 = group by 1 pixel (0 << 1)
exported::ParallelPixels({ 0, ParallelPixelsOptions::ImageSizeType::screen_resolution, true }, draw_pixel, {}, i);
exported::DisplayFrame({ true });
}
}
int main()
{
ParallelixMainExW(intro, L". -kc -fr");
}
По 64 пикселя (1180 vs 1125 fps):
#include "config.h"
#include "api_impl.h"
extern "C" int __stdcall ParallelixMainExW(void*, const wchar_t*);
void __stdcall draw_pixel(uint32_t thread_index, uint32_t y, uint32_t x, uint32_t group_size, PixelPointer pixel_addr, uint32_t spec_value, ServiceData* service_data)
{
for (uint32_t limit = x + group_size; x < limit; ++x) {
*pixel_addr.pixel8bpp++ = static_cast<uint8_t>(((x ^ y) + spec_value) >> 4);
}
}
void __stdcall intro(ServiceData* service_data)
{
using namespace api;
exported::InitVideo({ InitVideoOptions::ColorMode::palette_8bpp, VideoMode::default_full, 0,
InitVideoOptions::use_video_mode, InitVideoOptions::LineAlignment::align_64b });
for (int i = 0; ; ++i) {
// 6 = group by 64 pixels (1 << 6)
exported::ParallelPixels({ 6, ParallelPixelsOptions::ImageSizeType::screen_resolution, true }, draw_pixel, {}, i);
exported::DisplayFrame({ true });
}
}
int main()
{
ParallelixMainExW(intro, L". -kc -fr");
}
Please open Telegram to view this post
VIEW IN TELEGRAM
Теперь можно загружать вот такие DLL-ки через командную строку (компилится в 2.5 КБ с ☺️
P.S. Функция
/NODEFAULTLIB
) #include "config.h"
#include "api_impl.h"
extern "C" BOOL APIENTRY _DllMainCRTStartup(HMODULE hModule, DWORD fdwReason, LPVOID lpvReserved)
{
return TRUE;
}
/*
DLLEXPORT BOOL __stdcall ParallelixCheck(ServiceData* service_data)
{
return TRUE;
}
*/
void __stdcall draw_pixel(uint32_t thread_index, uint32_t y, uint32_t x, uint32_t group_size, PixelPointer pixel_addr, uint32_t spec_value, ServiceData* service_data)
{
*pixel_addr.pixel8bpp = static_cast<uint8_t>(((x ^ y) + spec_value) >> 4);
}
DLLEXPORT void __stdcall ParallelixStart(ServiceData* service_data)
{
using namespace api;
exported::InitVideo({ InitVideoOptions::ColorMode::palette_8bpp, VideoMode::default_full, 0,
InitVideoOptions::use_video_mode, InitVideoOptions::LineAlignment::align_default });
for (int i = 0; ; ++i) {
// 0 = group by 1 pixel (1 << 0)
exported::ParallelPixels({ 0, ParallelPixelsOptions::ImageSizeType::screen_resolution, true }, draw_pixel, {}, i);
exported::DisplayFrame({ true });
}
}
P.S. Функция
ParallelixCheck
специально закомменчена, т.к. является необязательной, и при её отсутствии считается, что всё ок.Please open Telegram to view this post
VIEW IN TELEGRAM
Мысли вслух.
🔹 Хочу перевести код под компилятор Intel и сравнить производительность с Visual C++.
🔹 По части распараллеливания циклов думаю подрубить Intel TBB (давно уже хочу изучить его). И также сравнить с моей реализацией🙂
Второе пока не горит, есть более приоритетные задачи.
А вот первое уже можно сделать, чтоб потом пришлось меньше исправлять код.
🔹 Хочу перевести код под компилятор Intel и сравнить производительность с Visual C++.
🔹 По части распараллеливания циклов думаю подрубить Intel TBB (давно уже хочу изучить его). И также сравнить с моей реализацией
Второе пока не горит, есть более приоритетные задачи.
А вот первое уже можно сделать, чтоб потом пришлось меньше исправлять код.
Please open Telegram to view this post
VIEW IN TELEGRAM
Задумался вот о чём.
Планируется, что Parallelix будет не только запускать интро, но и выполнять ряд других функций: проверять файл на совместимость, упаковывать и распаковывать, добавлять и удалять заголовок, проверять хэши, даже загружать файлы из интернета :)
Напомню, что сейчас платформа состоит из 2-х файлов (потом будет ещё документация и т.п.):
🔸
🔸
Зачем так сложно?
Платформа использует фиксированные линейные адреса для хранения кода API, загрузки кода интро, видеобуфера и пр. Так проще, поскольку не нужно делать интро позиционно-независимым.
✅️ Исторически было решено использовать довольно-таки малые адреса, которые при загрузке программы оказывались занятыми (т.к. код инициализации DLL эти адреса "прибирал к рукам"). Поэтому EXE-шник резервировал нужные адреса, а потом динамически загружал наш основной DLL. Сейчас я снова решил поменять адреса, поэтому в принципе, такой проблемы быть не должно.
✅️ Вторая причина — более веская: возможность создания прототипов, которые будут использовать API из DLL. Т.е. мы делаем интро либо в виде DLL, либо в виде EXE-шника, который использует функции из
Так вот, вернёмся к исходному вопросу. Есть несколько вариантов организовать возможность запускать разные функции.
Засунуть все эти функции в ту же DLL-ку. Да, она будет больше в размере, но глобально: 500 КБ или 5 МБ она будет весить — не особо-то и важно. И тут есть 2 варианта:
1️⃣ Использовать команды в, пардон, командной строке, т.е. формат
Здесь для облегчения жизни можно пойти на хитрость: разрешить опускать команду
Для надёжности (вдруг кто-то решит назвать файл именем
2️⃣ В случае необходимости использовать "специальную" функцию (не запуск), использовать опции вместо команд:
Планируется, что Parallelix будет не только запускать интро, но и выполнять ряд других функций: проверять файл на совместимость, упаковывать и распаковывать, добавлять и удалять заголовок, проверять хэши, даже загружать файлы из интернета :)
Напомню, что сейчас платформа состоит из 2-х файлов (потом будет ещё документация и т.п.):
🔸
parallelix.dll
— содержит парсер командной строки, загрузчик и среду исполнения, т.е. всё :)🔸
parallelix.exe
— загружает DLL и передаёт ей командную строку.Зачем так сложно?
Платформа использует фиксированные линейные адреса для хранения кода API, загрузки кода интро, видеобуфера и пр. Так проще, поскольку не нужно делать интро позиционно-независимым.
parallelix.dll
. В первом случае мы передаём наш DLL с прототипом в качестве параметра для parallelix.exe
(аналогично тому, как мы передаём имя сайз-закодированного файла с плоским кодом). Во втором случае мы имеем полноценно работающий EXE-шник (и тут parallelix.exe
нам вообще не нужен).Так вот, вернёмся к исходному вопросу. Есть несколько вариантов организовать возможность запускать разные функции.
Засунуть все эти функции в ту же DLL-ку. Да, она будет больше в размере, но глобально: 500 КБ или 5 МБ она будет весить — не особо-то и важно. И тут есть 2 варианта:
parallelix command [files and options]
:parallelix run intro.plx -kc -fr
parallelix run https://github/vasya/scene/intro.plx --check-hash md5:d41d8cd98f00b204e9800998ecf8427e
parallelix pack intro.plx -m 5 -o intro_packed.plx
parallelix remove-header intro.plx
parallelix version
Здесь для облегчения жизни можно пойти на хитрость: разрешить опускать команду
run
. Т.о. следующие 2 команды будут работать одинаково:parallelix intro.plx -kc -fr
parallelix run intro.plx -kc -fr
Для надёжности (вдруг кто-то решит назвать файл именем
pack
без расширения) все команды можно предварять каким-то символом, например, слешем:parallelix intro.plx -kc -fr
parallelix /run intro.plx -kc -fr
parallelix /run https://github/vasya/scene/intro.plx --check-hash md5:d41d8cd98f00b204e9800998ecf8427e
parallelix /pack intro.plx -m 5 -o intro_packed.plx
parallelix /remove-header intro.plx
parallelix /version
parallelix intro.plx -kc -fr
parallelix https://github/vasya/scene/intro.plx --check-hash md5:d41d8cd98f00b204e9800998ecf8427e
parallelix --pack intro.plx -m 5 -o intro_packed.plx
parallelix intro.plx --pack -m 5 -o intro_packed.plx
parallelix --remove-header intro.plx
parallelix --version
Please open Telegram to view this post
VIEW IN TELEGRAM
Ну и ещё пара вариантов...
3️⃣ Сделать для "специальных" функций отдельную прогу (при этом и основной DLL будет весить меньше):
Или даже несколько прог:
4️⃣ Совместить 1-й (или 2-й) и 3-й варианты. EXE-шник содержит в себе парсер команды (при её наличии) и запускает тул в случае, если это не
Т.е. следующие пары команд будут аналогичными:
Как вариант, вместо
и т.д.
Ну или вообще оставить 2 DLL-ки:
Заметьте, что в этих случаях все DLL будут загружаться динамически, т.е. если у вас нет какой-то DLL-ки, которая вам не нужна, система при запуске ругаться не будет.
————————————————————
Какой из вариантов вам кажется более удобным для использования? 😁
Хотелось бы мнений с обоснованием.
parallelix intro.plx -kc -fr
parallelix https://github/vasya/scene/intro.plx --check-hash md5:d41d8cd98f00b204e9800998ecf8427e
parallelix-tools pack intro.plx -m 5 -o intro_packed.plx
parallelix-tools remove-header intro.plx
parallelix --version
parallelix-tools --version
Или даже несколько прог:
parallelix intro.plx -kc -fr
parallelix https://github/vasya/scene/intro.plx --check-hash md5:d41d8cd98f00b204e9800998ecf8427e
parallelix-pack intro.plx -m 5 -o intro_packed.plx
parallelix-remove-header intro.plx
parallelix --version
parallelix-pack --version
/run
.Т.е. следующие пары команд будут аналогичными:
parallelix intro.plx -kc -fr
parallelix /run intro.plx -kc -fr
parallelix /pack intro.plx -m 5
или(?) parallelix --pack intro.plx -m 5
parallelix-tools pack intro.plx -m 5
parallelix /remove-header intro.plx
или(?) parallelix --remove-header intro.plx
parallelix-tools remove-header intro.plx
Как вариант, вместо
parallelix-tools.exe
можно наделать DLL-ок и использовать только команды, каждая из которых будет грузить свою DLL:parallelix.exe
— "менеджер" запуска всех командparallelix.dll
— загрузчик кода, среда исполненияparallelix-packer.dll
— упаковщик-распаковщикparallelix-header.dll
— работа с заголовкамии т.д.
Ну или вообще оставить 2 DLL-ки:
parallelix.dll
и parallelix-tools.dll
.Заметьте, что в этих случаях все DLL будут загружаться динамически, т.е. если у вас нет какой-то DLL-ки, которая вам не нужна, система при запуске ругаться не будет.
————————————————————
Какой из вариантов вам кажется более удобным для использования? 😁
Хотелось бы мнений с обоснованием.
Please open Telegram to view this post
VIEW IN TELEGRAM
Загрузил немного предыдущими лонг-постом? 😁
Наверное, лучше сделать так:
🔹
🔸
Примеры:
🔸
Примеры:
Получается, что через любую из прог можно запустить любую функцию. И если эта функция реализована в другой проге, то будет запускаться соответствующая.
Вот как-то так...
Ставьте лайк, если норм 👍
Наверное, лучше сделать так:
🔹
parallelix.exe +
parallelix.dll
🔹 parallelix-tools.exe
Форматы запуска:🔸
parallelix [/command] [files, options...]
Если команда не указана (а она может быть только первым параметром), подразумевается /run
, т.е. загрузка parallelix.dll
и передача ему управления. Если указана другая команда (кроме некоторых специальных типа /help
, /about
, /version
и т.п. — эти команды отрабатывает сам exe-шник), запускается parallelix-tools.exe
, которому передаётся вся командная строка.Примеры:
parallelix intro.plx -kc -fr
parallelix /run https://github/vasya/scene/intro.plx --check-hash md5:d41d8cd98f00b204e9800998ecf8427e
parallelix /pack intro.plx -m 5 -o intro_packed.plx
parallelix /remove-header intro.plx
parallelix /version
🔸
parallelix-tools /command [files, options...]
Здесь команда обязательна, и она тоже должна быть первым параметром. Если указана /run
, запускается parallelix.exe
, которому передаётся вся командная строка.Примеры:
parallelix-tools /run intro.plx -kc -fr
parallelix-tools /run https://github/vasya/scene/intro.plx --check-hash md5:d41d8cd98f00b204e9800998ecf8427e
parallelix-tools /pack intro.plx -m 5 -o intro_packed.plx
parallelix-tools /remove-header intro.plx
parallelix-tools /version
Получается, что через любую из прог можно запустить любую функцию. И если эта функция реализована в другой проге, то будет запускаться соответствующая.
Вот как-то так...
Ставьте лайк, если норм 👍
Работа пока стоит, а идеи в голову лезут 😄
1️⃣ Многослойность!
Фреймбуфер у меня большой (256 МБ) — это позволит хранить несколько кадров, несколько слоёв либо использовать масштабирование (зум).
С помощью специальной API-функции можно будет накладывать на область итогового слоя (по смещению 0; либо по любому другому смещению) блок из другой области памяти. При этом можно указать цвет прозрачности (опционально), а также размер области источника и приёмника. Вот только х/з что там с GDI-функциями на эту тему. Хорошо было бы вообще иметь возможность использовать 8 лишних бит в 32-битном режиме цветности как альфа-канал. Ну и, разумеется, указывать способы наложения (включая AND, OR, XOR и пр).
Таким образом, представьте, можно, например, нарисовать небольшой фон, растянуть его на весь экран с размытием, сверху нарисовать что-то ещё, а потом поверх этого генерировать анимацию (или 2, 3 анимации). При этом не нужно каждый раз перерисовывать нижние слои! Даже при необходимости сдвинуть или изменить их размер.
Пока писал, вспомнил свою интру DEEP TRIP (как пример, где можно было бы такое использовать), в которой вместо чёрного цвета фрактала Жюлиа рисуется пламя.
Ещё один пример — игра Тетрис. Не нужно рисовать большие квадраты, достаточно 1 пикселя.
На итоговый слой копируется фон, далее игровое поле (с масштабированием, без размытия). Ну и потом можно вывести счёт и т.п.
2️⃣ Спрайты!
Это почти то же самое (в целом можно даже совместить это всё в одной функции), только здесь появляются дополнительные плюшки. Например, глубина цвета спрайта может быть меньше глубины цвета видеорежима. Можно, скажем, использовать 1 бит на пиксель и дополнительно указать 1 или 2 цвета (в зависимости от необходимости прозрачности).
3️⃣ Шрифты!
О шрифтах я думаю уже давно. Их должно быть несколько: моноширинный, без засечек, с засечками и какой-нибудь интересный. Возможно, какой-то из них будет иметь вариант курсива, либо курсовом будет просто смещение верхней и нижней частей. И каждый из них должен иметь 2-4 размера (скорее всего, 4 размера моно и по 2-3 остальных).
Масштабировать их можно будет, но в естественном размере они, конечно, будут выглядеть лучше. Не знаю только насчёт полутонов (видеорежимы же могут быть и с палитрой, так что скорее нет, чем да). Разумеется, с указанием цвета текста и фона (либо прозрачный фон/текст). С возможностью поворота на угол, кратный 90 градусам.
Большой вопрос стоит относительно набора символов. Их должно быть 256, но туда нужно уместить и русские буквы, и буквы разных европейских народов (без иероглифов, арабских, грузинских, еврейских букв и т.п... греческие можно, если хватит места, но вряд ли его хватит). Пожалуй, стоит ввести диакритические знаки, которые будут накладываться поверх предыдущего символа. Но хорошо ли это будет выглядеть на немоноширинных шрифтах (хотя, Unicode же как-то приспособили)?
Ну вот как-то так...
Фреймбуфер у меня большой (256 МБ) — это позволит хранить несколько кадров, несколько слоёв либо использовать масштабирование (зум).
С помощью специальной API-функции можно будет накладывать на область итогового слоя (по смещению 0; либо по любому другому смещению) блок из другой области памяти. При этом можно указать цвет прозрачности (опционально), а также размер области источника и приёмника. Вот только х/з что там с GDI-функциями на эту тему. Хорошо было бы вообще иметь возможность использовать 8 лишних бит в 32-битном режиме цветности как альфа-канал. Ну и, разумеется, указывать способы наложения (включая AND, OR, XOR и пр).
Таким образом, представьте, можно, например, нарисовать небольшой фон, растянуть его на весь экран с размытием, сверху нарисовать что-то ещё, а потом поверх этого генерировать анимацию (или 2, 3 анимации). При этом не нужно каждый раз перерисовывать нижние слои! Даже при необходимости сдвинуть или изменить их размер.
Пока писал, вспомнил свою интру DEEP TRIP (как пример, где можно было бы такое использовать), в которой вместо чёрного цвета фрактала Жюлиа рисуется пламя.
Ещё один пример — игра Тетрис. Не нужно рисовать большие квадраты, достаточно 1 пикселя.
На итоговый слой копируется фон, далее игровое поле (с масштабированием, без размытия). Ну и потом можно вывести счёт и т.п.
Это почти то же самое (в целом можно даже совместить это всё в одной функции), только здесь появляются дополнительные плюшки. Например, глубина цвета спрайта может быть меньше глубины цвета видеорежима. Можно, скажем, использовать 1 бит на пиксель и дополнительно указать 1 или 2 цвета (в зависимости от необходимости прозрачности).
О шрифтах я думаю уже давно. Их должно быть несколько: моноширинный, без засечек, с засечками и какой-нибудь интересный. Возможно, какой-то из них будет иметь вариант курсива, либо курсовом будет просто смещение верхней и нижней частей. И каждый из них должен иметь 2-4 размера (скорее всего, 4 размера моно и по 2-3 остальных).
Масштабировать их можно будет, но в естественном размере они, конечно, будут выглядеть лучше. Не знаю только насчёт полутонов (видеорежимы же могут быть и с палитрой, так что скорее нет, чем да). Разумеется, с указанием цвета текста и фона (либо прозрачный фон/текст). С возможностью поворота на угол, кратный 90 градусам.
Большой вопрос стоит относительно набора символов. Их должно быть 256, но туда нужно уместить и русские буквы, и буквы разных европейских народов (без иероглифов, арабских, грузинских, еврейских букв и т.п... греческие можно, если хватит места, но вряд ли его хватит). Пожалуй, стоит ввести диакритические знаки, которые будут накладываться поверх предыдущего символа. Но хорошо ли это будет выглядеть на немоноширинных шрифтах (хотя, Unicode же как-то приспособили)?
Ну вот как-то так...
Please open Telegram to view this post
VIEW IN TELEGRAM
pouët.net
DEEP TRIP by Jin X
256b for MS-Dos, 1st at CAFe 2019
👍1
Салют!
Друзья, я снова сел за проект (наконец-то), уже кое-что перелопатил, подробнее чуть позже.
А пока важно вот что.
Я делаю область констант (чтобы не засорять код интро часто используемыми числами). Есть возможность разместить 64+15 = 79 чисел. По факту их будет гораздо больше, но уже за пределами области «удобной» адресации. Хотя можно разместить и 256 чисел (с «относительно» удобной адресацией) 😁
Сейчас планирую разместить там:
🟡 Последовательности 0...15 типа float и int32 (нужны в т.ч. для групповой обработки пикселей через SSE/AVX/AVX-512, чтобы добавлять их к номерам пикселей) [32 шт].
🟡 Обратные величины чисел 1...10 [10 шт].
🟡 Числа -4, -2, -1, 1.25, 1.5, 2.5, 7.5, 12.5, 16, 25, 32, 50, 64, 128, 256, 100, 125, 250, 500, 1000 (кроме нуля) и их обратные величины (кроме -1) [это 39 шт].
🟡 Маску знака (0x80000000), π, 2π, π/2, π/3, π/4, π/180, 180/π, sqrt(2), sqrt(3), 1/sqrt(2), 1/sqrt(3), sqrt(3)/2, 2/sqrt(3) (значения sin, cos, tg, cosec, sec, ctg стандартных углов), e, φ (золотое сечение) [14 шт].
🟡 Значения log2(e), log2(10), ln(2), ln(10), lg(2), lg(e) [6 шт].
Итого пока получается 101 шт. Можно ещё добавить степени 3, 5, степени 2 минус 1, больше степеней 2, 10, больше отрицательных чисел и их обратные величины. И т.д. Короче, добить «чем-нибудь» до 256 и (гораздо) более я смогу.
Главный вопрос...
❓ Какие константы ещё могут быть полезны для генерации изображений, анимации, трёхмерных объектов, векторных и матричных вычислений?
🟡 Можно добавить постоянные Эйлера (γ), Каталана (G), Фейгенбаума (α, β) и пр. Но нужны ли они?
🟡 Таблицы sin, cos, tg, cosec, sec, ctg и arc* я сделаю, но по «более дальним» адресам.
🟡 Коэффициенты для вычисления функций через ряды Маклорена (Тейлора) будут, тоже «туда дальше».
Кто шарит во всех этих математических делах, гляньте, плиз, вот это список.
Если что-то вы сочтёте полезным, напишите в чат названия констант.
Может, есть что-то, что я не знаю / не пришло в голову? Отдельные числа, таблицы, ряды...
В общем, надеюсь на вашу активность на эту тему🤝
Друзья, я снова сел за проект (наконец-то), уже кое-что перелопатил, подробнее чуть позже.
А пока важно вот что.
Я делаю область констант (чтобы не засорять код интро часто используемыми числами). Есть возможность разместить 64+15 = 79 чисел. По факту их будет гораздо больше, но уже за пределами области «удобной» адресации. Хотя можно разместить и 256 чисел (с «относительно» удобной адресацией) 😁
Сейчас планирую разместить там:
Итого пока получается 101 шт. Можно ещё добавить степени 3, 5, степени 2 минус 1, больше степеней 2, 10, больше отрицательных чисел и их обратные величины. И т.д. Короче, добить «чем-нибудь» до 256 и (гораздо) более я смогу.
Главный вопрос...
Кто шарит во всех этих математических делах, гляньте, плиз, вот это список.
Если что-то вы сочтёте полезным, напишите в чат названия констант.
Может, есть что-то, что я не знаю / не пришло в голову? Отдельные числа, таблицы, ряды...
В общем, надеюсь на вашу активность на эту тему
Please open Telegram to view this post
VIEW IN TELEGRAM
Я портировал Space Fungus на Parallelix с использованием AVX (вычисляется 8 точек за раз).
Мой комп выдаёт 450 fps в FullHD (1920x1080) / TrueColor на 20-потоковым процессоре😁
P.S. Расчётом занимается только CPU, GPU не задействован!
Шейдер-прототип выглядит так:
Мой комп выдаёт 450 fps в FullHD (1920x1080) / TrueColor на 20-потоковым процессоре
P.S. Расчётом занимается только CPU, GPU не задействован!
Шейдер-прототип выглядит так:
#define ITERATIONS 20
vec3 kaliset(vec3 p, vec3 u){
vec3 c=p;
for(int i=0;i<ITERATIONS;i++){
float len=length(p);
p=abs(p)/(len*len)-u; // abs(p)/dot(p,p)-u
c+=p;
}
return c/float(ITERATIONS);
}
void mainImage(out vec4 c, in vec2 xy)
{
vec2 uv=vec2(xy.x/iResolution.x-0.5,(xy.y-iResolution.y*0.5)/iResolution.x);
float m=iTime/60.0;
vec3 p=vec3(uv*iTime,0.1);
vec3 u=vec3(1.0,1.0,0.1)*m;
c.xyz=kaliset(p,u);
}
Please open Telegram to view this post
VIEW IN TELEGRAM
pouët.net
Space Fungus by Frigo
256b for MS-Dos, 3rd at Function 2019
Друзья, две новости.
Новость 1️⃣ (об этом я частично писал в чате).
Платформа Para||elix будет выпускаться в трёх редакциях: «A», «B» и «C».
🔤 SCETIC EDITION — версия с сокращёнными возможностями. Уменьшенное кол-во API-функций (нет поддержки спрайтов, генераторов палитры, функций рисования и пр.), малый набор палитр, один-два шрифта, отсутствуют области констант, встроенные текстуры.
Для тех, кто ценит простоту и любит делать всё своими руками.
🔤 ASIC EDITION — стандартная версия. Будут добавлены слои и спрайты, несколько стандартных палитр + простые генераторы палитры, больше шрифтов. Доступ к ограниченному кол-ву констант (что особенно удобно при работе с SIMD). Стандартные текстуры (рандом, шум Перлина). Сжатия кода. Конфигуратор.
Для тех, кто любит удобства, но без перегибов.
🔤 OMPLETE EDITION — всё по максимуму. Функции рисования (GDI, OpenGL), байт-коды FPU/SIMD, bytebeat/floatbeat с компиляцией, генератор нот. Генераторы сложных палитр, математические функции. Доступ к WinAPI. Большой массив констант. Конфигуратор.
Для тех, кто хочет выжать максимум из платформы. Удобно для создания демо.
Версии платформы будут называться в честь великих художников, музыкантов, математиков и пр.
Например, Aivazovsky, Mandelbrot, Dali, Fibonacci, Mozart, Van Gogh и т.д.
——————————
Новость 2️⃣.
Я снова изменил значение регистра EBP — базового регистра для доступа к API и структурам.
А также адрес загрузки интро. Думаю, на этот раз окончательно 😁. Плюс ко всему, на старте EDX = EBP + 0x100, что будет облегчать доступ к дальним адресам структур (палитре, константами, коду).
Расклад такой:
Структура памяти (без учёта API, констант, сервисных данных):
Код записывается по адресу
Раньше я думал о том, чтобы загружать код по адресу 0x10000000 либо 0x20000000. Значение 0x10000080 тоже делится на 9 (при старте с позиции 0x10000000), но там могут возникнуть проблемы при резервировании младших адресов (начиная с EBP). А вот 0x20000080 уже на 9 не делится. Но в целом адрес старта 0x1FFF0000 даже лучше, т.к. как я уже сказал, в этом случае код до 64 КБ не перекрывает большой массив для данных. Использовать для фрейм буфера адреса 0x60000000 и выше тоже не стоит, т.к. это может конфликтовать с DLL (в т.ч. системными) — проверено.
Сервисные данные адресуются через
Но самая красота — доступ к константам:
По-моему, получилось красиво😉
Новость 1️⃣ (об этом я частично писал в чате).
Платформа Para||elix будет выпускаться в трёх редакциях: «A», «B» и «C».
Для тех, кто ценит простоту и любит делать всё своими руками.
Для тех, кто любит удобства, но без перегибов.
Для тех, кто хочет выжать максимум из платформы. Удобно для создания демо.
Версии платформы будут называться в честь великих художников, музыкантов, математиков и пр.
Например, Aivazovsky, Mandelbrot, Dali, Fibonacci, Mozart, Van Gogh и т.д.
——————————
Новость 2️⃣.
Я снова изменил значение регистра EBP — базового регистра для доступа к API и структурам.
А также адрес загрузки интро. Думаю, на этот раз окончательно 😁. Плюс ко всему, на старте EDX = EBP + 0x100, что будет облегчать доступ к дальним адресам структур (палитре, константами, коду).
Расклад такой:
ebp = 0x038E1C80 (API table)
ebp*2 = 0x071C3900 (API table 2)
epb*3 = 0x0AAA5580 (constant area)
epb*4 = 0x0E387200 (reserved)
ebp*5 = 0x11C68E80 (service data)
ebp*8 = 0x1C70E400 (reserved)
epb*9 = 0x1FFF0080 (code start + 0x80)
Структура памяти (без учёта API, констант, сервисных данных):
User textures : 0x12000000 (6*16 MB)
Internal textures 1 : 0x18000000 (4*16 MB) — extra textures
Internal textures 2 : 0x1D000000 (2*16 MB) — random / Perlin noise
Internal fonts : 0x1F000000 (15.875 MB)
Extra variable area : 0x1FFE0000 (64 KB)
Code start address : 0x1FFF0000 (64 KB)
Large user data area : 0x20000000 (512 MB)
Frame buffer address : 0x40000000 (256 MB)
Second frame buffer : 0x50000000 (256 MB) — not fixed sometimes
Huge memory heap : 0x80000000 (2046 MB)
Код записывается по адресу
0x1FFF0000
. Если его размер не превышает 64 КБ, он не перекрывает область пользовательской памяти для данных в 512 МБ. Кроме того, такой адрес позволяет использовать [ebp+ebp*8]
(адрес 0x1FFF0080) для доступа к первым 256 байтам кода, а также [edx+ebp*8]
для доступа к следующим 256 байтам (ну и [ebp+edx*8]
, [edp+edx*8]
). Экономия в среднем 2 байта (иногда 1, иногда даже 3-4) при доступе к данным. Т.к. на старте ESI = code start, 128 байт перед кодом также будут в лёгком доступе (а вообще там 64 КБ для дополнительных переменных, опять же не пересекающихся с 512 МБ). Кстати, кому мало 512 МБ, те могут вызвать API-функцию а-ля malloc, с помощью которой можно выделить почти 2 ГБ дополнительной памяти (при наличии 64-битной винды).Раньше я думал о том, чтобы загружать код по адресу 0x10000000 либо 0x20000000. Значение 0x10000080 тоже делится на 9 (при старте с позиции 0x10000000), но там могут возникнуть проблемы при резервировании младших адресов (начиная с EBP). А вот 0x20000080 уже на 9 не делится. Но в целом адрес старта 0x1FFF0000 даже лучше, т.к. как я уже сказал, в этом случае код до 64 КБ не перекрывает большой массив для данных. Использовать для фрейм буфера адреса 0x60000000 и выше тоже не стоит, т.к. это может конфликтовать с DLL (в т.ч. системными) — проверено.
Сервисные данные адресуются через
[ebp+ebp*4]
и [edx+ebp*4]
. После 256 байт основных данных идут дополнительные 128, и там же начинается палитра, первые 32 цвета (128 байт) будут в лёгком доступе.Но самая красота — доступ к константам:
[ebp+ebp*2]
, [edx+ebp*2]
, [ebp+edx*2]
, [edx+edx*2]
, получается целый килобайт без дыр!По-моему, получилось красиво
Please open Telegram to view this post
VIEW IN TELEGRAM
Друзья, на YouTube появилось видео моего доклада о платформе Parallelix на фестивале Демодуляция 2023.
➡️ Смотрим выступление здесь.
➡️ Презентацию — здесь.
Please open Telegram to view this post
VIEW IN TELEGRAM
YouTube
Para||elix — платформа для сайзкодинга на x86 в хайрез с многопоточностью | Демодуляция 2023
Программист Евгений Красников, известный как Jin X, познакомит гостей с Para||elix: что это такое, зачем нужна ещё одна платформа, чем отличаются три еë редакции, а также какие возможности отладки и прототипирования она предлагает.
🔥1