PHP Sucks, But It Doesn’t Matter
Here’s a list of every function beginning with the letter «A» in the PHP function index:
| abs() acos() acosh() addcslashes() addslashes() aggregate() aggregate_info() aggregate_methods() aggregate_methods_by_list() aggregate_methods_by_regexp() aggregate_properties() aggregate_properties_by_list() aggregate_properties_by_regexp() aggregation_info() apache_child_terminate() apache_get_modules() apache_get_version() apache_getenv() apache_lookup_uri() apache_note() apache_request_headers() apache_reset_timeout() apache_response_headers() apache_setenv() apc_add() apc_cache_info() apc_clear_cache() apc_compile_file() apc_define_constants() apc_delete() apc_fetch() |
apc_load_constants() apc_sma_info() apc_store() apd_breakpoint() apd_callstack() apd_clunk() apd_continue() apd_croak() apd_dump_function_table() apd_dump_persistent_resources() apd_dump_regular_resources() apd_echo() apd_get_active_symbols() apd_set_pprof_trace() apd_set_session() apd_set_session_trace() apd_set_socket_session_trace() array() array_change_key_case() array_chunk() array_combine() array_count_values() array_diff() array_diff_assoc() array_diff_key() array_diff_uassoc() array_diff_ukey() array_fill() array_fill_keys() array_filter() array_flip() |
array_intersect() array_intersect_assoc() array_intersect_key() array_intersect_uassoc() array_intersect_ukey() array_key_exists() array_keys() array_map() array_merge() array_merge_recursive() array_multisort() array_pad() array_pop() array_product() array_push() array_rand() array_reduce() array_reverse() array_search() array_shift() array_slice() array_splice() array_sum() array_udiff() array_udiff_assoc() array_udiff_uassoc() array_uintersect() array_uintersect_assoc() array_uintersect_uassoc() array_unique() array_unshift() |
array_values() array_walk() array_walk_recursive() ArrayIterator::current() ArrayIterator::key() ArrayIterator::next() ArrayIterator::rewind() ArrayIterator::seek() ArrayIterator::valid() ArrayObject::__construct() ArrayObject::append() ArrayObject::count() ArrayObject::getIterator() ArrayObject::offsetExists() ArrayObject::offsetGet() ArrayObject::offsetSet() ArrayObject::offsetUnset() arsort() ascii2ebcdic() asin() asinh() asort() aspell_check() aspell_check_raw() aspell_new() aspell_suggest() assert() assert_options() atan() atan2() atanh() |
I remember my first experience with PHP way back in 2001. Despite my questionable pedigree in ASP and Visual Basic, browsing an alphabetical PHP function list was enough to scare me away for years. Somehow, perusing the above list, I don’t think things have improved a whole lot since then.
I’m no language elitist, but language design is hard. There’s a reason that some of the most famous computer scientists in the world are also language designers. And it’s a crying shame none of them ever had the opportunity to work on PHP. From what I’ve seen of it, PHP isn’t so much a language as a random collection of arbitrary stuff, a virtual explosion at the keyword and function factory. Bear in mind this is coming from a guy who was weaned on BASIC, a language that gets about as much respect as Rodney Dangerfield. So I am not unfamiliar with the genre.
Of course, this is old news. How old? Ancient. Internet Explorer 4 old. The internet is overrun with PHP sucks articles – I practically ran out of browser tabs opening them all. Tim Bray bravely bucked this trend and went with the title On PHP for his entry in the long-running series:
So here’s my problem, based on my limited experience with PHP (deploying a couple of free apps to do this and that, and debugging a site for a non-technical friend here and there): all the PHP code I’ve seen in that experience has been messy, unmaintainable crap. Spaghetti SQL wrapped in spaghetti PHP wrapped in spaghetti HTML, replicated in slightly-varying form in dozens of places.
Tim’s article is as good a place to start as any; he captured a flock of related links in the ensuing discussion. As you read, you’ll find there’s an obvious parallel between the amateurish state of PHP development and Visual Basic 6, a comparison that many developers have independently arrived at.
Every solution I’ve ever seen or developed in PHP feels clunky and bulky, there is no elegance or grace. Working with PHP is a bit like throwing a 10 pound concrete cube from a ten story building: You’ll get where you’re going fast, but it’s not very elegant. . I love PHP, and it’s the right tool for some jobs. It’s just an ugly, cumbersome tool that makes me cry and have nightmares. It’s the new VB6 in a C dress.
From my own experience, and the countless of online tutorials and blogs, many PHP developers are guilty of the same crap code VB developers were once renowned for. OO, N-Tier, exception handling, domain modeling, refactoring and unit testing are all foreign concepts in the PHP world.
Understand that as a long time VB developer, I am completely sympathetic to the derision you’ll suffer when programming in a wildly popular programming language that isn’t considered «professional».
I’ve written both VB and PHP code, and in my opinion the comparison is grossly unfair to Visual Basic. Does PHP suck? Of course it sucks. Did you read any of the links in Tim’s blog entry? It’s a galactic supernova of incomprehensibly colossal, mind-bendingly awful suck. If you sit down to program in PHP and have even an ounce of programming talent in your entire body, there’s no possible way to draw any other conclusion. It’s inescapable.
But I’m also here to tell you that doesn’t matter.
The TIOBE community index I linked above? It’s written in PHP. Wikipedia, which is likely to be on the first page of anything you search for these days? Written in PHP. Digg, the social bookmarking service so wildly popular that a front page link can crush the beefiest of webservers? Written in PHP. WordPress, arguably the most popular blogging solution available at the moment? Written in PHP. YouTube, the most widely known video sharing site on the internet? Written in PHP. Facebook, the current billion-dollar zombie-poking social networking darling of venture capitalists everywhere? Written in PHP. ( Update: While YouTube was originally written in PHP, it migrated to Python fairly early on, per Matt Cutts and Guido van Rossum.)
Notice a pattern here?
Some of the largest sites on the internet – sites you probably interact with on a daily basis – are written in PHP. If PHP sucks so profoundly, why is it powering so much of the internet?
The only conclusion I can draw is that building a compelling application is far more important than choice of language. While PHP wouldn’t be my choice, and if pressed, I might argue that it should never be the choice for any rational human being sitting in front of a computer, I can’t argue with the results.
You’ve probably heard that sufficiently incompetent coders can write FORTRAN in any language. It’s true. But the converse is also true: sufficiently talented coders can write great applications in terrible languages, too. It’s a painful lesson, but an important one.
Why fight it? I say learn to embrace it. Join with me, won’t you, in celebrating the next fifty years of glorious PHP code driving the internet. Just don’t forget to call the maintain_my_will_to_live() PHP function every so often!
За что все ненавидят PHP?

