LINUX.ORG.RU
ФорумTalks

Про линукс, графики и шмеля.

 , ,


1

4

Многие из присутствующих еще не успели застать времена, когда иксы еще назывались хfree86 и потому не знают, кого благодарить за то, что у них нвидия без шмеля не работает. Рассказываю почему в линуксе все плохо с графикой там, где в винде просто берет и работает.

Когда-то видяхи были только PCI и немного ISA. ISA это вообще чтоб страдать, я помню у меня была аудиокарта ISA.... ооооо, как безбожно тормозило видео на k6-500 с этой аудяхой и как оно игралось мплеером с pciшной картой. Короче, ISA - это вообще зло которое надо забыть как страшный сон. Поэтому его забыли и в ведре все устройства растут из «шины PCI».

Но в те далекие времена видяхи точились под windows gdi(посмотрите hardware caps той же s3trio64v+) и поэтому дров под линукс для них не было. а там кстати были аппаратные шрифты, если кто не знает. поэтому в линуксе была только опция framebuffer и страдать. А чтоб совсем уж не тормозить, картинки гоняли через MIT-SHM.

Затем появилось ускорение, и работало оно на стороне сервера х86 и то - только по талонам и для своих. Плюс иксы тупо лезли своими лапами прямо в железо, да так, что для них даже вход со двора делали в ведре, и вход этот назывался DRI1.

«The legacy DRI1 drivers expose highly broken interfaces to user-space. No modern system should enable them, or you will effectively allow user-space to circumvent most of your kernel security measures. The DRI1 kernel APIs are simply broken. User-space can always use vesafb/efifb/simplefb and friends to get working graphics. Lets hide the old drivers behind CONFIG_BROKEN. In case they turn out to be still used (really?), we can easily revert this and figure out a way to move them out of sight (e.g., moving all DRI1 drivers to drivers/gpu/dri1/).»

Затем появился DRI2, а с ним пришли и гениальные в кавычках решения. Вот например кто был тот долбо* кто решил что объекты DRI надо сделать обязательно файловыми дескрипторами? Ну он хоть что-то на opengl писал? Знает, что там GLsync это void* и его в poll не сунуть? Если хочешь добавить фичу ждания, добавь функцию в struct file_operations когда символьное устройство регистрируешь. А кстати, из-за этого гениального решения каждый процесс может потырить данные из текстур другого процесса. А еще прикольное решение: авторизация в DRI2 - клиент открывает устройство, спрашивает у сервера «пароль», сервер сообщает драйверу «пароль» и передает его клиенту чтоб тот тоже ввел этот пароль. Вот скажите, в юниксе, где можно файловые дескрипторы через unix socket передавать, заранее зарядив в них пару ioctlов, это что, не рукожопие? Кстати, длина этого «пароля» - 32 бита.

Далее, у нас появилась шина AGP. А с нею в линукс пришел agp_gart и такая пакость как ttm. Великий и ужасный генератор паник и багов ttm! А всё с чего: памяти у видях было мало, метров 16-32, и кто-то умный не в кавычках придумал, что можно одноразовую память и прямо из системной брать. Для этого в AGP сделали самый настоящий iommu, только назвали почему-то GART. если вы полазаете в биосе старых мамок, наверняка увидите такую вещь как AGP aperture - вот это и есть колво мегабайт, которое AGP может пробросить в видяху.

А я напомню, что графическую подсистему пишут не скотинки, а умные гениальные люди, которые уже запилили псевдо-файловые дескрипторы в DRI2 и пароли из 32бит, зато никогда ни гуй и игорей не пилили. Поэтому какой-то мудак придумал, что TTM эти текстуры должен свопать! То есть у вас обычно всего лишь одно 3д-приложение активно чтоб по-серьёзному. это либо игорь, либо композитор. все остальные живут набегами на видеокарту и держат в ней максимум кэш из буферов. Его не надо отсвопывать, его надо изначально положить в памяти и никуда не двигать, а лишь туда перенацеливать GART. Типовая задача отрисовки состоит в том, чтоб положить в буфер все свои пиксели, иконки(вы подумайте как нибудь, что стоит отрисовать маненькие такие иконки), сказать где что лежит и отправить в полёт. Одна операция с точки зрения драйвера - вот буфер с кучей команд, возьми, поработай и положи обратно. Но мы же не ищем легких путей! жизнь должна быть болью, причем желательно в жопе, причем желательно у всех пользователей линукса. Поэтому сделали сложно и глючно.

