Относительно недавно вышел новый Vulkan API — можно сказать, наследник OpenGL, хотя основан Vulkan на API Mantle от AMD.
Конечно, развитие и поддержка OpenGL не прекратилось, а также в свет вышел и DirectX 12. Что там с DirectX 12 и почему его поставили только на Windows 10 — я, к сожалению (а может и к счастью) не знаю. Но вот кроссплатформенный Vulkan меня заинтересовал. В чём же особенности Vulkan и как правильно его использовать я постараюсь рассказать вам в этой статье.

Vulkan Logo


Итак, для чего нужен Vulkan и где он может быть использован? В играх и приложениях, работающие с графикой? Конечно! Вычислять, как это делает CUDA или OpenCL? Без проблем. Обязательно ли для этого нам нужно окно или дисплей? Конечно нет, вы можете сами указать, куда транслировать ваш результат или не транслировать его вообще. Но обо всём по порядку.

Оформление API и основы


Пожалуй, стоит начать с самого простого. Так как над Vulkan API работали Khronous Group, синтаксис весьма похож на OpenGL. Во всём API есть префикс vk. К примеру функции (порой даже с очень длинными названиями) выглядят так: vkDoSomething(...), имена структур или хэндлов: VkSomething, а все константные выражения (макросы, макровызовы и элементы перечислений): VK_SOMETHING. Также, есть особый вид функций — команды, которым добавляется префикс Cmd: vkCmdJustDoIt(...).

Писать на Vulkan можно как на C, так и на C++. Но второй вариант даст, конечно же, больше удобства. Есть (и будут создаваться) порты на другие языки. Кто-то уже сделал порт на Delphi, кто-то желает (зачем?) порт на Python.

Итак, как же создать рендер контекст? Никак. Здесь его нет. Вместо это придумали другие вещи с другими названиями, которые даже будут напоминать DirectX.

Начало работы и основные понятия


Vulkan разделяет два понятия — это устройство (device) и хост (host). Устройство будет выполнять все команды, отправленные ему, а хост будет их отправлять. Фактически, наше приложение и есть хост — у Vulkan такая терминология.

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

Vulkan может быть легко загружен динамически. В SDK (разработали LunarG), если был объявлен макрос VK_NO_PROTOTYPES и загружать библиотеку Vulkan своими руками (не линковщиком, а определёнными средствами в коде), то прежде всего нужна будет функция vkGetInstanceProcAddr, с помощью которой можно узнать адреса основных функций Vulkan — те которые работают без экземпляра, включая функцию его создания, и функции, которые работают с экземпляром, включая функцию его разрушения и функцию создания устройства. После создания устройства можно получить функции, которые работают с ним (а также его дочерними хэндлами) через vkGetDeviceProcAddr.

Интересный факт: в Vulkan всегда нужно заполнить определённую структуру данными, чтобы создать какой-либо объект. И всё в Vulkan работает примерно таким образом: заранее подготовил — можно использовать часто и с высокой производительностью. В информацию об экземпляре можно также поместить информацию о вашем приложении, версии движка, версии используемого API и другую информацию.

Слои и расширения


В чистом Vulkan нет сильных проверок входящих данных на правильность. Ему сказали что-то сделать — он сделает. Даже если это приведёт к ошибке приложения, драйвера или видеокарты. Это сделали ради производительности. Тем не менее, можно без проблем подключить проверочные слои, а также расширения к экземпляру и/или устройству, если это необходимо.

Слои (layers)


В основном, предназначение слоёв — проверить входящие данные на ошибки и отслеживать работу Vulkan. Работают они очень просто: допустим, вызываем функцию, и попадает она в самый верхний слой, заданный при создании устройства или экземпляра ранее. Он всё проверяет на правильность, после этого передаёт вызов в следующий. И так будет, пока дело не дойдёт до ядра Vulkan. Конечно же, можно создать собственные слои. Например, Steam выпустила слой SteamOverlay (хотя и не знаю, что он вообще делает). Тем не менее, слои будут молчать, но не доведут до краха приложения. Как узнать, правильно ли всё сделано? Для этого есть специальное расширение!