Среди наиболее популярных и одновременно наиболее ненавистных языков программирования, PHP занимает почетное первое место. Всем известно, что изначально язык был создан лишь как инструмент для управления страницами (Personal Home Page) и обладал скудными возможностями. С тех пор прошло много времени и сейчас PHP обладает всеми возможностями современного языка программирования. В нем хорошо поддерживается ООП (абстрактные классы, интерфейсы, трейты, позднее статическое связывание). В связи с этим к нему легко применяются паттерны, что позволяет писать хорошо структурированный код. Его хейтили за динамическую типизацию, но с появлением php 7 можно включить строгую типизацию прописав в начале файла директиву declare(strict_types=1). С выходом версии 7.4 у него появилась строгая типизация свойств класса при помощи type hint.
Понятно, что бОльшая часть разработчиков, которые не считают PHP нормальным языком программирования, никогда не писали на нем , но так как это уже локальный мем для разработчиков, шутить над ним будут всегда. Но есть ли реальные причины ненавидеть PHP в качестве инструмента для разработки в 2020 году?
Поэтому вопрос, почему вы считаете PHP плохим/хорошим ЯП? Заслуживает ли он на жизнь за неимением лучших альтернатив? Виноваты ли индусы в плохой репутации языка, потому что большинство из них клепает говнокод на фрилансе?) В общем, поделитесь пожалуйста своим мнением о PHP, учитывая последние изменения и развитие языка вплоть до версии 7.4.
Натолкнуло меня на эти мысли юмористическое видео о жизни php разработчика. Поэтому я решил его перевести и озвучить.


58 постов 2K подписчика
Подписаться Добавить пост
Правила сообщества
— запрещено добавлять посты отличной от мира программирования тематики (так как пикабу — это в первую очередь развлекательный ресурс, то посты с юмором, историями, изображениями и всем, что связано с php — разрешены);
— запрещено добавлять посты с содержанием в виде вопроса, или просьбы о помощи, для этого есть тематические ресурсы;
— запрещено добавлять посты провакационной тематики, нацеленные на бессмысленные споры и оскарбления;
— в копипастах и переводах указывайте источники;
3 года назад
Опыта работы не много, 5 лет, но могу сказать, что нет плохого языка программирования. У каждого языка — своя задача и область применения. Конечно, можно натянуть сову на глобус и писать, например, на C++ веб-интерфейсы, но дороговато выйдет.
раскрыть ветку
3 года назад
Если мне не изменяет память, то проблема не в языке, а в тех кто на нем пишет. Предубеждение к программистам PHP возникло на фоне того, что порог вхождения в этот язык невысок. Студент может начать читать какой-нибудь самоучитель, прочитает две главы и воскликнет «О, да! Я теперь могу написать что-то своё, крутое!» И бегом пилить свой проект, не удосужившись дочитать хотя бы этот самоучитель до конца. Хотя во многих книжках есть примеры, где несколько строчек кода выделены жирным, а снизу подпись «если Вы решили реализовать это таким образом, то даже не смотря на то, что это может сработать, срочно выключите компьютер из розетки и подумайте, может быть программирование это не Ваше призвание в этой жизни?»
В результате мир заполонило невообразимое количество говнокода. Потому что кроме знания языка требуется еще знание методов, принципов, computer science, элементарной математики и кучи других важных вещей.
В качестве примера можно рассмотреть так же GOTO. Это ужас для некоторых программистов, ибо при помощи безусловных переходов можно такой ниндзя-код получить, в котором его создатель перестаёт что-либо понимать. Но если использовать с умом, то можно сильно облегчить код и избежать сложных конструкций.
раскрыть ветку
3 года назад
Я писал на PHP года 3-4, потом перешел на Java. Перешёл, т.к. мне не хватало енумов, типизации, паралелизма, генериков и прочих приятных и сложных фишичек. Не могу судить про современный PHP, но про версию ещё 5.3-5.4 могу сказать что язык неплохой, но есть куча недочётов и проблем с которыми мириться я бы не хотел (одна только концепция встраиваемости в html как тэг чего только стоит). Была на Хабре статья PHP — фрактал плохого дизайна. В целом там хорошо описаны все проблемы языка того времени.
Но для своей сферы применения он вполне хорошо подходит и хорошей замены ему, вероятно, нет. Но писать на нём я, конечно, не буду 🙂
раскрыть ветку
3 года назад
Язык программирования — это просто инструмент.
Отвёртка хороша болт крутить, но ей плохо пилить доски, а пилой хорошо пилить, но болты откручивать очень плохо.
А если есть какое-то расхожее мнение о том, что какой-то язык программирования плох, но тем не менее он продолжает использоваться, то это такие хитрые психологические игры.
Например, в среде 1С-ников принято лажать 1С мол нет в ней жёсткой типизации и ООП, и за многие другие моменты, но на самом деле все эти примочки в ней и не нужны, и ни на одном другом языке так быстро и просто не создать какую-либо учётную систему.
А лажают потому что хотят выжать из создателей 1С ещё что-нибудь.
Ну т.е. каких-то новых инструментов, возможностей и т.п.
Типо «гавно среда разработки сделайте получше».
Хотя на самом деле эта среда разработки великолепна.
А делают так потому что менталитет такой.
Не знаю даже как назвать такой феномен. поиск халявы, через унижение видимо, ну и оправдание собственной глупости и тупости.
раскрыть ветку
3 года назад
Порог вхождения низкий, поэтому и индусы и школьники на нем пишут. То же самое было когда-то с delphi.
Я не программист, но иногда приходится решать задачи по работе с парсингом данных, и другим скриптингом. Можно конечно было бы заморочиться с C#, python или любым другим языком, но апач настроен, php установлен, mysql шуршит. Какая связка еще более проста и удобна для обычного пользователя?
раскрыть ветку
Похожие посты
12 дней назад

