Вот код пока не окончательный, нет кнопок сворачивания и закрытия. Закрыть можно панели задач и Alt+F4 Сделал на основе: Там же в архиве версия и для браузера.
Демка не только демка, просто как демка проще версия для браузера. В общем, хотелось сделать шаблон кода для разных демок в том числе версии демок для дос. Использую GdipBitmapSetPixel, нарисовать пиксель в буфере, всё ничего, вот только эта функция весьма тяжёлая, от 1 до 3 тысяч тактов выполняется, если нам надо отрисовать каждый пиксель сцены то тут уже никакой динамики не получиться, 1-2 млн * 1..3 тыс = 0.2..1 сек. Варианты есть, если получить доступ непосредственно к массиву пикселей, то рисовать пиксели можно на скорости несколько тактов, ну ладно 10-20 тактов. Всё равно у нас буфер в ОЗУ, а не в видеопамяти. Я как понял, доступ к буферу можно получить GdipBitmapLockBits, но пока не понятно дели реализации. Кстати, сам код UpdateScene без GdipBitmapSetPixel выполняет несколько сот тактов, может чуть больше 1000, оптимизировать можно синусы если флагами задать минимальную точность float/real4.
Используй буфер и создавай битмап через GdipCreateBitmapFromScan0. Также быстрее будет использовать DIB-секцию, либо просто буфер и выводить его через SetDiBitsToDevice.
Thetrik, а может лучше использовать SetDIBits? Параметров надо вводить меньше. Пока не получается создать локальный буфер, как то сложно сделано. SetBitmapBits не работает как надо.
В общем заработало, ошибки были в неожиданном месте, например в макросе mrm. Так же надо было настроить структуру BITMAPINFO Код (ASM): mov scene.bmiBuff, GlobalAlloc(GPTR, sizeof_BITMAPINFO) ASSUME ecx:ptr BITMAPINFO mov ecx, eax mov [ecx].bmiHeader.biSize, sizeof BITMAPINFOHEADER mrm [ecx].bmiHeader.biWidth, scene.iBuffWidth mov eax, scene.iBuffHeight neg eax mov [ecx].bmiHeader.biHeight, eax mov [ecx].bmiHeader.biPlanes, 1 mov [ecx].bmiHeader.biBitCount, 32 mov [ecx].bmiHeader.biCompression, BI_RGB mrm [ecx].bmiHeader.biSizeImage, scene.iSizeBuff ASSUME ecx:nothing .... UpdateTexture MACRO scene:req SetDIBits(scene.hdcBuffForm, scene.bmBuff, 0, scene.iBuffHeight, scene.lpvBuffBits, scene.bmiBuff, DIB_RGB_COLORS) EXITM <> ENDM Заполняем наш локальный массив пикселей и копируем его UpdateTexture в буфер, который затем рисуем в окно. На очень дохлом ПК фэпес без рендера анимации 262, а было один, да один ФПС. ПэКа: проц Core 2 Duo E7500 2.93 ГГц, видео интегрированное очень слабое. В общем нашёл в мусорке на работе.
SetDIBits и SetBitmapBits обновляют данные в битмапе, но остается еще операция вывода на экран - т.е. получаем 2 действия. При использовании GdipCreateBitmapFromScan0 у нас уже есть буфер и мы рисуем в него как обычно и нам нужно только обновлять на экран - 1 операция. При использовании DIB-секции, мы также имеем буфер и выводим как обычно - 1 операция. При использовании SetDiBitsToDevice у нас также есть буфер который мы сразу выводим на экран - 1 операция. SetDIBits и SetBitmapBits - мы сначала копируем буфер в другой буфер (HBITMAP/DIBSECTION) - 1-я операция, а потом еще этот 2-й буфер выводим на экран (BitBlt/StretchBlt) - 2-я операция. Итого 2 операции вместо 1 и лишний буфер.
Thetrik, ну может так и быстрей. Я же говорю, я отключил генерацию сцены в буфер, т.е. гоняю пустой локальный буфер, экран чёрный, т.к. в локальном буфере нули, чёрный пиксели. И ФПС получаю 262-264, это очень много для такого драндулета(Intel GMA X4500), и на практике не нужно. Хотя вероятно с GdipCreateBitmapFromScan0 будет ещё быстрей, но это уже класс GDIplus, он у меня отделён от простого GDI, потом сделаю и с GDIplus. Главное у меня демка заработала, не эта, а с мандельбротом, и там ФПС всего 5.5, значит заморачиваться с оптимизацией рендера не имеет смысла, надо оптимизировать сам код мандельброта. А то медленновато что-то. А так же надо отрефакторить код, и сам классы, у меня типа ООП. --- Сообщение объединено, 2 апр 2025 --- Кстати, чего я стал использовать буфер, я так боролся с мерцанием, если что-то рисовать сразу в окно, то наблюдается мерцание. Хотя с локальным буфером может и не будет мерцание, но тогда я не смогу рисовать поверх что-то, типа текста или ещё чего.
Занятно, но по факту структура цвета тут обратная. Код (ASM): sColor struct ;(sizeof=4, align=4) b byte ? ;синий g byte ? ;зелёный r byte ? ;красный a byte ? ;альфа sColor ends Странно, может это виновато BI_RGB, не ясно. Но именно так работает правильно, раньше не замечал, т.к. не задавал конкретно цвета пикселям.
Да, всё официально. Почему-то цвета в формате GDI развёрнутые. Код (C): typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD; Хотя же ред первым т.е. нулевым байтом должен быть.
Thetrik, а каким способом обновить экран? Вот буфер сделал: Код (ASM): GdipCreateBitmapFromScan0(ecx, edx, &[ecx*4], PixelFormat32bppARGB, scene.FrameBuffer.lpvBuffBits, &&scene.bmpBackBuffer) GdipGetImageGraphicsContext(scene.bmpBackBuffer, &&scene.hBuffGraphics) bmpBackBuffer у меня *GpBitmap. Пытаюсь рисовать в окно GdipDrawImageI(scene.hFormGraphics, scene.bmpBackBuffer, 0, 0) Где hFormGraphics это *GpGraphics. Но вижу чёрный экран, && в макросе использовано чтобы получить &. Надо ещё функция, которая обновить bmpBackBuffer из моего буфера, но я не знаю какую функцию использовать.
Для чего GdipGetImageGraphicsContext? Ну и argb зачем? Ты альфу юзаешь? Мб дело в этом? Без полного кода и кодов ошибок не могу сказать точно. Для чего обновлять bmpBackBuffer? Сразу пиши в него. Ну если нужно скопировать то mamcpy.
Thetrik, у меня просто не обновляется bmpBackBuffer dword ? ;//*GpBitmap Ранее обновлял этой функцией SetDIBits(scene.hdcBuffForm, scene.bmBuff, 0, texture.iHeight, texture.lpvBuffBits, texture.bmiBuffInfo, DIB_RGB_COLORS) для GDI А теперь не знаю как заставить чтобы система скопировала из lpvBuffBits в bmpBackBuffer, само этого просто не происходит, все функции возвращают ноль т.е. Ок, но экран чёрный. В общем, надо аналог SetDIBits для GDI+ Да, у меня всё равно есть буфер: hFormGraphics dword ? ;GpGraphics* главное окно hBuffGraphics dword ? ;GpGraphics* буфер bmpBackBuffer dword ? ;GpBitmap* буфер .... FrameBuffer CLocalTexture <> ;фреймбуфер для главного окна(клиентской области) Это надо для фикса мерцание, поэтому в окно я ничего не рисую, сначала в буфер, потом в окно: GdipDrawImageI(scene.hFormGraphics, scene.bmpBackBuffer, 0, 0) Тогда мерцания нет, с GDI всё работает, а с GDI+ нет, надо аналог SetDIBits. В FrameBuffer хранится мой массив с пикселями, но этот буфер можно отключить, если попиксельный доступ не требуется. --- Сообщение объединено, 26 апр 2025 --- Возможно надо обновлять GdipSetClipRect, весь прямоугольник формы буфера.
Intro, без кода не могу ничего сказать. Я предположил что у тебя альфа нулевая. Вот пример: Код (C): #include <windows.h> #include <GdiPlus.h> #include <GdiPlusFlat.h> #pragma comment (lib, "GdiPlus.lib") using namespace Gdiplus; using namespace Gdiplus::DllExports; static DWORD s_pPixels[10000]; int main() { GdiplusStartupInput tInput; ULONG_PTR hToken; HANDLE hBmp; HANDLE hGraphics; GdiplusStartup(&hToken, &tInput, NULL); // Создаем 100х100 пикселей из статического буфера GdipCreateBitmapFromScan0(100, 100, 400, PixelFormat32bppRGB, (BYTE*)s_pPixels, (GpBitmap**)&hBmp); // Рисуем диагональную линию for (int i = 0; i < 100; i++) { s_pPixels[i * 100 + i] = i * 193273 + 123932; } // Выводим на экран GdipCreateFromHWND(CreateWindow(L"#32770", NULL, WS_POPUP | WS_VISIBLE, 200, 200, 200, 200, NULL, NULL, NULL, NULL), (GpGraphics**)&hGraphics); GdipDrawImageI((GpGraphics*)hGraphics, (GpBitmap*)hBmp, 50, 50); GdipDeleteGraphics((GpGraphics*)hGraphics); __debugbreak(); return 0; }
Кстати, у меня альфа получилось равна 0. Ну такой алгоритм, а это полная прозрачность. Переделал так чтобы альфа = 255, и всё заработало. Так что код у меня работал, просто я забыл, что в GDI альфа должна быть равна нулю, а в GDI+ надо обязательно по дефолту установлено в 255, это полная непрозрачность. Можно ещё отключить альфу, PixelFormat32bppRGB в место PixelFormat32bppARGB, тогда и основной код демки не надо будет переделывать, там прозрачность просто не используется.... Хотя попробовал, и что-то не заработало, придётся альфу пока использовать.
Thetrik, в общем доделал как надо функцию G2D@DrawText, это аналог DrawText в Raylib, чтобы выводить ФПС. И этот GDI+ показывает всего около 75 ФПС, просто GDI показывает 440, raylib показывает около 460. При этом алгоритм совершенно одинаковый. Так что функция GdipDrawImageI работает сильно медленней, SetDIBits и BitBlt в сумме, но проблема в том, что GDI не поддерживает прозрачность, алиасинг и другие фичи. --- Сообщение объединено, 5 май 2025 --- Сейчас отключил fillFramebuffer, а вывод ФПС сделал в консоль. Показывает 90 ФПС против 1000, причём тут PeekMessage притормаживает, это значит что SetDIBits и BitBlt быстрей GdipDrawImageI больше чем в 10 раз, может даже раз в 15. Таки дела, может надо придумать комбинацию GDI и GDI+.