2013-06-24 5 views
1

Проблема, которую я заметил однажды в своей системе Win7, считал, что это ошибка DWM, поскольку она была исправлена ​​после перезагрузки. Но теперь я понимаю, что это происходит (как поведение по умолчанию) в системах других людей, и это обычное поведение на Surface Pro.DWM in Win7/8 + GDI

Как воспроизвести проблему: реализовать, используя GDI, базовую систему лассо. Определите прямоугольник, управляемый мышью, когда прямоугольник изменится, приведет к недействительности старого (&) нового (или аннулирует объединение обоих прямоугольников, либо как новый прямоугольник, либо сложный регион, это не имеет значения, «ошибка», все равно показывается).

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

Итак, что вы увидите, если у вас есть такая система, как у меня (рабочий стол Win7 с geforce, aero on), это обычная система лассо, не имеющая больше ореолов, чем собственный монитор. В других системах (например, Surface Pro, чтобы определить полностью известную систему), вы увидите, что при расширении лассо наружу граница лассо исчезает. Немного напоминает ЖК-экран, но в целом более заметный.

Теперь, вместо того, чтобы аннулировать прямоугольник лассо, попробуйте сделать недействительным все окно. И там больше нет ореолов.

Я выяснил, что это не признак недействительности, который «исправляет» его, это доступ к GDI. Вы можете также аннулировать весь прямоугольник, но только красите зону лассо, все еще ореолы. Но если вы нарисуете зону лассо и нарисуете один маленький пиксель на каждом углу окна, больше нет ореолов.

Должно быть что-то в DWM, возможно, начиная с версии 1.1, которая использует какое-то кеширование ограничивающей рамки последнего доступа GDI, и по какой-то странной причине то, что попадает в последнюю ограничительную рамку, сразу же появляется на экране , в то время как новая часть будет отложена по меньшей мере на 1 кадр.

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

Опять же, скорее всего, в DWM 1.1, я не думаю, что вы можете получить это в Vista, но я не уверен. Я также не знаю, почему это не так на моем рабочем столе, возможно, это зависит от драйвера графической карты.

Так что, если кто-нибудь случится, чтобы узнать больше об этом ...

+0

Трудно следовать за ним. Вы получите более эффективные ответы, если вы составите компилируемый тестовый проект, чтобы проиллюстрировать проблему. Вы написали код на C++? – ahmd0

+0

нет, Delphi, поэтому. Я мог бы сделать видео о результатах. Но это довольно просто: 1. определить прямоугольник, 2. реализовать wm_mousemove, чтобы нижний правый прямоугольник следовал за мышью, недействительным прямоугольник (и только прямоугольник!) До и после перемещения, 3. реализовать wm_paint для заполнения фон и нарисуйте прямоугольник * с помощью GDI * (используйте двойную буферизацию, если хотите, не обязательно, проблема будет очевидна, если у вас есть). Это все. – user2516466

+0

Редактировать: видео из системы, на которой это происходит (если ссылки работают здесь): http://youtu.be/iU_zhKZKzNA – user2516466

ответ

0

Вот несколько общих GDI связанных указателей относительно your code:

  1. При вызове InvalidateRect API не может сразу же отправить WM_PAINT уведомление, так что вызов InvalidateRect два раза подряд, как вы делали в свой TForm1.FormMouseMove метод, безусловно, вызывает этот визуальный эффект. Первая перерисовка еще не обрабатывается, когда вы вызываете ее во второй раз.

  2. Я не уверен, что Canvas.Rectangle точно выполняет «внутри» или какие API-интерфейсы он вызывает. Я предполагаю, что он использует DrawFocusRect, и в этом случае вы должны знать, что его рисунок XORed, поэтому выполнение его дважды над одним и тем же прямоугольником приведет к его стиранию.

