Демка на основе спирографа

Тема в разделе "WASM.GUI", создана пользователем Intro, 28 окт 2024.

Метки:
  1. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    Вот код пока не окончательный, нет кнопок сворачивания и закрытия. Закрыть можно панели задач и Alt+F4
    Сделал на основе:

    Там же в архиве версия и для браузера.
     

    Вложения:

    alex_dz нравится это.
  2. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    Демка не только демка, просто как демка проще версия для браузера. В общем, хотелось сделать шаблон кода для разных демок в том числе версии демок для дос. Использую GdipBitmapSetPixel, нарисовать пиксель в буфере, всё ничего, вот только эта функция весьма тяжёлая, от 1 до 3 тысяч тактов выполняется, если нам надо отрисовать каждый пиксель сцены то тут уже никакой динамики не получиться, 1-2 млн * 1..3 тыс = 0.2..1 сек. Варианты есть, если получить доступ непосредственно к массиву пикселей, то рисовать пиксели можно на скорости несколько тактов, ну ладно 10-20 тактов. Всё равно у нас буфер в ОЗУ, а не в видеопамяти.
    Я как понял, доступ к буферу можно получить GdipBitmapLockBits, но пока не понятно дели реализации. Кстати, сам код UpdateScene без GdipBitmapSetPixel выполняет несколько сот тактов, может чуть больше 1000, оптимизировать можно синусы если флагами задать минимальную точность float/real4.
     
  3. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    894
    Используй буфер и создавай битмап через GdipCreateBitmapFromScan0. Также быстрее будет использовать DIB-секцию, либо просто буфер и выводить его через SetDiBitsToDevice.
     
  4. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    Thetrik, а может лучше использовать SetDIBits? Параметров надо вводить меньше. Пока не получается создать локальный буфер, как то сложно сделано. SetBitmapBits не работает как надо.
     
  5. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    В общем заработало, ошибки были в неожиданном месте, например в макросе mrm. Так же надо было настроить структуру BITMAPINFO
    Код (ASM):
    1. mov    scene.bmiBuff, GlobalAlloc(GPTR, sizeof_BITMAPINFO)
    2. ASSUME ecx:ptr BITMAPINFO
    3. mov    ecx, eax
    4. mov    [ecx].bmiHeader.biSize, sizeof BITMAPINFOHEADER
    5. mrm    [ecx].bmiHeader.biWidth, scene.iBuffWidth
    6. mov    eax, scene.iBuffHeight
    7. neg    eax
    8. mov    [ecx].bmiHeader.biHeight, eax
    9. mov    [ecx].bmiHeader.biPlanes, 1
    10. mov    [ecx].bmiHeader.biBitCount, 32
    11. mov    [ecx].bmiHeader.biCompression, BI_RGB
    12. mrm    [ecx].bmiHeader.biSizeImage, scene.iSizeBuff
    13. ASSUME ecx:nothing
    14. ....
    15. UpdateTexture MACRO scene:req
    16.     SetDIBits(scene.hdcBuffForm, scene.bmBuff, 0, scene.iBuffHeight, scene.lpvBuffBits, scene.bmiBuff, DIB_RGB_COLORS)
    17.     EXITM <>
    18. ENDM
    Заполняем наш локальный массив пикселей и копируем его UpdateTexture в буфер, который затем рисуем в окно. На очень дохлом ПК фэпес без рендера анимации 262, а было один, да один ФПС. ПэКа: проц Core 2 Duo E7500 2.93 ГГц, видео интегрированное очень слабое. В общем нашёл в мусорке на работе.
     
    Последнее редактирование: 2 апр 2025
  6. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    894
    SetDIBits и SetBitmapBits обновляют данные в битмапе, но остается еще операция вывода на экран - т.е. получаем 2 действия.
    При использовании GdipCreateBitmapFromScan0 у нас уже есть буфер и мы рисуем в него как обычно и нам нужно только обновлять на экран - 1 операция.
    При использовании DIB-секции, мы также имеем буфер и выводим как обычно - 1 операция.
    При использовании SetDiBitsToDevice у нас также есть буфер который мы сразу выводим на экран - 1 операция.

    SetDIBits и SetBitmapBits - мы сначала копируем буфер в другой буфер (HBITMAP/DIBSECTION) - 1-я операция, а потом еще этот 2-й буфер выводим на экран (BitBlt/StretchBlt) - 2-я операция. Итого 2 операции вместо 1 и лишний буфер.
     
  7. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    Thetrik, ну может так и быстрей. Я же говорю, я отключил генерацию сцены в буфер, т.е. гоняю пустой локальный буфер, экран чёрный, т.к. в локальном буфере нули, чёрный пиксели. И ФПС получаю 262-264, это очень много для такого драндулета(Intel GMA X4500), и на практике не нужно. Хотя вероятно с GdipCreateBitmapFromScan0 будет ещё быстрей, но это уже класс GDIplus, он у меня отделён от простого GDI, потом сделаю и с GDIplus. Главное у меня демка заработала, не эта, а с мандельбротом, и там ФПС всего 5.5, значит заморачиваться с оптимизацией рендера не имеет смысла, надо оптимизировать сам код мандельброта. А то медленновато что-то. А так же надо отрефакторить код, и сам классы, у меня типа ООП.
    --- Сообщение объединено, 2 апр 2025 ---
    Кстати, чего я стал использовать буфер, я так боролся с мерцанием, если что-то рисовать сразу в окно, то наблюдается мерцание. Хотя с локальным буфером может и не будет мерцание, но тогда я не смогу рисовать поверх что-то, типа текста или ещё чего.
     
    Последнее редактирование: 2 апр 2025
  8. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    Занятно, но по факту структура цвета тут обратная.
    Код (ASM):
    1. sColor struct ;(sizeof=4, align=4)
    2.     b                   byte ?      ;синий
    3.     g                   byte ?      ;зелёный
    4.     r                   byte ?      ;красный
    5.     a                   byte ?      ;альфа
    6. sColor ends
    Странно, может это виновато BI_RGB, не ясно. Но именно так работает правильно, раньше не замечал, т.к. не задавал конкретно цвета пикселям.
     
  9. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    Да, всё официально. Почему-то цвета в формате GDI развёрнутые.
    Код (C):
    1. typedef struct tagRGBQUAD {
    2.   BYTE    rgbBlue;
    3.   BYTE    rgbGreen;
    4.   BYTE    rgbRed;
    5.   BYTE    rgbReserved;
    6. } RGBQUAD;
    Хотя же ред первым т.е. нулевым байтом должен быть.
     
  10. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    Thetrik, а каким способом обновить экран? Вот буфер сделал:
    Код (ASM):
    1. GdipCreateBitmapFromScan0(ecx, edx, &[ecx*4], PixelFormat32bppARGB, scene.FrameBuffer.lpvBuffBits, &&scene.bmpBackBuffer)
    2. GdipGetImageGraphicsContext(scene.bmpBackBuffer, &&scene.hBuffGraphics)
    bmpBackBuffer у меня *GpBitmap.
    Пытаюсь рисовать в окно GdipDrawImageI(scene.hFormGraphics, scene.bmpBackBuffer, 0, 0)
    Где hFormGraphics это *GpGraphics. Но вижу чёрный экран, && в макросе использовано чтобы получить &. Надо ещё функция, которая обновить bmpBackBuffer из моего буфера, но я не знаю какую функцию использовать.
     
  11. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    894
    Для чего GdipGetImageGraphicsContext?
    Ну и argb зачем? Ты альфу юзаешь? Мб дело в этом? Без полного кода и кодов ошибок не могу сказать точно.
    Для чего обновлять bmpBackBuffer? Сразу пиши в него. Ну если нужно скопировать то mamcpy.
     
  12. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    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, весь прямоугольник формы буфера.
     
  13. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    894
    Intro, без кода не могу ничего сказать. Я предположил что у тебя альфа нулевая. Вот пример:

    Код (C):
    1. #include <windows.h>
    2. #include <GdiPlus.h>
    3. #include <GdiPlusFlat.h>
    4.  
    5. #pragma comment (lib, "GdiPlus.lib")
    6.  
    7. using namespace Gdiplus;
    8. using namespace Gdiplus::DllExports;
    9.  
    10. static DWORD s_pPixels[10000];
    11.  
    12. int main() {
    13.     GdiplusStartupInput tInput;
    14.     ULONG_PTR hToken;
    15.     HANDLE hBmp;
    16.     HANDLE hGraphics;
    17.  
    18.     GdiplusStartup(&hToken, &tInput, NULL);
    19.  
    20.     // Создаем 100х100 пикселей из статического буфера
    21.     GdipCreateBitmapFromScan0(100, 100, 400, PixelFormat32bppRGB, (BYTE*)s_pPixels, (GpBitmap**)&hBmp);
    22.  
    23.     // Рисуем диагональную линию
    24.     for (int i = 0; i < 100; i++) {
    25.         s_pPixels[i * 100 + i] = i * 193273 + 123932;
    26.     }
    27.  
    28.     // Выводим на экран
    29.  
    30.     GdipCreateFromHWND(CreateWindow(L"#32770", NULL, WS_POPUP | WS_VISIBLE, 200, 200, 200, 200, NULL, NULL, NULL, NULL), (GpGraphics**)&hGraphics);
    31.     GdipDrawImageI((GpGraphics*)hGraphics, (GpBitmap*)hBmp, 50, 50);
    32.     GdipDeleteGraphics((GpGraphics*)hGraphics);
    33.  
    34.     __debugbreak();
    35.  
    36.     return 0;
    37.  
    38. }
    upload_2025-4-26_15-20-29.png
     
    Research нравится это.
  14. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    Кстати, у меня альфа получилось равна 0.:umnik2: Ну такой алгоритм, а это полная прозрачность. Переделал так чтобы альфа = 255, и всё заработало. Так что код у меня работал, просто я забыл, что в GDI альфа должна быть равна нулю, а в GDI+ надо обязательно по дефолту установлено в 255, это полная непрозрачность.
    Можно ещё отключить альфу, PixelFormat32bppRGB в место PixelFormat32bppARGB, тогда и основной код демки не надо будет переделывать, там прозрачность просто не используется.... Хотя попробовал, и что-то не заработало, придётся альфу пока использовать.:wacko:
     
  15. Intro

    Intro Well-Known Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    1.692
    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+.
     
  16. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    894
    Intro, юзай AlphaBlend она аппаратно ускорена.