Форум: "Начинающим";
Текущий архив: 2014.06.29;
Скачать: [xml.tar.bz2];
ВнизКак ускорить снятие скриншота, Ассемблерная вставка Найти похожие ветки
← →
Вова (2013-08-14 16:21) [0]Обнаружил я такой вот код.
Питаю надежду, что он будет работать быстрей моего.
http://www.programmersforum.ru/showthread.php?t=35588
asm
push esi
push edx
mov ecx, BufLength // размер массива (Integer)
mov esi, PointerBuf1Scr // указатель на источник (Pointer)
mov edx, PointerBuf2Scr // указатель на приемник (Pointer)
@@mix:
movq xmm0, [esi] // buf1 -> в регистр
movq [edx], xmm0 // buf2 <- из регистра
add esi, 8
add edx, 8
loop @@mix
pop edx
pop esi
emms
end;
Естественно ничего из написаного мне не понятно. Но исходя из контекста у меня возникло ощущение, что это можно использовать для снятия скриншота. Только вот как это должно выглядеть на делфи я не знаю. В общем то в этом и вопрос.
Procedure TMyObject.getScreen(var tbmp: Tbitmap; tmpCoord: TCoord);
var
hDC1: HDC;
R: TRect;
Canvas: TCanvas;
begin
if tmpCoord.x2search = 0 then
tmpCoord.x2search := screen.Width;
if tmpCoord.y2search = 0 then
tmpCoord.y2search := screen.Height;
hDC1 := GetDC(0);
Canvas := TCanvas.Create();
Canvas.Lock;
Canvas.Handle := hDC1;
R := Rect(tmpCoord.x1search, tmpCoord.y1search, tmpCoord.x2search,
tmpCoord.y2search);
tbmp := Tbitmap.Create;
tbmp.Canvas.Lock;
tbmp.Width := R.Right - R.Left;
tbmp.Height := R.Bottom - R.Top;
tbmp.PixelFormat := pf24bit;
tbmp.Canvas.CopyRect(Rect(0,0,tbmp.Width,tbmp.Height), Canvas, R);
ReleaseDC(0, hDC1);
Canvas.Free;
Canvas.Unlock;
end;
как то так делался у меня скриншот, ну примерно в общем не суть, как это объединить?
← →
Вова (2013-08-14 16:26) [1]Procedure Copy(BufLength:Integer; PointerBuf1Scr:Pointer; PointerBuf2Scr : Pointer);
begin
asm
push esi
push edx
mov ecx, BufLength // размер массива (Integer)
mov esi, PointerBuf1Scr // указатель на источник (Pointer)
mov edx, PointerBuf2Scr // указатель на приемник (Pointer)
@@mix:
movq xmm0, [esi] // buf1 -> в регистр
movq [edx], xmm0 // buf2 <- из регистра
add esi, 8
add edx, 8
loop @@mix
pop edx
pop esi
emms
end;
End;
Но что есть указатели?
← →
Вова (2013-08-14 16:37) [2]
h := R.Bottom - R.Top - 1;
w := R.Right - R.Left;
BufLength := (h+1)*w;
PointerBuf2Scr := tbmp.ScanLine[h];
так??
а источник???
← →
Sapersky (2013-08-14 17:03) [3]А источник в видеопамяти. То есть - нет, не поможет.
Может немного помочь использование 32-битного битмапа вместо 24 и (возможно) BitBlt вместо CopyRect, т.к. CopyRect вызывает StretchBlt.
Если скриншот подразумевается не один, то создавать/задавать размеры битмапа заранее. При этом лучше сначала задать PixelFormat, потом Width/Height.
← →
Вова (2013-08-14 17:23) [4]но BitBlt как то же получает его из видеопамяти?
моя практика показывает, что WinApi функции далеки от идеалов быстроты.
← →
Sapersky (2013-08-14 17:40) [5]BitBlt дёргает функции драйвера видеокарты.
Больше ты до них никак не доберёшься, разве что через другой API (DirectX), но по моему опыту в этом нет смысла - копирование картинки работает (если усреднить результаты на разном железе) с одинаковой скоростью на любых API.
← →
Rouse_ © (2013-08-14 22:56) [6]
> movq xmm0, [esi] // buf1 -> в регистр
> movq [edx], xmm0 // buf2 <- из регистра
12 тактов на перемещение 8 байт? Оптимизация "однако"...
← →
Rouse_ © (2013-08-14 22:58) [7]да еще и "loop", мндя...
← →
ProgRAMmer Dimonych © (2013-08-14 23:19) [8]> [7] Rouse_ © (14.08.13 22:58)
Наверное, по размеру оптимизируют :) Хотя без процедуры компактнее было бы.
← →
Rouse_ © (2013-08-14 23:28) [9]
> Наверное, по размеру оптимизируют :) Хотя без процедуры
> компактнее было бы.
Допустим, но тут другое, это делается в разы проще и гораздо быстрее.
Если человек начинает писать на ассемблере, он должен понимать - зачем он это делает, чтоб не получить такой-вот грустный код...
← →
Rouse_ © (2013-08-14 23:43) [10]Врочем - код не рабочий чуть более чем полностью :)
В ХЕ4 плывет, а в семерке CopyMemory в 6 раз быстрее чем "это"...
← →
Smile (2013-08-15 00:03) [11]Куда спешим с ASM?
DC: HDC;
BitBlt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DC, 0, 0, SRCCOPY);
не???
← →
Sapersky (2013-08-15 02:49) [12]Ну да, BitBlt.
Но это же скучно, то ли дело - такты считать :)
procedure CopySSE(Src, Dst : Pointer; Cnt : Integer);
asm
shr ecx, 5
@@mix:
movapd xmm0, [eax]
movapd xmm1, [eax+16]
movapd [edx], xmm0
movapd [edx+16], xmm1
add eax, 32
add edx, 32
dec ecx
jnz @@mix
end;
В 2 раза быстрее Move, но работает только для идеальных случаев - кратный 32 размер и выравнивание на 16.
Неидеальные мне лень обрабатывать, возможно это сделано в FastCode Move Challenge, там были варианты с SSE.
← →
Вова (2013-08-15 09:46) [13]
> BitBlt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height,
> DC, 0, 0, SRCCOPY);
не, это уже есть.
эх....ну вот) а у меня уже пришла идея бил битом делать скрин в отдельном потоке, а потом этой штукой копировать его тыщу раз...а она оказываецо плохая (
> и выравнивание на 16
эт что значит?
← →
Вова (2013-08-15 09:49) [14]
Procedure getScreen(var tbmp: TBitmap; tmpCoord: TCoord);
var
hDC1: HDC;
hSrcDC: HDC;
hBmp: HBITMAP;
R: TRect;
begin
if tmpCoord.x2work = 0 then
tmpCoord.x2work := screen.Width;
if tmpCoord.y2work = 0 then
tmpCoord.y2work := screen.Height;
tbmp.Canvas.Lock;
R := Rect(tmpCoord.x1work, tmpCoord.y1work, tmpCoord.x2work, tmpCoord.y2work);
tbmp.Width := R.Right - R.Left;
tbmp.Height := R.Bottom - R.Top;
hSrcDC := CreateDC("DISPLAY", Nil, nil, nil);
hDC1 := CreateCompatibleDC(hSrcDC);
SelectObject(hDC1, tbmp.Handle);
BitBlt(hDC1, 0, 0, tbmp.Width, tbmp.Height, hSrcDC, tmpCoord.x1work,
tmpCoord.y1work, SRCCOPY);
tbmp.PixelFormat := pf24bit;
DeleteDC(hDC1);
DeleteDC(hSrcDC);
tbmp.Canvas.Unlock;
end;
вот такая актуальная моя процедура. без пиксель формата почему то не работает, потом посмотрю.
← →
брат Птибурдукова (2013-08-15 12:23) [15]
> hSrcDC := CreateDC("DISPLAY", Nil, nil, nil); hDC1 := CreateCompatibleDC(hSrcDC);
а это зачем?
← →
Sapersky (2013-08-15 15:20) [16]
> > и выравнивание на 16
эт что значит?
Адреса Src, Dst должны быть кратны 16. Для битмапов это условие выполняется, если будешь копировать полностью (ну или фрагмент с начала).
MMX-у в [0] тоже требуется выравнивание на 8, иначе никакого ускорения в принципе не получится.
← →
Вова (2013-08-15 16:01) [17]нет...буду копировать квадрат по случайным координатам, ну можно увеличить немного квадрат. 32 битный битмап сделаю. Как его выровнять? потому что я так и не понял, почему это адрес вдруг кратен 16 )
← →
ProgRAMmer Dimonych © (2013-08-15 17:26) [18]> [17] Вова (15.08.13 16:01)
> потому что я так и не понял, почему это адрес вдруг кратен
> 16 )
IMHO, значит, рано ещё про ассемблер думать.
← →
Вова (2013-08-15 17:34) [19]
> IMHO, значит, рано ещё про ассемблер думать.
IMHO мне нужен конкретный результат, а не ассемблер или что либо еще, так что это неправильное ИМХО
← →
Вова (2013-08-15 17:36) [20]Я ведь делфи когда учил я не потому это делал, что мне нужно было стать мастером делфи, а потому что мне нужно было что то сделать и я так же мыкался, прогресс по моему на лицо )
← →
Sapersky (2013-08-15 18:10) [21]В данном случае ради результата как раз не стоит заморачиваться - наверняка копирование картинки (тем более фрагмента картинки) не будет узким местом, всё остальное будет тормозить сильнее. BitBlt с экрана, обработка, сжатие или что там у тебя - на этом фоне ускорение копирования в 2 раза не будет заметно.
> Как его выровнять? потому что я так и не понял, почему это
> адрес вдруг кратен 16 )
Требование такое у SSE-команд, иначе вываливается с ошибкой.
Если битмап 32-битный, значит x-координата должна быть кратна 4.
← →
antonn_tmp (2013-08-15 20:12) [22]Требование к выравниванию у movaps, movups переварит и невыровненные, но скорость будет не выше чем через РОН :)
← →
Rouse_ © (2013-08-15 21:48) [23]
> antonn_tmp (15.08.13 20:12) [22]
> Требование к выравниванию у movaps, movups переварит и невыровненные
Скорее AV c кодом С0000005 будет, впрочем, предлагаю потестировать всем желающим данный код под ХЕ4:program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
windows,
System.SysUtils;
Procedure Copy1(BufLength:Integer; PointerBuf1Scr:Pointer; PointerBuf2Scr : Pointer);
asm
push esi
push edx
mov ecx, BufLength // размер массива (Integer)
mov esi, PointerBuf1Scr // указатель на источник (Pointer)
mov edx, PointerBuf2Scr // указатель на приемник (Pointer)
@@mix:
movq xmm0, [esi] // buf1 -> в регистр
movq [edx], xmm0 // buf2 <- из регистра
add esi, 8
add edx, 8
loop @@mix
pop edx
pop esi
emms
end;
procedure CopySSE(Src, Dst : Pointer; Cnt : Integer);
asm
shr ecx, 5
@@mix:
movapd xmm0, [eax]
movapd xmm1, [eax+16]
movapd [edx], xmm0
movapd [edx+16], xmm1
add eax, 32
add edx, 32
dec ecx
jnz @@mix
end;
var
A: array [0..63] of Byte;
B: array [0..63] of Byte;
S: Cardinal;
I: Integer;
begin
try
S := GetTickCount;
for I := 0 to 1000 do
CopyMemory(@a[0], @b[0], 64);
Writeln(GetTickCount - S);
S := GetTickCount;
for I := 0 to 1000 do
CopySSE(@a[0], @b[0], 64);
Writeln(GetTickCount - S);
S := GetTickCount;
for I := 0 to 1000 do
Copy1(64, @a[0], @b[0]);
Writeln(GetTickCount - S);
except
on E: Exception do
Writeln(E.ClassName, ": ", E.Message);
end;
readln;
end.
CopySSE выдаст:Project Project2.exe raised exception class $C0000005 with message "access violation at 0x00418d57: read of address 0xffffffff".
а Copy1 (переименованная процедура от "Вова (14.08.13 16:26) [1]") выдаст вообще печальную картинку (обратите внимание на изменения итератора I на каждой итерации цикла)
← →
Sapersky (2013-08-15 23:51) [24]
> antonn_tmp (15.08.13 20:12) [22]
Про movups - да, слышал, что медленная, поэтому даже не пробовал. Я уже как-то привык, что любому SIMD нужно выравнивание.
> Rouse_ © (15.08.13 21:48) [23]
Я не утверждал, что это универсальная замена Move на все случаи жизни. Если бы подходило для всех случаев, оно бы уже давно было в RTL.
Type
PByteArr = ^TByteArr;
TByteArr = array [0..MAXINT-1] of Byte;
DByteArr = array of Byte;
function Arr_Aligned(Var Arr : DByteArr; ByteSize : Integer; AShift : Integer = 3): Pointer;
Var i, Offs : NativeInt;
AVal : Integer;
begin
AVal := (1 shl AShift);
SetLength(Arr, ByteSize + AVal);
i := NativeInt(Arr);
While ( ((i shr AShift) shl AShift) <> i ) do
Inc(i, 4);
Result := Pointer(i);
end;
procedure TForm1.Button2Click(Sender: TObject);
Const
Cycles = 100000;
Size = 64 * 1024;
Var n : Integer;
a : DByteArr;
pa : PByteArr;
S: Cardinal;
begin
pa := Arr_Aligned(a, Size*2, 4);
For n:=0 to Size-1 do pa[n] := n;
S := GetTickCount;
For n:=0 to Cycles-1 do
CopySSE(pa, @pa[Size], Size);
// Move(pa[0], pa[Size], Size);
Caption := InttoStr(GetTickCount - S);
end;
(ну или можно включить в FastMM опцию "выравнивать на 16")
А про код из [0] никто не сомневается, что он нерабочий, не мучай трупик.
← →
Вова (2013-08-16 00:59) [25]
> В данном случае ради результата как раз не стоит заморачиваться
> - наверняка копирование картинки (тем более фрагмента картинки)
> не будет узким местом, всё остальное будет тормозить сильнее.
> BitBlt с экрана, обработка, сжатие или что там у тебя -
> на этом фоне ускорение копирования в 2 раза не будет заметно.
>
ну самое забавное как раз в том, что благодаря Sha теперь самое узкое место у меня скриншот ) потому что фильтр делается за 2 милисекунды, что как то теряется на фоне 30 миллисекунд за скриншот ) ну и все что осталось на небольших участках картинки тож в пределах 30 миллисекунд пролетает )
← →
ProgRAMmer Dimonych © (2013-08-16 01:03) [26]> ну самое забавное как раз в том, что благодаря Sha теперь
> самое узкое место у меня скриншот ) потому что фильтр делается
> за 2 милисекунды, что как то теряется на фоне 30 миллисекунд
> за скриншот ) ну и все что осталось на небольших участках
> картинки тож в пределах 30 миллисекунд пролетает )
А конечная цель тогда сколько? И чем сделаны замеры с 2 мс?
← →
Вова (2013-08-16 01:07) [27]
> А конечная цель тогда сколько? И чем сделаны замеры с 2
> мс?
GetTickCount - за 1000 итераций. конечная цель чем быстрее тем лучше ) и у меня пока есть ощущение, что еще есть куда быстрее. ну не фильтр быстрее 2 миллисекунд, а все остальное ) тем более у меня есть еще 1 фильтр он очень тормозной ) есть куда стремиться )
← →
ProgRAMmer Dimonych © (2013-08-16 01:26) [28]> GetTickCount - за 1000 итераций. конечная цель чем быстрее
> тем лучше ) и у меня пока есть ощущение, что еще есть куда
> быстрее. ну не фильтр быстрее 2 миллисекунд, а все остальное
> ) тем более у меня есть еще 1 фильтр он очень тормозной
> ) есть куда стремиться )
GetTickCount() точнее 15 мс не расскажет. И, пардон, если 1000 копирований выполняется за 30 мс, то кто-то растрачивает своё время на экономию на спичках.
1. Скопировать кусок памяти быстрее, чем за O(N) не удастся и оптимизации с ассемблером и инструкциями не помогут сделать O(log N).
2. С низкоуровневыми оптимизациями надо очень осторожно, ибо здесь и сейчас работает шустро, а завтра что-то поменялось (процессор, версия ОС) — и вдруг оказывается, что так только хуже (если не ошибаюсь, те же loop/loopcc на старых процессорах были быстрее, чем циклы с jcc, сейчас наоборот). Плюс сопровождаемость падает: наоптимизировав, можно выжать лишние 15 мс, а через полгода не понять ни строчки. Да ещё и для нового формата данных оптимизация окажется неподходящей.
3. Написанием своих замен для стандартных функций (тех же GDI) лучше не злоупотреблять: Microsoft не гарантирует, что в одной из следующих версий не изменятся внутренние структуры данных. Хотя битмап-то вряд ли, конечно.
← →
Вова (2013-08-16 04:51) [29]нет, 1000 итераций и вычисляется среднее, т.е. 30 за одну. и это не копирование из одного битмапа в другой, а именно скриншот.
← →
Sapersky (2013-08-16 05:06) [30]По поводу скриншотов: я заметил, что скорость получения картинки конкретного окна гораздо выше, чем картинки десктопа. Очевидно, из-за того, что в Win7 картинка окна изначально рисуется (если через GDI) в системной памяти, а десктоп со всеми стеклянными рюшечками уже в видео. Можно это использовать.
Ещё, отключение рюшечек (Aero) ускоряет скриншот с десктопа.
А копирование скриншота можно совместить с фильтром, т.е. сделать у фильтра исходную и результирующую картинку.
← →
Вова (2013-08-16 18:28) [31]
>
> > hSrcDC := CreateDC("DISPLAY", Nil, nil, nil); hDC1 :=
> CreateCompatibleDC(hSrcDC);
> а это зачем?
чтобы работало, есть конструктивные предложния?
← →
Вова (2013-08-16 19:20) [32]
procedure TScreenShot.Execute;
var
T: cardinal;
i:integer;
begin
T := GetTickCount;
for I := 0 to 2000 do
BitBlt(fhDC1, 0, 0, fScreen.Width, fScreen.Height, fhSrcDC, 0,0, SRCCOPY);
T := GetTickCount - T;
fScreen.SaveToFile("C:\test.bmp");
MessageDlg("Процедура заняла " + IntToStr(Round(t/2000)) + " миллисекунд",mtError, [mbOK], 0);
end;
Ощем оттакот, быстрее некуда 46 милисекунд...
← →
Вова (2013-08-16 19:26) [33]тююю, впомнил что битмап 32 нужен, поменял. разницы никакой.
← →
Алканавт расправил плечи (2013-08-16 21:14) [34]
> чтобы работало, есть конструктивные предложния?
А банальное GetDC(0) не работает? Мне до сих пор хватало его.
← →
Styx (2013-08-16 21:44) [35]
> А банальное GetDC(0) не работает?
Да это не важно. Тормозит-то не здесь. А BitBlt с Aero всегда тормозил и тормозить будет. Не знаю, может, через DirectX быстрей будет, но едва ли.
← →
Sapersky (2013-08-17 00:07) [36]Через DX тоже особо не развернёшься. Брать картинку с экрана (а не из своего окна) без Fullscreen можно только в DX7 и более ранних, при этом единственный способ, который не отключает Aero - копирование в системную память наподобие того же BitBlt. И работает с такой же скоростью или медленнее.
Я предлагал в [30] брать картинки с окон, ну или хотя бы с одного активного окна, таким образом бОльшую часть площади экрана можно получить быстрым методом. Хотя если окно рисуется не через GDI, а через какой-нибудь Direct2D, тогда наверное будет тормозить так же как с десктопом.
← →
Вова (2013-08-17 00:27) [37]Почему CopyScreen ошибку выдает, ошибка записи в память?
Procedure TScreenShot.CopyScreen(var tbmpDest:TBitmap; Coord:TCoord);
var
Source,Destination:Pointer;
begin
//WaitForSingleObject(MyMutex, INFINITE);
Source := fScreen.ScanLine[0];
Destination := tbmpDest.ScanLine[0];
Move(Source, Destination, 1000);
//ReleaseMutex(MyMutex);
end;var
fScreen: TBitmap;
tmpCoord: TCoord;
ScreenObj :TScreenShot;
begin
ScreenObj := TScreenShot.Create;
//ScreenObj.FreeOnTerminate := true;
//ScreenObj.Resume;
fScreen := TBitmap.Create;
fScreen.Width := 100;
fScreen.Height := 100;
fScreen.PixelFormat := pf32bit;
tmpCoord.x1work := 0;
tmpCoord.y1work := 0;
tmpCoord.x2work := 99;
tmpCoord.y2work := 99;
ScreenObj.getScreen;
Sleep(400);
WaitForSingleObject(ScreenObj.MyMutex, INFINITE);
ScreenObj.ScreenShot.SaveToFile("C:\Scrn.bmp");
ReleaseMutex(ScreenObj.MyMutex);
ScreenObj.CopyScreen(fScreen, tmpCoord);
CODE>
← →
Вова (2013-08-17 00:30) [38]
> Я предлагал в [30] брать картинки с окон, ну или хотя бы
> с одного активного окна, таким образом бОльшую часть площади
> экрана можно получить быстрым методом. Хотя если окно рисуется
> не через GDI, а через какой-нибудь Direct2D, тогда наверное
> будет тормозить так же как с десктопом.
не брать скриншот окна не вариант, т.е. я так раньше делал, все круто, но мне так нельзя ))
← →
Вова (2013-08-17 00:45) [39]
Source := fScreen.ScanLine[fScreen.Height-1];
Destination := tbmpDest.ScanLine[tbmpDest.Height-1];
Move(Source, Destination, 400);
и так тоже...
← →
Sapersky (2013-08-17 00:48) [40]Move(Source^, Destination^, 1000);
Я предлагал брать окно и потом остальное вокруг этого окна с десктопа.
Страницы: 1 2 3 вся ветка
Форум: "Начинающим";
Текущий архив: 2014.06.29;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.004 c