Техническое примечание TN2229

Поддержка многократного GPUs на Mac OS X

Многократная поддержка GPU существовала в Mac OS X в течение длительного времени, и с введением Конца MacBook Pro 2008 приносит первые аппаратные средства Macintosh, прибывающие стандарт с 2 GPUs. Это делает более важным чем когда-либо гарантировать, что Ваше приложение работает правильно с многократным GPUs. Этот документ описывает то, что необходимо знать для содержания OpenGL для рендеринга правильно на всех аппаратных средствах.

Что необходимо знать при поддержке Многократного GPUs
Включение использования оффлайновых средств рендеринга
Обнаружение изменений средства рендеринга
PBuffers по сравнению с текстурами OpenGL и буферными объектами
История версии документа

Что необходимо знать при поддержке Многократного GPUs

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

Технические Вопросы и ответы QA1248: Советы по Делящемуся Контекста описывают, каков контекст, совместно использующий, как его используемый и что совместно используется. Эти понятия важны, работая с многократными контекстами OpenGL и должны быть хорошо поняты прежде, чем попытаться работать с многократным GPUs. Необходимо отметить, что Полноэкранные контексты только поддерживают один GPU за один раз, таким образом делая совместное использование более сложного. Если необходимо покрыть многократные дисплеи содержанием OpenGL, то можно просто создать нормальный контекст, покрывающий весь дисплей вместо этого.

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

Quartz Display Services, Программируя главу Тем Quartz Display Services, Программируя Темы описывает, как Ваше приложение может обнаружить экранные изменения конфигурации, который необходим для знания, когда Вы должны повторно запросить возможности OpenGL и особенно жизненно важны при использовании PBuffers.

Введение в Руководство по программированию OpenGL для Mac OS X описывает много ключевых понятий для использования OpenGL на Mac OS X, включая понятие виртуальных экранов, базовое понятие при работе с OpenGL на Mac OS X в целом. В частности Вы захотите быть уверенными, что Вы знакомы с методами в разделе Updating a Rendering Context.

Самая важная вещь помнить состоит в том, что приложения, планирующие воспользоваться максимальным преимуществом аппаратных средств OpenGL, должны были уже реализовать большую часть того, что описывает этот документ. Основные изменения, которые описывает этот документ, для понимания этих тех же понятий в контексте работы с аппаратными средствами, которым установили многократный GPUs.

В любой конфигурации, где существует больше GPUs, чем дисплеи тогда, по крайней мере 1 из этих GPUs считается «оффлайновым». Оффлайновые GPUs обычно не участвуют в аппаратном выборе средства рендеринга, поскольку эти аппаратные средства не имеют никаких средних значений отображения содержания, все же все еще иногда полезно быть в состоянии использовать в своих интересах эти аппаратные средства. С другой стороны, поведение по умолчанию OpenGL разработано, чтобы гарантировать, что рендеринг обычно происходит на GPUs, которые способны к отображению того содержания. Этот документ описывает то, что необходимо сделать, чтобы и использовать в своих интересах оффлайновый GPUs и гарантировать, что заключительный рендеринг всегда происходит с онлайновым GPU.

Чтобы сделать это, необходимо сообщить реализации OpenGL, что приложение понимает, как работать правильно и с онлайн и с оффлайновый GPUs (описанный во Включении использования оффлайновых средств рендеринга). Как только Вы сообщили реализации об этом, тогда необходимо расположить получить уведомления об изменениях состояния GPU (см. Изменения Средства рендеринга Обнаружения). Когда изменения состояния GPU происходят, необходимо повторно запросить возможности средства рендеринга, чтобы гарантировать, что рендеринг продолжается правильно, поскольку онлайновое средство рендеринга, к которому Вы хотите нарисовать, возможно, изменилось. Наконец, в то время как весь OpenGL возражает (текстуры, буферные объекты, и т.д.) сохранит их содержание, выведет на экран запоминающие устройства, и PBuffers должен будет быть обновлен, чтобы гарантировать, чтобы их содержание осталось допустимым (см. PBuffers по сравнению с Текстурами OpenGL и Буферными Объектами).

Включение использования оффлайновых средств рендеринга

Любая конфигурация системы Macintosh, включающая больше GPUs, чем дисплеи, будет иметь и онлайн и оффлайновый GPUs. Онлайновые GPUs - те, которые подключены к дисплею (такому как ЖК-панель или проектор), в то время как оффлайновые GPUs - те, которые не имеют таких выходных присоединенных аппаратных средств. Одним примером этого является Mac, Pro сконфигурированный с 2 видеокартами, но с дисплеями, только подключенными к одной из тех карт.

