Скопируйте задний буфер OpenGL непосредственно в пиксельные данные GDI DC.

Я пишу графический интерфейс, который использует OpenGL через OpenTK и GLControl на C #, и я пытаюсь использовать грязные прямоугольники для рисования только тех элементов управления, которые необходимо нарисовать. Очевидно, что неразумно перерисовывать всю развернутую форму только для обновления кнопки наведения мыши.

Моя первая попытка состояла в том, чтобы использовать glScissors, но это не ограничивает SwapBuffers, которые на моей платформе, как я подозреваю (из-за того, что производительность почти полностью зависит от размера окна) не «свопируются», а делают полную копию назад буфер на передний буфер.

Второй попыткой был glAddSwapHintRectWIN, который теоретически ограничил бы область подкачки (в данном случае скопированную) SwapBuffers, но это только подсказка, и она вообще ничего не делает.

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

Похоже, что полное обновление области все еще происходит независимо от того, что я делаю.

Итак, я пытаюсь использовать glReadPixels() и каким-то образом получить указатель для рисования непосредственно на пиксельных данных hdc, полученных из CreateGraphics() элемента управления. Это возможно?

ИЗМЕНИТЬ:

Я думаю, что что-то не так с GLControl, почему производительность этого кода зависит от размера экрана, я не делаю никаких свопбуферов или очисток, просто рисую треугольник постоянного размера на переднем буфере: Может быть, проблема с драйвером?

        GL.DrawBuffer(DrawBufferMode.Front);

        Vector4 Color;
        Color = new Vector4((float)R.NextDouble(), 0, 0, 0.3F);

        GL.Begin(BeginMode.Triangles);

        GL.Color4(Color.X, Color.Y, Color.Z, Color.W);



        GL.Vertex3(50, 50, 0);
        GL.Vertex3(150F, 50F, 0F);
        GL.Vertex3(50F, 150F, 0F);

        GL.End();

        GL.Finish();

РЕДАКТИРОВАТЬ 2 Это решение нежизнеспособно: рисование на текстуре и использование glGetTexImage для рисования на растровом изображении GDI, а затем рисование этого растрового изображения на окне HDC

Чтение пикселей буфера из буфера с помощью glReadPixels в растровое изображение GDI и последующее рисование этого растрового изображения на HDC окна.

Разделение окна на сетку окон просмотра и обновление только ячеек, содержащих грязный прямоугольник


person Rafael    schedule 18.03.2014    source источник
comment
Что возвращает GL.GetString(StringName.Renderer)?   -  person The Fiddler    schedule 26.03.2014


Ответы (2)


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

Имейте в виду, что существует несколько ограничений при попытке объединить GDI и OpenGL на одном HDC. Действительно, в большинстве случаев это отключит аппаратное ускорение и даст вам OpenGL 1.1 через программный рендерер Microsoft.

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

Есть два решения:

  1. не звони SwapBuffers(). Установите GL.DrawBuffer(DrawBufferMode.Front) и используйте одиночную буферизацию для обновления грязных прямоугольников. Это имеет серьезные недостатки, в том числе отключение композиции рабочего стола в Windows.
  2. не выполняйте рендеринг напрямую в фреймбуфер по умолчанию. Вместо этого выделяйте и визуализируйте в объект кадрового буфера. Таким образом, вы можете обновить только те регионы FBO, которые были изменены. (Вам все равно нужно будет копировать FBO для отображения каждого кадра, так что это может быть или не быть выигрышем в производительности в зависимости от сложности вашего графического интерфейса.)

Редактировать:

40-60 мс для одного треугольника означает, что вы не получаете никакого аппаратного ускорения. Проверьте GL.GetString(StringName.Renderer) — он дает имя вашего графического процессора или возвращает "Microsoft GDI renderer"?

Если это последнее, вы должны установить драйверы OpenGL с веб-сайта поставщика вашего графического процессора. Сделайте это, и проблема с производительностью исчезнет.