Далее, видяхи стали комбинироваться. Пользователи нвидии, вы наверно слышали про «оптимус»? Болит, да? А хотите я вам скажу, что всё это время вы могли не страдать если бы не долбо*-разработчики? Бесплатные курсы 3д-графики на ЛОРе!

В OpenGL есть такое понятие как framebuffer. Немного погрубже под ним, есть такое понятие как swapchain, обычно вы его видите как double-buffer но может быть и тройной(он лучше, т.к. не долбит синхронизацией) и даже больше буферов(бывает нужно если видяха сильно запаздывает при сохранении фреймрейта). «Нулевой» фреймбуфер собственно и подключен к этому свопчейну и вы именно в этот свопчейн запихиваете картинку командой чототамSwapBuffers. Помимо него, вы можете накастовать своих фреймбуферов командов glGenFramebuffers, тоже красивых и модных но без фреймбуфера. Чтоб брать картинки оттуда вам надо симулировать свопчейн своими руками, а именно:

0) в самом начале создать свой swapchain c 2-3 текстурами типа RGBA. одну из них приаттачить к фреймбуферу. ну и depthbuffer с шаблоном, по требованию. кроме того надо сделать две очереди:

typedef struct {
   GLsync* mRenderComplete;
   GLuint  mOutputTexture;
   GLuint  mPixelBuffer;
   GLsync* mCopyPixelsComplete;
   int     mWidth, mHeight;
   Image * mPixels;
} SwapItem;
SwapItem swapitems[N];
typedef std::deque<SwapItem*> SwapChain;
SwapChain in; //уже ненужные кадры
SwapChain out;//кадры на отправку

само окно создаете чисто 2Дшное.

1) в момент вызова kokokoSwapBuffer, создать объект типа GLsync, отцепить от фреймбуфера текущую текстуру GL_COLOR_ATTACHMENT0 и вдвоем положить их в очередь. GLsync кладется в mRenderComplete, текстура все время лежит в mOutputTexture

2) проверить, что в очереди in есть новые текстуры. если нет, ждать в первом в очереди элементе

2а)если !null SwapItem->mRenderComplete должен отсигналиться

2б)если !null SwapItem->mCopyPixelsComplete должен отсигналиться

2в) удалить все ненужные GLsync которые вышеописаны

3) зайти в каждый элемент out, проверить SwapItem->mRenderComplete если !null.

3а) если он отсигналился, mPixelBuffer это pixel buffer, который надо замапить в режиме GL_MAP_PERSISTENT|GL_MAP_UNSYNCHRONIZED и адрес положить в mPixels. Если размер окна изменился, этот буфер надо пересоздать и перемапить.

3б) запускате glPackPixels в этот буфер. ставите mCopyPixelsComplete.

3в) шаг 4 пропускаете для всех таких элементов

4) зайти в первый элемент out, проверить SwapItem->mCopyPixelsComplete если !null.

4а) если он отсигналился, mPixels это и есть ваша картинка. рисуете её средствами обычного 2D.

4б) Как только она вам не нужна, и у вас следующий элемент в out тоже отсигналился, вы просто перекладываете SwapItem из out в in.

5) PROFIT!!!!!!

И мое решение будет работать под SDL2, в Qt, Gtk, и так далее. Ему просто будет похеру. Потому что именно так и работает оптимус в винде. А то что он у вас в линуксе работает через боль - это потому, что графон вам пилят мудаки безрукие. А поскольку они умные а я нет - поцелуйте их в жёппу за то, что нулевой фреймбуфер они гвоздями прибили к DRI2-шному свопчейну и вышеописаный фокус провернуть не удастся именно из-за этого.