3D видеокарта-«декселератор» из 90-х. Как работала S3 ViRGE «под капотом»?

Друзья! Многие ли из вас застали такую легендарную видеокарту, как S3 ViRGE? Когда-то этот GPU стоял чуть ли не в каждом втором офисном компьютере: благодаря дешевизне и заявленной поддержке 3D-ускорения, эту видеокарту просто сметали с полок магазинов. Далеко не все могли себе позволить ATI Rage, Riva TNT и уж тем более 3dfx Voodoo и очень разочаровывались в свежекупленной видеокарте, когда пытались поиграть в новомодные игры тех лет. На момент написания статьи, в сети слишком мало материала о том, как работали видеокарты 90-х «под капотом», однако мне удалось найти даташит на видеочип, SDK для программирования 3D-графики специально под него и некоторую документацию. Я решил исправить это недоразумение и начать развивать отдельную рубрику о работе старых видеочипов: начиная от S3 ViRGE и заканчивая GPU PS2 и PSP. Сегодня мы с вами: вспомним о S3 ViRGE, узнаем о том, как работали видеокарты в 90-х годах, затронем 2D и 3D режим и почему они тесно связаны между собой, посмотрим на проприетарное графическое API S3 ViRGE и раскроем причину, почему же этот GPU был таким медленным!
❯ 3D графика на ПК: начало
В начале 90-х годов 3D-графика на обычных домашних компьютерах была редкостью. Профессиональные GPU применялись только на дорогущих графических станциях, которые использовались в кинематографе или различных симуляциях, а также на дорогих японских игровых автоматах. У простого обывателя не было доступа к аппаратным средствам рендеринга 3D-графики.

Однако это не значит, что 3D-графики не было вообще. Прогресс развития домашних процессоров шёл семимильными шагами и гиганты рынка —Intel,AMDи в некоторой степени Cyrix, выпускали всё новые и новые процессоры с повышенными тактовыми частотами, а ближе к середине 90-х — и с SIMD (MMX). Поскольку многие техники для отрисовки трехмерного изображения были разработаны ещё в 60-х — 70-х годах, игроделы к началу-середине 90-х во всю использовали некоторые наработанные техники из кинематографа для растеризации 3D-графики прямо на процессоре — так называемыйсофтварный рендеринг.

Одной из самых известных техник 90-х являлась 2.5D графика с использованием рейкастинга — когда картинка на экране выглядит как трёхмерная, однако по факту весь мир представлен в виде 2D-координат, а эффект «пола и потолка» был как бы фейковым. Принцип его работы довольно прост: от глаз игрока для каждого горизонтального пикселя (т. е. при разрешении 240х320, у нас будет 240 проходов) пускаются «лучи» и ищется пересечение с ближайшей стеной относительно угла обзор из глаз игрока. Из этого пересечения берется дистанция до этой стены (на основе дистанции и угла считается «высота» данной строчки стены) и считается какую строчку текстуры необходимо вывести в этой точке. Одними из первых игр с применением этой технологии стал Hovertank и Wolfenstein 3D, а технология применялась практически до конца 90-х. Одной из самых лучших реализаций рейкастинга — движок Duke Nukem 3D, Build Engine, написанный Кеном Сильверманом.

Однако не одним 2.5D мы были едины. Шли годы, в СНГ многие люди продолжали наслаждаться 8-битными и 16-битными играми на клонахNESи SMD. У некоторых уже появлялась PS1, которая позволяла играть в игры с довольно хорошей 3D-графикой, однако на ПК 3D-игры были доступны не всем. Но в 1996 году выходитQuake— новейший шутер от первого лица от id Software с настоящей, трушной 3D-графикой и переворачивает всю индустриюFPSс ног на голову. Посудите сами: Джон Кармак умудрился реализовать достаточно быстрый софтварный рендерер, который мог вполне сносно работать на Pentium 75Мгц в разрешении 320×240. А ведь помимо отрисовки кадра, игре нужно было просчитывать логику монстров (довольно примитивную, к слову), обрабатывать столкновения, просчитывать видимую геометрию с помощью BSP-дерева и обрабатывать клиент-серверную логику самой игры. Это была самая настоящая революция в мире 3D игр на ПК.

В 1997 году, id Software выпустили glQuake — порт Quake с софтрендера на OpenGL, плюс своеобразную прослойку для совместимости с API 3dfx Glide (на видеокартах Voodoo) и подмножества OpenGL, используемым в игре. Порт на OpenGL позволял разгрузить ЦПУ, перенеся всю отрисовку графики с процессора на 3D-ускоритель. Сам по себе, OGL как графическое API, представлял из себя лишь набор спецификаций, который мог быть реализован как в программном виде, так и в аппаратном производителем видеокарты (на примере Windows — OpenGL32.dll это программная реализация, которая при необходимости обращается к atioglxx.dll/nvoglvxx.dll — аппаратной реализации OpenGL от вендора видеочипа). Однако, OpenGL корнями уходил именно в отрисовку промышленной графики, а DirectX всё ещё находился в зачаточной форме, из-за чего многие производители разрабатывали собственное графическое API: из известных мне, могу подчеркнуть ATI CIF (C Interface), 3dfx Glide и проприетарное SDK S3 ViRGE. Некоторые вендоры поддерживали целые игровые движки — например, BRender и RenderWare.

Отдельные 3D-акселлераторы потихоньку начали завоевывать сердца геймеров и создавать новый сегмент рынка. Серьезные видеокарты от известных производителей, такие как 3dfx Voodoo, ATI Rage и Riva TNT стоили достаточно дорого и многим были не по карману. Зато существовало множество видеокарт с 3D-ускорителями от других производителей, про некоторые из них вы могли даже не слышать: отдельные дискретные видеокарты Intel (i740), видеокарты от производителя чипсетов SiS и конечно же, видеокарты от S3 с сериями ViRGE и Savage. Видеочипы от Intel и SiS делали упор на D3D 7.

