Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 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
15-1386687468
МАКсим007
2013-12-10 18:57
2014.06.29
авторское право


2-1377689079
DZM
2013-08-28 15:24
2014.06.29
Модальное окно оказывается позади


15-1386952352
Kipan
2013-12-13 20:32
2014.06.29
GDI


15-1386966602
Юрий
2013-12-14 00:30
2014.06.29
С днем рождения ! 14 декабря 2013 суббота


1-1326095552
solomon
2012-01-09 11:52
2014.06.29
Странность при работе с Record





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский