Hi all, as promised, here are the patch files for MS Windows to use Device Independent Bimaps to speed up. This code is still a bit buggy, so you have been warned :-)
As always, patches are against stock 2.4.5. Add the compiler switch -DUSE_DIB to your makefile to activate the DIB patches.
* Changes: - In vdrivers/vd_win32.c: Forgot a call to GetObject() to map the bitmap into the process address space. - in fdrivers/fd_win32.c: Changed the access method from GDI Handles to bitmaps for BitBlt ram2video, video2ram and GetPixel().
Known Bugs: - GrWRITE and GrIMAGE are the same, the transparent color is ignored (TODO...) - There seems to be a small time window when the graphics window is not updated after it becomes visible. I noticed that once in speedtst, but was unable to reproduce that. Weird.
Here are some measurements with speedtst: On my Laptop, without the readpixel patch, graphics window partly obscured, so some of the blts are too fast to be realistic: Driver readp drawp line hline vline block v2v v2r r2v SVGA24 320x 240 0.21 0.06 0.40 0.36 0.33 61.92 7.56 56.74 9.38 SVGA24 640x 480 0.21 0.06 0.43 0.40 0.37 62.01 6.66 35.82 9.67 RAM24 320x 240 6.99 9.52 10.51 134.95 11.18 161.74 99.51 0.00 0.00 RAM24 640x 480 5.92 7.31 6.26 57.76 4.44 58.91 31.75 0.00 0.00
A real test, with readpixel patch: Graphics driver: "win32"
Driver readp drawp line hline vline block v2v v2r r2v SVGA24 640x 480 5.13 0.06 0.44 0.41 0.38 62.01 6.80 36.42 9.78 RAM24 640x 480 5.92 7.72 6.39 59.04 4.52 59.45 32.35 0.00 0.00
Same on my desktop. This one has a "natural" representation as 32-Bit Pixels, so some of the operations are actually slower.
Graphics driver: "win32"
Driver readp drawp line hline vline block v2v v2r r2v SVGA24 640x 480 3.93 0.10 2.02 8.97 2.72 94.39 1.16 75.10 27.64 RAM24 640x 480 4.91 3.97 7.84 122.14 4.97 105.13 71.22 0.00 0.00
On a sidenote: Is there a way to make the listserver ignore Outlook "Out-Of-Office Replies"? It is odd to (un)subscribe everytime I am on a trip, and I find these replies to lists stupid...
Anyway, here are the patches, feedback welcome.
<<grx20.pat.0128>> <<vd_win32.pat.0218>> <<fd_win32.pat.0218>>
Ciao Tom
Thomas Demmer Kraft Foods R&D Munich Phone: +49 89 62738-6302 Fax: +49 89 62738-86302
Thought of the Day: Certainly there are things in life that money can't buy, but it's very funny-- Did you ever try buying them without money? -- Ogden Nash
Demmer, Thomas wrote:
Hi all, as promised, here are the patch files for MS Windows to use Device Independent Bimaps to speed up.
Thanks a lot for your work Thomas.
- About the "get rid of GRXMain" part:
I will integrate it in the next pre-release. I have changed the vdriver a bit, I think is much cleaner. I attach it for review.
- About the DIB aproach:
Is very interesting and really fast!. But now than you have a pointer to a "24 bpp linear frame buffer" (the bitmap), why not use the standard GRX framebuffer with them?
The idea is to clone the fdrivers/lfb24.c in fdrivers/lfb24w32.c (by example) using a new driver name (like GR_frame24W32_LFB by example). I suspect the only things to add are the apropiate InvalidateRects.
Greetings, M.Alvarez
/** ** vd_win32.c ---- the standard Win32-API driver ** ** Author: Gernot Graeff ** E-mail: gernot.graeff@t-online.de ** Date: 13.11.98 ** ** This file is part of the GRX graphics library. ** ** The GRX graphics library is free software; you can redistribute it ** and/or modify it under some conditions; see the "copying.grx" file ** for details. ** ** This library is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** ** Changes by Josu Onandia (jonandia@fagorautomation.es) 21/02/2001 ** - The colors loaded in the ColorList are guaranteed to be actually used ** by Windows (GetNearestColor), for the GR_frameWin32 to work. ** - When the window is created, it gets the maximum size allowed by the ** current mode. Indeed this size is stored (maxWindowWidth/ ** maxWindowHeight). ** When the window is going to be resized (WM_GETMINMAXINFO) it's not ** allowed to grow bigger than this maximum size (it makes nosense). ** - Added some modes for 24bpp colors. ** - When changed to text-mode, the graphics window is hidden. If the ** application has a console (linked with -mconsole) it can use ** printf/scanf and the like. ** When changed again into graphics mode, the window reappears. ** - Inter-task synchronization. In some cases the two threads are ** manipulating at the same time the main window, and the on-memory ** bitmap. I guess this is causing trouble, so in some cases the main ** thread suspends the worker thread, make its operation, and then ** resumes it. ** - The window title is selectable with a define, at compile time. ** If not defined, it defaults to "GRX". ** ** Changes by M.Alvarez (malfer@telefonica.net) 02/01/2002 ** - Go to full screen if the framemode dimensions are equal to ** the screen dimensions (setting the client start area at 0,0). ** ** Changes by M.Alvarez (malfer@telefonica.net) 02/02/2002 ** - The w32 imput queue implemented as a circular queue. ** - All the input related code moved to w32inp.c ** - The main window is created in WinMain, so the grx program ** can use other videodrivers like the memory one. ** ** Changes by M.Alvarez (malfer@telefonica.net) 11/02/2002 ** - Now the GRX window is properly closed, so the previous app ** gets the focus. ** ** Changes by M.Alvarez (malfer@telefonica.net) 31/03/2002 ** - Accepts arbitrary (user defined) resolution. ** ** Changes by Thomas Demmer (TDemmer@krafteurope.com) ** - Instead of begin with WinMain and start a thread with the main ** GRX program, do it backward: begin in main and start a thread to ** handle the Windows window. With this change we get rid of the ** awful GRXMain special entry point. ** ** Changes by M.Alvarez (malfer@telefonica.net) 12/02/2003 ** - Sanitize the Thomas changes. **/
#include "libwin32.h" #include "libgrx.h" #include "grdriver.h" #include "arith.h"
#ifndef GRXWINDOW_TITLE #define GRXWINDOW_TITLE "GRX" #endif
HWND hGRXWnd = NULL; HDC hDCMem = NULL; HBITMAP hBitmapScreen = NULL;
CRITICAL_SECTION _csEventQueue; W32Event *_W32EventQueue = NULL; int _W32EventQueueSize = 0; int _W32EventQueueRead = 0; int _W32EventQueueWrite = 0; int _W32EventQueueLength = 0; SColorList *ColorList = NULL;
static int maxScreenWidth, maxScreenHeight; static int maxWindowWidth, maxWindowHeight;
static HANDLE windowThread = INVALID_HANDLE_VALUE; static HANDLE mainThread = INVALID_HANDLE_VALUE;
static volatile int isWindowThreadRunning = 0; static volatile int isMainWaitingTermination = 0;
static DWORD WINAPI WndThread(void *param); static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
/* NOTE: Adjusts the color to the real RGB used by Windows */ static void loadcolor(int c, int r, int g, int b) { SColorList *clTmp, *clPrev; HDC hdcW; COLORREF color;
hdcW = GetDC(hGRXWnd); color = RGB(r, g, b); color = GetNearestColor(hdcW, color); ReleaseDC(hGRXWnd, hdcW); if (ColorList == NULL) { clTmp = malloc(sizeof(SColorList)); clTmp->nIndex = c; clTmp->color = color; clTmp->pNext = NULL; ColorList = clTmp; return; } else { clTmp = ColorList; do { if (clTmp->nIndex == c) { clTmp->color = color; return; } clPrev = clTmp; clTmp = clTmp->pNext; } while (clTmp); clPrev->pNext = malloc(sizeof(SColorList)); clTmp = clPrev->pNext; clTmp->nIndex = c; clTmp->color = color; clTmp->pNext = NULL; return; } }
static void ColorList_destroy(void) { SColorList *clTmp;
while (ColorList != NULL) { clTmp = ColorList; ColorList = ColorList->pNext; free(clTmp); } }
static int setmode(GrVideoMode * mp, int noclear) { RECT Rect; HDC hDC; HBRUSH hBrush; int inipos;
if (mp->extinfo->mode != GR_frameText) { inipos = 50; if (mp->width == maxScreenWidth && mp->height == maxScreenHeight) inipos = 0; Rect.left = inipos; Rect.top = inipos; Rect.right = mp->width + inipos; Rect.bottom = mp->height + inipos; AdjustWindowRect(&Rect, WS_OVERLAPPEDWINDOW, FALSE); maxWindowWidth = Rect.right - Rect.left; maxWindowHeight = Rect.bottom - Rect.top; SetWindowPos(hGRXWnd, NULL, Rect.left, Rect.top, maxWindowWidth, maxWindowHeight, SWP_DRAWFRAME | SWP_NOZORDER | SWP_SHOWWINDOW);
if (hBitmapScreen) { DeleteObject(hBitmapScreen); hBitmapScreen = NULL; } hDC = GetDC(hGRXWnd); if (hDCMem == NULL) hDCMem = CreateCompatibleDC(hDC); hBitmapScreen = CreateCompatibleBitmap(hDC, mp->width, mp->height); SelectObject(hDCMem, hBitmapScreen); SetRect(&Rect, 0, 0, mp->width, mp->height); hBrush = CreateSolidBrush(RGB(0, 0, 0)); FillRect(hDCMem, &Rect, hBrush); FillRect(hDC, &Rect, hBrush); ReleaseDC(hGRXWnd, hDC); DeleteObject(hBrush); UpdateWindow(hGRXWnd); SetForegroundWindow(hGRXWnd); ColorList_destroy(); } else { /* If changing to text-mode, hide the graphics window. */ if (hGRXWnd != NULL) ShowWindow(hGRXWnd, SW_HIDE); } return (TRUE); }
static void setbank_dummy(int bk) { bk = bk; }
GrVideoModeExt grtextext = { GR_frameText, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ {0, 0, 0}, /* color precisions */ {0, 0, 0}, /* color component bit positions */ 0, /* mode flag bits */ setmode, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ NULL, /* bank set function */ NULL, /* double bank set function */ NULL /* color loader */ };
static GrVideoModeExt grxwinext8 = { GR_frameWIN32_8, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ {8, 8, 8}, /* color precisions */ {0, 8, 16}, /* color component bit positions */ 0, /* mode flag bits */ setmode, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ setbank_dummy, /* bank set function */ NULL, /* double bank set function */ loadcolor /* color loader */ };
static GrVideoModeExt grxwinext24 = { GR_frameWIN32_24, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ {8, 8, 8}, /* color precisions */ {0, 8, 16}, /* color component bit positions */ 0, /* mode flag bits */ setmode, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ setbank_dummy, /* bank set function */ NULL, /* double bank set function */ NULL /* color loader */ };
static GrVideoMode modes[] = { /* pres. bpp wdt hgt BIOS scan priv. &ext */ {TRUE, 8, 80, 25, 0x00, 80, 1, &grtextext},
{TRUE, 8, 320, 240, 0x00, 320, 0, &grxwinext8}, {TRUE, 8, 640, 480, 0x00, 640, 0, &grxwinext8}, {TRUE, 8, 800, 600, 0x00, 800, 0, &grxwinext8}, {TRUE, 8, 1024, 768, 0x00, 1024, 0, &grxwinext8}, {TRUE, 8, 1280, 1024, 0x00, 1280, 0, &grxwinext8}, {TRUE, 8, 1600, 1200, 0x00, 1600, 0, &grxwinext8},
{TRUE, 24, 320, 240, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 640, 480, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 800, 600, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 1024, 768, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 1280, 1024, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 1600, 1200, 0x00, 0, 0, &grxwinext24},
{FALSE, 0, 9999, 9999, 0x00, 0, 0, NULL} };
static int detect(void) { static int inited = 0; WNDCLASSEX wndclass;
if (!inited) { wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = GetModuleHandle(NULL); wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "GRXCLASS"; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (RegisterClassEx(&wndclass)== 0) return FALSE; inited = 1; }
return TRUE; }
static int init(char *options) { int i; DWORD thread_id;
/* WARNING: mainThread can not be used in the windowThread */ mainThread = GetCurrentThread();
InitializeCriticalSection(&_csEventQueue);
/* The modes not compatible width the configuration */ /* of Windows are made 'non-present' */ maxScreenWidth = GetSystemMetrics(SM_CXSCREEN); for (i = 1; i < itemsof(modes); i++) { if (modes[i].width > maxScreenWidth) modes[i].present = FALSE; } maxScreenHeight = GetSystemMetrics(SM_CYSCREEN); for (i = 1; i < itemsof(modes); i++) { if (modes[i].height > maxScreenHeight) modes[i].present = FALSE; }
windowThread = CreateThread(NULL, 0, WndThread, NULL, 0, &thread_id);
/* Wait for thread creating the window. This is a busy */ /* waiting loop (bad), but we Sleep to yield (good) */ while (isWindowThreadRunning == 0) Sleep(1);
return TRUE; }
static void reset(void) { isMainWaitingTermination = 1; PostMessage(hGRXWnd, WM_CLOSE, 0, 0);
while (isWindowThreadRunning == 1) Sleep(1);
isMainWaitingTermination = 0; DeleteCriticalSection(&_csEventQueue); ColorList_destroy();
if (hBitmapScreen != NULL) { DeleteObject(hBitmapScreen); hBitmapScreen = NULL; } if (hDCMem != NULL) { DeleteDC(hDCMem); hDCMem = NULL; } }
static GrVideoMode * _w32_selectmode(GrVideoDriver * drv, int w, int h, int bpp, int txt, unsigned int * ep) { GrVideoMode *mp, *res;
if (txt) { res = _gr_selectmode(drv, w, h, bpp, txt, ep); goto done; } for (mp = &modes[1]; mp < &modes[itemsof(modes)-1]; mp++) { if (mp->present && mp->width == w && mp->height == h) { res = _gr_selectmode(drv, w, h, bpp, txt, ep); goto done; } } /* no predefined mode found. Create a new mode if we can*/ if (w <= maxScreenWidth && h <= maxScreenHeight) { mp->present = TRUE; mp->width = w; mp->height = h; if (bpp <= 8) { mp->bpp = 8; mp->extinfo = &grxwinext8; mp->lineoffset = mp->width; } else { mp->bpp = 24; mp->extinfo = &grxwinext24; mp->lineoffset = 0; } } res = _gr_selectmode(drv, w, h, bpp, txt, ep); done: return res; }
GrVideoDriver _GrVideoDriverWIN32 = { "win32", /* name */ GR_WIN32, /* adapter type */ NULL, /* inherit modes from this driver */ modes, /* mode table */ itemsof(modes), /* # of modes */ detect, /* detection routine */ init, /* initialization routine */ reset, /* reset routine */ _w32_selectmode, /* special mode select routine */ GR_DRIVERF_USER_RESOLUTION /* arbitrary resolution possible */ };
static DWORD WINAPI WndThread(void *param) { MSG msg;
hGRXWnd = CreateWindow("GRXCLASS", GRXWINDOW_TITLE, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL); ShowWindow(hGRXWnd, SW_HIDE);
isWindowThreadRunning = 1;
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
isWindowThreadRunning = 0;
ExitThread(0); return 0; }
static int convertwin32keystate(void) { int fkbState = 0;
if (GetKeyState(VK_SHIFT) < 0) fkbState |= GR_KB_SHIFT; if (GetKeyState(VK_CONTROL) < 0) fkbState |= GR_KB_CTRL; if (GetKeyState(VK_MENU) < 0) fkbState |= GR_KB_ALT; if (GetKeyState(VK_SCROLL) < 0) fkbState |= GR_KB_SCROLLOCK; if (GetKeyState(VK_NUMLOCK) < 0) fkbState |= GR_KB_NUMLOCK; if (GetKeyState(VK_CAPITAL) < 0) fkbState |= GR_KB_CAPSLOCK; if (GetKeyState(VK_INSERT) < 0) fkbState |= GR_KB_INSERT; return fkbState; }
static void EnqueueW32Event(UINT uMsg, WPARAM wParam, LPARAM lParam, int kbstat) { if (_W32EventQueue == NULL) return;
EnterCriticalSection(&_csEventQueue); _W32EventQueue[_W32EventQueueWrite].uMsg = uMsg; _W32EventQueue[_W32EventQueueWrite].wParam = wParam; _W32EventQueue[_W32EventQueueWrite].lParam = lParam; _W32EventQueue[_W32EventQueueWrite].kbstat = kbstat; if (++_W32EventQueueWrite == _W32EventQueueSize) _W32EventQueueWrite = 0; if (++_W32EventQueueLength > _W32EventQueueSize) { _W32EventQueueLength--; if (++_W32EventQueueRead == _W32EventQueueSize) _W32EventQueueRead = 0; } LeaveCriticalSection(&_csEventQueue); }
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static int cursorOn = 1; int kbstat; BOOL fInsert;
switch (uMsg) {
case WM_NCHITTEST: { LRESULT res = DefWindowProc(hWnd, uMsg, wParam, lParam); if (res == HTCLIENT) { if (cursorOn) { ShowCursor(FALSE); cursorOn = 0; } } else { if (!cursorOn) { ShowCursor(TRUE); cursorOn = 1; } } return res; }
case WM_CLOSE: if (!isMainWaitingTermination && MessageBox(hWnd, "This will abort the program\nare you sure?", "Abort", MB_APPLMODAL | MB_ICONQUESTION | MB_YESNO ) != IDYES) return 0; DestroyWindow(hWnd); if (!isMainWaitingTermination) ExitProcess(1); break;
case WM_DESTROY: PostQuitMessage(0); break;
case WM_GETMINMAXINFO: { LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam;
lpmmi->ptMaxSize.x = lpmmi->ptMaxTrackSize.x = maxWindowWidth; lpmmi->ptMaxSize.y = lpmmi->ptMaxTrackSize.y = maxWindowHeight; } return 0;
case WM_CHAR: kbstat = convertwin32keystate(); EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_SYSCHAR: fInsert = FALSE; kbstat = convertwin32keystate(); if (kbstat & GR_KB_ALT) { if (wParam >= 'a' && wParam <= 'z') fInsert = TRUE; if (wParam >= 'A' && wParam <= 'Z') fInsert = TRUE; if (wParam >= '0' && wParam <= '9') fInsert = TRUE; } if (!fInsert) break; EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_KEYDOWN: case WM_SYSKEYDOWN: kbstat = convertwin32keystate(); /* if (kbstat & GR_KB_ALT && wParam == VK_F4) PostMessage(hWnd, WM_CLOSE, 0, 0); */ EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: kbstat = convertwin32keystate(); EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_MOUSEMOVE: kbstat = convertwin32keystate(); EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_PAINT: { RECT UpdateRect; HDC hDC; PAINTSTRUCT ps;
if (GetUpdateRect(hWnd, &UpdateRect, FALSE)) { BeginPaint(hWnd, &ps); hDC = GetDC(hWnd); BitBlt(hDC, UpdateRect.left, UpdateRect.top, UpdateRect.right - UpdateRect.left + 1, UpdateRect.bottom - UpdateRect.top + 1, hDCMem, UpdateRect.left, UpdateRect.top, SRCCOPY); ReleaseDC(hWnd, hDC); EndPaint(hWnd, &ps); } } return 0;
case WM_SYSCOMMAND: case WM_NCCREATE: case WM_NCPAINT: case WM_NCMOUSEMOVE: case WM_PALETTEISCHANGING: case WM_ACTIVATEAPP: case WM_NCCALCSIZE: case WM_ACTIVATE: case WM_NCACTIVATE: case WM_SHOWWINDOW: case WM_WINDOWPOSCHANGING: case WM_GETTEXT: case WM_SETFOCUS: case WM_KILLFOCUS: case WM_GETICON: case WM_ERASEBKGND: case WM_QUERYNEWPALETTE: case WM_WINDOWPOSCHANGED: case WM_GETDLGCODE: case WM_MOVE: case WM_SIZE: case WM_SETCURSOR: case WM_HELP: case WM_KEYUP: case WM_SYSKEYUP: break;
default: /* char szMsg[255]; sprintf(szMsg, "Msg %x, wParam %d, lParam %d", uMsg, wParam, lParam); MessageBox(NULL, szMsg, "Msg", MB_OK); */ break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam); }