Расширения (extensions)


Как следует из названия, они расширяют работу Vulkan дополнительным функционалом. Например, одно расширение (debug report) будет выводить ошибки (и не только) со всех слоёв. Для этого нужно будет указать необходимую Callback функцию, а что делать с информацией, поступившей в эту функцию — решать уже вам. Учтите, что это Callback и задержка может вам дорого обойтись, особенно если выводить всю полученную информацию прямиком в консоль. После обработки сообщения, можно указать, передавать ли вызов функции дальше (в следующий слой) или нет — так можно избежать критических ошибок, но постараться работать дальше с менее опасными ошибками.
Есть также и другие расширения, о некоторых я расскажу позже в этой статье.

Устройство


Vulkan разделяет понятия физического устройства и логического. Физическим устройством может быть ваша видеокарта (и не одна) или процессор, поддерживающий графику. Логическое устройство создаётся на основе физического: собирается информацию о физических устройствах, выбирается нужное, подготавливается другая необходимая информация и создаётся устройство. Может быть несколько логических устройств на основе одного физического, но вот объединять для единой работы физические устройства (пока?) нельзя.

Итак, что же за информацию мы собираем? Это, конечно же, поддерживаемые форматы, память, возможности и, конечно же, семейства очередей.

Очереди (queue) и семейства очередей (queue family)


Устройство может (или не может) делать следующие 4 вещи: рисовать графику, производить разные вычисления, копировать данные, а также работать с разреженной памятью (sparse memory management). Эти возможности представлены в виде семейств очередей: каждое семейство поддерживает определённые (может быть все сразу) возможности. И если идентичные семейства были разделены, Vulkan всё равно представит их как одно семейство, чтобы мы не так сильно страдали с кодом и выбирали нужное семейство.

После того, как вы выбрали нужное (или нужные) семейства, из них можно получить очереди. Очереди — это место, куда будут поступать команды для устройства (потом устройство их будет брать из очередей и выполнять). Очередей и семейств, кстати, не сильно много. У NVIDIA обычно 1 семейство со всеми возможностями на 16 очередей. После того, как вы закончили с подбором семейств и количеством очередей, можно создавать устройство.

Команды, их исполнение и синхронизация


Все команды для устройства помещаются в специальный контейнер — командный буфер. Т.е. не существует ни одной функции в Vulkan, которая сказала бы устройству сделать что-либо сразу, и при завершении операции вернуть управление приложению. Есть только функции заполнения командного буфера определёнными командами (например, нарисовать что-либо или скопировать изображение). Только после записи командного буфера на хосте мы можем его отправить в очередь, которая, как уже известно, находится в устройстве.

Командный буфер бывает двух видов: первичный и вторичный. Первичный отправляется прямо в очередь. Вторичный же не может быть отправлен — он запускается в первичном. Записываются команды в таком же порядке, в каком были вызваны функции. В очередь они поступают в таком же порядке. А вот исполнятся они могут почти в «хаотичном» порядке. Чтобы не было полного хаоса в приложении разработчики Vulkan предусмотрели средства синхронизации.

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

Есть 4 примитива синхронизации: забор (fence), семафор (semaphore), событие (event) и барьер (barrier).

Забор самый простой метод синхронизации — он позволяет хосту ожидать выполнение определённых вещей. Например, завершения выполнения командного буфера. Но используется забор редко.

Семафор — способ синхронизации внутри устройства. Никак нельзя посмотреть его состояние или подождать его на хосте, нельзя также ждать его внутри командного буфера, но можем указать, какой семафор должен подать сигнал при завершении исполнения всех команд буфера, и какой семафор ждать перед тем, как начать выполнение команд в буфере. Только ждать будет не весь буфер, а его определённая стадия.