Легко заявлять «фак ю нвидия», когда на стороне нвидии выступают бессловесные разработчики, которые (наверно как и в АМД) пилят дрова для линукса в режиме партизан, в то время как куроводство требует сосредоточиться на винде. Которые не могут ответить линуксу, что он сам должен быть хоть немного в ответе за свой курятник и факи свои должен был отнести в dri-devel местным долбозверям, в первую очередь дятлам что из интела.

Далее, это еще не всё. Продолжаем уроки OpenGL на ЛОРе. Самая главная фишка OpenGL4.4+ - это мапание буферов с GL_MAP_PERSISTENT|GL_MAP_UNSYNCHRONIZED, когда ты организуешь циклический буфер достаточно большого размер. Или несколько их. В один буфер кладешь новые данные, в другой команды для indirect draw, в третий - обновления для текстур и так засылаешь на GPU. Буферы эти статичны в процессе всей игры или жизни приложения, ну или почти статичны(возможно их надо будет время от времени уширять). Так вот, в плане драйвера весь этот процесс можно уместить в один общий для всех видях драйвер который стукает в серверный процесс, где сидят уже драйвера видях и не оттормаживая приложение и не засирая кэши отрисоывывают всё-всё-всё и сигналят GLsyncи, которые можно сделать через futexи. И это будет быстро, волшебно и безопасно! Хоть сколько видях напихай - все будут работать и дружить друг с другом. Более того, драйвер может сидеть вообще через UIO и ядро не трогать.

Но вместо этого вам приходится наблюдать цирк с libGL от разных производителей и пляски с бубном. Кому надо сказать за это спасибо? Конечно же мне, потому что я дурак, а те, кто этот цирк с графоном в линуксе соорудил они умные.

☆☆☆

Если честно, некоторые слова даже впервые прочитал. Но отчего так? И почему никто не предложили чего получше, а вулкан похоже еще маразмее.

praseodim ★★★★★
()

ты щас говоришь об одном ГПУ или о случае нвидиа+интел одновременно?

в случае одной карты и одного ОпенГЛ рендера будет работать как описано

в случае нвидии, у которой своя видеопамять и «финальный кадр» просто находится в памяти карты, откуда он копируется в память интела

то что ты описал никак не ускорит отрисовку, копирование кадра самое долгое(по 10мсек на кадр, 60фпс=600 мсек на копирвание и 400мсек остается на «остальные операци» на отрисовку в самой нвидии плюс рендеринг окошен в интеле)

missxu
()
Ответ на: комментарий от DawnCaster

Угадал автора поста по стилю после прочтения первой пары предложений

А я думал, что это RTP или Зенитар.

На самом деле, хорошо, что он пытается разобраться. Побольше бы таких людей.

pacify ★★★★★
()
Ответ на: комментарий от missxu

копирование кадра самое долгое(по 10мсек на кадр

PCIe x8 за 10мсек перегоняет 30 мегабайт, а кадр 2к*2к*RGBA32 = 16 мегабайт, и видяхи это делают асинхронно.

более того, этот кадр один хер копируется. вот что так, что эдак.

ckotinko ☆☆☆
() автор топика
Последнее исправление: ckotinko (всего исправлений: 1)
Ответ на: комментарий от DawnCaster

Угадал автора поста по стилю после прочтения первой пары предложений )

А я по заголовку догадался. SWAG

fornlr ★★★★★
()
Ответ на: комментарий от fornlr

А я по заголовку догадался.

А я по нику автора рядом с названием темы в трекере.

i-rinat ★★★★★
()

Мы уже все поняли, какой ты молодец. Но тут дело какое... У тебя есть конкретные предложения по изменениям? Может, какой-то PoC? Или тебе только поныть?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

ну не совсем.

Новогоднее предложение

я хочу отодрать драйвера от ведра и положить их в процесс, а железки подцепить через UIO

уточню зачем это надо: для начала я хочу всё проверить на «фейковом GPU».

ckotinko ☆☆☆
() автор топика
Последнее исправление: ckotinko (всего исправлений: 1)
Ответ на: комментарий от DawnCaster

Угадал автора поста по стилю после прочтения первой пары предложений

+1.

intelfx ★★★★★
()
Ответ на: комментарий от pacify

Аааа я то при каких делах?
Вроде как не был замечен за использованием линукса на ноутбуках «в проде».