S3 ViRGE была весьма неплохой видеокартой с точки зрения 2D-ускорения. Сейчас 2D принято считать частным случаем 3D (по факту, 2D-спрайты — это 3D-квады, состоящие из двух треугольников), однако в то время для работы с памятью видеокарты и аппаратного ускорения некоторых операций, таких как блиттинг (BitBlt) существовало отдельное графическое API — DirectDraw. С этим у ViRGE было всё хорошо — он поддерживал довольно высокое разрешение экрана (при желании, объём видеопамяти можно было нарастить и установить разрешение ещё выше) и умел ускорять часть операций как DDraw, так и GDI.
Однако, ViRGE разочаровывал многих геймеров 90х своей производительностью в 3D-графике. На коробке с бюджетной видеокартой красовались красивые надписи о 3D-графике следующего поколения, а на фотографии можно было увидеть некую игру про мехов с невиданной графикой!

По факту, ViRGE подходил для 3D-игр не особо хорошо. Конечно в те годы никто особо не плевался от FPS и при желании, игру могли пройти и в 15, и в 20 FPS. Однако производительность софтварного рендерера иногда была даже выше, чем у растеризатора ViRGE, а игры должны были быть специально адаптированы под неё (т. е. портированы для использования S3DTK). Тайтлов с адаптацией по этот GPU было немало: как минимум, Tomb Raider и MechWarrior 2 (который шел в комплекте с игрой). Польские ребята из известной многим Techland даже написали прослойку S3D -> OpenGL, позволявшей запускать Quake на ViRGE. Производительность была не ахти…
Видеокарт от S3 нашлись и у меня, причём сразу несколько — ViRGE в PCI-исполнении и Trio в AGP-исполнении! Иногда я их использую для проверки старых материнских плат, которых у меня не так уж и много — рабочих на PGA370 и ниже у меня совсем нет. Однако остаётся вопрос, как эти видеокарты работали под капотом? Давайте узнаем!
❯ «Под капотом»
Исторически сложилось так, что 3D и 2D акселераторы могли быть отдельными и формально не зависящими друг от друга устройствами. Архитектура IBM PC в зависимости от «поколения», предполагала сразу несколько типов видеоадаптеров, которые были стандартизированы под определенный тип мониторов. Один из таких адаптеров, VGA, стал стандартом на долгие годы, в то время как два других использовались в совсем ранних машинах. Их ключевое отличие было в организации видеопамяти и цветности — CGA/EGA предполагал разбитие пространства экрана на т. н. битплейны (один байт содержал информацию о нескольких пикселях и если не ошибаюсь, для сохранения адресного пространства сегменты экрана необходимо было переключать аля банки памяти) и былпалитровым, в то время как VGA предполагал как палитровый режим, так и полноценный RGB и мог отразить весь фреймбуфер в линейную область адресного пространства. Кроме того, долгое время VGA использовался для обозначения разрешения дисплея: QVGA — половина VGA (320×240), VGA (640×480), широкоформатный WVGA (800×480) и т. п.

Другой особенностью была полная (насколько мне известно) обратная совместимостью друг с другом. Например, GeForce 7xx, как один из последних GPU, который поддерживал Legacy BIOS, теоретически вполне мог работать и с EGA режимами, и с CGA через соответствующие видеорежимы int 10h!
3D-режимы же никак не были стандартизированы и каждый производитель реализовывал работу с ними по разному — как уже говорилось ранее, кто-то реализовывал поддержку совсем молодого D3D и OpenGL (насколько мне известно, лучше всего с OpenGL было у NVidia. Остальные вендоры поддерживали OGL, но были свои болячки — у ATI они тянулись чуть ли не до середины-конца нулевых), а кто-то делал собственное графическое API и работал с видеочипом почти напрямую. Первые 3D GPU использовали шину PCI, которую почти сразу заменила более скоростная, но интерфейсно и софтварно почти идентичная шина AGP, а затем уже появился PCI-E, который оставался тем же PCI в софтовом плане, но был дифференциальным и последовательным, а не параллельным как интерфейсы-предшественники.

Дабы понять, как работают первые видеокарты, необходимо узнать о том, как происходит процесс отрисовки 3D-графики в общем случае. В мире программирования графики это называетсяконвейероми состоит он как минимум из нескольких этапов:
Установка состояний: Программа задаёт источники света на сцене, параметры Z-буфера и Stencil-буфера, какую текстуру(ы) следует наложить на рисуемую геометрию и с какой фильтрацией, какой тип аппаратного сглаживания использовать и т. п.
Ранее, каждый стейт необходимо было устанавливать отдельно, при необходимости — для каждого DrawCall’а. После подготовки состояния, программа вызывает соответствующую функцию отрисовки.
Обработка геометрии: Геометрия не поступает в растеризатор «как есть», в мировых координатах. Растеризатор оперирует вершинами в нормализованныхClip Spaceкоординатах — обычно, это [-1, -1… 1, 1], где 0.5 — центр экрана по каждой оси. Именно поэтому сначала необходимо провести этап трансформации геометрии для перевода из некой глобальной системы координат (которая может выражаться в метрах или, например, в пикселях) в Clip Space. Для этого чаще всего координаты (корректнее — трансформации) представляются в виде трех перемноженных матриц — model (мировые координаты геометрии), view (положение «глаз» в мире, или по простому камера. Умножая model на неё, мы получаем координаты объекта в пространстве камеры) и projection (матрица проекции, которая преобразовывает координаты из пространства глаз в тот самый Clip Space. Именно в этой матрице задается FOV для перспективной проекции и виртуальные размеры экрана для ортографической матрицы). После этого, координаты каждой вершины трансформируются полученной ModelViewProjection матрицей и получается финальная позиция для Clip Space. Звучит как сложный учебник матану, по факту всё очень просто. 🙂

