триада WVP (World, View, Projection) так-же перенесены на DSA
Добавлена поддержка нескольких активных сцен (миров)
Добавлен класс камеры
исправлено неопределенное поведение при обновлении трансформации юнита сцены (синхронизация состояний трансформов в начале кадра)
Комментарий
В очередной раз отваливался по рабочим вопросам =/ Но, вроде, вернулся. Чендж лог сегодня очень небольшой по этой-же причине.
Итак, миры и камеры.
Мир определяется рут объектом GameWorld, который хранит все объекты сцены, камеры итд. Камер у сцен может быть сколь угодно много, но активной одномоментно (во всем движке) может быть только одна (дальше будет сделано исключение для RenderTarget камер, но это отдельно). При этом, сцена без активной камеры продолжит свою работу в фоне. Она продолжит тикать, обновляется, пересчитывается. Просто не будет выводится на экран.
Переключение камер происходит функцией Activate соответствующего Юнита.
как пример, каждые 10 секунд мы переключаем активную камеру между первым и вторым акторами
При этом, если камера принадлежит другой сцене, то сцена так-же переключится.
Где-то тут был пост, про то, что любого человека можно завалить на собеседовании. Да, не переживайте, почти любого можно укатать, если экзаменатор "правильно" психологически подходит к этому. Вспомнилось несколько интересных историй о собеседованиях в которых если я и не принимал участие, то как минимум стоял рядом.
История 1: IT-фирма ищет программиста
Заходит человек, которого будут собеседовать на должность то ли мидл, то ли джуниор (С++ разработчик). До этого он уже прошел какие-то ступени, но не суть. Сходу, ему задают простой вопрос: какое максимальное количество переменных можно обьявить в теле функции 😂 Ну и все на этом как бы. Что с него дальше спрашивать? 😂😂😂
История 2: та же IT-фирма ищет программиста
Снова заходит человек, должность тоже вроде джун-мидл, С++. Тестирующий - уже другой специалист. Здрасьте-здрасьте. Вопрос: смотрите, у нас точка входа - это void main, это всем известно, но нам нужно запустить процедуру до вызова void main. Опишите процесс запуска программы и как можно бы осуществить этот вызов, вот бумажка, можете на ней писать код\рисовать.
Первые две истории - это то, что меня тогда очень удивило 😂
История 3: собеседование в Газпром (вроде на должность в гостиницу)
Человек прошел все ступени, осталось поговорить с интервьюером по видео и продемонстрировать знание английского. Вопрос, который задал интервьюер: переведите, пожалуйста, стихотворение Пушкина "У Лукоморья дуб зеленый" 😂 Человек как-то перевел 😂 После этого был только 1 вопрос : "а почему нескладно-то получилось?"
-- По вечерам разрабатываю сервис для общения. Кому интересено, можете подписаться куда-нибудь на меня, попробуете его в числе первых. Постепенно буду продолжать делиться успехами разработки сервиса.
Конец ноября в Питере — крайне мрачное время года, на улице лишь холод, сырость и уныние. Что определенно влияет на психику местных обитателей, порождая в головах самые нездоровые желания и идеи, которые затем попадают в криминальную хронику.
Видимо по этой причине, одним мрачным осенним днем мне захотелось создать графическое приложение на современном C++ под.. классический Mac из 90х.
В действии.
История
Честно не знаю что вам сказать, на этот раз у меня нет оправданий или логических объяснений для того что я опять сотворил.
Cпишем на плохую погоду и рептилоидов с Нибиру:
рассказываю и показываю как создавать приложения на современном C++ для классического Apple Macintosh начала 90х.
Примерно такого:
Macintosh Quadra 700. Компьютер как компьютер.
The Macintosh Quadra 700 is a personal computer designed, manufactured and sold by Apple Computer from October 1991 to March 1993.
Сам я никогда не видел такие машины в живую, поскольку в годы когда они выпускались был совсем зеленой школотой и жил в далеких сибирских пердях. Хотя даже живи я посреди Нью-Йорка в 90е — врядли бы смог позволить компьютер за почти шесть штук баксов:
The Quadra 700 originally had a list price of US$5,700
Такое и сегодня далеко не для всех.
Однако времена славы Макинтошей давно прошли, ныне все эти некогда «звездные» компьютеры — те что еще остались в рабочем состоянии, являются музейными экспонатами и предметами коллекционирования.
Поэтому, то что опишу ниже — точно не имеет никакого практического применения:
все это лишь эталонная, дистиллированная дичь, без примесей и добавок.
Это на тот случай, если вы вдруг ожидаете от статьи чего-то большего.
Весь сетап целиком.
Все началось, когда были выкованы мегакольц.. ээ нет, это из немного другой эпической сказки. В нашей все было проще — автор самым банальным образом набрел в сети на один интересный репозиторий, обещающий экзотическое путешествие в мир ужаса неведомые дали:
A GCC-based cross-compilation environment for 68K and PowerPC Macs. Why? Because there is no decent C++17 Compiler targeting Apple's System 6. If that's not a sufficient reason for you, I'm sure you will find something more useful elsewhere.
Разве можно было пройти мимо столь вдохновляющего описания?
Я тоже не удержался, убив в итоге несколько лишних дней на оживление и запуск этого чудовища чуда. Но прежде чем пойдем дальше, стоит рассказать читателям немного старой доброй матчасти:
почему автор так возбудился на какой-то неведомый набор консольных программ без картинок с голыми женщинами, еще и делающий с точки зрения обывателя не пойми что.
Матчасть
Представьте, что на вашем столе лежит вот такой кусок текстолита с железом, для которого надо написать программу:
Хотя именно этот девайс на самом деле весьма мощный.
Да, на фото выше самый настоящий компьютер:
c процессором, памятью и портами ввода-вывода — все как положено.
Но только слишком слабый для развертывания полноценной среды разработки непосредственно на нем.
Поэтому если вы не маньяк-психопат LISP-разработчик — будет откровенно сложно вести какую-либо разработку на такой хне без кросс-компиляции.
Кросс-компиляция это когда на обычном офисном компьютере без отрыва от игор cобирается приложение, предназначенное для запуска на принципиально другом устройстве:
смартфоне, роутере, соковыжималке, встраиваемой платформе умного дома, коптере или еще каком боевом роботе.
Или на другой операционной системе — сборка на линуксе под Windows это тоже вполне себе кросс-компиляция.
Тулчейн для кросс-компиляции обычно устанавливается (и даже собирается) отдельно, часто бывая очень сложным. Еще он содержит компиляторы, линкеры, ассемблеры для «вражеской» архитектуры и многое другое, не менее интересное.
В некоторых случаях тулчейн включает и части целевой системы:
заголовочные файлы, ресурсы и даже готовые библиотеки для линковки.
Без которых создавать что-то сложнее «Hello, world» с помощью кросс-компиляции было бы проблематично.
Так выглядит тот самый "Hello,world", собранный через кросс-компиляцию и запущенный в эмуляторе с MacOS 7.
Retro68
Теперь, после раскрытия матчасти можно переходить к этому замечательному проекту, чтобы оценить по достоинству масштаб проделанной работы и ее важность:
a gcc-based cross-compiler for classic 68K and PPC Macintoshes
Во-первых «gcc-based», что означает определенную совместимость с нормальными и современными компиляторами, а также отсутствие экзотических проблем, характерных для редких и/или устаревших компиляторов, вроде bcc.
Во-вторых тут две разных целевых архитектуры: 68k и PowerPC.
А значит можно собирать софт для всей линейки Маков с начала 90х и до начала 2000х — до MacOS X Tiger, последней поддерживающей архитектуру PowerPC.
Но пожалуй самое важное это поддержка трех разных UI-фреймворков на целевых системах:
Что означает возможность создавать приложения с графическим интерфейсом для всего зоопарка винтажных маков.
Типа такого:
Так выглядит демо с диалогом, созданное при помощи кросс-компиляции.
И все это без доступа к самому физическому устройству и пыток разработкой непосредственно в эмуляторе. Кстати процесс переноса собранного приложения (в примерах) в целевую систему также максимально упрощен:
сразу собирается готовый образ диска, который затем можно записать на носитель, либо подсунуть эмулятору.
Терминал
Когда только начинал собирать материал для этой статьи, узнал что в первых MacOS (внезапно) не было консоли и абсолютно все приложения были только и исключительно графическими:
возможно собрать в графическое, с таким псевдо-терминалом в комплекте:
Тоже является заслугой тулчейна Retro68 и ключем линковки при сборке:
LDFLAGS=-lRetroConsole
Сборка на FreeBSD
Тулчейн официально поддерживает несколько популярных операционных систем:
Linux, Mac OS X or Windows (via Cygwin)
И действительно совершенно спокойно, без каких-либо проблем и нюансов собирается на линуксе.
Что мне показалось слишком скучным, поэтому ради гусарской забавы портировал этот проект на FreeBSD.
Всегда так делаю.
Если вам такие забавы не близки, сообщаю что для линукса достаточно по шагам выполнить инструкцию в README. И все сразу будет хорошо, только не у вас.
Ну а мы как обычно пойдем путем хардкора — соберем весь тулчейн на FreeeBSD с нуля.
Кстати форк Retro68 с поддержкой сборки на FreeBSD выложен в отдельном репозитории. Первым делом надо установить ряд инструментов для разработки и системных библиотек:
Теперь надо немного изменить скрипт сборки, для учета особенностей FreeBSD.
Первым делом заменяем вызовы make на gmake, поскольку FreeBSD использует свою версию утилиты make, несовместимую с GNU-версией. Которая конечно также присутствует в пакетах, но только называется gmake.
Дальше необходимо переделать вызовы configure для вложенных проектов из скрипта сборки, суть правок:
пробросить указание на использование библиотек из каталога /usr/local, куда устанавливаются все внешние с точки зрения системы библиотеки в FreeBSD.
Тут уже нельзя использовать переменные окружения CFLAGS и LDFLAGS из-за особенностей сборки GCC и необходимо передавать пути для ключевых библиотек специальными аргументами:
Таким же образом необходимо поправить еще два места в скрипте сборки: раз и два.
После чего можно запускать сборку.
Запускается она из отдельного каталога:
mkdir ../Retro68-build cd ../Retro68-build ../Retro68/build-toolchain.bash
После весьма продолжительной (даже на мощном компьютере) и жутко выглядящей сборки, по накалу страстей на экране напоминающей знаменитый make buildworld из FreeBSD:
..все упадет на вот такой «страшной» ошибке:
Для исправления ситуации, надо всего лишь изменить один import в файле portable_endian.h , в этом месте:
Суть проблемы в том что расположение заголовка endian.h поменялось и теперь его необходимо включать без указания каталога sys:
# include <endian.h>
Исправляем импорт и перезапускаем сборку.
Однако через какое-то время напряженной работы и в очередной раз напугав обывателя простыней текста, сборка.. опять упадет.
На этот раз падение произойдет на стадии сборки из cmake:
Несмотря на весь внешний ужас, исправляется эта ошибка очень просто (если знать где копать разумеется):
удалением импорта этого заголовка из файла PEFTools/MakePEF.cc
Поскольку его содержимое в последних версиях FreeBSD добавили в stdlib. Так что смело удаляем импорт и перезапускаем сборку.
Следующее падение сборки и следующая проблема заключается в пересечении заголовков — системного, из самой FreeBSD и локального — из библиотеки libelf:
Тут к сожалению я не нашел изящного решения, достойного быть упомянутым в летописях, поэтому сделал «колхоз»:
скопировал файл gelf.h из libelf/include в каталог Elf2Mac, и поменял ссылки на этот заголовок из глобального на локальный.
Было:
#include <gelf.h>
Стало:
#include "gelf.h"
Поменять ссылки необходимо во всех файлах проекта Elf2Mac где встречается данный импорт. Наконец последняя ошибка сборки, связанная с особенностями FreeBSD:
Связана эта ошибка с тем, что часть необходимых структур для работы с сетью на FreeBSD вынесена в отдельный заголовок.
Готовый к использованию тулчейн будет находиться в каталоге toolchain, который стоит добавить в переменную окружения PATH.
Теперь переходим к эмулятору, поскольку живого Mac тех лет, на котором возможно запустить результат сборки у меня нет.
Эмулятор
Вообще эмуляторов винтажных Mac довольно много, поскольку маководы это сектанты очень увлеченные люди и многие из них используют и устаревшие компьютеры Apple и устаревший софт до сих пор, всячески отвергая прогресс и здравый смысл.
Такова сила бренда, созданного Стивом Джобсом.
Я буду использовать один из самых популярных и известных эмуляторов классических Macintosh:
Basilisk II is an Open Source 68k Macintosh emulator.
Разработан он был еще в начале 2000х, немецким инженером Christian Bauer, ныне активная разработка уже не ведется. Поэтому вместо нее я использовал более современный форк, в котором решены вопросы совместимости с современным же окружением и обновлены используемые библиотеки.
И внезапно добавлен JIT (Just-In-Time Compiler).
Еще форкнутая версия немного стабильнее, с более адекватным поведением перехвата курсора.
Эмулятор использует следующие библиотеки, которые необходимо установить до попыток сборки:
Еще вот тут находится крайне интересная табличка, с описанием практически каждого доступного ROM, его особенностей и работоспособности.
Напомню, что для этой статьи я эмулировал модель Quadra 600:
Для эмулятора использовался вот такой ROM-файл:
Указав в разделе «Volumes» путь к образу диска с MacOS7, затем в разделе «Memory/Misc» путь к ROM-файлу, можно наконец запускать эмулятор:
Теперь переходим к самому веселому — к разработке.
Разработка с помощью Retro68
Собственно после сборки Retro68, у вас будет все что нужно для разработки конечного софта, поскольку в каталоге build-target/Samples будут собранные примеры приложений:
Эти примеры собираются автоматически, во время сборки самого тулчейна Retro68, в качестве дополнительных тестов работоспособности.
Для сборки во всех случаях используется cmake, исходники находятся в каталоге Retro68/Samples.
Чтобы собрать проект с HelloWorld отдельно, выполняем:
сd /opt/src/Retro68/Samples/HelloWorld mkdir build cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=/opt/src/Retro68-build/toolchain/m68k-apple-macos/cmake/retro68.toolchain.cmake gmake
Результат сборки:
Каждый пример при сборке генерирует образ диска, который можно сразу подключить к эмулятору:
Достаточно указать путь к dsk-файлу, который находится в каталоге build и запустить эмулятор:
На рабочем столе появится иконка с названием, совпадающим с названием примера:
После двойного клика по этой иконке, раскроется окно с содержимым этого виртуального диска в виде единственного запускаемого файла:
Двойной клик по файлу HelloWorld запустит наше приложение.
Примеры приложений
Как уже упоминал, в составе Retro68 есть довольно много демонстрационных приложений.
Каждый пример раскрывает аспект разработки и создан так, чтобы его можно было использовать в качестве основы для собственных проектов.
Автор тулчейна постарался, поэтому все примеры логичны, просты и понятны.
Retro68/Samples/Raytracer
Трассировщик лучей, реализующий генерацию псевдотрехмерной картинки в реальном времени. Собственно процесс отрисовки в этом приложении как раз показан на заглавной картинке к статье.
В проекте на самом деле присутствуют сразу две реализации, одна на С, другая на C++, видимо для сравнения во время работы.
Так выглядит финальный результат рендеринга:
Проект ценен в первую очередь как демонстрация расчетной логики и операций с числами с плавающей точкой.
Retro68/Samples/Dialog
Демонстрация работы с модальными диалогами — как задавать размеры и расположение элементов, как создавать формы ввода, как получать вводимые данные и так далее.
В работе:
Retro68/Samples/WDEF
Демонстрация работы с многооконными интерфейсами (MDI), создание окна с кастомным оформлением и работа с ресурсами окон.
Так это выглядит в действии:
Кратко по остальным примерам, реализующим какую-то специфическую логику.
Retro68/Samples/MPWTool
Реализует расширение для MPW (Macintosh Programmer's Workshop), точнее для MPWShell, с обработкой запуска оттуда и корректного возвращения в консоль.
Retro68/Samples/SharedLibrary
Реализует разделяемую библиотеку и приложение, его использующее в рантайме.
Retro68/Samples/SystemExtension
Пример простейшего расширения для самой операционной системы MacOS 7, в данном случае вся видимая логика заключается в показе новой иконки при запуске ОС.
Retro68/Samples/Launcher
Реализует запуск сторонних приложений через перетаскивание (Drag&Drop).
"Masters of Hardcore" - вбейте в поиск если не жалко уши и соседей.
Настоящий хардкор
Вы же не думали, что я успокоюсь на банальной сборке тулчейна с эмулятором под FreeBSD и запуске пары тестов?
Clapkit (CLassic APplication KIT) is a framework for developing applications for Classic Mac OS (System 7.x - Mac OS 9.x), basically a C++/object-oriented wrapper for Macintosh Toolbox functions.
Да, глаза вас не подводят, это самый настоящий современный графический фреймворк для компьютеров начала 90х.
Так это выглядит:
Картина неизвестного питерского художника "Мак под маком".
Так выглядит код:
#include <ckApp.h>
int main() {
CKApp* app = CKNew CKApp();
app->CKNewMsgBoxNote("Hello world!", nullptr, "OK", nullptr, [app](int button) { app->CKQuit(); }); // Run loop: without this, your app will quit as soon as it launches. while (!app->CKLoop(5)); delete app; return 0; }
Так выглядит мое тестовое приложение на современном C++, собранное с помощью cmake на FreeBSD и запущенное на MacOS 7 из 90х:
Поддержки юникода тогда еще не было, как и локализации на русский, поэтому "великий и могучий" есть лишь в комментариях.
Ну что дорогие читатели, ударим шизой по осенней летней хандре?
MPW
Конечно ударим, но прежде стоит рассказать про нормальную разработку для продукции Apple, как это происходило у нормальных людей.
Ее вполне можно использовать до сих пор, благо образов дисков в сети хватает. Именно так я и советую поступать (т.е. использовать MPW) в случае любого более-менее серьезного проекта, поскольку где и когда закончатся возможности кросс-компиляции и вы опустите руки — предсказать не берусь.
Как-то так MPW выглядит в эмуляторе:
Тут находится отличная статья по созданию «Hello, world» на MPW в эмуляторе, которой вполне хватит для начала приключений. Но в этот раз нам нужен не MPW а только его часть — системные заголовки, которые мы будем использовать для злодейства сборки.
В Retro68, в каталоге InterfacesAndLibraries находится файл Readme.md, содержимое которого явно намекает на дальнейшие действия:
Find a copy of Apple's Universal Interfaces 3.x (preferrably 3.4) and put them here.
Вообще «Find a copy» это такой толерантный и социально приемлемый синоним старого доброго «скачать варез», прямо из 90х. Потому как купить официально старое ПО уже не представляется возможным, если подобное вдруг придет в голову. Даже если компания-разработчик существует (как в случае Apple), старые версии своих продуктов она так просто не продаст.
Потому что есть поддержка, гарантии и защита прав потребителей.
И репутация компании.
Поэтому сообщаю по секрету, что «найти» эти самые универсальные интерфейсы можно например тут:
Сборка построена на cmake, но перед тем как собирать Clapkit, необходимо указать путь к Retro68 в скрипте сборки:
# Ensure Retro68 toolchain is set BEFORE defining project set(CMAKE_TOOLCHAIN_FILE "/opt/src/Retro68-build/toolchain/m68k-apple-macos/cmake/retro68.toolchain.cmake")
К сожалению в проекте есть небольшой баг с пропущенными заголовками, из-за которых он не собирается.
Для исправления достаточно добавить в include/ckTypes.h два импорта:
#include <Quickdraw.h> #include <Events.h>
В репозитории проекта находится готовый Showcase с демонстрацией возможностей фреймворка, на базе которого я и делал свое тестовое приложение (см. ниже).
Так выглядит сам Showcase в работе:
Напоминаю, что все это создано с помощью кросс-компиляции и затем запущено в целевой MacOS 7.
Выглядит заметно веселее, чем примеры из самого Retro68, согласитесь.
Тестовый образец
Я не хотел сразу пускаться во все тяжкие и портировать Doom/Quake/Cyberpunk 2077 под винтажные Маки из 90х — надо же оставить место воображению, поэтому мой тестовый образец для этой статьи это в первую очередь пример автономного, отделяемого приложения с минимальной логикой.
Которое собирается без привязки к иерархии каталогов Clapkit или Retro68.
отображает меню «Yo» в Finder, с единственным пунктом «Run me», по клику на который вызывается модальный диалог с двумя кнопками. По нажатию на «Quit» произойдет завершение работы программы.
Фигня-фигней, но только разработано и собрано оно полностью и целиком вне инфраструктуры MacOS и без средств разработки от Apple, даже без техники Apple.
На современном С++, под FreeBSD темной питерской ночью.
Так выглядит основной код на C++, файл main.cpp:
#include "main.h" #include <ckMenu.h>
CKTestAlexs* app;
int main() { // класс переименован ради тестов app = CKNew CKTestAlexs(); // false - не использовать стандартные пункты меню CKMenuBar* menuBar = CKNew CKMenuBar(false); // добавляем свой пункт меню в Finder CKMenu* menuTests = CKNew CKMenu("Yo"); menuBar->AddMenu(menuTests); // затем элемент в выпадающем списке CKMenuItem* item = CKNew CKMenuItem("Run me", 'R', [&item](CKEvent e) { // по клику произойдет отображение стандартного // модального диалога app->CKNewMsgBoxNote("Welcome to old school!", "Hello world", "Mkay", "Quit", [](int button) { // если нажали Quit - завершить работу приложения. if (button == 0) { app->CKQuit(); } }); }); // связывание элемента с пунктом меню menuTests->AddItem(item); // связывание меню с приложением app->CKSetMenu(menuBar); // бесконечный цикл ожидания для отрисовки, // без которого приложение сразу завершится while (!app->CKLoop(5)); delete app; return 0; }
Класс описан в заголовке main.h, декларации методов я убрал за ненадобностью:
#include <ckApp.h>
class CKTestAlexs : public CKApp { };
Так выглядит скрипт сборки для cmake, как можно заметить тут происходит автоматическое скачивание родительского фреймворка Clapkit и наложение патча с пропущенными импортами (описаны выше):
cmake_minimum_required(VERSION 3.16)
# Ensure Retro68 toolchain is set BEFORE defining project set(RETRO68_PATH "/opt/src/Retro68-build/toolchain/m68k-apple-macos") set(CMAKE_TOOLCHAIN_FILE "${RETRO68_PATH}/cmake/retro68.toolchain.cmake")
# Include Retro68 headers include_directories(${RETRO68_PATH}/include)
# Set default build type to Release if not specified if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (Debug or Release)" FORCE) endif()
set(CLAPKIT_LOCAL_PATH "${CMAKE_SOURCE_DIR}/../../") option(USE_LOCAL_CLAPKIT "Use local checkout of clapkit instead of fetching from Git" ON)
if(USE_LOCAL_CLAPKIT AND EXISTS "${CLAPKIT_LOCAL_PATH}/CMakeLists.txt") message(STATUS "Using local clapkit at ${CLAPKIT_LOCAL_PATH}") add_subdirectory(${CLAPKIT_LOCAL_PATH} clapkit) set(USE_LOCAL_CLAPKIT ON) else()
Through some luck and a little persistence I have actually managed to get Rust code running on classic Mac OS (I’ve tried Mac OS 7.5 and 8.1)
Репозиторий находится тут, проект достаточно свежий (2023 год) и вполне рабочий.
Но предупреждаю сразу, что от исходного кода может случиться сердечный приступ, если любите классический C — на это лучше не смотреть.
Так оно выглядит в работе:
Кстати приложение сетевое, так что на Rust реализована не только работа с интерфейсом, но и с TCP/IP и даже HTTP протоколом.
Для операционной системы из 1994 года.
P.S.
Статья была опубликована на Хабре, более вольный оригинал как обычно в нашем блоге, форк Retro68 с поддержкой сборки на FreeBSD выложен в отдельном репозитории на Github.
Иногда нам нужно найти значение в отсортированном массиве. Простейший алгоритм заключается в последовательном переборе значений, пока мы не встретим искомое значение или не достигнем конца массива. Такой алгоритм иногда называют линейным поиском. В C++ добиться такого же эффекта можно с помощью функции std::find.
Для больших массивов лучшего результата можно достичь с помощью бинарного поиска. Бинарный поиск является классическим алгоритмом, который эффективно находит целевое значение в отсортированном массиве, многократно деля интервал поиска пополам. Начиная со всего массива, он сравнивает целевое значение со средним элементом: если цель меньше, верхняя половина отбрасывается; если больше — отбрасывается нижняя. Этот процесс продолжается, пока цель не будет найдена или интервал не станет пустым. На больших наборах данных бинарный поиск значительно быстрее линейного. В C++ он реализован функцией std::binary_search, которая возвращает true или false в зависимости от существования элемента.
Популярный формат Roaring Bitmap использует массивы 16-битных целых чисел размером от 1 до 4096 элементов. Иногда нам приходится проверить, существует ли значение в этом массиве. Для этого мы используем бинарный поиск.
Я захотел создать более быстрый подход. У меня были две мысли:
Практически все процессоры сегодня имеют инструкции для параллельной обработки данных (также называемая SIMD), которые могут проверять несколько значений за раз. И 64-битные ARM, и x64 процессоры (Intel/AMD) всегда поддерживают сравнение восьми 16-битных чисел с искомым значением используя всего одну инструкцию. Это подсказывает, что не стоит углубляться в бинарном поиске до блоков размером меньше восьми элементов. Кроме того, имеет смысл сравнивать шестнадцать элементов и больше.
Бинарный поиск проверяет одно значение за раз. Однако, современные процессоры могут загружать и проверять более одного значения одновременно. Они обладают прекрасным параллелизмом на уровне памяти. Это говорит о том, что вместо бинарного поиска, стоит попробовать четверичный поиск: вместо того чтобы делить массив пополам, мы можем разделить их на четыре части. Конечный результат может потребовать чуть больше инструкций, но количество инструкций перестанет являться ограничивающим фактором.
Так я и создал алгоритм, который называю SIMD Quad. Это эффективный алгоритм поиска в отсортированных массивах с 16 битными беззнаковыми целыми числами, сочетающий четверичный интерполяционный поиск с SIMD (Single Instruction, Multiple Data). Алгоритм делит массив на блоки фиксированного размера по 16 элементов (возможно, за исключением последнего блока) и использует последний элемент каждого блока как интерполяционный ключ, чтобы быстро сузить область поиска до одного блока, а затем применяет SIMD-инструкции для одновременной проверки всех 16 элементов в этом блоке.
В основе лежит идея выполнения иерархического поиска: сначала используется интерполяционный поиск на более грубом уровне (по границам блоков), чтобы найти блок, содержащий целевое значение, а затем происходит переключение на SIMD для точной параллельной проверки внутри блока. Этот гибридный подход использует и сильные стороны алгоритмической оптимизации, так и аппаратного ускорения (SIMD проверяет несколько элементов за раз).
Начальная проверка: если в массиве менее 16 элементов, выполняется простой линейный поиск
Разбиение на блоки: массив делится на блоки по 16 последовательных элементов. Для массива размером cardinality существует num_blocks = cardinality / 16 полных блоков.
Четверичный интерполяционный поиск: используем последний элемент каждого блока (на позициях 16-1, 32-1 и других) как ключи для интерполяции. Поиск выполняет четверичную (по основанию 4) интерполяцию, чтобы найти блок, в котором, вероятно, находится целевое значение. Это включает в себя сравнение цели с точками, делящими текущий диапазон поиска на четверти.
Выбор блока: после сужения диапазона выбирается подходящий индекс блока lo на основе результатов интерполяции.
SIMD-проверка: если найден верный блок, 16 элементов загружаются в SIMD-регистры (с использованием NEON на ARM или SSE2 на x64), после чего выполняются параллельные сравнения на равенство с целевым значением. Если найдено найдено хотя бы одно совпадание, возвращается true.
Проверка остатка: для элементов, не вошедших в полные блоки (остаток) выполняется линейный поиск.
Каковы результаты этого? Я написал бенчмарк. Бенчмарк работает следующим образом: для каждого массива размером от 2 до 4096 элементов генерируется 100,000 сортированных массивов с 16-битными беззнаковыми целыми числами. Для каждого размера выполняется 10 миллионов запросов на принадлежность в «холодном» режиме (каждый запрос ищет в другом массиве, имитируя промахи кэша) и 10 миллионов запросов в «горячем» режиме (запросы сгруппированы по массивам, каждый массив обыскивается 100 раз подряд, имитируя попадания в кэш). Бенчмарк измеряет среднее время на один запрос для трёх алгоритмов: линейного поиска (std::find), бинарного поиска (std::binary_search) и нового алгоритма SIMD Quad.
Я использую две системы: Apple M4 с Apple LLVM и процессор Intel Emerald Rapids с GCC.
Во-первых, давайте сравним линейный и бинарный поиск.
Intel/GCC
Apple/LLVM
Результат ясен. Бинарный поиск лучше чем линейный поиск, как только массивы становятся большими. Этого следовало ожидать.
На холодном кэше линейный поиск относительно хуже. Этого следует ожидать, потому что он получает доступ к большему количеству данных, вызывая больше ошибок кэша.
Мы установили, что бинарный поиск является чистым победителем по линейному поиску. Давайте теперь сравним с алгоритмом SIMD Quad.
Intel/GCC
Apple/LLVM
Результаты заметно различаются между Intel и Apple. На Intel SIMD Quad более чем вдвое быстрее бинарного поиска на «горячем» кэше. На «холодном» кэше преимущества меньше. На платформе Apple ситуация обратная: именно на «холодном» кэше SIMD Quad оказывается более чем вдвое быстрее, тогда как на «горячем» кэше выигрыш более скромный.
Но важней вывод заключается в том, что во всех случаях SIMD Quad быстрее бинарного поиска.
SIMD-компонент алгоритма довольно прост: мы используем специализированные инструкции, которые сокращают объем работы. ПОэтому легко понять, почму это может ускорить процесс - меньше инструкций, меньше ветвлений.
Но как насчет «четверичной» (quad) части? Тогда я опробовал бинарную версию того же алгоритма. В нем присутствует та же SIMD-оптимизация, но я отказался от четверичного интерполяционного поиска и заменяю его стандартным бинарным поиском.
Intel/GCC
Apple/LLVM
Говоря простыми словами, подход с четверичным поиском (quad) почти и не дает эффекта на Apple, однако на Intel он является неплохой оптимизацией для больших массивов в «холодном» режиме. Четверичный поиск лучше задействует параллелизм на уровне памяти на моём Intel-сервере.
Заключение: мои результаты говорят о том, что, хотя классический бинарный алгоритм из учебника — вполне достойный, его можно улучшить, причем ощутимо. Стандартные алгоритмы зачастую не проектировались под компьютеры с таким объемом параллелизма. Алгоритм SIMD Quad пытается задействовать и параллелизм на уровне памяти, и параллелизм на уровне данных. Более того, я подозреваю, что можно добиться даже большего, чем даёт мой алгоритм. Будьте креативнее!
❯ Исходный код
bool simd_quad(constuint16_t*carr, int32_t cardinality, uint16_t pos) { constexprint32_t gap = 16; if (cardinality < gap) { for (int32_t j = 0; j < cardinality; j++) { if (carr[j] == pos) return true; } return false; } int32_t num_blocks = cardinality / gap; int32_t base = 0; int32_t n = num_blocks; while (n > 3) { int32_t quarter = n >> 2;
У нас было 640Кб памяти, CGA-экран, 20-мегабайтовый диск и целых четыре мегагерца тактовой частоты. А еще старые пятидюймовые дискеты на 360кб. Не то чтобы это был необходимый набор для современного разработчика C++, но если уж начали коллекционировать дичь, то сложно остановиться..
Весь сетап целиком.
Проклятая выдача
Наверное все уже в курсе, что социальные сети и поисковые системы постоянно подкручивают ленту выдачи под каждого пользователя на основе его предыдущих запросов и проявленных интересов?
На меня эти алгоритмы тоже начали влиять, но.. особенным образом:
формируя персональную ленту из самой отмороженной компьютерной дичи, которая только есть на свете.
Когда я уже не мог выдерживать накал выдаваемого проклятыми алгоритмами — начал про всю эту дичь писать. На свет стали появляться статьи, испортившие безвозвратно психику, взгляды на жизнь и карьеру многим несчастным.
Если серьезно, то конечно же этот материал появился не на пустом месте и не за один день, это результат довольно долгого изучения матчасти, ковыряния исходников и темных ритуалов из Некрономикона.
Все ради того чтобы в очередной раз показать читателям невозможное, по мнению ИИ и обитателей StackOverflow.
Технический ультрахардкор
Коль уж читаете эту статью — скорее всего и так знаете через какое место как именно программы появляются на свет и какова роль компилятора в этом сложном процессе.
Для простых обывателей поясняю:
чтобы получить тот самый steam.exe, которым вы жадными ручками запускаете любимые игры, его необходимо собрать из исходников.
Сотрудник компании Valve вместо работы над Half-Life 3 запускает компилятор, который собирает из набора файлов с исходниками «финальный билд», который затем упаковывается в инсталлятор и выкладывается на официальный сайт Steam.
Откуда его потом скачивают ушастые простые пользователи.
Теперь представьте:
внезапно настал зомби-апокалипсис и всех программистов, которые занимались разработкой игр в Valve сожрали зомби, после чего процесс сборки, вся документация и тулчейны были утрачены.
C концами.
Все что осталось — несколько старых бекапов на забытых носителях, с трудом извлеченные из.. ээ останков голодного зомби. Из задней части.
Спустя пару сотен лет, вы — представитель жалких остатков человечества, выживших на небольшой колонии с обратной стороны Луны, вновь ступаете на опустевшую Землю в поисках артефактов погибшей цивилизации.
И находите на развалинах бывшего офиса Valve ту самую флешку с исходниками альфа-версии Half-Life 3.
Конечно прогресс в разработке программ к тому времени успел уйти далеко вперед, у вас в ходу на лунной базе теперь какие-нибудь C++-157 и clang-777, с встроенным Rust, тремя слоями виртуализации и сборщиком мусора, работающим через промпты LLM. А про язык C вы узнали лишь из лекции по истории, про исторический период между египетскими пирамидами и вторжением инопланетян.
Встает очевидный вопрос:
как же все-таки насладиться шедевром далеких предков, так и не увидевшим свет в свое время?
Многое можно отдать за запуск этой красоты, даже спустя пару сотен лет после зомби-апокалипсиса:
Нет, это не релизная версия.
Несмотря на весь этот «апокалиптический треш», ситуация сама по себе — более чем реальная:
прямо сейчас эксплуатируются полностью виртуальные системы, собственных средств разработки для которых либо уже нет совсем, либо такая разработка чрезвычайно затруднена.
В первую очередь это мейнфреймы, которые до сих пор живее всех живых, вроде такого красавца от Unisys:
Далеко не только IBM занимается мейнфреймами до сих пор.
Если немного снизить накал технического фетишизма экстрима и обратить взор к современной коммерческой разработке — можно внезапно обнаружить, что фактически вся ее продуктовая часть (там где создается приложение, устанавливаемое у конечного пользователя) работает с обязательной поддержкой устаревшего окружения:
поддержка Windows 7/10, поддержка старых версий Android и iOS, обязательная работа на JRE 1.8+ и так далее и тому подобное.
Разумеется на машинах разработчиков используется современный софт, но собирает он так чтобы конечное приложение без проблем запускалось и работало в устаревшем окружении.
Обратное портирование
Процесс, с помощью которого в окружении 21го века создается нечто работающее на оборудовании из века 20го называется обратным портированием (backporting). И мы уже неоднократно о нем рассказывали, например тут или тут.
Обычно бекпорт создается для окружения с не очень большим устареванием — лет на 5-10 с момента релиза, поскольку на таком промежутке и проходят все основные обновления пользовательского окружения.
В редких случаях (и за отдельную плату) разработчик ПО может согласиться покопаться в чем-то еще более древнем, но уже без гарантий.
Однако то что покажу в этой статье — однозначно выходит за любую границу разумности, проходя даже среди коллег по категории отбитой дичи научной фантастики:
приложение на современном C++, с современными фичами, созданное в современной ОС, которое работает в окружении из 1986 года!
Операционной системой, которую выпустили 40 лет назад выступит известная MS-DOS 3.20. Именно эта версия была выбрана ввиду одной интересной исторической особенности:
Version 3.20 – First retail release (non-OEM); Release date: July 1986
Это первая версия MS-DOS, продаваемая конечным пользователям напрямую от самой Microsoft, с полок магазинов.
До нее Microsoft продавала свою операционную систему только производителям компьютеров, которые включали ее в поставку своих продуктов и под своим собственным брендом:
Наконец показываю, как выглядит компьютер тех лет, для которого мы будем сейчас писать код на современном C++:
Фото современное, таких машин в живом состоянии сохранилось довольно много.
Комната жениха
Для начала, опишу тестовое окружение, которое будет использовано для столь задорной задачи. Три ключевых компонента, необходимых для осуществления задуманного злодейства:
Компилятор C++ DigitalMars, который до сих пор официально поддерживает сборку под DOS;
Думаю уже по одному этому абзацу можно догадаться о накале предстоящей дичи, хотя ряд нюансов все же стоит пояснить.
Во-первых Вена это не только столица Австрии, но еще и небольшой городок на севере США, на 15к жителей, где (внезапно) находится штаб-квартира серьезной софтверной компании, выпускающей эпические компиляторы аж с 1988 года:
In 1988, Zortech was the first C++ compiler to ship for Windows. PC Magazine ran a graphics benchmark and reported that most executables produced by Zortech ran faster than executables produced by Microsoft C 5.1 and by Watcom C 6.5.[3]
Вот так, парни переплюнули официальный компилятор от самого Microsoft, несмотря на весь инсайд и поддержку. Кстати «Zortech» — старое название компилятора (если вдруг будете искать в архивах), продукт по какой-то причине несколько раз переименовывался.
Есть еще один удивительный факт, связанный с этой компанией:
The company gained notice in the software development community for creating the D programming language. D resulted from Bright's frustration with the direction of the C++ language and from his experience implementing it.
Ребята до такой степени угорели по С++, что создали свой собственный язык, «по мотивам»:
resulted from Bright's frustration with the direction of the C++ language
Но вернемся к другим талантам этой замечательной компании — к разработкам для совсем старой школы:
Includes C++ templates, exception handling and rtti.
Most other compilers for 16 bit code were abandoned nearly a decade ago. Digital Mars has a modern compiler for 16 bits.
Все перечисленные выше радости — для официально поддерживаемой сборки под DOS и 16-битных приложений.
Догадываюсь, что современным программистам тяжело воспринимать реалии разработки тех лет и технические ограничения софта из далекого прошлого, поэтому в качестве иллюстрации, покажу таблицу моделей памяти создаваемых приложений:
Сравните это с современными "запускаемыми архивами" с играми, по 100Гб в одном .EXE
Это предельные размеры создаваемого приложения, в случае MS-DOS 3.20 мы будем использовать «s» модель памяти, поэтому размер финальной сборки не должен превышать 64Кб.
Сборка и запуск
Компиляторы C и C++ от DigitalMars изначально были коммерческим продуктом и до сих пор существует коммерческая версия, включающая помимо компиляторов еще и собственную среду разработки (IDDE). Однако не так давно компиляторы стали бесплатным и открытым проектом, с исходниками на Github.
К сожалению проект пока не портируемый — собирается и работает исключительно под Windows.
Тем не менее, поскольку создавали его очень опытные люди — все отлично работает в Wine, что мы и будем использовать для его запуска на FreeBSD. Чтобы снова не раздувать статью — не стал заморачиваться сборкой компилятора из исходников (хотя это не очень сложный процесс), использовав готовую сборку версии 8.5.7.
Инсталлятора тут нет, в архиве сразу готовые бинарники, поэтому распаковываем и запускаем:
Внутри будут дополнительные бинарники, главный из которых это довольно известный EXE2BIN.COM, используемый для генерации COM-файлов из EXE.
С EXE2BIN есть один важный нюанс — он не запустится с помощью Wine, поскольку является DOS-приложением.
Чтобы использовать эту утилиту для создания COM-файлов — необходимо запускать ее с помощью DOSbox:
Теперь переходим к основному эмулятору.
Официальный логотип 86Box
86Box
Достаточно новый проект (разрабатывается с 2016 года), который сейчас активно развивается и позволяет эмулировать огромное множество x86-совместимых систем:
86Box is an IBM PC system emulator that specializes in running old operating systems and software designed for IBM PC systems and compatibles from 1981 through fairly recent system designs based on the PCI bus.
Готовой сборки для FreeBSD пока нет (как и присутствия в портах) и еще год назад были проблемы, требующие ручных правок исходников:
Initially exclusive to Windows, it was ported to Linux in version 3.2 and macOS in version 3.4.
Но на момент написания статьи все поправлено и эмулятор отлично собирается из исходников без каких-либо проблем, а в инструкции по сборке появился раздел про FreeBSD.
Для релизных версий эмулятора — ROM-файлы надо брать в другом месте, со страницы releases. Запускается эмулятор с указанием путей к каталогу с ROM-файлами следующим образом:
./src/86Box --rompath /opt/src/roms
После запуска эмулятора (по прямой аналогии с каким-нибудь VirtualBox) необходимо создать новую виртуальную машину, задав ей вот такие настройки:
Это не единственный возможный сетап эмулируемого оборудования, но максимально приближенный к реалиям 1986 года.
Так выглядела обложка коробочной версии с MS-DOS. Помимо префикса, слово "Microsoft" встречается трижды ;)
MS-DOS
Образы самого MS-DOS достать в сети очень легко, например отсюда. Все они уже давно находятся в публичном пользовании (public domain), поэтому за их использование Microsoft вас не съест засудит.
Более того, даже исходный код некоторых версий MS-DOS был выложен в публичный доступ:
The original sources of MS-DOS 1.25, 2.0, and 4.0 for reference purposes
Коробочная версия поставлялась на 5-дюймовых дискетах, размером в 360Кб, собственно их образы и выложены в сети.
Нам нужен лишь первый:
Этот файл нужно выбрать в меню после запуска эмулятора:
После чего перезагрузить эмулируемую систему.
При запуске пойдет проверка памяти, затем MS-DOS будет просить ввести ей дату и время, достаточно нажать пару раз <Enter> для пропуска:
MS-DOS из 1986 года готов к работе.
Mtools
Разумеется есть проблемы с пробросом данных с хоста в столь старую эмулируемую систему. Существуют несколько возможных решений, но самое простое и очевидное:
с помощью генерации образа floppy-диска на хосте и подключения его на эмулируемой системе, в виртуальный floppy-привод.
Тот самый, с которого мы выше загружали образ MS-DOS.
Для генерации образов флоппи я использовал известный пакет GNU Mtools,
In addition to file access, it supports many FAT-specific features: volume labels, FAT-specific file attributes (hidden, system, ...), "bad block" map maintenance, access to remote floppy drives, Iomega ZIP disk protection, "secure" erase, display of file's on-disk layout, etc.
Пакет довольно известный, поэтому присутствует практически везде, для FreeBSD установить его можно так:
pkg install mtools
Так выглядят те самые пятидюймовые дискеты (5.25").
Как видите все отлично работает в операционной системе 40-летней давности:
Покажите вашему преподу по C++.
Следующим шагом проверил обработку ошибок:
#include <iostream.h> #include <exception>
int main() { try { int age = 15; if (age >= 18) { cout << "Access granted - you are old enough."; } else { throw 505; } } catch (int myNum) { cout << "Access denied - You must be at least 18 years old.\n"; cout << "Error number: " << myNum; } return 0; }
Тут стоит уточнить, что поддержку обработки ошибок надо включать отдельно, ключом -Ae:
dmc -0 -ms -Ae -NV -o+space ex.cpp
Не поверите, но оно тоже работает:
Вот такие дела, конструкция try-catch на языке высокого уровня спокойно работает в MS-DOS образца 1986 года.
Еще больше балета
Разумеется я показал лишь малую часть возможностей компилятора, в первую очередь потому что уже декабрь а статья опять превратилась в простыню не успел закончить историю с STL:
STLport 4.5.3 ported to Digital Mars C++ (requires Digital Mars C++ 8.32 or later)
Это отдельная, весьма своеобразная версия STL, совместимая с DigitalMars, доступная для скачивания в собранном виде (плюс исходники). Но собрана она для целевой платформы Win32, поэтому для сборки под DOS ее готовую версию использовать не получится, надо пересобирать самостоятельно.
Сама возможность такой пересборки под DOS существует, но необходима либо платная версия DigitalMars, которая содержит собственную систему сборки (smake), либо очередной «кровавый патчинг», чтобы собрать STLport вручную.
Не поверите, но еще существует Boost (хоть и очень старый) с поддержкой компилятора DigitalMars:
Boost 1.30.0 for Digital Mars C++
Вот уж где раздолье и настоящий угар: писать под DOS c помощью самой жирной библиотеки для C++ на свете!
Чтобы окончательно добить вашу психику и чувство прекрасного, показываю несколько проектов «обратного портирования» STL — как раз для особых случаев, вроде истории с DigitalMars.
Первый проект:
A recreation of much of the modern C++ standard library in ISO C++98
Тут основная цель это C++11, который DigitalMars поддерживает очень слабо, плюс будут проблемы с Windows-платформой:
Does it work on Windows? Sorry, POSIX mostly. Would love for some Windows devs to help.
Но проект сам по себе весьма интересный и ничего не мешает надергать отсюда необходимых кусков в свою реализацию.
Возвращение на Землю
Несмотря на описанное в статье, дела с компиляторами C/C++ от DigitalMars обстоят несколько печальней чем кажется. Во-первых этот компилятор очень сильно устарел и совместимость со стандартом языка осталась на уровне C++98.
Для более новых версий стандарта есть поддержка лишь отдельных фич и планов по улучшению ситуации я не видел.
Фактически активная разработка остановилась в 2004м году, с тех пор выпускаются лишь мелкие патчи и обновления, поскольку фокус авторов сместился на поддержку языка D.
Во-вторых этот компилятор по прежнему имеет серьезные проблемы с лицензированием:
несмотря на открытие исходного кода, для полноценной сборки требуется закрытый инструментарий.
Так что использовать DigitalMars без цели угара для обычных проектов наверное мало осмысленно, хотя и привнесет определенную толику веселья в тяжелые будни C++ разработки.
Добавлю, что компилятор для наших краев довольно редкий, по крайней мере лично я не встречал проектов, где бы он использовался.
Но для целей кросс‑компиляции под DOS (если вас жизнь заставит) — инструмент более чем интересный.
P.S.
Статья была опубликована на Хабре, оригинал как обычно в нашем блоге, процесс всех этих изысканий и отбитых экспериментов выкладываем в нашем Телеграм канале.
Удален юнион UniformValue, как ненужный и не используемый
Заменена функция Set на шаблон с предопределёнными типами для удобства
UniformType удален из движка и используется только для разбора входных динамических параметров материала в RatTools
Замены легаси OpenGL буферы на DSA в коде создания динамических экземпляров (Instancing) материалов
Создание инстанций, кроме инстанции по умолчанию, теперь используют прямое копирование данных на стороне GPU, заместо заполнения буфера через шину
Добавлена заготовка под шейдерный домен Surface на основе физически корректного рендеринга (PBR)
Комментарий
Думается мне, тему инстансов на этом можно закрыть. Оно работает как нужно, параметры определяются, обновляются, читаются и рисуются ровно так, как требуется.
Для закрытии самой темы материалов осталось реализовать только вставки пользовательского кода в скрипт материала. Думаю, завтра/послезавтра уже сделаю. SPIR-V контейнеры отложим в долгий ящик. Сейчас это явно не приоритет.
Следующий этап - класс камеры и юниты источников света (чтобы можно было начать полноценную работу над PBR)
Обнаружена классная статья о том, что сейчас происходит в мире C++. Пока индустрия спорит о безопасности памяти, комитет ISO и разработчики продолжают ковать будущее любимого (пусть одного из самых сложных) многими языка. Здесь собраны не только новости, но и записи выступлений, полезные подкасты.
Комитет ISO завершил техническую работу над C++26. На ряде встреч комитет уточнил функции будущего стандарта, и это превзошло многие ожидания. Вот ключевые нововведения:
compile-time рефлексия: позволяет программе описывать свою структуру и генерировать код на этапе компиляции;
std::execution: предоставляет унифицированный фреймворк для асинхронности, конкурентности и параллелизма;
контракты: pre, post, contract_assert: позволяют задавать предусловия, постусловия и утверждения прямо в объявлениях функций;
новые параллельные алгоритмы, #embed для бинарных данных, улучшенное метапрограммирование и многие другие важные обновления.
Компиляторы GCC и Clang уже реализовали большинство функций C++26 в процессе стандартизации, в ближайшее время они появятся и в других компиляторах.
Профили безопасности
Борьба за безопасность продолжается и для будущего C++29. Бьёрн Страуструп активно продвигает концепцию C++ Profiles. Профили представляют собой набор дополнительных правил, которые разработчик может применить в коде для обеспечения безопасности.
Профили помогают избежать распространённых проблем, таких как разыменования нулевого указателя или выходы за границы выделенного буфера. Также профили предоставляют унифицированную основу, позволяющая повысить безопасность без ущерба для ключевых преимуществ C++.
Обновление LLVM 22
Этой весной вышло новое обновление LLVM 22.1. В этой версии добавлено большое количество изменений, которые не только расширяют совместимость с различными архитектурами, но и внедряют инструменты глубокого управления памятью. Вот основные изменения:
поддержка новых процессоров: добавлена поддержка Intel Nova Lake, Wildcat Lake, RISC-V и других архитектур;
стандарты C++: компилятор получил поддержку новых возможностей C++26;
контроль памяти: инструменты глубокого управления памятью, включая специальные токены для маркировки аллокаций.
Языки C и C++ являются основой современного программного обеспечения. Операционные системы, базы данных, игровые движки и компиляторы — все они берут свое начало именно в этих языках. И долгое время выбор в их пользу был очевиден: они предлагали скорость, мощность и надёжность. Но сегодня есть ещё вариант – Rust. Этот язык взял в себя основные преимущества С++ и решил некоторые из его проблем, включая безопасность памяти и неопределённое поведение. Так что же выбрать разработчику? Автор статьи подробно рассказывает в каких ситуациях использовать Rust или C++, а также описывает плюсы и минусы этих языков.
Что представляет собой неквалифицированный поиск имени n в области S? Это функция компилятора, которая выявляет все объявления n, расположенные непосредственно в этой области. На первый взгляд, это кажется простым, но даже такой механизм часто не работает так, как ожидает программист. Например, есть пространство имён N и локальная переменная с тем же именем N. Могут ли они сосуществовать? Да, потому что они находятся в разных областях видимости. А как насчёт пространства имён и глобальной переменной с одинаковым именем? Подробнее вопрос о поиске имён разобрал автор статьи, с примерами и пояснениями. Эта публикация является продолжением темы механизма поиска имён, которая была затронута в статье ниже.
Хорошей практикой в C++ считается размещение функций рядом с типами, для которых они предназначены. Однако, чтобы такой подход работал корректно, важно понимать механизмы поиска имён и знать, где можно размещать функции, не нарушая правил языка. Автор статьи раскрывает тему поиска имён в целом, так и тему поиска, зависящего от аргументов (ADL), в частности.
Вы когда-нибудь задумывались, как хранятся данные в памяти компьютера? Каждая переменная занимает место в памяти как последовательность байт по конкретному адресу. Однако доступ к этим данным не прямой: процессор сначала обращается к быстрому кэшу, который обменивается с оперативной памятью блоками данных.
Эксперты из Forgotten Empires, Playrix и PVS-Studio разобрали, какие инструменты входят в арсенал GameDev-команд, зачем они нужны и почему профилировщики играют ключевую роль в разработке. А бонусом – показали, как превратить ваш код в настоящий город!
На C++ Russia тоже выходят интересные доклады. В этом докладе спикер рассказал о том, как компиляторы на основе LLVM, такие как Clang, решали проблему представления неопределенного поведения в своем IR. В докладе вы познакомитесь с понятием отложенного UB, из-за которого опасный код не взрывается сразу, и увидите, как LLVM с ним работает. Как было раньше и как стало сейчас, как от этого выигрывает оптимизатор, и какие опасности подстерегают тех, кто недостаточно осторожен с конструкциями, которые могут производить UB.
CppCon делится докладами с прошедшей конференции. В этом докладе спикер рассказывает, почему создание собственных задач для обучения сотрудников — это один из лучших способов освоить современные стандарты C++. А также обсуждается, как можно досконально изучить конкретную фичу языка, чтобы уметь объяснить её другим через код и быстро внедрить её в проект.
Почему увеличение штата не работает? Как статический анализатор может сэкономить время команды разработки? На эти и другие вопросы ответили на вебинаре. Спикеры поговорили о системном управлении, автоматизации и полезных инструментах.
Выпуск про сильные и слабые стороны C++, об инструментах, которые помогают держать код под контролем — санитайзеры, линтеры, clang-format, ranges. И, конечно же, сравнение с Rust и Go: где они выигрывают в DX и безопасности, а где всё упирается в экосистему и совместимость.
В выпуске про новые стандарты C++ и главные направления развития языка: безопасность, рефлексию, модули, стандартную библиотеку. Снова про противостояние C++ и Rust: нужен ли C++ borrow checker, можно ли догнать Rust по безопасности и стоит ли вообще это делать.
В подкасте принял участие Слава Грис, разработчик игр и блогер. Спикеры обсудили точку входа в геймдев, как инди-разработчику найти своего игрока, и зачем делать игры, которые поймут немногие. Также можно посмотреть на VK Video.
Как LLM справляются с реальным кодом — и с огромными проектами? В чём разница между агентами и ассистентами, и почему параллельность может стать ключом к прорыву? Заменит ли ИИ обычных разработчиков? Эти и другие вопросы мы обсудили с Евгением Сергеевым (S0ER). Также можно посмотреть на VK Video.
Ассет материала теперь содержит отдельный блок с возможными вводными параметрами шейдера по умолчанию отдельно от юниформов шейдера меатерила
В шейдер введен макет std140 для юниформов
Добавлен отдельный класс в модуле рендера для работы с инстанциями материалов через Uniform Buffer Object (UBO)
Для материалов, имеющих хотя бы один input параметр создается инстанция по умолчанию со значениями юниформов по умолчанию, с запретом на изменение
Добавлено "ленивое" создание инстанций на стороне модуля отрисовки по мере необходимости
Исправлена синхронизация потоков при передаче параметров инстанции материала. Теперь утилизируется uniq_cmd потока отрисовки вместо прямой установки
Комментарий
Переделал инстанции материалов на UBO. Все еще считаю их несколько сырыми и буду их еще какое-то время тестировать и дописывать их код. Кратенько опишу как это работает.
Во первых: UBO требует в шейдере макетной структуры, и поля в ней не могут иметь инициализаторов, поэтому пришлось написать довольно массивный парсер параметров с утилизацией variant и type_identity
glsl именной uniform макет
сущности парсера инпутов при импорте материала в движок через RatTools
Во вторых: эта структура требует выравнивая данных, поэтому, уже на стороне движка пришлось делать особую уличную магию перегоняя сырой буфер в выровненный cогласно стандарту
msvc считает что она дофига умная и настойчиво требует cSize обернуть в sizeof()
В третьих: чтобы не пересоздавать UBO буфер каждый раз, пришлось высчитывать офсеты юниформов и хранить их отдельно в виде пары офсет-размер. Таким образом при изменении одного параметра, в GPU отправится только он.
а вот от текстовой метки до конца так и не удалось избавится, благо у unordered_map итерация идет как 0(1)
ну и в четвертых, т.к. действие происходит сразу в двух потоках (игровом и отрисовки), то на выходе получается довольно много операций копирования (ссылки не доживают до исполнения очереди)
В общем снова на очереди вычитка, удаление старого кода, правка нового. Что-то упростить можно, структуры собрать в одном месте, чтобы не были раскиданы по юнитам. Возможно, выбросить нафиг union, ибо от него сейчас пользы никакой уже, но зато сильно меньше удобства при обновлении переменной юниформа, или обертку над ним хотя бы сделать удобства для.
было/стало. SetVector3 выглядит как-то более удобоваримо