Привет. Вот приспичило мне обрабатывать некими алгоритмами массив точек(пикселей) области экрана. Чтобы была анимация неких генерируемых процессов в реальном(хотелось бы быстро чтобы наглядно) времени. Встал вопрос выбора подходящего инструмента - языка программирования. Подходящего для задачи и подходящего такому программисту, как я(в коде ниже возможна куча ошибок).
Для теста скорости отрисовки конкретным языком(библиотекой) решил просто рисовать массив точек черного или белого цвета. Как белый шум на экране аналогового тв, когда нет сигнала. И посмотреть сколько кадров в секунду вообще возможно рисовать. Ведь я еще даже не буду обрабатывать массив точек по нужному мне алгоритму, просто рандом(белый ИЛИ черный).
Попробовал Free Pascal(Lazarus). Если в лоб, то очень медленно. С разрешением поля 800х600 всего 3.5 к/с(кадров в секунду).
Рисую 60 циклов и считаю за какое время отрисовалось:
begin
myBitmap := TBitmap.Create;
myBitmap.SetSize(800, 600);
M1 := Now;
for Count := 0 to 60 do
begin
for x := 0 to 800 do
for y := 0 to 600 do
begin
if Random > 0.5 then
myBitmap.Canvas.DrawPixel(x, y, TColorToFPColor(clWhite))
else
myBitmap.Canvas.DrawPixel(x, y, TColorToFPColor(clBlack));
end;
PaintBox1.Canvas.Draw(0, 0, myBitmap);
//myBitmap.Free;
end;
M2 := Now;
fps := 60 / (MilliSecondsBetween(M1, M2) / 1000);
labCap := FloatToStr(fps);
Label1.Caption := labCap;
end;
Медленно. Погуглил. Есть у Free Pascal быстрые библиотеки для манипуляции пикселями. Немного замороченные, не осилил. Да и как-то не понравилось в целом.
======================================
Ну, наверное, чистый С с какой-нибудь графической библиотекой покажет скорость? Наткнулся как раз на интересный фреймворк raylib. Код на С:
#include "raylib.h"
int main(void)
{
const int screenWidth = 1600;
const int screenHeight = 900;
InitWindow(screenWidth, screenHeight, "WhiteNoise window");
while (!WindowShouldClose())
{
int x, y, rnd;
BeginDrawing();
for (x = 0; x < screenWidth; x++) {
for (y = 0; y < screenHeight; y++) {
rnd = GetRandomValue(0, 1);
if(rnd==1) DrawPixel(x, y, RAYWHITE);
if(rnd==0) DrawPixel(x, y, BLACK);
}
}
DrawFPS(0, 0);
EndDrawing();
}
CloseWindow();
return 0;
}
С, при большем разрешении поля 1600х900 даёт всего-то 10 к/с. Интересно, что ГСЧ там не очень хороший, изображение(картинка1) как бы смещается вправо-вверх. Но это претензии не к скорости. Язык С дает всего 10 кадров? Наверное не правильную библиотеку выбрал. Или не умею его готовить.
======================================
Посмотрел, что может FreeBasic. Старый Демон плохого не посоветует же? Код:
Dim As Integer screenWidth, screenHeight
screenWidth = 1600
screenHeight = 900
Screenres screenWidth, screenHeight, 32
Dim As Integer x, y, rand
Do
If Inkey = Chr$(27) Then Exit Do
For x = 0 To screenWidth
For y = 0 To screenHeight
rand = Rnd
If rand = 1 Then
Pset (x, y), Rgb(255,255,255)
Else
Pset (x, y), Rgb(0,0,0)
End If
Next y
Next x
Loop
End
Как подсчитывать в FreeBasic к/с не нашел. Но визуально около 10 к/с. И еще синхронизация с монитором не совпадает, плывет. Почитал документацию. Использовал буфер, чтобы синхронизация не плыла. Рисую 600 циклов и просто засекаю отдельным секундомером:
Dim As Integer screenWidth, screenHeight
screenWidth = 1600
screenHeight = 900
ScreenRes screenWidth, screenHeight, 32
Dim As Integer x, y, rand, count
Dim image As Any Ptr = ImageCreate( screenWidth, screenHeight, RGB(0, 0, 0) )
For count = 1 To 600
For x = 0 To screenWidth
For y = 0 To screenHeight
rand = Rnd
If rand = 1 Then
PSet image, (x, y), RGB(255, 255, 255)
Else
PSet image, (x, y), RGB(0, 0, 0)
End If
Next y
Next x
Put (0, 0), image, PSet
Next count
End
FreeBasic: 600 циклов за ~45 секунд. ~13 к/с. FreeBasic медленный.
======================================
Что там далее? Попробовал язык Processing (Java):
void setup() {
size(1600, 900);
frameRate(60);
background(0);
}
void draw() {
if (keyCode == ESC) exit();
int rnd;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
rnd = (int)random(2);
if(rnd==1) set(x, y, color(255,255,255));
else set(x, y, color(0,0,0));
}
}
String txt = String.format("Window 1 %6.2fps", frameRate);
windowTitle(txt);
println(frameRate);
text(frameRate,0,0);
}
Processing: 32 к/с! Это рекорд, Java быстрее всех. А если еще переписать так:
void setup() {
size(1600, 900);
frameRate(60);
background(0);
}
void draw() {
if (keyCode == ESC) exit();
loadPixels();
for (int i = 0; i < pixels.length; i++) {
float rand = random(255);
color c = color(rand);
pixels[i] = c;
}
updatePixels();
println(frameRate);
text(frameRate, 0, 0);
}
То здесь получаем 53 к/с. Выбираю Processing.
Может кто-то перепишет примеры кода правильнее, чтобы быстрее было? Я где-то ошибся? Может кто-то напишет код на другом языке/фреймворке? Алгоритм я думаю понятен. Еще напишу, что хочется писать алгоритм обработки массива(вместо rand), а не длиннющие портянки кода инициализации и работы с OpenGl напрямую.
Не знаю, чем вы тут занимаетесь, но, мне кажется стоит посмотреть в сторону готовых графических библиотек. Зачем рисовать на экране, если можно рисовать в окне в полно экранном режиме в уже приспособленных для этих целей программах
https://www.opennet.ru/docs/RUS/gtk_plus/x2486.html (https://www.opennet.ru/docs/RUS/gtk_plus/x2486.html)
или Simple C++ OpenGL program to draw points on a 2D canvas :
#include<GL/glut.h>
void display() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_POINTS);
glVertex2f(10.0, 10.0);
glVertex2f(150.0, 80.0);
glVertex2f(100.0, 20.0);
glVertex2f(200.0, 100.0);
glEnd();
glFlush();
}
void myinit() {
glClearColor(1.0, 1.0, 1.0, 1.0);
glColor3f(1.0, 0.0, 0.0);
glPointSize(5.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 499.0, 0.0, 499.0);
}
void main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(0, 0);
glutCreateWindow("Points");
glutDisplayFunc(display);
myinit();
glutMainLoop();
}
Хорошо, тогда я отвечу по существу вопроса: на каком яп можно быстро манипулировать пикселями?
Все об этом хорошо знают. Этот язык - ассемблер.
ОС на Assembler
Форум программистов и сисадминов Киберфорум > Форум программистов > Низкоуровневое программирование > Assembler, MASM, TASM > ОС на Assembler
Как нарисовать пиксель?
вопрос:
Здравствуйте, делаю мега-примитивную OC, скажите пожалуйста, как правильно закрасить пиксель каким нибудь цветом, под словом "правильно", я подразумеваю, что как лучше в видеобуффер загружать каким-то образом пиксель или вообще сделать универсальный мини-драйвер, и куда тогда лучше смотреть ?
ответ:
Как-то занимался я регистрами видеокарты. Щас надо поднимать старый исходник. Почитай Роберта Джордейна он рассказывает как это работает. И второе поищи инфу о стандарте ultra vbe 2.0 на картах с этим стандартом можно рисовать в svga без драйверов(почти все современные поддерживают этот стандарт).
ps: Джордейн Р. Справочник программиста персональных компьютеров типа IBM PC, XT и AT
рандом на ассемблере:
random:
push cx
push dx
push di
mov dx, word [seed]
or dx, dx
jnz @f
rdtsc
mov dx, ax
@@:
mov ax, word [seed2]
or ax, ax
jnz @f
in ax, 40h
@@:
mul dx
inc ax
mov word [seed], dx
mov word [seed2], ax
xor dx, dx
sub di, si
inc di
div di
mov ax, dx
add ax, si
pop di
pop dx
pop cx
ret
m.A, вот, поправил, через указатель на видеобуфер. Это работает быстрей, чем оператор Pset. Две видеостраницы. Рисуем в одной, потом копируем в другую. На моём компе 24-25 кадров в секунду. Лучше собирать под x32 ( в x64 тупит, почему-то... надо допетривать, почему).
Freebasic
Const screenWidth = 1600
Const screenHeight = 900
ScreenRes screenWidth, screenHeight, 32,2
Dim As Integer w, h, depth, frames, fps, x, y, rand
Dim As String driver, s
Dim As Double t
t = Timer
Screeninfo w,h,depth,,,,driver
?
? " "& w & " x " & h & " x " & depth
?
? " Using driver " & driver
?
? " Press any key"
Sleep
ScreenSet 1, 0
Dim As Ulong Ptr p = Screenptr, dest
Do
dest = p
Screenlock
For x = 0 To screenWidth-1
For y = 0 To screenHeight-1
rand = Rnd
*dest = Iif (rand=1,&hffffffff,0)
dest += 1
Next y
Next x
Screenunlock
Pcopy 1,0
s = "fps: " & fps
Windowtitle s
If Int(t) <> Int(Timer) Then
t = Timer
fps = frames
frames = 0
End If
frames += 1
Sleep 1,1
Loop until inkey=Chr(27)
End
Наткнулся на интересную библиотеку с++ https://github.com/OneLoneCoder/olcPixelGameEngine
Там прямо даётся хеловорлд почти как надо(изменил на чёрно-белый и размер пикселей уменьшил).
Стабильно 40 к/с. Продолжаю вести наблюдения, держу вас в курсе. ;)
#define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h"
class Example : public olc::PixelGameEngine
{
public:
Example()
{
sAppName = "Example";
}
public:
bool OnUserCreate() override
{
// Called once at the start, so create things here
return true;
}
bool OnUserUpdate(float fElapsedTime) override
{
// called once per frame
for (int x = 0; x < ScreenWidth(); x++)
for (int y = 0; y < ScreenHeight(); y++)
//Draw(x, y, olc::Pixel(rand() % 255, rand() % 255, rand() % 255));
if (rand() % 2) Draw(x, y, olc::Pixel(olc::BLACK));
else Draw(x, y, olc::Pixel(olc::WHITE));
return true;
}
};
int main()
{
Example demo;
if (demo.Construct(1600, 900, 1, 1))
demo.Start();
return 0;
}
Заменил стандартную встроенную функцию получения случайного числа на более быстрый и более случайный алгоритм, который подсмотрел там же где фреймворк взял. Теперь 150-170 к/с
#define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h"
class Example : public olc::PixelGameEngine
{
public:
Example()
{
sAppName = "White Noise";
}
public:
bool OnUserCreate() override
{
// Called once at the start, so create things here
return true;
}
bool OnUserUpdate(float fElapsedTime) override
{
// called once per frame
for (int x = 0; x < ScreenWidth(); x++)
for (int y = 0; y < ScreenHeight(); y++)
{
bool bBlWt = false;
bBlWt = rnd() % 2;
Draw(x, y, bBlWt ? olc::Pixel(olc::BLACK) : olc::Pixel(olc::WHITE));
}
return true;
}
private:
uint32_t nProcGen = 0;
uint32_t rnd() //fast random alg Lehmer
{
nProcGen += 0xe120fc15;
uint64_t tmp;
tmp = (uint64_t)nProcGen * 0x4a39b70d;
uint32_t m1 = (tmp >> 32) ^ tmp;
tmp = (uint64_t)m1 * 0x12fad5c9;
uint32_t m2 = (tmp >> 32) ^ tmp;
return m2;
}
};
int main()
{
Example demo;
if (demo.Construct(1600, 900, 1, 1))
demo.Start();
return 0;
}
Рисую на спрайте, который потом копируется в память видеокарты. 220-250 к/с
#define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h"
class Example : public olc::PixelGameEngine
{
public:
Example()
{
sAppName = "White Noise";
}
public:
bool OnUserCreate() override
{
sprDemo = new olc::Sprite(ScreenWidth(), ScreenHeight());
decDemo = new olc::Decal(sprDemo);
return true;
}
bool OnUserUpdate(float fElapsedTime) override
{
for (int x = 0; x < ScreenWidth(); x++)
for (int y = 0; y < ScreenHeight(); y++)
{
sprDemo->SetPixel(x, y, rnd() % 2 ? olc::Pixel(olc::BLACK) : olc::Pixel(olc::WHITE));
}
decDemo->Update();
DrawDecal({ 0, 0 }, decDemo);
return true;
}
private:
olc::Sprite* sprDemo = nullptr;
olc::Decal* decDemo = nullptr;
uint32_t nProcGen = 0;
uint32_t rnd() //fast random alg Lehmer
{
nProcGen += 0xe120fc15;
uint64_t tmp;
tmp = (uint64_t)nProcGen * 0x4a39b70d;
uint32_t m1 = (tmp >> 32) ^ tmp;
tmp = (uint64_t)m1 * 0x12fad5c9;
uint32_t m2 = (tmp >> 32) ^ tmp;
return m2;
}
};
int main()
{
Example demo;
if (demo.Construct(1600, 900, 1, 1))
demo.Start();
return 0;
}