КИ, так вот что я буду делать, если я рисовал, что поле выбора:

  1. Вызова InvalidateRect API только один раз. Для этого вам придется вычислять ограничивающий прямоугольник, который будет включать положение блока выбора до и после перемещения. Если я не ошибаюсь, вы можете использовать API UnionRect для этого или просто вычислить ограничивающий прямоугольник самостоятельно.

  2. Если вы избегаете визуального отставания, я бы сделал весь чертеж окна выбора в TForm1.FormMouseMove. Вы можете получить контекст устройства, вызвав API GetDC на свой дескриптор окна. После этого сделайте тот же рисунок. В этом случае вам не понадобится аннулирование. (К сожалению, я не могу дать вам процедуру Delphi Это, как я хотел бы сделать это с простым WinAPIs..)

EDIT: После просмотра вашей C++ code я был в состоянии воспроизвести visual flicker вас описывая. Я также сделал два проекта на C++, полученных из вашего исходного кода, в надежде на его решение: Вот simple version и here's the one with mouse dragging support. Однако никто из них не решил проблему.

Я смог воспроизвести мерцание на рабочем столе Windows 7. Проведя некоторые тесты, я убежден, что этот визуальный артефакт вызван драйвером дисплея. В моем случае на настольном компьютере установлена ​​видеокарта ATI Radeon. Вот как я смог заключить, что: я запускал тестовый исполняемый файл на другом (более старом) рабочем столе с ОС Windows XP, и мерцания не было. Я запускал тот же самый исполняемый файл на виртуальной машине с установленной Windows XP на настольном компьютере, который создавал мерцание. Фликер присутствовал, когда исполняемый файл работал в Windows XP на этой виртуальной машине. Поскольку один и тот же видеодрайвер отвечает за рендеринг фактического рабочего стола и того, что есть на виртуальной машине, в видеодрайвере определенно есть «забавный бизнес», который включает в себя оптимизацию или кэширование, что вызывает этот артефакт.

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

Ваши дальнейшие шаги должны быть, вероятно, это:

  • Связаться с производителем драйвер видеокарты, что вы столкнулись с этой проблемой на. Смотрите, помогают ли они. Я покажу им ваше видео на YouTube о проблеме и дам им проект C++, чтобы воспроизвести его.

  • Задать вопрос на форуме по разработке драйверов Windows, желательно для разработчиков видеодрайверов. К сожалению, это сокращающееся сообщество, поэтому ожидайте длительных задержек.

  • Изменить теги на свой вопрос здесь. Добавьте их: C, C++, WinAPI, GDI, DWM и удалите то, что у вас есть сейчас. Таким образом, это будет видно для сообщества разработчиков Win32, поэтому вы получите больше просмотров.

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

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

+0

О, давай, не давай мне ответа, как будто я ноб, я работаю над FL Studio в течение 15 лет, я программист на 20. Что в коде вряд ли связано с Delphi, он полагается на чистые сообщения Windows: wm_MouseMove, InvalidateRect, wm_Paint, GDI FillRect и Rectangle. 4 основных элемента Windows, а код - крошечный. И, как я уже писал, это влияет и на другие приложения. Я описываю проблему, которая влияет на способ больше, чем я думал (только не мое, но это влияет на Surface Pro, что означает, что это достаточно серьезно). Btw, как я написал, недействительным объединение обоих прямоугольников имеет тот же эффект. – user2516466

+0

(Я прошу кого-то перевести. на C++, если это то, что требуется, чтобы кто-либо воспринял это всерьез). Btw «InvalidateRect может не сразу отправить WM_PAINT», он * будет * не сразу, никогда не будет, никогда не будет. И да результат проблемы полностью выглядит так: отправка один, другой ead о недействительности исправлений. Также обратите внимание, что у меня есть репутация. проблема в M $ здесь http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/7de13a8d-0bcb-4e4b-a975-e2935c226e24/dwm-invalidaterect, но из crs очень мало активности на их форумы, поэтому я не ожидаю, что кто-нибудь увидит его там. – user2516466

+0

Источники и исполняемые файлы C++ здесь: flstudio.image-line.com/help/publicfiles_gol/lassotest_cpp.zip – user2516466

3

Обновление по этому вопросу. Я не нашел никакого чистого решения, но это взлом, который работает.

Во-первых, я также обнаружил, что эта «ошибка», похоже, влияет на все системы, но не так.