В этих конфигурациях можно хотеть использовать в своих интересах аппаратные средства, не подключенные к дисплею, или быть в состоянии начать представлять на этих аппаратных средствах должно дисплей быть соединенным на дату в будущем, не имея необходимость реконфигурировать и повторно загрузить все содержание OpenGL. Для включения этого поведения, необходимо добавить надлежащий атрибут к формату пикселя. Для NSOpenGL Вы добавляете NSOpenGLPFAAllowOfflineRenderers, для CGL добавить kCGLPFAAllowOfflineRenderers и для AGL добавить AGL_ALLOW_OFFLINE_RENDERERS к списку атрибутов Вы используете для создания формата пикселя. Для поддержки Mac OS X 10.4 или ранее необходимо будет удалить этот атрибут из списка атрибутов, поскольку иначе создание формата пикселя перестанет работать. Использование в качестве примера NSOpenGL (Перечисление 1), CGL (Перечисление 2) и AGL (Перечисление 3) следует.

Перечисление 1  , Создающее оффлайновое средство рендеринга осведомленный Формат пикселя с NSOpenGL.

NSOpenGLPixelFormatAttribute attribs[] =
{
    NSOpenGLPFAColorSize, 24,
    NSOpenGLPFADoubleBuffer,
    NSOpenGLPFAAllowOfflineRenderers, // lets OpenGL know this context is offline renderer aware
    (NSOpenGLPixelFormatAttribute)0
};
pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];

#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
if(pixFmt == nil)
{
    // NSOpenGLPFAAllowOfflineRenderers is not supported on this OS version
    attribs[3] = (NSOpenGLPixelFormatAttribute)0;
    pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
}
#endif

Перечисление 2  , Создающее оффлайновое средство рендеринга осведомленный Формат пикселя с CGL.

CGLPixelFormatAttribute attribs[] =
{
    kCGLPFAColorBits, 24,
    kCGLPFADoubleBuffer,
    kCGLPFAAllowOfflineRenderers, // lets OpenGL know this context is offline renderer aware
    (CGLPixelFormatAttribute)0
};
CGLPixelFormatObj pixelFormatObj;
GLint numPixelFormats;
CGLError err;

err = CGLChoosePixelFormat(attribs, &pixelFormatObj, &numPixelFormats);

#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
if(err == kCGLBadAttribute)
{
    // kCGLPFAAllowOfflineRenderers is not supported on this OS version
    attribs[3] = (CGLPixelFormatAttribute)0;
    err = CGLChoosePixelFormat(attribs, &pixelFormatObj, &numPixelFormats);
}
#endif

Перечисление 3  , Создающее оффлайновое средство рендеринга осведомленный Формат пикселя с AGL.

GLint attribs[] =
{
    AGL_COLOR_BITS, 24,
    AGL_DOUBLE_BUFFER,
    AGL_ALLOW_OFFLINE_RENDERERS, // lets OpenGL know this context is offline renderer aware
    AGL_NONE
};
AGLPixelFormat pixelFormatObj = aglChoosePixelFormat(NULL, 0, attribs);
GLenum err = aglGetError();

#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
if(err == AGL_BAD_ATTRIBUTE || pixelFormatObj == NULL)
{
    // AGL_ALLOW_OFFLINE_RENDERERS is not supported on this OS version
    attribs[3] = AGL_NONE;
    pixelFormatObj = aglChoosePixelFormat(NULL, 0, attribs);
}
#endif

Используя CGLQueryRendererInfo

Если Ваши ссылки на приложение против Mac OS X 10.4u SDK, то CGLQueryRendererInfo(), не возвратит информацию об оффлайновых средствах рендеринга, работая на Mac OS X 10.5 или позже. Apple рекомендует, чтобы Вы соединились против Mac OS X 10.5 (или позже) SDK и использовали надлежащую цель развертывания, если Ваше программное обеспечение должно воздействовать на 10,4 или ранее. Если это не возможно, тогда свяжитесь dts@apple .comfor с помощью.

Обнаружение изменений средства рендеринга