Стадии конвейера (pipeline stages) и зависимости исполнения
Как уже было сказано, не обязательно команды в очереди будут исполнятся по порядку. Если быть точнее, то последующие команды не будут ждать завершения предыдущих. Они могут выполнятся параллельно, или исполнение предыдущей команды может завершиться намного позже последующих. И это вполне нормально. Но некоторые команды зависят от исполнения других. Вы можете разделить их на два берега: «до» и «после», и также указать, какие стадии берега «до» должны обязательно выполнится (т.е. команды могут завершиться не полностью или не все), прежде чем начнут выполняться указанные стадии команд берега «после». Например, отрисовка изображения может приостановиться, чтобы сделать определённые вещи, а потом снова продолжить делать рисовать. Также может быть и цепочка зависимостей, но не будем уходить глубоко в леса Сибири Vulkan.


События — элемент «тонкой» настройки. Подать сигнал можно как с хоста, так и с устройства, ждать можно также и на устройстве, и на хосте. Событие определяет зависимость двух сетов команд (до и после) в командном буфере. И для события есть также специальная псевдо-стадия, которая позволяет ждать хост.

Барьер опять может быть использован только в устройстве, а ещё точнее — в командном буфере, объявляя зависимости первого и второго сета команд. Также можно дополнительно указать барьеры памяти, которые бывают трёх видов: глобальный барьер, барьер буфера и барьер изображения. Они не дадут ненароком прочитать данные, которые в данный момент записываются и/или наоборот, в зависимости от указанных параметров.

Конвейеры


Ниже показаны два конвейера Vulkan:

Vulkan Pipeline

Т.е. в Vulkan есть два конвейера: графический и вычислительный. С помощью графического, мы, конечно же, можем рисовать, а вычислительный… вычислять. Что же ещё? Результаты вычислений могут потом отправится в графический конвейер. Так можно с лёгкостью сэкономить время на системе частиц, например.

Изменить порядок или изменить сами стадии конвейера нельзя. Исключение составляют программируемые стадии (шейдеры). Также можно отправлять разновидные данные в шейдеры (и не только) через дескрипторы.

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

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

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


Проход отрисовки, графический конвейер и фреймбуфер


Итак, получаем следующую матрёшку:

Для того, чтобы можно было использовать команды отрисовки, нужен графический конвейер. В графическом конвейере необходимо указать проход отрисовки (Render Pass), который содержит информацию о подпроходах (subpass), их зависимостей друг от друга и прикреплениях (attachment). Прикрепление — информация о изображении, которое будет использоваться во framebuffer'ах. Framebuffer создаётся специально для определённого прохода отрисовки. Чтобы начать проход, нужно указать как сам проход (а также, если нужно, подпроход), так и framebuffer. После начала прохода можно рисовать. Можно также переключаться между подпроходами. После того, как рисование завершено, можно завершить проход.

Управление памятью и ресурсы


Память в Vulkan распределяется хостом и только хостом (за исключением swapchain). Если изображение (или другие данные) нужно поместить в устройство — выделяется память. Сначала создаётся ресурс определённых размеров, затем запрашивается его требования к памяти, выделяется для него память, затем ресурс ассоциируется с участком этой памяти и только потом можно копировать в этот ресурс необходимые данные. Также, есть память, которая может быть непосредственно изменена с хоста (host visible), есть локальная память устройства (память видеокарты, например) ну и также другие виды памяти, по своему влияющие на скорость доступа к ним.

В Vulkan можно также написать своё распределение памяти хоста, настроив Callback функции. Но учтите, что требования к памяти, это не только её размер, но и выравнивание (alignment).

Сами ресурсы бывают двух видов: буферы (buffers) и изображения (images). И те и другие разделяются по назначению, но если буфер — просто коллекция различных данных (вершинный, индексный или буфер констант), то изображение всегда имеет свой формат.

Наставление тем, кто пишет на Vulkan
Выделяйте участок памяти, в который можете поместить сразу несколько ресурсов. Количество выделений ограничено, и вам может не хватить. Зато количество ассоциаций не ограничено.


Шейдеры