Ну по факту то печально, что конечный пользователь должен вникать в низкий уровень железа, для запуска своей любимой ОС. Тут можно пуститься в долгое разглагольствование про энтерпрайзное оборудование 90х и сослаться на второе или первое издание Немет, типа не дергал джамперы - не одмин.

Но всё же, ноутбук это такое мобильное устройство, должное быть готовым к работе в полевых условиях...

Deleted
()
Последнее исправление: RTP (всего исправлений: 1)
Ответ на: комментарий от pacify

О! А с чего ты взял, что я имею склонность к написанию таких развернутых постов, как у ТСа? О_О

Ну может в бложике что-то шкарябал длинное, но всё же, я за краткость на ЛОРе, TL;DR - не приветствую. Да и клавиатуры не располагают.

Deleted
()

Кхм, хорошие многабукв. Но может вместо запиливания совсем-совсем другой модели отрисовки (я с трудом понимаю, как из кучи раскиданных физически страниц, содержащих текстуры и прочее закидывать данные в видеокарту БЕЗ dma. ну т.е. обявить всю доступную физическую память как «условно-доступную» для dma hw на видеокарте - это да, но ядру-то при этом свои куски тоже нужно использовать и не-графическим процессам давать..я так понимаю что-то типа этого сейчас через HMM делают (https://lkml.org/lkml/2017/6/28/683) ) попробовать тут же проблему «нулевой фреймбуфер они гвоздями прибили к DRI2-шному свопчейну» попробовать решить? Пусть хаком, но хоть что-то, от чего можно отталкиваться и писать публичные письма разработчикам (я однажды видел как 3d ускорения прикручивали к qemu-user , в мэйнстрим оно не попало, но сам факт был!

https://patchwork.ozlabs.org/patch/386528/

https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg05331.html)

Andrew-R ★★★★
()
Ответ на: комментарий от ckotinko

Оно есть всегда, но как его делать через UIO я не представляю? (особенно если там много процессов, которым нужна изоляция)

Andrew-R ★★★★
()
Ответ на: комментарий от ckotinko

Я просто не очень понял, что конкретно будет делаться - дисплейный сервер (?) поверх drm/libdrm (и ttm под всем этим), или некий прототип, который как минимум часть ttm переносит в пространство пользователя?

Andrew-R ★★★★
()
Ответ на: комментарий от Andrew-R

короче, как это всё планируется вертеть:

демон gpud создает как х-сервер свой unix-socket, через который желающие могут подключаться. далее клиенты должны представиться. у сервера же есть список правил, по которым клиентам назначаются права.

клиент может запросить список gpu, и их умения. далее, он может узнать, какие у gpu есть видеовыходы, их видеорежимы и инфу о дисплее а также некоторые соображения относительно положения дисплея в свободной форме(пока). это может быть нужно для игорей с кучей мониторов например.

клиент может также создавать буфера, с указанием их назначения(uvd в радеонах например шибко не любит, когда ему буфер не той системы суют). эта возможность ограничена двумя путями.

во-первых, все клиенты делятся на привилигерованых(игори, композитор), и обычных.

для обычных буфер можно создать по дефолту в системной памяти. и параллельно - в видеопамяти только если рядом нет привилигерованного клиента. для обычных приложух этого достаточно с большим запасом, колво памяти в видяхе для них ограничивается парой мегабайт для шейдеров, которые скорее всего будут общими для кучи приложух(их родят qt и cairo в gtk). ибо эти данные один хер генерятся на каждый кадр заново, нет смысла их в видяху копировать.

далее буфер можно создавать и в видяхе, и свопать буфера мы не будем. это охеренно бесит когда в своей игре(а я еще и игоря пишу) ты напарываешься на саботаж со стороны TTM а узнать где он съагриться - не можешь. если буфер должен быть в видеопамяти - он должен там быть. попытки сделать программу «работоспособной» свопая текстуры - это саботаж, ибо свопающая игра уже не играбельна.

когда привилигерованному процессу не хватит видеопамяти, объекты обычных процессов будут если изменены - откачиваться в системную память, и удаляться из видеопамяти.

в системной памяти объекты создаем так: в /dev/ugpu/ монтируем tmpfs, создаем каталоги для каждого процесса. про права пока не уверен, потому что тырить текстуры это грех. а далее создаем файлы по запросу. их мы mmapаем к себе, делаем mlock, и передаем через unix socket клиенту. отдельно создаем файл sync, где будут лежать futexи - это наши GLsyncи. клиент тоже мапает себе эти файлы и радуется.

со стороны процесса сервера, мы после mlock, смотрим в /proc/self/pagemap и по этому файлу составляем инфу для драйверов видеокарт. у них обычно mmu 40 бит адреса держит, так что можно собирать всех в одну бочку.

ckotinko ☆☆☆
() автор топика
Ответ на: комментарий от Andrew-R

то есть не будет ни libdrm, ни кусочка кода в ядре. он там не нужен вообще. я хочу libGL сократить до тоненькой обертки, отдающей запросы отдельному процессу, который уже скребет башку над расположением пикселей в текстурах и прочей мурой.

ckotinko ☆☆☆
() автор топика
Ответ на: комментарий от Andrew-R

из минусов - надо uio_pci_generic патчить, чтоб он в poll поднимал обратно разрешение прерываний.

ckotinko ☆☆☆
() автор топика
Ответ на: комментарий от Andrew-R

прочел таки про HMM...

я чо-то не понимаю. видеокарты же не дают в открытый доступ свою память. только небольшой кусок под фрембуфер и всё. остальное через DMA копируется между памятью видяхи и системной памятью.

чего они городят-то? mmap+mlock+/proc/self/pagemap+mmu видяхи всё решают.

ckotinko ☆☆☆
() автор топика

Сходи на любое занятие по любому предмету, посмотри, как материал излагать надо.

t184256 ★★★★★
()
Ответ на: комментарий от Andrew-R

так а толку что opencl. хоть openOLOLO - железо-то от нового API не изменится.

хитросчитаемые нелинейные форматы буферов

это называется tiling. причем он двух сортов бывает - macrotiling и microtiling. микротайлинг - это просто перемешивание младших битов адресов текселов для более удобного доступа к ближним текселям. макротайлинг это тоже самое только биты постарше, с учётом размера страниц и раскладки адресов по чипам памяти на карте.

по сути же пусть у тебя есть ulong X, ulong Y, ulong Z;

в зависимости от тайлинга тебе надо из битов X,Y,Z собрать новое ulong offset по заданным битовым маскам. это цикл, с неприятным асмом внутри. но тем не менее задача очень даже решаемая.

ckotinko ☆☆☆
() автор топика
Ответ на: комментарий от ckotinko

это называется tiling.

Ну да, под новые AMD/radeon этим занимается addrlib. А вот под старые, особенно интегрированные ...

https://bugs.freedesktop.org/show_bug.cgi?id=35998

Ну да, железо-то позволяет сейчас подобные трюки (использовать себя с тонкой софт-обвязкой) проделывать. Но 1) абстракция даже для текущих видях трёх вендоров занимает порядочно времени 2) все хотят пользоваться «старыми» приложениями (и играми).. Но эксперименты это не должно отменять - вон, был же Xgl ... Так что удачи!

ЗЫ: узнал с некоторым удивлением, что даже под ужасно висючий Rendition был свой нативный 3D API в 90-х - http://vintage3d.org/verite1.php (Speedy3D). Это я к тому, что хотя для демо привяка к карте опр. поколения и производителя - ок, в целом .... не у всех АМД.

Andrew-R ★★★★
()
Ответ на: комментарий от Andrew-R

Аддрлиб это какой то кошмарный костыль со свичами внутри. Тайлинг по сути это только лишь перемешивание битов. Т.е. его можно задать как три битовые маски и алгоритм, который смешает тебе их «как надо». Конкретно оный делается через цикл с командами типа shrd,lzcnt. Если хоцца сразу много пикселей пощупать, avx2 к вашим услугам.

Абстракцию для делать особо и не надо. Гпу должен возвращать поддерживаемые наборы параметров тайлига прямо масками, а далее мы можем подобрать наименьшее общее если хотим данные гонять

ckotinko ☆☆☆
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.