Страницы

Поиск по вопросам

понедельник, 15 октября 2018 г.

Разное поведение программы на Win7 и WinXP

Есть такой код (минимальный рабочий пример):
#include #include
HDC hDC; HDC hDCMem; HBITMAP hbitmap; HWND hwnd; int ScreenMaxX; int ScreenMaxY;
short pattern[8]={~0xFF, ~0xFF, ~0xFF, ~0xFF, ~0xFF, ~0xFF, ~0xFF, ~0xFF}; HBRUSH brush=::CreatePatternBrush(::CreateBitmap(8, 8, 1, 1, pattern));
void bar(int nLeft, int nTop, int nRight, int nBottom) { RECT rect; rect.left = nLeft; rect.right = nRight; rect.top = nTop; rect.bottom = nBottom;
::SetTextColor(hDCMem, 0xFF00FF); ::SetBkColor(hDCMem, 0xFF00FF); //brush=::CreatePatternBrush(::CreateBitmap(8, 8, 1, 1, pattern)); ::FillRect(hDCMem, &rect, brush); }
void flush(){ ::BitBlt(hDC, 0, 0, ScreenMaxX, ScreenMaxY, hDCMem, 0, 0, SRCCOPY); }
CRITICAL_SECTION graphics_cs;
uint8_t thread_cnt=0; uint8_t total_threads=2; HANDLE turnstile1=CreateSemaphoreW(nullptr, 0, 2, nullptr);
void thread_func(int num){ int x,y; if(num==0){ x=20; y=0; } else { x=110; y=0; }
while(true) { while(true) { EnterCriticalSection(&graphics_cs); if (thread_cnt == num) { thread_cnt++; bar(x, y, x+40, y+40); y+=1; //flush(); if(thread_cnt==total_threads){ thread_cnt = 0; flush(); ReleaseSemaphore(turnstile1, total_threads, nullptr); } LeaveCriticalSection(&graphics_cs); break; } else { LeaveCriticalSection(&graphics_cs); } }
WaitForSingleObject(turnstile1, INFINITE);
Sleep(100); } }
void mainx () { InitializeCriticalSection(&graphics_cs); for(int i=0; iDWORD Th(LPVOID param) // новая нить { (void)param; ::SetWindowPos(hwnd, HWND_TOP, // дескриптор окна и порядок размещения 10, // позиция по-горизонтали 10, // по-вертикали 400, // ширина 500, // высота SWP_SHOWWINDOW // отобразить ); mainx(); flush(); return 0; } DWORD g_nMainThreadID;
//обработка сообщений главного окна приложения long FAR PASCAL WindowProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam ) { switch (message) { case WM_PAINT: flush(); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, message, wParam, lParam); }
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) { (void)hPrevInstance, (void)lpCmdLine; WNDCLASS wc; MSG msg;
wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "Menu_one"; wc.lpszClassName = "NAME";
if (!RegisterClass(&wc)) {return 0; };
//создаем главное окно приложения hwnd = CreateWindow("NAME", // имя класса окна "!", // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, //р-ры и расположение по-умолчанию CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, // идентификатор родительского окна NULL, // идентификатор меню hInstance, // идентификатор приложения NULL // доп параметры ); ScreenMaxX = ::GetSystemMetrics(SM_CXSCREEN); ScreenMaxY = ::GetSystemMetrics(SM_CYSCREEN); hDC = ::GetDC(hwnd); hDCMem = ::CreateCompatibleDC(hDC); hbitmap = ::CreateCompatibleBitmap(hDC, ScreenMaxX, ScreenMaxY ); ::SelectObject(hDCMem, hbitmap); auto hbrush = (HBRUSH)::GetStockObject(WHITE_BRUSH); ::SelectObject(hDCMem, hbrush); ::PatBlt(hDCMem, 0,0, ScreenMaxX, ScreenMaxY, PATCOPY ); ::DeleteObject(hbrush);
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Th, (LPVOID)hwnd, 0,&g_nMainThreadID);
ShowWindow(hwnd, nShowCmd); UpdateWindow(hwnd);
// Запускаем цикл обработки сообщений while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
Этот код медленно рисует две розовые полосы сверху вниз в разных потоках (потоку соответствует функция thread_func). Причём на Windows 7 полосы рисуются равномерно, а на Windows XP — с разной скоростью.

(На Windows 7 левая полоса движется с той же скоростью, что и правая)
Я обнаружил, что если раскомментировать хотя бы одну из закомментированных строк с кодом, то поведение будет одинаковым. Почему поведение программы различается и почему при раскомментировании указанных строк она работает одинаково на разных ОС?


Ответ

Как написано, в ответе @SeNS, причина различия скоростей в том, что прямоугольники отрисовываются после 18 вызовов FillRect. Однако это не ошибка, а, в принципе, документированное поведение.
Данный механизм называется «пакетирование» (batching). Оказывается, если вызывать одну и ту же GDI функцию несколько раз в одном и том же потоке, то эти вызовы накапливаются в пакете (в каждом потоке создаётся свой пакет). И пока либо не вызовется другая GDI функция, либо размер пакета не превысит указанный максимальный размер (который возвращает функция GdiGetBatchLimit), либо не заполнится буфер пакетирования, отрисовки не происходит. В моём случае выполняется третье условие, так как максимальный размер пакета в моей системе равен 20-ти, а прямоугольник появляется после 18-го вызова.
Поэтому решением проблемы может быть, например, вызов функции GdiFlush после FillRect. Из названия понятно, что эта функция производит выполнение функций в пакете и его очистку. Но, на мой взгляд, в моей программе намного лучше вообще отключить пакетирование: GdiSetBatchLimit(1) в начале кода потока, так как всегда рисуется только один прямоугольник и сразу же выводится на экран.
Почему на некоторых Windows 7 пакетирование не работает, несмотря на то, что размер пакета также равен 20-ти, мне всё ещё не ясно. Возможно, дело в более новом менеджере рабочего стола, который как-то более хитро производит отрисовку. Также, не совсем очевидно, какие функции могут попасть в пакет, например, Ellipse срабатывает сразу.

Комментариев нет:

Отправить комментарий