Условие гонки Java AWT drawImage - как использовать синхронизированный, чтобы избежать этого - PullRequest
3 голосов
/ 10 февраля 2011

После многих часов отладки и анализа мне наконец удалось выделить причину состояния гонки. Решение это другое дело!

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

http://screencast.com/t/aTAk1NOVanjR

Итак, ситуация: у нас есть реализация поверхности с двойной буферизацией (т. Е. Java.awt.Frame или Window), где есть непрерывный поток, который по существу непрерывно зацикливается, вызывая процесс рендеринга (который выполняет макет пользовательского интерфейса и отображает его). в буфер), а затем после рендеринга перетаскивает визуализированную область из буферного пространства в экран.

Вот версия псевдокода (строка полной версии 824 из Surface.java ) рендеринга с двойной буферизацией:

public RenderedRegions render() {
    // pseudo code
    RenderedRegions r = super.render();
    if (r==null) // nothing rendered
        return
    for (region in r)
        establish max bounds
    blit(max bounds)
    return r;
}

Как и в любой другой реализации AWT-поверхности, она также реализует (строка 507 в AWT.java - ограничение канала: ( - использовать ссылку Surface.java, замените core / Surface.java на plat / AWT.java ) переопределения рисования / обновления, которые также переносятся из заднего буфера на экран:

        public void paint(Graphics gr) {
            Rectangle r = gr.getClipBounds();
            refreshFromBackbuffer(r.x - leftInset, r.y - topInset, r.width, r.height);
        }

Блиттинг реализован (строка 371 в AWT.java) с использованием функции drawImage ():

    /** synchronized as otherwise it is possible to blit before images have been rendered to the backbuffer */
    public synchronized void blit(PixelBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2) {
        discoverInsets();
        try {
            window.getGraphics().drawImage(((AWTPixelBuffer)s).i,
                              dx + leftInset, dy + topInset,     // destination topleft corner
                              dx2 + leftInset, dy2 + topInset,   // destination bottomright corner
                              sx, sy,                            // source topleft corner
                              sx + (dx2 - dx), sy + (dy2 - dy),  // source bottomright corner
                              null);
        } catch (NullPointerException npe) { /* FIXME: handle this gracefully */ }
    }

(Внимание: именно здесь я начинаю делать предположения!)

Проблема здесь заключается в том, что drawImage является асинхронным и что первый вызов метода refreshBackBuffer () посредством рисования / обновления вызывается первым, а происходит секунд.

Итак ... блит уже синхронизирован. Очевидный способ предотвратить состояние гонки не работает. (

Пока я придумал два решения, но ни одно из них не идеально:

  1. re-blit на следующем проходе рендера
    минусы: снижение производительности, все еще немного мерцание из-за состояния гонки (действующий экран -> неверный экран -> действительный экран)

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

Здесь (1) представляется меньшим из двух зол. Редактировать: и (2) не работает, получая пустые экраны ... (1) работает нормально, но просто маскирует проблему, которая потенциально все еще существует.

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

Или, возможно, использовать ImageObserver?

Обратите внимание, что из-за характера приложения (Vexi, для тех, кто интересуется, веб-сайт устарел, и я могу использовать только 2 гиперссылки) поток рендеринга должен быть вне рисования / обновления - он имеет однопоточный скрипт Модель и процесс макета (подпроцесс рендеринга) запускает скрипт.

Ответы [ 2 ]

0 голосов
/ 25 июля 2011

Обновление: хороший подход здесь: Пользовательский рендеринг AWT - захват плавного изменения размера и устранение мерцания изменения размера


Ответ здесь заключался в том, чтобы удалить все блиты из потока paint(), то есть когда-либо обновлять только из буфера в потоке программы. Это противоположно ответу, предложенному Йохеном Бедерсдорфером, но его ответ никогда не сработал для нас, потому что программа имеет свою собственную модель сценариев, которая интегрирована с моделью макетов, которая управляет рендерингом, поэтому все это должно происходить последовательно.

(Предположение) Некоторые проблемы связаны с поддержкой менее чем нескольких звездных мониторов в Java с ускоренными наборами графических чипсетов, когда я столкнулся с этой проблемой при адаптации к использованию BufferStrategy, которая была Direct3D + несоответствие Java.

По существу paint() и update() сводятся к блокировке вызовов. Это работает намного лучше, но имеет один недостаток - нет плавного изменения размера.

private class InnerFrame extends Frame() {
    public void update(Graphics g) { }
    public void paint(Graphics g) { }
    ....
}

В итоге я использовал буферную стратегию, хотя я не на 100% удовлетворен этим подходом, так как мне кажется, что неэффективно выполнять рендеринг на изображение, затем копировать полное изображение в BufferStrategy и затем выполнять show() на экран.

Я также реализовал альтернативу на основе Swing, но опять же, мне это не особо нравится. Он использует JLabel с ImageIcon, в результате чего программный поток (не EDT) рисует изображение, обернутое ImageIcon.

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

0 голосов
/ 10 февраля 2011

Не уверен, но что произойдет, если вы Blit в потоке краски AWT?

...