Как только Ваш контекст OpenGL сконфигурирован, чтобы позволить аппаратному средству рендеринга изменяться, необходимо будет обнаружить эти изменения путем ответа на Кварцевые Уведомления Дисплея (если Вы не будете использовать NSOpenGLView) или путем переопределения -update (если Вы используете NSOpenGLView подкласс) для надлежащего обнаружения изменений функциональности. Если Вы не делаете, то Ваше получение OpenGL может перестать работать различными потенциально катастрофическими способами. Каждый раз, когда виртуальный экран изменяется, возможности видеокарты, к которой Вы в настоящее время представляете, могут измениться, таким образом, необходимо повторно запросить те возможности и скорректировать пути получения по мере необходимости для поддержки недавно активного GPU.

Регистрация для использования уведомлений Изменения Дисплея Quartz Display Services

Если Ваше приложение не на основе, Using Quartz Display Services является соответствующей NSOpenGLView, независимо от того, если Ваше приложение является оконным или полноэкранным. Можно зарегистрироваться для использования уведомлений изменения дисплея CGDisplayRegisterReconfigurationCallback и запросите прекратить получать такие уведомления с CGDisplayRemoveReconfigurationCallback. Перечисление 4 демонстрирует, как зарегистрироваться для этих уведомлений.

Перечисление 4  , Обнаруживающее виртуальный экран, изменяется с Quartz Display Services.

void MyDisplayReconfigurationCallBack(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo);

// When the application has finished launching,
// this message is sent to the Application Delegate
// We use it to register for display reconfiguration notices.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    CGDisplayRegisterReconfigurationCallback(MyDisplayReconfigurationCallBack, self);
}

// Before the application terminates we'll remove our registration callback.
// This isn't strictly necessary, but we demonstrate this here in case you have
// more complex logic for removing the configuration callback
// (for example, you might remove the callback when you have no windows open).
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
    CGDisplayRemoveReconfigurationCallback(MyDisplayReconfigurationCallBack, self);
}

// When displays are reconfigured our callback will be called. You can take this opportunity
// to do further processing or pass the notification on to an object for further handling.
// In this example we've passed 'self' for the userInfo pointer,
// so you can cast it to an appropriate object type and forward the message onwards.
void MyDisplayReconfigurationCallBack(
    CGDirectDisplayID display,
    CGDisplayChangeSummaryFlags flags,
    void *userInfo)
{
    if (flags & kCGDisplaySetModeFlag)
    {
        // Display has been reconfigured.
        // Adapt to any changes in capabilities
        // (such as max texture size, extensions and hardware capabilities such as the amount of VRAM).
    }
}

Используя NSOpenGLView

Если Вы используете NSOpenGLView это автоматически регистрируется для уведомления об изменениях в конфигурации дисплея для Вас и вызывает свое собственное -update метод в ответ на те изменения. Можно тогда переопределить -update обеспечить дополнительную обработку как показано в Перечислении 5.

Перечисление 5  , Обнаруживающее виртуальный экран, изменяется с NSOpenGLView.

-(void) update
{
    [super update];
    GLint newVirtualScreen = [[self openGLContext] currentVirtualScreen];
    if (currentVirtualScreen != newVirtualScreen)
    {
        currentVirtualScreen = newVirtualScreen;
        // Adapt to any changes in capabilities
        // (such as max texture size and hardware capabilities).
    }
}

PBuffers по сравнению с текстурами OpenGL и буферными объектами

Это настоятельно рекомендовано это, Вы используете Объекты Кадрового буфера по PBuffers, когда это возможно. Если необходимо работать с PBuffers, необходимо гарантировать, что виртуальный экран PBuffer использует то же устройство контекста, в который Вы хотите нарисовать его содержание. Когда виртуальный экран PBuffer изменится, его conents будет потерян, таким образом, необходимо обновить содержание PBUFFER в то время. Примеры для AGL (Перечисление 6) и CGL (Перечисление 7) следует.

Перечисление 6  , обновляющее PBuffer с AGL.

void MyAGLPBufferDraw(
    AGLContext targetCtx,
    AGLContext pbufferCtx,
    AGLPbuffer pbuffer,
    GLint *pbVS)
{
    // make PBuffer context current (as we are drawing into the PBuffer with it)
    GLboolean success = aglSetCurrentContext(pbufferCtx);

    GLint targetVS = aglGetVirtualScreen(targetCtx);
    if(*pbVS != targetVS)
    {
        *pbVS = targetVS;
        success = aglSetPBuffer(pbufferCtx, pbuffer, 0, 0, targetVS);

        // re-query capabilities, including VRAM, if needed
        // rebuild assets only as needed such as with max texture size shrinking
        // set any state needed to make next draw use proper rendering paths
    }

    // ...
    // Do drawing into PBuffer here
    // ...

    // make available to the target context - use glFlushRenderAPPLE for single buffered contexts
    glFlushRenderAPPLE(); 

    aglSetCurrentContext(NULL); // ensure we are not still drawing to PBuffer
}