Vulkan поддерживает 6 видов шейдеров: вершинный, контроль тесселяции, анализ тесселяции, геометрический, фрагментный (он же пиксельный) и вычислительный. Написать их можно на читаемом SPIR-V, а потом собрать в байт код, который в приложении мы запечатаем в модуль, т.е. создадим shader-модуль из этого кода. Конечно же, мы можем написать его на привычном GLSL и потом конвертировать в SPIR-V (транслятор уже есть). И, конечно же, вы можете написать свой транслятор и даже ассемблер — исходники и спецификации выложены в OpenSource, ничто не мешает написать вам сборщик для своего High Level SPIR-V. А может кто-то уже написал.
Байт код потом транслируется в команды, специфичные для каждой видеокарты, но делается это намного быстрее, чем из сырого GLSL кода. Подобная практика применяется и в DirectX — HLSL сначала преобразуются в байт код, и этот байт код может быть сохранён и потом использован, чтобы не компилировать шейдеры снова и снова.

Окна и дисплеи


А закончит эту статью рассказ о WSI (Window System Integration) и цепочке переключений (swapchain). Для того, чтобы выводить что-либо в окно или на экран — нужны специальные расширения.

Для окон это базовое расширение плоскости и расширение плоскости, специфичной для каждой из систем (win32, xlib, xcb, android, mir, wayland). Для дисплея (т.е. FullScreen) нужно расширение display, но в целом и то и другое используют расширение swapchain.

Цепочка переключений не связана с графическим конвейером, поэтому простой Clear Screen выходит без настройки всего этого. Всё достаточно просто. Есть определённый движок показа (presentation engine), в котором есть очередь изображений. Одно изображение показывается на экран, другие дожидаются своей очереди. Количество изображений мы также можем указать. Есть также несколько режимов, которые позволят дождаться сигнала вертикальной синхронизации.

Метод работы примерно таков: мы запрашиваем индекс свободного изображения, вызываем командный буфер, который скопирует результат из Framebuffer в это изображение, и отправляем команду о отправки изображения в очередь. Звучит легко, но с учётом того, что потребуется синхронизация — всё чуточку сложнее, так как единственное, чего ожидает хост — это индекс изображения, которое вскоре будет доступно. Командный буфер ждёт сигнала семафора, который будет свидетельствовать о доступности изображения, и потом сам подать сигнал через семафор о том, что выполнение буфера, в следствии и копирование, завершено. И изображение действительно поступит в очередь по сигналу последнего семафора. Всего два семафора: о доступности изображения для копирования и о доступности изображения для показа (т.е. о завершении копирования).

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

В этой статье я попытался рассказать о наиболее важных частях Vulkan API, но многое всё ещё не рассказано и это вы можете узнать сами. Стабильного вам FPS и приятного кодинга.
Поделиться с друзьями
-->