Детали реализации низкоуровневого матана, в том числе перемножения матриц и построения матриц трансформаций и проекции знать желательно, но необязательно. Сейчас этим занимаются очень удобные математические библиотеки — например, glm, dxmath или d3dx.
Кроме того, ранее именно на вершинном этапе считалось освещение для уровня. В некоторых видеочипах была возможность аппаратного расчета источников света, в некоторых — только программная на ЦПУ.
На видеокартах тех лет, в том числе и S3 Virge, трансформацией вершин занимался центральный процессор, из-за чего было довольно серьёзное ограничение на количество вызовов отрисовки и число треугольников в одной модели. Видеокарты с аппаратной, но всё ещё не программируемой трансформацией вершин появились лишь к GeForce 2 — называлась эта технология T&L (Transform and Lightning) и её преимущество было в том, что у видеокарты были специализированные векторные сопроцессоры, способны быстро пересчитывать векторные операции (а у ЦПУ, в свою очередь, развивались SIMD наборы инструкций, позволяющие выполнять несколько операций над float одновременно). В некоторых случаях, был даже отдельный программируемый векторный сопроцессор как, например, в PlayStation 2, что позволяло реализовать вершинные шейдеры ещё в 2000 году! На современных видеокартах, этапом трансформации в самом простом случае управляют вершинные шейдеры. Помимо этого, есть возможность создания геометрии «на лету» с использованием тесселяции и геометрических шейдеров, а совсем недавно появились Mesh-шейдеры, которые объединили несколько подэтапов конвейера в один.

Растеризация: Сам процесс отрисовки геометрии на дисплей с данными, полученными с прошлого этапа. Именно на этом этапе треугольники (или иные геометрические примитивы) закрашиваются определенным цветом или на них накладывается текстура. В процессе растеризации есть такое понятие, как интерполятор — специальный модуль, который интерполирует несколько значений в барицентрических координатах растеризуемого треугольника, дабы текстурный юнит мог наложить определенный участок текстуры на фрагмент треугольника.
В современных видеокартах этот этап конвейера программируется пиксельными (или фрагментными) шейдерами. В старых видеочипах (исключение — вроде-бы частично программируемый GPU Nintendo 64, поправьте в комментариях, если не прав) этот процесс строго определен в каждом GPU и не программировался. Именно поэтому такой подход к рисованию графики назывался Fixed function pipeline. Были ещё комбайнеры, но они появились заметно позже — когда в видеокартах появилось уже несколько текстурных юнитов, способных смешивать несколько текстур одновременно.