Перечисление 7  , обновляющее PBuffer с CGL.

void MyCGLPBufferDraw(
    CGLContext targetCtx,
    CGLContext pbufferCtx,
    CGLPBufferObj pbuffer,
    GLint *pbVS)
{
    CGLError err;

    // make PBuffer context current (as we are drawing into the PBuffer with it)
    err = CGLSetCurrentContext(pbufferCtx);

    GLint targetVS;
    err = CGLGetVirtualScreen(targetCtx, &targetVS);
    if(*pbVS != targetVS)
    {
        *pbVS = targetVS;
        err = CGLSetPBuffer(pbufferCtx, pbuffer, 0, 0, targetVS);

        // re-query capabilities, including VRAM, if needed
        // rebuild assets only as needed such as with max texture size shrinking
        // set any state needed to make next draw use proper rendering paths
    }

    // ...
    // Do drawing into PBuffer here
    // ...

    // make available to the target context - use glFlushRenderAPPLE for single buffered contexts
    glFlushRenderAPPLE(); 

    CGLSetCurrentContext(NULL); // ensure we are not still drawing to PBuffer
}

Выбор корректного средства рендеринга для PBuffer

Если у Вас будет PBuffer, содержание которого никогда не выводится на экран через OpenGL (тот, который мог бы иметь место, если Вы будете использовать PBuffer для работы GPGPU), тогда, то необходимо будет определить устройство для PBuffer на основе некоторого экранного элемента. Предположение, что у Вас нет экранных контекстов OpenGL, которые можно использовать для этого присвоения, самый простой способ сделать так, должно использовать атрибут формата пикселя для присвоения PBuffer устройству, способному к рендерингу на основной экран. См. Перечисление 8

Перечисление 8  , Определяющее виртуальный экран от NSWindow

NSOpenGLPixelFormat *CreatePixelFormat()
{
    // Create pixel format
    NSOpenGLPixelFormatAttribute attributes[] =
    {
        NSOpenGLPFADoubleBuffer,
        NSOpenGLPFANoRecovery,
        NSOpenGLPFAAccelerated,
        // If this attribute is used on an OS that doesn't understand it
        // then pixel format creation will fail. You can use one of the
        // techniques described above to verify if the
        // NSOpenGLPFAAllowOfflineRenderers pixel format attribute
        // is supported.
        NSOpenGLPFAAllowOfflineRenderers,
        (NSOpenGLPixelFormatAttribute)0,
    };
    return [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
}

GLint VirtualScreenForWindow(NSWindow *window)
{
    // If we're passed nil, then use the menu bar screen.
    NSScreen *screen = (window != nil) ? [window screen] : [[NSScreen screens] objectAtIndex:0];

    CGDirectDisplayID displayID = (CGDirectDisplayID)[[[screen deviceDescription]
        objectForKey:@"NSScreenNumber"] unsignedIntValue];
    if (displayID == 0)
    {
        // This is an error case because NSScreen does not have the necessary key.
        // This should never happen.
        return -1; // Error case
    }

    CGOpenGLDisplayMask currentDisplaymask = CGDisplayIDToOpenGLDisplayMask(displayID);
    if(currentDisplaymask == 0)
    {
        // The display ID is unknown and not mappable to an OpenGL context
        // This should never happen.
        return -1; // Error case
    }

    // create pixel format
    NSOpenGLPixelFormat *pixelFormat = CreatePixelFormat();

    // find virtual screen
    GLint virtualScreen = 0;
    GLint numberOfVirtualScreen = [pixelFormat numberOfVirtualScreens];
    GLint i;
    for(i = 0; i < numberOfVirtualScreen; i++)
    {
        GLint mask;
        [pixelFormat getValues:&mask
            forAttribute:NSOpenGLPFAScreenMask forVirtualScreen:i];
        if(mask & currentDisplaymask)
        {
            virtualScreen = i;
            break;
        }
    }
    [pixelFormat release];

    return virtualScreen;
}


История версии документа


ДатаПримечания
05.06.2009

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