Комментарии (33)


  1. AllexIn
    13.05.2016 12:02

    Что печалит, так это то, что с каждой новой итерацией развития графических API — минимально рабочее приложение становится всё сложнее.


    1. Sirikid
      13.05.2016 12:27
      +2

      Это естественно, (насколько я понял) Vulkan более низкоуровневое API чем OpenGL. Думаю со временем появятся фреймворки-надстройки над ним.


    1. TargetSan
      13.05.2016 12:46
      +3

      Vulkan судя по виду и не предназначен для рукопашного кодирования каждый раз. Это скорее «графический ассемблер», как когда-то называли OpenGL. Хотя в такой ситуации OpenGL переходит в лигу «графического Си».


      1. AllexIn
        13.05.2016 12:47

        Так и OpenGL уже не позволяет в пять строчек Hello World набросать.


        1. TargetSan
          13.05.2016 12:54

          Толщина кода для инита контекста рисования величина б/м постоянная, или по крайней мере всегда отличная от нуля. Дело, думаю, в этом. Как там дела в современном OpenGL — не в курсе, смотрел только мельком.


          1. staticlab
            13.05.2016 15:40
            +1

            Дело не в ините контекста, а в коде для непосредственно рендеринга. В старые добрые времена можно было просто через glBegin… glEnd тот же треугольник вывести, а теперь нужно загружать буфер вершин и шейдеры.


            1. iOrange
              13.05.2016 16:13
              +3

              Сраведливости ради — вы и сейчас можете писать на OpenGL 1.1 со всем glBegin / glEnd.

              A Vulkan — это очень низкоуровневое GAPI для нас — граф. программеров — чтобы можно было еще больше выжимать из железа.


              1. staticlab
                13.05.2016 20:05

                Да, я в курсе, что Vulkan — это низкоуровневое API. Как говорилось выше, «Так и OpenGL уже не позволяет в пять строчек Hello World набросать», — вот определяющее слово тут «уже». То есть подразумевался и здесь, и у меня в посте програамируемый конвейер OpenGL 2+.


    1. Gorthauer87
      13.05.2016 12:54
      +1

      На самом деле на ассемблере вот hello world тоже не так уж просто написать, но зато он напрямую в машинные команды транслируется. Тоже самое относится и ко всяким байткодам виртуальных машин.
      Зато драйвер для вулкана смогли написать за год, а драйвер для openGL 4.5 до сих пор не все смогли.


      1. TargetSan
        13.05.2016 12:57
        +3

        Получается, когда наступит счастье основные вендоры реализуют поддержку Vulkan, можно будет иметь одну реализацию OpenGL поверх него?


        1. Gorthauer87
          13.05.2016 13:02

          Возможно, но боюсь, что не очень хорошую. Говорили, что его идеология плохо сочетается с Вулканом.


        1. TrueBers
          13.05.2016 13:10
          +1

          Вулкан никак не отменяет OpenGL. Их даже одновременно можно использовать, насколько я знаю.


      1. MrGobus
        13.05.2016 19:48

        Откуда такая уверенность в том, что драйвер для вулкана написан. В случае с NVidia, есть бета со скудным набором железа, начиная с GT6х серии, ито не полностью =) При этом моя карта хоть и должна была соответсвовать минимальным требованиям, но, что-то несраслось и ничего на заработало =) И что самое печальное, исправлять сие положение дел никто не собирается. В общем пока все плохо с дровами и обещанной совместимотью.
        Кстати, в случае с ГЛ4.5 были драйверы для разработки которые моя карта поддреживала но в релиз они так и не вышли.
        Итого одно растройство, вулкан не для нищебродов, покупайте современные 3д ускорители если хотите летать =)


    1. Salabar
      13.05.2016 17:03

      Я не слышал ни про кого, кому платят за рисование синих треугольников. А сложные приложения в любом случае писать сложно.


      1. AllexIn
        13.05.2016 17:17

        Есть такая штука — прототипирование.
        Многим за него платят. И часто там ничего круче синих треугольников не нужно.


        1. Salabar
          13.05.2016 17:25
          +2

          Прототип можно и на OpenGL написать. Или вообще на каком-нибудь Cocoa2D. Многопоточный рендерер на Вулкане всегда требует очень обширного проектирования, чтобы появилось преимущество в производительности, а не чтобы было.


          1. AllexIn
            13.05.2016 17:40

            Полагаю — OpenGL накроется скоро медным тазом. Тем более проблемы совместимости за ним тянутся с 90х.


            1. Salabar
              13.05.2016 18:34

              Тут сложный вопрос. Для 90% процентов есть готовые движки. 9% достаточно OpenGL 3.2, и в таком случае нет буквально ни одной причины не использовать именно его. Вот в 1% случаев, когда обязателен именно собственный убийца CryEngine\RenderMan по вкусу, я действительно не понимаю, чего ради начинать проект на D3D11 или опенгл4. Это заведомо уменьшает пределы возможностей движка, с которым жить много лет.


              1. AllexIn
                13.05.2016 18:40

                Сарказм у вас прикольной, но не уместный, мир использования GAPI не сводится к игрострою.
                И как раз за пределами игростроя прекрасно работает OpenGL 1.5


                1. Salabar
                  13.05.2016 19:08

                  Я про новые проекты. Даже для CAD выкапывать функции, которые уже не везде поддерживаются это нечто странное. Legacy оно и в Африке Legacy.


                  1. AllexIn
                    13.05.2016 21:47

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


  1. nxrighthere
    13.05.2016 16:45
    +5

    Vulkan API — можно сказать, наследник OpenGL.

    Стоит упомянуть что API основано на технологии AMD Mantle и по сути Vulkan его потомок.


  1. MrShoor
    13.05.2016 20:06

    Меня мучают такие вопросы.

    1. Вот мы сами менеджим память. У меня ресурсы, которые нужны для рендрера фрейма полностью занимают скажем 600Мб, а на устройстве 512Мб физических. То есть на этапе рендера одного изображения я должен сам выгружать ресурсы, которые мне в данный момент не нужны, так?

    2. Допустим теперь я хочу написать фреймворк, который предоставит более высокоуровневый интерфейс. Если я захочу спрятать код менеджмента ресурсов на девайсе — то мне фактически придется написать «умный» менеджер памяти (а чтобы был профит — не хуже того, что есть в OGL драйверах). Либо вытягивать потроха менеджмента ресурсов наружу фреймворка, так?

    3. И самая большая сложность — это то, что я не знаю вообще ничего о других приложениях использующих видеокарту. Неизвстно нужно выгружать ресурсы, чтобы другие программы могли использовать память, или не нужно, им достаточно памяти, и я могу придержать текущую память для следующего фрейма. Получается раньше видеодрайвер знал все о окружении (был глобальным) и мог сам менеджить память, а сейчас мы попадаем в ситуацию когда мы этого делать не можем. Я очень надеюсь что я ошибаюсь… но это так или нет?


    1. Salabar
      13.05.2016 20:25
      +1

      1. Можно самостоятельно, можно доверить драйверу: память виртуализируется. В принципе, самым правильным вариантом может оказаться окошко с надписью «Выкинь свои дрова и купи видеокарту».
      2. Если хотите повторить Direct3D 11, то будьте готовы повторить путь, который NVIDIA и AMD прошли за 8+ лет, всё просто. На самом деле, управление памятью — наименьшая проблема в Вулкане.
      3. ОС может сказать, на какой объем памяти мы приблизительно можем рассчитывать. Всегда нужно быть готовым к тому, что запрос на выделение памяти вернет 0 и адекватно на это реагировать.

      Вообще, вот: http://32ipi028l5q82yhj72224m8j.wpengine.netdna-cdn.com/wp-content/uploads/2016/03/d3d12_vulkan_lessons_learned.pdf


    1. faserg1
      13.05.2016 20:49
      +1

      Vulkan может предоставить информацию о физическом устройстве.
      Это типы памяти устройства (0, Device local, Cashed Host, Coherent Host) и кучи памяти (в которых указан индекс используемой памяти).
      Информацию о свободной памяти мы получить не можем. Я раньше тоже задавался таким вопросом, на что получил примерно такой ответ: количество свободной памяти постоянно меняется и мы не можем предугадать, сколько будет использовано памяти потом. И, как уже было ранее сказано, всегда нужно быть готовым получить нулевой указатель, такая же фишка нужна и в обычных программах, которые динамически выделяют память (malloc, new).
      Главное — это правильно управлять памятью, и не загружать всё подряд в память устройства. Нужно уметь распределять ресурсы между хостом (кэш) и устройством.

      И последнее, если всё же действительно не хватает памяти, то есть sparse memory. Как точно работает такая память я пока ещё не в курсе.


      1. Salabar
        13.05.2016 21:09
        +2

        Sparse memory — это частично не обеспеченный реальной памятью диапазон в виртуальной памяти. Запись туда игнорируется, чтение возвращает неопределенное значение. Т.е., если мы знаем, что 90% изображения не будет использоваться, мы создаем огромную текстуру, но реально занимать она будет пару мегабайт. Позволяет делать очень большие карты теней малыми затратами, например.


      1. MrShoor
        13.05.2016 23:03

        Главное — это правильно управлять памятью, и не загружать всё подряд в память устройства. Нужно уметь распределять ресурсы между хостом (кэш) и устройством.
        Так вот мне например все еще непонятно как это делать. Вот есть у нас 10 приложений, которые кушают видеопамять и съели её практически всю. Я запускаю одиннадцатое, и при попытке выделить память на девайсе говорят что она закончилась. А все потому что предыдущие 10 приложений не выгрузили свою память. Раньше за меня это сделал бы драйвер, он просто бы сам выгрузил память этих приложений, загрузил мою память, отрендерил. А сейчас что? Что мне, разработчику 11-го приложения делать? Как в старые добрые времена показывать пользователю мессадж бокс «Не хватает памяти. Закройте не нужные графические приложения.»?


        1. faserg1
          14.05.2016 01:21

          Если речь идёт о приложениях, которые всё ещё работают и кушают память — да, придётся высвечивать то самое сообщение о нехватке памяти. А как иначе? Стоп, и с каких пор драйвер выгружает память? (Я про такое не в курсе)


          1. MrShoor
            14.05.2016 03:48
            +1

            Драйвер сам менеджит, какие ресурсы сейчас используются а какие нет. Скажем у вас 2Гб видеопамяти, и 11 запущенных приложений, каждое из которых требует 200Мб на текстуры. Несмотря на то, что памяти вроде как не хватает — приложения корректно работают. А все потому, что драйвер, подготавливает конвеер для рендера конкретного кадра конкретного приложения может выгрузить из резидентной памяти текстуры другого приложения.
            Собственно сейчас все это происходит в момент биндинга текстур/буферов прозрачно для нас. Это одна из причин, почему биндинг такой дорогой, и почему bindless текстуры рвут обычные. Для bindless текстур мы биндим текстуру один раз, и потом «pointer» в видеопамяти используем без всяких оверхедов.

            Вы даже можете насоздавать ресурсов больше 2Гб в одном приложении, и драйвер будет покорно работать за вас, до тех пор, пока видеопамяти хвататет, чтобы разместить все ресурсы необходимые на очередной дравколл.

            В DirectX9 раньше разработчик мог указывать, кто будет менеджить ресурсы:
            https://msdn.microsoft.com/en-us/library/windows/desktop/ee418784%28v=vs.85%29.aspx

            Но потом видимо системной памяти стало много, и в DirectX10 и выше все ресурсы стал менеджить драйвер (на уровне WDDM):
            https://msdn.microsoft.com/en-us/library/windows/hardware/ff568683%28v=vs.85%29.aspx

            В OGL этот менеджмент был всю жизнь изкоробки, пока bindless текстуры не завезли.

            Ну а в Vulkan я чет не представляю как это теперь разруливать. Хотя возможно драйвер будет так же пейджить ресурсы, как он делает это сейчас.


            1. faserg1
              14.05.2016 06:30

              Понял, о чём ты. Ну, я могу ответить так: Vulkan явно не для того, чтобы запускать 11 приложений параллельно (или типа того). Для этого есть тот же самый DX или OGL. Vulkan же, как упоминалось ранее — «графический ассемблер», который позволяет выжимать максимум производительности для приложения.
              Но если всё же приложению действительно нужно жить параллельно с другими, то не думаю, что Vulkan (по крайней мере сейчас) подойдёт для этого дела.
              И всё же, задам странный вопрос: зачем запускать столько?


  1. lostmsu
    14.05.2016 00:01

    Если кто-то разбирается в теме, можете пояснить, чем Vulkan отличается от OpenCL в плане compute pipeline?


    1. faserg1
      14.05.2016 01:35

      Точно сказать не могу, но вот интересный факт в том, что в SPIR-V шейдер можно транслировать из кода написанного на OpenCL. Так что скорее всего — ничем.


    1. Salabar
      14.05.2016 02:24
      +1

      Там нет штук из OpenCL 2.0. В остальном, вроде бы, можно делать всё то же самое, но лично я точных сравнений не делал.