А можно рассмотреть старые компы типа XT? Эмулятор для них есть в проекте "мурмулятор". Там же схемы эмуляции различных приставок, компьютеров типа "Радио-86РК" и "ZX-Spectrum".
Надеюсь скоро можно будет эмулировать i286 и i386 - и, возможно, даже получится запустить OS/2 Warp 4.0 Merlin или что-то подобное для офисной работы...
Я маргинал, который знает исключительно вершки-корешки и не умеет ни код нормальный писать, ни что то доводить до конца. Я не закончил три пту в своем городе из за того, что просто отбитый маргинал. Поэтому мне не светит ни госка, ни частная контора, ни даже работа на галере - у меня нет скиллов, а умело продавать себя я не умею и у меня не хватает хватки.
Так что фигня это все, мой максимум - второсортные развлекательные статейки, чисто как бульварное чтиво, только про IT. Почти как нейромусор.
Осторожно: Статья написана максимально простым языком. Так что если вы гик, но не умеете программировать - вам всё равно будет интересно!
Недавно я наткнулся на DIY-игровую консоль за 1.500 рублей - Waveshare GamePi13. Когда гаджет приехал ко мне, я запустил примеры игр от производителя... и оторопел от 5 FPS в Pong - это ж как плохо нужно код писать!
Не желая мириться с этим, я открыл схему устройства, даташит на RP2040 и принялся писать свой собственный BIOS. Если вам интересно узнать, как работают DIY-консоли «изнутри», можно ли запускать внешние программы на микроконтроллерах из RAM, как реализованы различные подсистемы BIOS, а в конце даже написать «Змейку» - добро пожаловать под кат!
❯ Предисловие
Иногда китайские производители выпускают на рынок дешевые гаджеты с ориентиром исключительно на гиков. Чего-уж говорить, с какой-нибудь R36s чего только не сделали: и кастомные прошивки, и порты игр с ПК, и даже достаточно сложные аппаратные модификации. Однако в тусовке DIY'щиков обычно всё куда хардкорнее...
«Андерграундные» консоли выходят чуть ли не каждый день, но лишь единицы из них становятся хоть сколь либо популярными и попадают на массовый конвейер. От «больших» консолей их отличает простая схемотехника, использование распространенных и дешевых микроконтроллеров общего назначения и полная свобода творчества — что хочешь, то и твори! По характеристикам они чаще всего близки к оригинальному GameBoy или GameBoy Advance, а покупают их инженеры, демосценеры и ретро-энтузиасты, которые не только играют во что-то готовое, но и пишут небольшие игрушки сами!
Самые известные консоли такого формата — это нашумевший Playdate и чуть менее известный Arduboy. Обе консоли сильно ограничены в характеристиках и это подстегивает интерес гиков к постоянной оптимизации кода и попыткам впихнуть «невпихуемое». Выделился даже российский «Микрон», представив свою DIY-консоль «для хардкорных ардуинщиков» — некий MikBoy на базе своего же МИК32 «Амур»!
Я уверен что Микроновцы будут читать эту статью... Если вдруг всё получится и MikBoy пойдёт в серию — то напишите мне пожалуйста сообщение :)
Подобным «ардуинщиком» являюсь и я. Ещё со школьных лет меня нереально тянет к микроконтроллерам и Embedded-электронике в целом. О консоли собственной разработки я мечтаю с 14 лет, при этом мне не просто хочется собрать прототип и «забить», но и запустить мелкосерийное ручное производство и продавать устройства подписчикам! К своим 24-годам я сделал два прототипа и развел три платы, но все эти проекты так или иначе откладывались в долгий ящик...
Один из ранних-ранних прототипов, предназначенный для обкатки драйвера дисплея.
И вот, 25 сентября мне стукнуло 24 годика. Уже взрослый мальчик получил в качестве подарка донат от постоянного читателя и пошёл изучать маркетплейсы в поисках интересного железа. По ключевым словам «tft lcd diy» был найден «ESP32 Bitcoin Miner V2» (выгодный девкит с 2.8" и ESP32-S2), девкит ESP32 с 4.3" дисплеем и емкостным тачскрином, а также некий Waveshare GamePi13, о котором мы сегодня с вами и поговорим!
Отдельное спасибо хотелось бы сказать тем самым подписчикам. Без вашей поддержки этой статьи бы не было!
Waveshare — знаменитый в кругах энтузиастов SBC производитель. В основном компания занимается дисплеями, модулями расширения и одноплатными компьютерами.
В тот же день я заказал устройство, и уже через 3 недели трепетного ожидания, GamePi13 оказался у меня на столе. На первый взгляд консоль показалась очень маленькой: её 1.3" дисплей был даже меньше, чем у Nokia 6230i, а кнопки оказались расположены непривычно близко друг к другу. Ко всему прочему, у консоли не было предусмотрено вообще никакого корпуса: ни «болванки» от производителя, ни STL-файлов для печати. Что-ж, это только придаёт брутальности нашему устройству!
Оба устройства помещаются в одну ладошку... А ведь когда-то 6230i казался реально большим!
Как вы уже могли заметить, консоль состоит из двух независимых модулей: платы разработки Waveshare RP2040-PiZero и «бутербродного» геймпада с дисплеем, который подключается к гребёнке основной платы. В этом и кроется главный секрет устройства: геймпад изначально рассчитан именно для «одноплатников» Raspberry Pi, но поскольку Waveshare также выпускает плату RP2040 с Pi-совместимой гребёнкой, они решили заодно адаптировать его и для PiZero.
❯ Что внутри?
Хоть PiZero и похожа на референсную плату в лице Raspberry Pi Pico, у неё есть несколько серьёзных отличий:
Во первых, на плате установлена SPI-флэшка объёмом аж в 16МБ. Это максимальный объём, который поддерживает XIP-контроллер в RP2040. В RPi Pico же используется флэш-память объёмом всего в 2МБ.
Далее внимание привлекает использование менее эффективного ULDO RT9193 вместо полноценного DC-DC преобразователя в оригинальном Pico. Сам микроконтроллер сможет работать при разрядке аккумулятора ниже 3.6В, а вот периферия — под вопросом. Иными словами, мы не сможем использовать «все соки» из аккумулятора и нам придётся реализовывать отсечку по напряжению.
На плате распаяна микросхема-чарджер литий-ионных аккумуляторов ETA6096 с током зарядки аж в 1А. Если захотите использовать аккумулятор меньшей емкости — стоит подобрать резистор ISET большего номинала, иначе есть риск перегрева.
Из разъёмов распаян HDMI (да, я тоже в шоке), слот для MicroSD (под него отдали весь SPI0) и два Type-C: один для аппаратного USB-контроллера в RP2040, второй для USB через PIO. В общем, пытались угодить всем.
Плата с геймпадом не менее интересная. С фронтальной стороны у нас расположилось 10 кнопок и 1.3" IPS-дисплей с разрешением 240x240, использующий контроллер ST7789. Вообще, для такой диагонали разрешение дисплея крайне избыточно: оно не только съедает драгоценные килобайты оперативной памяти для фреймбуфера, но и значительно грузит DMA-контроллер и всю шину SPI. Я бы на месте инженеров установил бы сюда «золотой стандарт» — недорогой 1.8" 128x160. Все кнопки подключены к отдельным пинам без сдвигового регистра и занимают значительную часть доступных GPIO.
Я бы сделал лучше!
С обратной стороны расположился небольшой динамик, усилитель, построенный на базе NS8002, 3.5мм джек для подключения наушников, а также токоограничивающий резистор подсветки и обвязка для дисплея. Подсветка подключена напрямую к VSYS и рассчитана на питание от 3.3В, так что никакой регулировки яркости и продвинутых режимов сна!
Производитель платы — компания SpotPear.
Ну что-ж, собираем наш бутерброд обратно, подключаем Type-C и смотрим на одну из представленных демо-игр — Тетрис!
Нет, это не пережатая гифка, игра действительно идёт буквально в 1 FPS и с мерцанием — и это на микроконтроллере с ядром Cortex-M0+ на частоте аж в 150МГц! Я напомню, что N-Gage с процессором TI OMAP на более старом ядре ARM926EJ-S с частотой 104МГц умудрялся тянуть первый Tomb Raider с полностью программным рендерингом в 25 FPS!!!
Далее я решил открыть официальный вики Waveshare и изучить информацию о консоли, где нашел несколько примеров игр для неё, одной из которых был Pong. Какое же было моё разочарование, когда я узнал, что обе игры написаны полностью на Python: игровая логика, маршалинг данных, работа с «железом» — всё это было на интерпретируемом языке и более того, написано плохо и крайне неэффективно!
Ни о каком подобии SDK или библиотеки для абстрагирования работы с железом даже речи не шло, практически всё, кроме номеров пинов, было захардкожено прямо в коде игры. О хорошей архитектуре тоже речи не идёт: один класс на всю логику с глобальными переменными... В общем, сэмплы писал либо новичок, либо прожженный эмбеддер :)
Драйвер дисплея даже не пытается использовать DMA, из-за чего даже Понг, состоящий из трёх прямоугольников умудряется тормозить.
def blit_buffer(self, buffer, x, y, width, height): """ Copy buffer to display at the given location.
Args: buffer (bytes): Data to copy to display x (int): Top left corner x coordinate Y (int): Top left corner y coordinate width (int): Width height (int): Height """ self.set_window(x, y, x + width - 1, y + height - 1) self.write(None, buffer)
Звуковая подсистема, состоящая из одноканальной тональной пищалки на аппаратном ШИМ-контроллере, тоже была со своими «приколами». Например «тишина» — это 0, то есть магнит всегда прижат к нижней части, хотя должно быть PWM_MAX / 2.
Под впечатлением от такого кода, я решил попробовать написать SDK для этой консоли сам. Однако моё видение идеальной DIY-консоли сильно отличалось от того-же Arduboy или Playdate!
❯ Архитектура
При проработке архитектуры будущего «BIOS», я сразу же поставил для себя несколько чётких задач:
Во первых, BIOS должен быть достаточно абстрактным для того, чтобы скрывать от игры детали реализации конкретного «железа». Иными словами, игра оперирует не DMA-контроллерами, FPU-сопроцессором и SPI, а набором простых и понятных подсистем: графика, ввод, звук, хранилище. Кроме того, это позволяет легко портировать игры для такого BIOS'а на другие платформы: можно без проблем реализовать симулятор (не эмулятор!) консоли на ПК или портировать её на ESP32 с минимальными изменениями.
Во вторых, мы ставим производительность в основной приоритет при разработке устройства. В конце-концов это же позорище, что простейшая игра тормозит и мерцает на мощном микроконтроллере, но при этом тетрисы с трёхмерной графикой вполне шустро работали на телефонах Sony Ericsson 2005 года. Именно поэтому для написания игр используются не скриптовые языки по типу Lua или JS, а самый обычный «C с классами».
В третьих, сам BIOS должен быть легко портируем между разными платами (у SpotPear есть вторая похожая плата — уже с 1.5" и стиком) и даже аппаратными платформами. Этот проект может стать основной прошивкой для консоли уже моей разработки и иметь вот такую «кроссплатформу» было бы отнюдь не лишним!
Руководствуясь критериями выше, я решил писать BIOS на C++ (на деле C с классами) с активным использованием интерфейсов и VMT. Это позволяет не только удобно структурировать модули и повышает читаемость кода игры, но и избавляет от необходимости вручную составлять таблицу системных вызовов к API. Тем не менее, в таком подходе есть один серьёзный нюанс: когда у подсистем появляются новые методы или добавляются перегрузки к прошлым, их необходимо по порядку добавлять в конец интерфейса, иначе VMT ломается.
vtable for CTest: .word 0 .word typeinfo for CTest .word CTest::Test() .word CTest::Abc() vtable for ITest: .word 0 .word typeinfo for ITest .word __cxa_pure_virtual .word __cxa_pure_virtual
В своё время Microsoft решила эту проблему в COM с помощью QueryInterface и миллиона вариаций этих самых интерфейсов: IDirectSound8, IDirectDraw7 и т.д, но мы можем не изобретать велосипед, а просто предоставлять «старым» играм такие же «старые» версии VMT.
Основным объектом в BIOS'е является CSystem, который содержит в себе ссылки на другие подсистемы консоли, а также на информацию о текущей аппаратной платформе:
/// @brief Primary system service, supplied to both games and system modules. class ISystem { public: virtual CSystemInfo* GetSystemInfo() = 0;
Несмотря на кажущуюся «динамическую» натуру системы, никаких IID я переизобретать не стал. BIOS должен реализовывать ровно тот минимальный функционал системы, который нужен. Экземпляр CSystem создаётся так называемым «портом» на конкретную плату, который должен заполнить структуру с указателями на реализации подсистем — прямо как machine-файлы в Linux! И RAII не нарушили, и полный контроль без костылей сохранили — ляпота!
void InitializePlatform() { CommManager = new CCommunicationManager(); CDebugService* dbgSvc = new CDebugService();
/* Print some userful debug information */ CJEDECFlashID* flashId = FlashManager.GetFlashID();
while (true) { /* Tick all platform-depend services here */ CommManager->Tick(); PowerStateManager.Tick(); InputService->Tick();
System->Tick(); } }
В целом, базовая архитектура примитивная и понятная. Перейдем же к деталям реализации конкретных модулей.
❯ Графика
Первая подсистема, которую я реализовал — была графической. Концептуально она разделена на два отдельных модуля: драйвер дисплея, который позволяет получить его параметры и в будущем управлять его состоянием, а также модуль для рисования на поверхностях. Прямо как в DirectDraw:
class IDrawingSurface : public ISystemService { public: virtualvoid Clear(CColor color) = 0; virtualvoid DrawBitmap(CBitmap* bitmap, int x, int y) = 0; virtualvoid DrawBitmapEx(CBitmap* bitmap, int x, int y, CSpriteInfo* spriteInfo) = 0; virtualvoid DrawRect(CColor color, int x, int y, int width, int height) = 0; virtualvoid FillRect(CColor color, int x, int y, int width, int height) = 0; virtualvoid DrawLine(CColor color, int x1, int y1, int x2, int y2) = 0; virtualvoid DrawString(CColor color, int x, int y, CAnsiChar* str) = 0; };
class IGraphicsService : public ISystemService { public: virtualvoid SetPowerState(bool isPowerEnabled) = 0; virtualvoid SetBacklightState(bool isBacklightEnabled) = 0; /* Maybe some controller-related functions in future? Like BIAS and HW rotation? */
Сам драйвер дисплея классический: в его задачи входит инициализация контроллера, выделение памяти под фреймбуфер и регулярное обновление изображения на матрице. Поскольку в таких устройствах используются стандартные MIPI DBI экраны с набором команд DCS, часть кода инициализации и работы с дисплеем стало возможным унифицировать:
/* Perform hardware reset */ gpio_put(PIN_LCD_RST, 0); sleep_ms(DISPLAY_INIT_SLEEP_TIME); gpio_put(PIN_LCD_RST, 1); sleep_ms(DISPLAY_INIT_SLEEP_TIME); /* Wait for display controller to complete initialization */
Reset(); /* Perform software reset to maintain default register state */ SendCommand(cmdSLPOUT, 0, 0); /* Disable sleep mode */ SendCommand(cmdCOLMOD, 0x05); /* Set color format and decoding*/ SendCommand(cmdINVON, 0, 0); /* Disable inversion */ SendCommand(cmdNORON, 0, 0); /* Enable normal mode */ SendCommand(cmdMADCTL, cmdMADCTL_RGB); /* Set pixel size */
uint8_t windowSize[] = { 0 >> 8, 0, DISPLAY_WIDTH >> 8, DISPLAY_WIDTH }; /* Set display window (note this is not safe for displays with sides not equal in size) */ SendCommand(cmdCASET, windowSize, 4); SendCommand(cmdRASET, windowSize, 4);
SetPowerState(true); /* Enable display */
Вероятно читатель может спросить: «зачем выделять целых 115КБ под фреймбуфер, если можно использовать команды CASET/RASET и рисовать отдельные спрайты прямо в память дисплея?». Дело в том, что в таком случае скорость отрисовки будет падать обратно пропорционально размеру и числу рисуемых изображений. Если мы попытаемся нарисовать параллакс-фон, состоящий из трёх картинок с размерами 240x240, то нашим узким местом станет не только цена обращения к XIP-кэшу, но и производительность SPI-контроллера (который напрямую тактируется от системного PLL) и мы получим те самые 1-2 FPS. Кроме того мы потеряем возможность использования DMA и нам придётся ждать каждой транзакции на экран: это проблема многих «самодельных» консолей, которую, впрочем, можно решить обратившись к опыту предков — а именно PPU.
В своём проекте я решил активно задействовать DMA-контроллер для отправки фреймбуфера на дисплей. Концепция простая: мы указываем ему переслать фреймбуфер, начинаем подготавливать следующий кадр и если транзакция ещё не завершена - то дожидаемся её окончания, дабы картинка оставалась целостной. Однако если обновление логики следующего кадра завершается быстрее, чем DMA-контроллер успевает отправить сканлайны - мы можем получить эффект тиринга.
/* Setup DMA for SPI */ dmaChannel = dma_claim_unused_channel(true);
Далее переходим к фактической отрисовке изображений. На данный момент поддерживается только один формат пикселей — RGB565, поскольку нет особого смысла использовать 8-битную палитру для изображений 32x32 (но есть смысл использовать 4х-битную, как на NES). Процесс рисования называется блиттингом и поскольку реализация полноценного альфа-блендинга слишком дорогая для реалтайм графики на микроконтроллерах, для описания прозрачности используется техника колоркеев.
ColorKey — это как ChromaKey, но для описания прозрачного цвета используется только базовый цвет, а не цвет + порог допустимых цветов. Помните как в играх 90-х были картинки с розовым фоном цвета Magenta? Вот это оно самое :)
Рисование текста реализовано знакомым для Embedded-инженеров способом: шрифты описываются в формате 8x8, где 8 битов каждого байта обозначают наличие или отсутствие пикселя в текущей позиции. Такие шрифты не только занимают очень мало места, но их также очень легко и быстро рисовать, а также масштабировать под различные разрешения экранов. На данный момент я задумываюсь — стоит ли добавлять в консоль поддержку полноценного UTF-16, если учесть что основной таргет на русскоязычную аудиторию, где и CP866 хватает с головой?
Какой же дисплей чёткий...
❯ Ввод
Далее мы плавно переходим к реализации драйвера ввода. Как я уже говорил выше, все кнопки подключены к своим отдельным GPIO без использования сдвигового регистра или I/O Expander'а, что с одной стороны и хорошо (некоторые китайские производители реализовывают консоли с кнопками, основанными на матричном (!!!) принципе), а с другой — отъедает большинство GPIO у RP2040. Свободными пинами мы могли бы выполнять множество полезной работы: получать уровень заряда аккумулятора у Fuel Gauge, управлять уровнем подсветки с помощью ШИМ-контроллера и ключа, или, в конце-концов, сделать порт для подключения периферии... но нет так нет.
Сам по себе драйвер ввода до жути примитивный: он позволяет получить состояние отдельных кнопок, осей (как Input.GetAxis в Unity) и проверить, нажата ли хоть какая-то кнопка:
Для удобства и портабельности BIOS'а между платами, кнопки геймпада маппятся к соответствующим GPIO в отдельной таблице трансляции, которая также содержит состояния этих самых кнопок:
// Should be layouted in order of EKeyCode enum CButtonState ButtonMapping[] = { { PIN_KEY_LEFT }, { PIN_KEY_RIGHT }, { PIN_KEY_UP }, { PIN_KEY_DOWN }, { PIN_KEY_A }, { PIN_KEY_B }, { PIN_KEY_X }, { PIN_KEY_Y }, { PIN_KEY_LEFT_TRIGGER }, { PIN_KEY_RIGHT_TRIGGER } };
Дело в том, что в нашем проекте недостаточно иметь лишь одно булево: нажата-ли кнопка или нет, для компенсации дребезга кнопок у нас также реализуется задержка перед следующей проверкой и дополнительное состояние для удобства реализации меню — «только что отпущена».
for(int i = 0; i < ButtonMappingCount; i++) { CButtonState* buttonState = &ButtonMapping[i]; bool gpioState = !gpio_get(buttonState->GPIO); // Buttons are pull-up to high when not pressed
// Check if there was elapsed enough time if(timeStamp > buttonState->LastStateChange) { if(buttonState->State == EKeyState::ksReleased) buttonState->State = EKeyState::ksIdle;
Таким образом, мы получаем куда более удобную подсистему ввода, чем условная битовая маска с обозначением каждой кнопки и ручной обработкой её состояний в игре...
Вот мы и подошли к, возможно, самой интересной подсистеме в нашем BIOS'е. Думаю многие читатели так или иначе интересовались тем, как же компилятор и линкер превращают исходный код и объектный файлы в пригодные для выполнения программы и библиотеки. Вопрос запуска нативных программ на микроконтроллерах интересовал и меня — я даже написал целых три статьи об этом: в первой мы поговорили о ESP32 и Xtensa, а во второй реализовали BinLoader путём реверс-инжиниринга и хакинга кнопочного телефона, а в третьей сделали полу-универсальный ElfLoader для нескольких моделей телефонов на разных платформах.
Но начнём мы с простого. Каждая программа делится на три основных секции:
.text — содержит в себе машинный код функций и так называемые Literal pools. Может быть как в ROM, так и в RAM. На системах, где есть возможность выполнять код и в ROM, и в RAM, есть отдельная секция - .iram.
.data — содержит инициализированные переменные, которые обычно попадают в оперативную память. Для статических констант есть отдельная секция, называемая .rodata.
.bss — содержит в себе не-инициализированные переменные, обычно это нули. В исполняемый файл секция .bss напрямую не записывается, остаётся лишь информация о том, каков её размер, а саму секцию затем выделит динамический линкер.
Куда попадут части программы определяет специальная утилита — линкер, которая на основе специального скрипта «раскладывает» данные по нужным секциям. Благодаря этому скрипту, мы можем, например, перенести часть функций в оперативную память для более быстрого исполнения или добавить в начало программы заголовок с описанием приложения.
В моём случае, я решил загружать игры в SRAM и дабы не реализовывать нормальный динамический линкер и релокации, решил выделить под игру фиксированный кусочек оперативной памяти объёмом в 128КБ. Для этого я отредактировал скрипт линкера Pico C SDK так, чтобы сразу после вектора прерываний шла наша программа:
Для компиляции программы также используется кастомный скрипт для линкера и особый Makefile, где после сборки программы мы копируем все её секции в выходной файл в «сыром» виде. Поскольку программа собирается под выполнение из конкретного адреса — пока речь идёт о переносимости только между одной аппаратной платформой. На RP2040, RP2350 и возможно STM32 такое «прокатит», но вот на других ARM-процессорах — большой вопрос!
Каждое приложение, как и базовая система, предполагает использование ООП и поэтому представляет из себя реализацию класса IApplication. Для этого нам нужна некоторая runtime-поддержка: аллокатор, функция для создания экземпляра приложения, а также указатель на ISystem. Именно поэтому каждая программа должна экспортировать специальный заголовок, где содержится указатель на функцию-инициализатор:
Таким образом, для выполнения нашей программы и вызова её обработчиков событий нам достаточно лишь загрузить файл по адресу 0x200000c0 и создать экземпляр IApplication. Всё очень просто и понятно!
Но "моргалка" ведь слишком просто, согласитесь? Поэтому мы с вами напишем ремейк классической игры Змейка, которая работает в настоящие 60 FPS!
❯ Заключение
Вот таким нехитрым образом я понемногу реализовываю свою мечту детства: «андерграунд" консоль собственной разработки. Конечно здесь ещё много чего нужно доделывать перед тем, как начинать разводить свою плату, но начало ведь положено! В контексте GamePi13, я считаю что моя реализация SDK для консоли всё таки немного лучше, чем то, что предлагает производитель «из коробки».
Я понимаю что мой не совсем трушный эмбеддерский подход может вызвать разные ощущения у читателей: так что приглашаю всех заинтересованных в комментарии, обсудим с вами «сломанный Branch-prediction из-за виртуалов», «UB из-за того, что порядок указателей на реализации в VMT может отличаться» и «какого фига игры у тебя оказались в SRAM, а высокопроизводительный код на Flash, если у XIP кэш всего в 16КБ!».
А если вам интересна тематика ремонта, моддинга и программирования для гаджетов прошлых лет — подписывайтесь на мой Telegram-канал «Клуб фанатов балдежа», куда я выкладываю бэкстейджи статей, ссылки на новые статьи и видео, а также иногда выкладываю полезные посты и щитпостю. А ролики (не всегда дублирующие статьи) можно найти на моём YouTube канале.
Если вам понравилась статья...
И у вас появилось желание что-то мне задонатить (например прикольный гаджет) - пишите мне в телегу или в комментариях :) Без вашей помощи статьи бы не выходили! А ещё у меня есть Boosty.
Что думаете о таком формате статей?
Если бы я собрался с духом и произвел 20-50 штучек консолей-самоделок с полностью готовым SDK, примерами и туториалами, купили бы себе такую
Мой молодой человек (24 года) безумно увлечён робототехникой. Это не просто хобби «посмотреть YouTube» — он реально покупает моторы, сам чертит и печатает детали на 3D-принтере, собирает манипуляторы, искусственные мышцы, программирует их, тестирует… У него дома что-то типа мини-лаборатории.
Но знаете, что самое грустное? Он в этом практически один.
Друзья и знакомые либо «вообще не в теме», либо считают это чем-то детским или странным. А ему очень не хватает живого общения с такими же увлечёнными. Чтобы не просто по форумам шариться, а как в детстве — прийти куда-то, показать свои наработки, вдохновиться чужими, обсудить идеи, вместе что-то делать.
Не курсы, не лекции. А именно тусовку, сообщество, где взрослые люди занимаются роботами, мехатроникой, инженерными поделками для души.
Вопрос: есть ли в Санкт-Петербурге такие кружки, клубы, мастерские, где собираются взрослые технари/робототехи? Что-то вроде открытых лабораторий?
Если вы знаете такое место — пожалуйста, поделитесь. Это будет для него реально ценно. А может, вы сами такой же и тоже ищете «своих»?
P.S. Если кто-то хочет собраться маленьким кругом где-нибудь и обсудить железки, проекты и платы — он будет только рад! :)
Методология Scrum была создана американцами Джеффом Сазерлендом и i Швабером в 1993 году.
Понятие MVP (Minimum Viable Product, минимально жизнеспособный продукт) появилось в 2001 году благодаря Фрэнку Робинсону, президенту консалтинговой фирмы SyncDev.
С этого времени - качество , надежность, эффективность и управляемость программного обеспечения были принесены в жертву идолу рынка. Пути инженеров и программистом стали расходится.
Ибо - Хуяк , хуяк и в продакшн ( Гимн современного поколения разрабов и манагеров).
Слава Богу , что программисты не строят дома , мосты , самолеты и другие высокосложные критичные инженерные системы.
MVP по русски называется - *уяк, *уяк и в продакшн.
Когда настанет апокалипсис и закат цивилизации ? Например , когда результаты новомодных методик внедрят в системы управления АЭС и РВСН.
P.S. Они в общем то не виноваты. Их так учили. Они просто не знают, что можно работать по другому. Плюс постоянный прессинг иффиктивных манагеров и ведь реально надо как то работать и семью кормить. Вот и приходится клепать фичи.
Работаю в регионе программистом с 3 курса вуза и по сей день. Вот что я понял за время работы.
1. Айтишники, которых считают супербогатыми бездельниками в 99.99% не такие. Зарплаты среднего программиста с 1-3 годами опыта не сильно отличаются от зарплат слесарей на заводе, автослесарей или продавцов дверей/окон/машин или мастеров маникюра. В большинстве случаев разница при аналогичном опыте с такими работниками может вообще отсутствовать или составлять около 25к (причём не всегда программист получает больше). Через 4+- года работы зп зависит от приобретённых навыков решения задач и немного от наглости.
2. Спина - наше всё. Расходы на поддержание нормального состояния спины будут кусаться. Необходимы сеансы массажа пару раз в год и ортопедические приблуды для спины и ног. Разминка каждый час по 3 минуты стоя на ногах очень нужна. Йога каждый вечер по 20 минут - вообще огонь.
3. Если ты не кликаешь миллион раз по пустому месте на экране и не нажимаешь случайно по клавиатуре в случае загрузки приложения - это не значит, что другие пользователи так не будут делать.
4. Если компьютер не отвечает на действия - надо подождать секунд 20. И на всякий случай почитать инструкцию к программе - разные расчёты и отрисовка могут занимать от 1 минуты и до бесконечности по времени.
5. Защита от дурака нужна всегда и везде, где можно. Если у пользователя есть возможность вбить "1ц3" вместо "123" - он вобьёт, не проверит и будет удивляться почему не работает так как он хотел.
6. Перед началом работы нужно разобраться с понятийным аппаратом. Ты и заказчик должны чётко понимать что нужно сделать. Все цифры чётка прописывать в тех.задании. Если кнопка красная, то её цвет "RGB(250,0,0)", а не "что-то красивое и красное".
7. Если ты что-то понимаешь - убедись, что другой человек понимает также как ты. А потом ещё раз повтори.
8. Спросить не стрёмно - стрёмно сделать плохо не понимая или не сделать к нужным срокам.
9. Слушать музыку или аудиокниги, гулять, быть на природе и не пялиться в экран - это реально круто.
10. Математика - всему голова. Реально хорошо кодить без знания математики крайне сложно.
11. Соблюдай баланс жизнь-работа иначе выгоришь и окажешься в депрессии.
12. Много кофе пить вредно и вызывает привыкание. Вода или зелёный чай - лучше.
В графических настройках компьютерных игр часто встречается опция «Тесселяция», от включения которой в современных проектах на первый взгляд картинка особо не меняется, а производительность становится ниже. За что она отвечает, как работает, и в чем смысл этой технологии?
Для многих любителей компьютерных игр не секрет, что выдаваемая на экраны картинка, в случае современных проектов так часто потрясающая воображение, состоит из полигонов. Полигоны — это мелкие треугольники, которые с помощью множества этапов сложной обработки, выполняемых видеокартой, в итоге превращаются в игровую картинку. Качество компьютерной 3D-графики зависит от многих переменных, но за всю ее историю остается неизменным одно правило — чем больше полигонов в кадре, тем детализированнее получается итоговое изображение.
Видеокарты становятся все мощнее с каждым годом, но бесконечно наращивать число полигонов невозможно, так как нужные для них ресурсы — геометрическая производительность и канал шины — у любого графического ускорителя все же ограничены. Именно поэтому потребовалась относительно простая техника увеличения детализации без чрезмерной нагрузки на графический процессор. Ею стала тесселяция.
2001: TruForm от ATI
В начале века, еще до появления техник рельефного текстурирования, единственным способом повысить геометрическую сложность сцены было добавление в нее полигонов, что значительно сказывалось на производительности графических процессоров того времени. Первую технологию, призванную повысить детализацию сцены без чрезмерной нагрузки на ГП в 2001 году внедряет компания ATI. Называется она TruForm, и, по сути, является первой реализацией тесселяции в компьютерных играх.
Обычный треугольник в представлении видеокарты состоит из трех вершин и трех нормалей к ним. Используя информацию от игрового движка, графический процессор создает треугольники, соединяя их вершины. После этого ГП накладывает освещение и тени с помощью нормалей — они показывают, как свет должен отражаться от треугольника.
TruForm работает иначе. После передачи ГП информации о треугольнике, движок начинает разбивать базовый треугольник сеткой из N-патчей, используя в качестве опоры две контрольные точки. Вычисляются они с помощью информации из той самой карты нормалей — ведь именно способ отражения света от объекта указывает его форму и кривизну поверхности. Внутри базового треугольника формируется кривая поверхность из более мелких треугольников, которых в сумме теперь девять. После преобразования поверхности в кривую происходит еще несколько этапов деления на мелкие треугольники — столько, сколько прописано в игровом движке для объекта, частью которого является базовый треугольник.
По сути, TruForm является собственной для ATI реализацией N-патчей, включенной в состав графических API DirectX 8 и OpenGL. Тем не менее, компания первой среди производителей видеокарт доработала блок трансформации и освещения собственного графического процессора, позволив ему выполнять простую по современным меркам тесселяцию поверхностей.
Для качественной работы без графических артефактов TruForm требовал, чтобы нужные полигональные модели в сцене были явно указаны для обработки и изначально разрабатывались с ее учетом — иначе ненужное «округление» могло появиться и у объектов, которые должны быть плоскими.
Патчи с поддержкой технологии получили в основном только крупные известные проекты, такие как: Half-Life, Counter-Strike, Rainbow Six, первая и вторая части Soldier of Fortune, трилогия Quake, Return to Castle Wolfenstein, первые три части Unreal Tournament. Конкурирующая NVIDIA немного позже предложила собственную реализацию N-патчей, не совместимую с ATI, но это решение было распространено еще меньше. Разработчики игр не торопились тратить силы на внедрение технологии, положительные изменения от которой увидели бы только владельцы карт Radeon. В итоге перспективная технология была на несколько лет забыта.
2002: Displacement Mapping от Matrox
С развитием распространившихся после провала TruForm технологий рельефного текстурирования — сначала Bump mapping, затем Normal mapping, и, наконец, Parallax mapping, — стало возможно визуально добавлять плоским поверхностям объема и делать их «бугристыми» без непосредственного добавления новой геометрии. Простоту использования в играх дополняло то, что технологии этого типа работают с помощью пиксельного шейдера, не требуя других специальных блоков в видеокарте. Однако реального увеличения полигонов в кадре с такими техниками не происходит, и увеличить детализацию с их помощью можно далеко не для любого объекта в сцене.
В 2002 году компания Matrox Graphics, тогда еще являющаяся игроком рынка дискретных видеокарт наряду с NVIDIA и ATI, представила новую технологию Displacement Mapping. В отличие от вышеописанных видов, Displacement Mapping использует технологию N-патчей. Но не как TruForm, а по-другому: плоская поверхность разбивается на множество треугольников, а затем с помощью заранее заданной карты смещений «вытягивается» в нужных местах согласно ей, создавая объемный объект.
Детализация объектов в демо-сценах, созданных с помощью Displacement Mapping, была великолепной для своего времени при скромном потреблении ресурсов. Аппаратной поддержкой технологии обладал выпущенный в 2002 году графический ускоритель Matrox Parhelia-512. Однако к этому моменту компания уже была в незавидном положении — по чистой производительности ее видеокарты были медленнее конкурентов.
Немалая цена, проблемы с драйверами и неполное соответствие Parhelia-512 стандартам DirectX 9.0 стали последними каплями, упавшими на камень производителя графического оборудования — от дальнейшей разработки собственных ГП компания отказалась. Несмотря на то, что описание перспективной технологии было у конкурентов, никто из них тогда не взялся за ее собственную реализацию. Вследствие этого Displacement Mapping так и не появился в игровой графике тех лет.
2005: тесселяция в Xbox 360
Графический процессор приставки Xbox 360 под кодовым названием «Xenos» впервые получил поддержку тесселяции в том виде, в котором она применяется и в наши дни.
Разработка графического процессора была поручена компании ATI, которая к тому времени уже делала наброски архитектуры своей первой серии видеокарт с поддержкой DirectX10 и универсальными шейдерными процессорами — Radeon HD2000. Учитывая ограниченную вычислительную способность консольного чипа, компания разработала для него аппаратный блок тесселяции: ведь помимо насыщения геометрии уже имеющейся сцены, грамотная тесселяция может использоваться для воссоздания геометрии определенного уровня из меньшего количества исходной. Для приставки игры пишутся с учетом «железа», поэтому возможности тесселятора были весьма кстати — в отличие от компьютерных проектов, где разработчики не горели желанием оптимизировать проекты под одного из производителей графических чипов.
Графическая часть Xbox 360 получилась достаточно успешной. В 2007 году ATI интегрировали похожий блок тесселяции и в десктопные видеокарты серии HD2000. Тесселятор в графических процессорах линейки программируемый, и может использовать кроме N-патчей и другие типы поверхностей: патчи Безье, B-Spline, NURBs и Subdivision Surfaces. Это дает больше выбора разработчикам игр, для корректной работы тесселяции необходимо было лишь написать необходимый вершинный шейдер.
С таким блоком тесселяции можно было использовать и Displacement Mapping. Но на практике все оказалось не так-то просто: разработчики игровых движков не были заинтересованы во внедрении технологии в компьютерные игры, потому что у графических процессоров NVIDIA подобного блока не было — история повторилась. Подобный программируемый тесселятор был и в следующих сериях карт серий Radeon HD3000 и HD4000, но в итоге этот блок так и не был задействован в реальных играх, за исключением собственных демок ATI.
2009: тесселяция как стандарт DirectX 11
После успешного старта Xbox 360 компания Microsoft стала задумываться о внедрении поддержки тесселяции в стандарт графического API DirectX. Поддержка тесселяции появилась в DirectX 11 в 2009 году, одновременно с выходом знаменитой ОС Windows 7.
Реализация в старых картах Radeon не была совместима с API Microsoft, поэтому в плане поддержки этой технологии они остались не у дел. Но ATI вовремя подсуетилась, и к выходу новой операционной системы выпустила новую линейку видеокарт серии HD5000 с обновленным тесселятором, полностью совместимым с DirectX 11. NVIDIA запустила линейку совместимых видеокарт серии GTX400 в 2010 году. В 2012 году совместимость с новым API получила встроенная графика Intel.
Для удобства работы технологии в стандарт DirectX 11 были добавлены новые типы шейдеров: Hull и Domain. Тесселяция в новом API позволяет использовать еще более широкий набор методов и алгоритмов, чем в ранней реализации ATI — поддерживаются N-патчи, патчи Безье, Displacement Mapping, Catmull-Clark Subdivison, адаптивная тесселяция с применением LOD и прочие методы.
В 2010 году аналогичная поддержка тесселяции появилась и в другом графическом API — OpenGL 4.0. Естественно, что поддерживают технологию и современные «наследники» этих графических API: DirectX 12 и Vulkan.
При всем многообразии применяемых методов, суть тесселяции не изменилась со времен TruForm: разбиение существующих полигонов на более мелкие с целью увеличить детализацию моделей в 3D-сцене. Это все так же самый «легкий» для графического оборудования способ проделать аналогичную работу. Помимо этого, грамотная тесселяция помогает сэкономить графические ресурсы при одинаковом или лучшем уровне картинки, чем без использования этой технологии.
Влияние на производительность
У первых графических процессоров с поддержкой тесселяции включение последней отнимало достаточно много производительности. Особенно этим страдали «пионеры» серии Radeon HD5000 — первый блок тесселяции, совместимый с DirectX 11, был довольно медленным и часто просаживал производительность в первых играх с ее поддержкой до некомфортных значений. Серия карт Radeon HD6000 получила улучшенный блок тесселяции, но значительный рывок в этом плане совершила только следующая серия карт на новой графической архитектуре AMD GCN — HD7000, увидевшая свет в 2012 году.
У первых карт NVIDIA с поддержкой новой технологии — GeForce GTX 400 и 500 серии на архитектуре Fermi — блок тесселяции был быстрее, чем у конкурирующих линеек Radeon. Но принимая во внимание рывок тесселятора в Radeon HD7000, для GTX 600 серии на новой графической архитектуре Kepler тоже был разработан новый, более производительный блок тесселяции. Начиная с видеокарт этих поколений, тесселяция перестала быть крайне трудоемкой задачей для производительных ГП обеих компаний в большинстве игровых проектов.
На сегодняшний день тесселяция стала стандартной техникой для многих игровых движков, и в некоторых играх она работает по умолчанию без опции отключения. В других же играх такая опция есть, и ее отключение может как не добавлять производительности вообще, так и заметно «бустить» FPS, особенно на бюджетных видеокартах. В чем же подвох?
Дело в том, в разных играх используются разные объекты для тесселяции. В одних обрабатывается большее количество объектов, в других — меньшее, в третьих — лишь определенные, например только колеса машин и прочие круглые предметы. К тому же, и сама тесселяция бывает разного уровня — от x2 до x64. То есть один и тот же базовый треугольник может быть разбит на более мелкие от 2 до 64 раз, это значение задает движок игры. В играх 2010-х годов нередко встречались настройки уровня тесселяции, сейчас же их практически не встретить — иногда эта настройка зависит от других графических опций, но чаще всего управление ею отсутствует вообще.
В современных проектах тесселяция может как почти не влиять на производительность, так и забирать ее часть. Но таких провалов кадровой частоты, как ранее, она не вызывает — одна из самых «тяжелых», реализация тесселяции на движке 4A Engine в игре Metro: Exodus, понижает частоту кадров на 15–25%, в зависимости от сцены. В большинстве игр и такого падения от активации технологии нет — в среднем, оно на уровне около 5–10%, хотя для самых бюджетных карт может составлять и больше.
Бенчмарки
Самым известным бенчмарком с поддержкой и явной визуализацией тесселяции, безусловно, является Unigine Heaven. Он был презентован на выходе Windows 7, и является первым графическим тестом с поддержкой API DirectX 11.
Несмотря на почтенный возраст, бенчмарк до сих пор показывает вполне неплохую графику. Но, что самое главное, в нем есть инструменты активации и регулирования уровня тесселяции в режиме реального времени. За счет этого можно оценить вносимые ей улучшения графики, меняя параметры и получая изменения прямо «на лету».
К тому же, имеется специальный режим включения сетки — при его задействовании края полигонов очерчиваются. Комбинируя с этим режимом инструменты изменения уровня тесселяции, можно наглядно наблюдать, как сильно растет геометрическая сложность сцены с повышением ее уровня.
3DMark 11 и последняя версия теста 3DMark без цифрового префикса тоже используют тесселяцию. Но в режиме реального времени ее параметры изменить не получится — для этого нужно остановить тест и перейти в его расширенные настройки. К тому же, тесты серии сфокусированы не только на тесселяции, поэтому в динамике существенную разницу в картинке с разными настройками будет заметить достаточно сложно.
Однако тесты серии гораздо лучше и комплексно оценивают производительность современных видеокарт, чем Unigine Heaven, который для актуальных моделей среднего класса достаточно легок даже на максимальных настройках графики.
Примеры в играх
Так как же выглядит «волшебная» тесселяция в играх? Часто в случае с современными проектами заметить ее влияние сложно, так как актуальные игры и без ее применения выдают достаточно насыщенные полигонами кадры. В динамике эта разница становится еще менее различимой, поэтому обратимся к статическим изображениям — скриншотам популярных игр, где управление тесселяцией отдано на откуп пользователю.
Metro: Exodus. Тесселяцию можно включить или отключить, управления уровнями не предусмотрено. Итак, сначала кадр без тесселяции, затем с тесселяцией:
Первое, что бросается в глаза — «округлившееся» колесо грузовика. При детальном рассмотрении становится заметно, что тесселяция применяется и на земле: она становится более неровной и бугристой, что лучше всего заметно в правой части кадра. На остальных объектах в кадре технология либо не применяется, либо ее влияние малозаметно.
Sniper Elite 3. Кадр сначала с отключенной, затем со включенной технологией.
Становятся заметны более гладкие края автомобильных шин, бочки и особенно камней под ногами — это тесселяция в деле. Но обратите внимание на следующие скриншоты:
Здесь тесселяция заметна только на камнях на земле. Несмотря на сложное строение скал, при включенной и отключенной тесселяции они выглядят одинаково — то есть рисуется такая непростая картинка без использования этой технологии.
В отличие от прошлых игр, в Red Dead Redemption 2 есть управление уровнем тесселяции. Правда, отключить ее полностью нельзя — доступно лишь переключение между низким, средним и высоким уровнем. Согласно описанию опции в графических настройках, тесселяция в игре применяется лишь для деревьев, следов на снегу, грязи и воды.
И действительно, негусто: по ходу игры не удалось обнаружить отличия между низким и высоким уровнем тесселяции, если не искать специально описанные в настройках объекты. Пришлось так и поступить, для начала посмотрим на дерево при низком и высоком уровне тесселяции:
Ствол дерева становится объемнее и немного детальнее, но каких-либо значимых изменений в картинке не происходит. Что ж, тогда обратимся ко следам на снегу, может хоть по ним будет видна более значимая разница. Высокий уровень тесселяции:
На высоком уровне исчезают присутствующие ранее артефакты, и следы становятся наиболее гладкими, но на этом все.
Заключение
Тесселяция — современная графическая технология, призванная увеличить количество геометрии в сцене и сделать ее более достоверной. В играх в первую очередь она используется для создания объектов с округлыми формами — колес, камней, частей тел персонажей и прочего. Помимо этого, тесселяция часто применяется для создания ландшафта и эффектов сложных поверхностей, вроде водной.
С помощью бенчмарка Unigine Heaven можно убедиться, насколько сильно можно преобразить виртуальные миры благодаря тандему тесселяции с Displacement Mapping. Почему же такой разительной разницы при активации технологии даже спустя 13 лет после появления бенчмарка нет в современных играх?
Дело в том, что разработчики игровых проектов чаще всего все также предпочитают рисовать геометрию вручную для большинства внутриигровых объектов. Происходит это и благодаря появившимся в последние годы более производительным видеокартам, которые значительно прибавили в мощности. В итоге тесселяция в современных играх используется гораздо менее активно, чем могла бы — в основном, лишь для малой части объектов игровых миров.
В современных играх нечасто можно найти настройки работы тесселяции, а во многих проектах она и вовсе неотключаема. Из громкой новинки со временем технология стала бойцом невидимого фронта: ее «шестеренки» тихо крутятся внутри современных графических движков, успешно делая свою работу: круглое — круглым, а угловатое — гладким.