Делая вывод, мы можем понять, что S3 Virge и другие видеочипы были устройствами, которые умели рисовать лишь тот уровень графики, который был заложен производителем с завода. Такой подход называется фиксированным конвейером — Fixed Function Pipeline. Сейчас разработчики видеочипов перешли с фиксированного конвейера на программируемый (шейдерный). Уже начиная с SM2.0-SM3.0, на современных видеокартах появилась возможность создавать крутое и достаточно сложное освещение и различные эффекты, которые стали неотъемлемыми в современных играх.
Кроме того, важно понимать, что в видеопамяти ранних видеочипов хранился только фреймбуфер, а немного позже — текстуры, именно поэтому VRAM в старой документации называют «текстурной памятью». Вообще, некоторые нюансы первых версий OpenGL тянуться именно из особенностей работы первых видеокарт. Вспомнить хотя бы первые функции для старта отрисовки геометрии и загрузке вершин на видеокарту — это были связки glBegin/glVertex/glEnd:
glBegin(GL_TRIANGLES);
glVertex3f(0, 0, 0);
glVertex3f(1, 0, 0);
glVertex3f(1, 1, 0);
glEnd(); // Для одного треугольника
glBegin(GL_TRIANGLES);
for(int i = 0; i
glEnd(); // Для меша
Даже сам glBegin/glVertex/glEnd появились не спроста. Геометрию на видеокарте начали хранить только в начале нулевых (и то не везде — привет встройкам Intel и S3).
Но перейдем к особенностям работы S3 ViRGE. Даташит лежит в свободном доступе, благодаря чему мы можем более подробно ознакомиться с характеристиками этого видеочипа и о том, как он работал под капотом.

В основе у нас лежит 64-х битное ядро, которое могло обрабатывать как 2D-графику с аппаратным ускорением, так и 3D-графику. Ядро работало на частоте 135МГц с встроенным RAMDAC (модуль, отвечающий за вывод картинки на аналоговые разъемы — VGA и DVI, однако выводом на TV-тюльпаны занимался отдельный чип TV-энкодер). Современные видеочипы перешагнули планку 1ГГц, однако сравнение исключительно по частоте некорректны — архитектуры очень сильно отличаются. Помимо этого, видеочип умел декодировать видео с интерполяцией и аппаратно «помогать» процессору с скейлингом видео (например, когда вы разворачиваете плеер на весь экран) и даже рендерить видео в текстуру (что позволяло реализовать, например, телевизоры в играх)!
3D движок поддерживал следующие возможности:
- Затенение по Гур.о
- Маппинг текстур с перспективной коррекцией и билинейной/трилинейной фильтрацией, а также мипмаппингом.
- Depth-буфер, сэмплинг тумана и поддержка альфа-блендинга (прозрачной геометрии).
Чип поддерживал две шины — PCI и менее известную VLB (Vesa Local Bus, очень условно ISA)
Помимо этого, у чипа не было встроенной памяти — к нему необходимо было подключать внешнюю DRAM-память 2/4/8Мб. От её количества зависело максимально-поддерживаемое разрешение экрана. Текстуры при необходимости хранились в ОЗУ.

Видеопамять когда-то расширялась за счёт дополнительных модулей! Эту видеокарту можно расширить аж до 8МБ!
Поддерживаемые разрешения экрана:

Для DirectDraw и ускорения 2D-графики в Windows была реализация аппаратного BitBLT — копирования пикселей в точку на экране. Она поддерживала все режимы, которые были в реализации этой функции в Windows — от монохромных, до 24-х битных. Без альфа-блендинга, само собой. Но тут нет ничего необычного — многие видеочипы тех лет предоставляли простое 2D-ускорение.
Интереснее реализация отрисовки 3D-графики. Каждый треугольник описывался 3-мя регистрами на каждый параметр — координата X, Y для каждой точки, текстурные координаты и т. п. Всего для отрисовки одного треугольника могло потребоваться до 43 регистров! Весьма немало. И именно из-за этого в свое время появились glBegin/glVertex/glEnd!

Параметры сэмплера (текстурного юнита) задавались регистрами, которые определяли формат пикселя текстуры и сам тип фильтрации. Как я уже говорил выше — поддерживалась билинейная и трилинейная фильтрация и проприетарный формат сжатия текстур, который стал стандартом: S3TC или DXT.

Для программирования S3 ViRGE было разработано собственное C SDK — S3DTK, которое состояло из сэмплов и заголовочных файлов для общения с GAPI видеочипа (или видеочипом напрямую, если игра предназначена для DOS). При этом вполне не исключено, что GAPI для Windows работало с видеокартой напрямую, предоставляя PCI-драйвер лишь как прослойку для обмена данными. Поскольку это не D3D, для игр с поддержкой видеоускорения требовалось качать специфические версии. Некоторые игры (как Quake 2) поддерживали мультирендер, но не поддерживали S3 ViRGE.
Весь графический API помещался в один заголовочный файл. API было не простым, а очень простым и понятным — думаю, даже разработчикам-новичкам было легко начать программировать под ViRGE!

Формат вершин был фиксированным и зависел от того, как вы рисовали геометрию на экране:

GAPI поддерживало различные типы треугольных списков, а также точки (POINT для спрайтов и систем частиц) и линии:
#define S3DTK_TRILIST 0
#define S3DTK_TRISTRIP 1
#define S3DTK_TRIFAN 2
#define S3DTK_LINE 3
#define S3DTK_POINT 4
Фактическое API для рисования умещалось в 9 функций и ещё несколько функций для инициализации библиотеки, преобразования адресного пространства и работы с Windows.
Для работы с состоянием видеочипа служили две функции — SetState и GetState. Именно они отвечали за то, как рисовалась геометрия на экране:

А для фактического рисования примитивов служили функции TriangleSet и TriangleSetEx! Да, это альтернатива DrawPrimitives/DrawArrays в современных GAPI. Никаких индексов тогда ещё не использовалось! Функции принимали указатель на массив вершин и их количество, а также на тип рисуемой геометрии (треугольники, линии и т. п.). В Ex версии, можно было «пачкой» установить стейты параллельно с рисованием — такой подход используется в DX10+ API — стейты тоже задаются исключительно «пачками», только теперь они поделены на подгруппы.

Для 2D-рисования были свои, отдельные функции — для блиттинга. Поддерживался ColorKey/хромакей — прозрачным считался определенный цвет, переданный как параметр функции

Основной причиной медлительности S3 ViRGE был низкий филлрейт. При отрисовке примитивов, которые занимают большое пространство экрана, FPS резко просаживался даже с примитивными кубиками и пирамидками. Однако, если не насаживаться на филлрейт и делать что-то типа 2D-поля и 3D-танчиков, то производительность оставалась вполне приемлимой.
❯ Заключение
История S3 закончилась поглощением компанией VIA. После этого, компания разрабатывала интегрированную графику специально для чипсетов VIA, а материнские платы на этих чипах пользовались довольно высоким спросом. Поэтому нередко взяв старый бюджетный ноутбук, года эдак 2005, можно найти в нём VIA Chrome — наследника легендарного S3 Savage! Проблемы у такого подхода тоже были — из-за наследия из конца 90х, ранние Chrome по сути поддерживали только D3D 7.0 и OpenGL ~1.4. Несколько позже, в 2009 году, компания выпустила S3 Chrome 540 GTX — одну из последних видеокарт на собственной архитектуре. Этот видеочип был достаточно современным и поддерживал DX10.1, OpenGL 3.0. Интересно, реально ли найти эту видеокарту сейчас?

По итогу мы можем сделать вывод, что первые 3D-ускорители были относительно простыми устройствами «под капотом» и их можно было программировать чуть ли не «напрямую». Многие старые видеочипы получили свои локальные прозвища и стали легендарными, однако их архитектура и принцип работы оставались тайной. По крайней мере, в рунете точно.

Насколько я понимаю, неравнодушные инженеры после закрытия 3dfx и слияния S3 с VIA решили «слить» даташиты в сеть, за что им большое спасибо! Ведь теперь мы имеем возможность посмотреть на принцип работы таких устройств сами!
Материал подготовлен при поддержке TimeWeb Cloud. Подписывайтесь на меня, мой Telegram и @Timeweb.Cloud, чтобы не пропускать новый материал каждую неделю!
Чем плох php
+- SA-MP Forums Archive (https://sampforum.blast.hk)
+— Forum: Non-English (https://sampforum.blast.hk/forumdisplay.php?fid=9)
+— Forum: Languages (https://sampforum.blast.hk/forumdisplay.php?fid=33)
+—- Forum: Русский/Russian (https://sampforum.blast.hk/forumdisplay.php?fid=32)
+—- Thread: Хранение вещей в ящиках. (/showthread.php?tid=591703)
Хранение вещей в ящиках. — Daea — 15.10.2015

Добрый день.
Делаю систему ящиков и возникло пара вопросов. Хотелось бы слышать мнение экспертов
1) Сохранение вещей и их количества на MySQL, увы, на той версии, которая у меня, приходится создавать калбэк.
2) Сохранение на MxIni.
3) Сохранение в ячейках.
4) Сохранение в Streamer. E_Extra же массив?

1, 2, 4 подразумевается полное отсутствие использования глобальных переменных. Т.к, ё хотелось бы, чтобы количество ящиков было неограничено. Т.е. узнаем из бд, файла, streamer и сразу используем
Как было бы быстрее, удобнее?
Как сделали бы Вы?
Re: Хранение вещей в ящиках. — cm666 — 15.10.2015
1 Чем плох калбэк ?
2 Mxini не пойдет если будет очень много данных
3 лимиты и прочее
Re: Хранение вещей в ящиках. — Daea — 16.10.2015
Просто нужно было узнать информацию из бд и сразу использовать её в месте запроса. А так результаты будут известны только в калбэке.
На счет mxini. Можно же создавать много файлов xD. Знаю, что бред, поэтому, можно расуидать их по каталогам и тогда быстро бы загружалась и использовалась информация. Где то 36 значений нужно сохранить.
А на счет стримера Вы не вкурсе?)
Re: Хранение вещей в ящиках. — Richard_Gere — 16.10.2015
Ну если я правильно понял, то будет логичнее сделать так:
1. При старте загружаем из бд все данные, создаем ящики
2. Данные ящиков можно прямо в стример записывать в extra, туда же id в бд
3. При использовании/удалении ящика изменять/удалять данные в бд
Re: Хранение вещей в ящиках. — Daea — 16.10.2015
В общем, нашёл, как по мне, оптимальное решение. Вся информация о предметах содержится в стримере в array.
new data[5] = ; Streamer_SetArrayData(STREAMER_TYPE_OBJECT, DObjects[gorik][g_gid], E_STREAMER_EXTRA_ID, data); Streamer_GetArrayData(STREAMER_TYPE_OBJECT, DObjects[gorik][g_gid], E_STREAMER_EXTRA_ID, data); format(string, sizeof(string), "%d %d %d %d %d", data[4], data[3], data[2], data[1], data[0]); SCM(playerid, -1, string);
Как только ящик используется он сохраняется. Как только он ставится — загружается.
Хотелось бы услышать мнение «старичков»
Re: Хранение вещей в ящиках. — Daea — 16.10.2015
Блин, обрадовался уж, а тут фигня какая-то.. Загружается только 9 чисел. Другие превращаются в 0. Что такое? В документациях ничего не сказано про подобное.
new data[54]; for(new d = 0;d < 54;d++) < data[d] = 1+random(9); format(string, sizeof(string), "%s %d", string, data[d]); >Streamer_SetArrayData(STREAMER_TYPE_OBJECT, DObjects[gorik][g_gid], E_STREAMER_EXTRA_ID, data, 54); print(string);
new data[54]; Streamer_GetArrayData(STREAMER_TYPE_OBJECT, DObjects[gorik][g_gid], E_STREAMER_EXTRA_ID, data, 54); for(new d = 0;d < 54;d++) format(string, sizeof(string), "%s %d", string, data[d]); print(string);
Вот что выводит..
[20:34:43] 2 7 1 8 7 2 3 8 3 5 3 7 8 2 5 9 1 6 4 3 9 8 8 8 9 1 9 2 5 4 2 5 5 9 5 6 7 5 7 5 1 1 1 2 7 4 5 8 9 3 6 2 9 3 - СОХРАНЕНИЕ [20:34:44] 4 6 9 5 3 8 1 7 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - ЗАГРУЗКА
Re: Хранение вещей в ящиках. - S4D - 16.10.2015
Че-т странно ты заносишь данные. Попробуй так:
for(new d = 0 ; d < 54 ; d ++)
<
Streamer_AppendArrayData ( STREAMER_TYPE_OBJECT , DObjects [ gorik ][ g_gid ], E_STREAMER_EXTRA_ID , 1 + random ( 9 ));
>
Re: Хранение вещей в ящиках. - Daea - 17.10.2015
Толку нет, всё-равно всё так же)
Re: Хранение вещей в ящиках. - cm666 - 17.10.2015
Попробуй записать и сразу считать
Re: Хранение вещей в ящиках. - Richard_Gere - 17.10.2015
Какая версия стримера?
На последней версии всё нормально, сейчас проверил, вот результат:
switch (true)
Недавно столкнулся с необоснованным, на мой взгляд, злоупотреблением конструкцией switch в PHP (язык в этом вопросе — важный момент!). И, кажется, сторонников у меня по этому вопросу особо и нет, как нет и достаточно авторитетного источника, где бы было сказано, чем такое использование плохо. Авторитетным мой бложик не назовёшь, но пусть эта запись станет той песчинкой, что, возможно, поможет сформировать будущий фундамент, а также даст подкрепление альтернативного мнения. Всё написанное далее — моё личное мнение. Я могу ошибаться, поэтому буду рад замечаниям, уточнениям, дополнениям и аргументам. Кроме того, если вы приверженец switch (true), пожалуйста, напишите мне свои аргументы, почему может быть удобнее/выгоднее/эффективнее использовать эту конструкцию?
Чем плох switch (true)?
Первое, что приходит в голову — это семантика. Скажем так, классический пример использования switch предполагает перечисление уникальных случаев (case).
switch ($value) case 1:
// do something
break;
case 2:
// do something else
break;
case 3:
// do another thing
break;
default:
// default behaviour
>
Тут все кейсы разные и, по умолчанию, порядок следования кейсов значения не имеет. При этом есть прямая связь между тестируемым значением ($value) и кейсами.
Использование switch (true) ломает представление как об уникальности кейсов, так и о об отсутствии важности порядка следования кейсов.
То есть, в моем представлении, правильная конструкция switch с boolean значением должна выглядеть так:
switch ($value) < case true: // do something break; case false: // do something else break; >
(Но тут сразу явно видно, что вместо неё надо использовать if/else.)
В этот момент мы подходим ко второму моменту, чем плоха данная конструкция. Появление приоритета кейсов делает конструкцию более подверженной ошибкам, так как явно этот порядок никак не выражается и не “навязывается”. И это даже без уже давно известной критики (и классической ошибки) break.
Ещё одной проблемой конструкции switch/case в PHP (не только switch (true)) я считаю лишнюю вложенность кода. В некоторых современных языках, где к проблемам этой конструкции подошли серьезнее (Go, Swift) не только заставили явно обозначить желание провалиться в следующий кейс (что, собственно, и является классической ошибкой с break), но и форматировании по умолчанию (go fmt, и форматирование в Xcode) убирают лишнюю вложенность кода.
Вложенность плоха тем, что обычно свидетельствует об увеличении цикломатической сложности кода. Здесь по сути такого усложнения нет, но мы будто намеренно хотим себе навредить и себя же обмануть, поставив обманчивый указатель (в виде лишнего отступа): “Здесь сложность. (Но это не точно.)”
И, наверное, последний момент, который я сейчас вспомнил. Приведу частный случай использования switch (true):
switch (true) case $a instanceof ClassA:
return 1; // обратите внимание, это версия лишена проблемы с break, как будто
case $a instanceof ClassB:
return 2;
case $a instanceof ClassC: // а вот это удобное на первый взгляд схлопывание условий на самом деле возвращает проблему с break :)
case $b instanceif ClassA:
return 3;
>
Тут налицо все вышеописанные проблемы. Отдельно хочется отметить отсутствие связи между кейсом и тестируемым значением. Я в этот пример также намеренно добавил кейс, где неожиданно появляется какое-то $b, о котором ранее никто не знал. Именно так нередко и бывает в реальном коде.
Собственно, чем плох этот пример — он намекает, что никакого switch тут быть не должно, его место должен был занять полиморфный вызов. Кстати, если бы мы заменили тут switch (true), например, на if/elseif/else, то лучше бы не стало по той же причине.
Когда использовать switch?
Как уже понятно, я против использования switch (true). Но это ещё не всё: мне кажется, что использование switch в PHP должно быть ограничено фабриками, которые помогают с реализацией полиморфизма.
Возможно. Возможно, есть другие оправданные случаи, которые скорее всего будут похожи на мой первый классический пример использования switch.
Что же использовать?
Вы не поверите, но специально для работы с логическим типом (boolean) у нас есть старый добрый if.
И он лишён описанных выше недостатков switch (true).
И да, его тоже можно использовать неправильно — аналог кода с instanceof легко найти и в варианте с if/elseif/else.
Вкусовщина?
Конечно! Меня очень забавляет, когда на критику использования чего угодно отвечают: “Вкусовщина!” Для меня как красная тряпка.
Действительно, не важно что мы выбрали: if или switch, чёрный хлеб или белый, нарезать дольками или кольцами. Это всегда будет дело вкуса и предпочтений. Но за ними всегда стоят аргументы: с чёрным хлебом я наберу меньше килограмм, дольки скорее поместятся ко мне в рот, а по поводу выбора между if и switch я уже написал выше.
Но дело не ограничивается этим: какие отступы выбрать (ага, вечный холивар пробелы против табуляции), какой алгоритм использовать, как назвать метод, какой длины должен быть класс, имеет ли право сущность иметь только геттеры и сеттеры?
Не будем обсуждать холиварные вопросы. Какой алгоритм использовать? Кто-то скажет, конечно, более эффективный по процессору. Другой ответит, что более эффективный по памяти. Третий предпочтёт более простой для понимания. И всё это будет делом вкуса, пока на первое место не выходят другие требования.
Если мы хотим снизить вероятность появления ошибки из-за человеческого фактора, то выберем более простой алгоритм. Если же перед нами встаёт вопрос ограничения ресурсов, мы будем решать эту проблему, оптимизируя работу программы.
Код пишется для людей
Стоит ли об этом напоминать, поэтому я обычно за более простой вариант. Что важно, код пишется (не только с нуля, речь и про внесение доработок) людьми. Поэтому я предпочитаю выбирать такой вариант, который помогает избегать возможности появления ошибки. Кто-то скажет, что на это у нас есть тесты, но и их пишут люди. Я сам хороший пример написания не лучших тестов, которые могут пропустить вполне очевидную ошибку. Что уж говорить про те ошибки, о которых не подумал автор теста.
В общем, использование некоторых техник защитного программирования как раз делает свой вклад в повышение качества кода.
И в данном случае даже выбор конструкции if вместо switch (true) я отношу именно к такому защитному механизму.
Оптимизация со switch (true)?
Выбор в пользу использования switch иногда аргументируют оптимизацией. Мол, не надо выполнять выражение несколько раз.
Это справедливо для такого варианта:
switch ($service->doSomeExternalRequestsAndMath()) case 1:
// .
>
Но о какой оптимизации мы говорим в варианте с switch (true)? Я специально повторю свой пример с instanceof тут:
switch (true) case $a instanceof ClassA:
return 1; // обратите внимание, это версия лишена проблемы с break, как будто
case $a instanceof ClassB:
return 2;
case $a instanceof ClassC: // а вот это удобное на первый взгляд схлопывание условий на самом деле возвращает проблему с break :)
case $b instanceif ClassA:
return 3;
>
Читаемость
Вот тут ярче всего может проявиться противоборство вкусов (как с пробелами против табов). Несмотря на мой аргумент с лишним отступом, кто-то найдёт это улучшением —код со switch более “разряженный”, читать легче. Другой скажет, что в варианте с if код выглядит более сбитым, что позволяет вычленить из спагетти логический блок (а потом возможно выделить его и в отдельный метод?).
Рассмотрим два упрощенных примера.
switch (true) case $a > 911:
$exchange->publish(['a' => $a]);
$db->saveNewValue($a);
break;
case $a > 112:
$logger->log('problem here!', ['a' => $a]);
break;
// .
>
if ($a > 911) $exchange->publish(['a' => $a]);
$db->saveNewValue($a);
> elseif ($a > 112) $logger->log('problem here!', ['a' => $a]);
> // .
Мы видим, что вариант с if такой же плохой как и вариант со switch, только менее многословный. Для меня это уже становится флажком, что понять проще.
switch (true) case $a > 911:
$this->registerIncomingValue($a);
break;
case $a > 112:
$this->reportProblem($a);
break;
// .
>
if ($a > 911) $this->registerIncomingValue($a);
> elseif ($a > 112) $this->reportProblem($a);
> // .
Мы упростили тела наших ветвлений и код стал гораздо проще и понятнее в варианте с if, а в switch это улучшение как будто потерялось на фоне многословности.
Но if нередко винят в плохой читаемости из-за “тяжёлого” условия, которое как будто читается лучше на отдельной строке (менее загруженной лишними символами) case. В нашем примере нет такого условия, но всё же продемонстрирую, как легко это исправляется и попутно улучшает читаемость кода (тут имеется в виду: делая язык более натуральным):
if ($this->isNormal($a)) $this->registerIncomingValue($a);
> elseif ($this->isTooLow($a)) $this->reportProblem($a);
> // .
