Закладка в OS X, продлевающая работу от батарейки для избранных приложений

Зачем 2 GPU?


Ноутбуки с двумя GPU появились уже очень давно. Первый MacBook Pro с такой технологией вышел еще в 2008 году.
Преимущество двух GPU в гибкости. Когда вам не нужна вся мощь видео системы, вы используете встроенное в процессор видео, наслаждаясь долгой работой от батарейки. Однако если вы захотели развлечься, то к вашим услугам мощный дискретный GPU. Да, он ест батарейку и жужжит вентиляторами, но дает хороший FPS в играх. Как же одному приложению переключать GPU?

В теории, переключение должно происходить автоматически при изменении нагрузки на видеокарту. Однако на практике все не так просто.
В Windows за переключение отвечают драйвера видеокарты. В них прописана куча игр и приложений с предпочитаемыми настройками. А пользователь может сам выбрать, когда задействовать мощное дискретное GPU, а когда хочется поработать в тишине.
В OS X с переключением все сложение. Начнем с того, что драйвера для OS X пишет сама Apple.

Кстати, это приводит к таким казусам, как очень устаревшая версия OpenGL. И никакие петиции не помогают исправить это положение.
image

gfxCardStatus


В OS X вы даже не можете понять, какой GPU сейчас работает. Если только по жужжанию вентиляторов :)
К счастью, есть отличная утилита gfxCardStatus, которая показывает какая видеокарта активна, и даже позволяет переключаться между ними.
Когда включается дискретный GPU, утилита показывает, какое приложение вызвало переключение.

Проблема переключения и официальная документация


Подразумевается, что под OS X переключение происходит автоматически.
Например, в Safari обычно используется встроенный GPU. Но, если вы зайдете на сайт с WebGL, то произойдет переключение на дискретный GPU.
Однако, если вы сами захотите написать приложение, которое бы могло само выбирать режим работы, вас ждет неприятный сюрприз.
Начнем с того, что в официальной документации нет ответа на вопрос, как же правильно переключаться. При это написаны очень странные вещи. Но документация дает набор ключевых слов, по которым можно найти информацию.

Решение в Chromium


У нас есть отличный open-source проект chromium, который смог решить эту проблему. Надо создавать CGLPixelFormatObj с флагом kCGLPFAAllowOfflineRenderers при инициализации OpenGL контекста. В этом случае будет использоваться текущий (встроенный) GPU.
А если вы хотите переключится на дискретный GPU, то надо просто создать CGLPixelFormatObj без флагов. Даже OpenGL контекст не нужен. Для того, чтобы вернуться на встроенный GPU, просто удалить этот CGLPixelFormatObj:

CGLContextObj ctx;
CGLPixelFormatObj pix;
GLint npix;
std::vector attribs;
    
// Use current GPU
attribs.push_back(kCGLPFAAllowOfflineRenderers); // comment this line for discrete GPU
attribs.push_back((CGLPixelFormatAttribute) 0);
    
CGLError err = CGLChoosePixelFormat(&attribs.front(), &pix, &npix);
CGLCreateContext(pix, NULL, &ctx);
CGLSetCurrentContext(ctx);
....

Закладка


Я написал тестовое приложение, которое почти работало. Почему почти? Потому что удаление CGLPixelFormatObj не приводило к переключению обратно на встроенный GPU! Причем у chromium это получалось.
Зная, что в драйверах Nvidia для Windows есть специальные настройки для chromium, и то, что они определяют chromium по имени exe’шника, я ожидал что-то подобное и здесь.
Под OS X есть более надежный способ определять приложение — Bundle Id. И если поменять Bundle Id моего приложения на org.chromium.Chromium, то, о чудо, все заработает как надо.
Самое неприятное, что без правильного Bundle Id, никак нельзя переключится обратно на встроенный GPU. А это уже не просто настройка, а серьезная проблема для написания хорошей программы.
Так же работает Bundle Id: com.apple.Safari. О себе позаботились :)
А вот com.operasoftware.Opera не работает. В итоге в Opera наблюдается такое же поведение: однажды захваченный дискретный GPU никогда не освобождается, даже если вы закрываете прожорливую вкладку. Единственный способ вернутся на встроенный GPU — это закрыть приложение.

Тестовое приложение и видео


Тестовое приложение, на котором можно убедиться в отличии поведения программы при разных Bundle Id, можно скачать тут.
Так же я снял видео, на котором воспроизвел проблему.

Решение


Как же в итоге решить задачу? Так как активный GPU один на систему, то можно создавать отдельный процесс, когда вам нужен дискретный GPU. И завершать этот процесс, если нужно вернуться на встроенный GPU.

© Habrahabr.ru