Используя DwmGetCompositionTimingInfo, можно сделать v-синхронизированную анимацию GDI. Это теоретически, потому что на практике это не сработает из-за той же «ошибки», даже на системах, явно не затронутых тем, что я описал выше. DWM просто решит не обновлять что-либо, когда его недостаточно, чтобы обновить его, и это приведет к тому, что при прокрутке окна с использованием ScrollWindowEx и недостаточно пикселей будут отменены.

Так в чем же решение? Обманите DWM, подумав, что он должен немедленно заменить буфер, так как это проблема. Когда вы выполняете операции GDI, можно подумать, что результат появится в следующем обновлении vwank-синхронизации DWM, но это не так. Некоторые части будут, некоторые будут отложены. Таким образом, трюк заключается в том, чтобы заставить DWM поменяться местами, вот что я нашел об этом:

- Во-первых, DWMFlush этого не делает (и не делает GDIFlush). DWMFlush более или менее в любом случае WaitForVBlank

-DWMSetPresentParameters, кажется, не позволяют это тоже, хотя я не стал, что много с этой функцией, так как он уже ушел в ОС Windows 8

-The DWM кажется для обновления, когда есть достаточно пикселей для обмена, но он также, похоже, раздроблен, вполне возможно, в широкие, но короткие прямоугольники (это то, что кажется на Surface Pro, но не на моем рабочем столе). Связано ли это с апертурами VRAM или сегментацией на небольшие текстуры, я понятия не имею, действительно, может быть, кто-то знает?

Так что работает для меня: рассказывая GDI об обновлении вертикальных полос шириной 1 пиксель, примерно на 500 пикселей друг от друга, по всему экрану. Если вы делаете это только по частям экрана, вы все равно будете мерцать на других частях. Он также работает с использованием горизонтальных полос, но тогда вам понадобится намного больше, поэтому я считаю, что сегментация выполняется в широких коротких прямоугольниках. Вы можете видеть эти прямоугольники, когда обманываете DWM.

Какие функции GDI работают? Многие делают это, но не все имеют одинаковое использование ЦП. Во-первых, забудьте GetPixel, он работает, но использование процессора явно чрезвычайно велико. Во-вторых, GDI кажется достаточно интеллектуальным, чтобы обнаруживать вещи, которые можно буферизировать точно так же, как команды, поэтому заполнение прямоугольника нулевой кистью или рисование пустого текста не заставит DWM обновляться. Что такое BitBlt или AlphaBlend, используя пустые вертикальные полосы. Использование ЦП по-прежнему в порядке, хотя он не так блистает на весь экран. Это должно быть сделано в окне верхнего уровня, а не на рабочем столе.

Создание Direct2D цели визуализации для DC & делает начать/enddraw также работает, но это нормально, так как это заставит обновления полного прямоугольника, & таким образом, стоимость процессора выше.

Если кто-то знает лучший способ принудительно обновить DWM, я хотел бы знать. Но так как Windows 8 уже выработала устаревшую большинство интересных функций DWM (к счастью, DwmGetCompositionTimingInfo все еще частично работает, или было бы невозможно делать синхронизированные таймеры без DirectX), я не уверен, что есть лучший способ.

Кроме того, это не обязательно должно выполняться на всех окнах верхнего уровня.Вы можете увидеть эффект, воздействующий на рабочий стол Windows blue lasso, когда он приближается к окну верхнего уровня, который делает недействительными полоски, и перестает мерцать, как только он входит в зону, расположенную рядом с ней (о сегментации, о которой я говорю выше).

+0

Спасибо, что поняли это! Странно, что такой жестокий человек воспринимается всеми как небольшая визуальная мелочь. – Alex

+1

Обратите внимание, что обходной путь, описанный здесь, дает проблемы с OpenGL. Мне пришлось пойти на еще один хак, который представляет собой гигантское, сквозное и прозрачное многослойное окно поверх реальных окон. Это можно сделать «обновленным» при низкой стоимости ЦП, таким образом, чтобы не требовалось обменивать огромную порцию памяти с графической картой. В качестве бонуса он также позволяет получить плавный 60FPS с включенным компоновкой рабочего стола, что-то обычно невозможно. – user2516466

 Смежные вопросы

  • Нет связанных вопросов^_^