person The Fiddler    schedule 18.03.2014
comment
Я думаю, вы ошибаетесь, вы могли бы реализовать грязные прямоугольники при двойной буферизации, да, задний буфер недействителен, поэтому вы очищаете его каждый кадр, но идея состоит в том, чтобы рисовать и очищать только недействительную область в заднем буфере, все остальное по-прежнему будет неопределенным, и скопируйте эту область в буфер кадра. У меня ATI Radeon Mobility HD 4250 на Windows 7. Я не могу оставить двойную буферизацию по понятным причинам, и второй вариант был бы таким же, пришлось бы отрисовывать весь задний буфер на передний буфер - person Rafael; 18.03.2014
comment
the idea is to clear only the invalidated area on the back buffer, everything else still would be undefined. Поскольку все не определено, отсюда следует, что все инвалидировано — иными словами, при использовании двойной буферизации приходится все перерисовывать. Я не понимаю, как это будет работать. - person The Fiddler; 19.03.2014
comment
Извините, мой плохой, я хотел очистить и нарисовать только грязный прямоугольник в заднем буфере и скопировать эту область в передний буфер. Площадь грязного прямоугольника на практике, конечно, будет намного меньше, чем общая площадь заднего буфера. - person Rafael; 19.03.2014
comment
Это все равно (обязательно) не поможет: (а) для этого требуется GraphicsMode с семантикой копирования (медленнее!) и (б) SwapBuffers в любом случае скопирует весь задний буфер в передний буфер, а не только грязные биты. Для (а) отправьте запрос функции. Я не знаю, есть ли обходной путь (б). Редактировать: какую производительность вы получаете? - person The Fiddler; 20.03.2014
comment
Когда окно развернуто, частота кадров снижается примерно в 50 раз, DirectX уже поддерживает грязные прямоугольники Я больше не использую OpenTK, я импортировал некоторые функции OpenGL с DLLImport и p/invokes, так что это не проблема OpenTK. У меня есть некоторые частичные решения, такие как чтение текстуры на растровое изображение, а затем рисование этого растрового изображения, но в любом случае это медленно, я бы просто работал с очень маленькими грязными прямоугольниками. - person Rafael; 21.03.2014
comment
Какую частоту кадров вы получаете в абсолютных значениях? Падение в 50 раз требует некоторого контекста (это с 50 кадров в секунду до 1 кадра в секунду или с 50000 кадров в секунду до 1000 кадров в секунду?) - person The Fiddler; 21.03.2014
comment
Отрисовываю сцену 10 раз с выключенной vsync, при размере окна 150х150 на отрисовку одного треугольника на переднем буфере уходит менее 2 мс (с glClear и без частота кадров одинаковая), при максимальном размере окна (1440x900) для рисования одного треугольника требуется 40-60 мс - person Rafael; 22.03.2014
comment
Возможно, это проблема с драйвером, но я уже скачал последние версии драйверов от AMD, сейчас перехожу на DirectX. - person Rafael; 28.03.2014

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

При тестировании на примере ansi c у меня были те же результаты.

Проведение той же пары тестов под linux дало такие же результаты.

Под Linux я заметил, что частота кадров меняется, когда я переключаюсь с одного дисплея на другой. Даже с отключенной вертикальной синхронизацией.

Следующим шагом будет проверка, ведет ли DirectX такое же поведение. Если да, то ограничение находится на шине между дисплеем и видеокартой.

РЕДАКТИРОВАТЬ: вывод:

Такое поведение создает у вас ложное впечатление. Подумайте только о том, чтобы построить свой интерфейс на FBO с грязными механизмами rect и визуализировать его на quad (лучше сделать из tri) и подкачивать, как обычно, не думая, что вы можете улучшить подкачку для заданного размера окна, обрезав некоторые операции.

person j-p    schedule 18.03.2014