Recent

Author Topic: Fastest way to stretch and render to canvas a BGRA buffer  (Read 5523 times)

Shebuka

  • Sr. Member
  • ****
  • Posts: 427
Fastest way to stretch and render to canvas a BGRA buffer
« on: October 20, 2014, 05:10:52 pm »
Hi,

I have a BGRA buffer from network thread, which is moved, inside a critical section, to a preallocated memory. Then the Form global pointer I set some vars and call .Repaint.

Inside .OnPain event of the Form (TFrm_Preview) after reading Tutorial 4 i do this:

Code: [Select]
procedure TFrm_Preview.FormPaint(Sender: TObject);
var
  x, y: integer;
  p: PBGRAPixel;
  buffer: PChar;
  bufferAllign: Integer;
  formSizeBitmap: TBGRABitmap;
begin
  if not (csDestroying in ComponentState) then
    if (FWidth > 0) and (FHeight > 0) and (not Img_Overlay.Visible) and (Self.Visible) then
    begin
      EnterCriticalSection(FCritSection);  //ACQUIRE

      if (FVideoBufferBitmap = nil) then
        FVideoBufferBitmap := TBGRABitmap.Create(FWidth, FHeight);
      buffer := FVideoBuffer;
      bufferAllign := FStride - FWidth*4;

      if (bufferAllign = 0) then
      begin
        BGRABitmapDraw(Canvas, Rect(0, 0, Self.Width, Self.Height - Self.StBr_Name.Height), buffer, False, FWidth, FHeight, False);
      end
      else
      begin
        for y := 0 to FVideoBufferBitmap.Height-1 do
        begin
          p := FVideoBufferBitmap.Scanline[y];
          for x := 0 to FVideoBufferBitmap.Width-1 do
          begin
            p^.blue := Byte((buffer)^);
            p^.green := Byte((buffer+1)^);
            p^.red := Byte((buffer+2)^);
            p^.alpha := Byte((buffer+3)^); //which is 255 allways;
            inc(p);
            buffer += 4;
          end;
          buffer += bufferAllign;
        end;
      end;
      LeaveCriticalSection(FCritSection);   //RELEASE

      if (bufferAllign <> 0) then
      begin
        FVideoBufferBitmap.InvalidateBitmap; // changed by direct access
        if (Self.Tag = TAG_PREVIEW_FRM) then
          FVideoBufferBitmap.HorizontalFlip;

        formSizeBitmap := FVideoBufferBitmap.Resample(Self.Width, Self.Height - Self.StBr_Name.Height, rmSimpleStretch) as TBGRABitmap;
        //formSizeBitmap := FVideoBufferBitmap.Resample(Self.Width, Self.Height - Self.StBr_Name.Height, rmFineResample) as TBGRABitmap;

        formSizeBitmap.Draw(Canvas, 0, 0, True);
        formSizeBitmap.free;
        //FVideoBufferBitmap.Draw(Canvas, Rect(0, 0, Self.Width, Self.Height - Self.StBr_Name.Height), True);    //is this better/faster that do .Resample(..., rmSimpleStretch)?
      end;
    end;
end;

BGRABitmapDraw call was added after reading this topic, but the bad of this method is that by bypassing copy of buffer to TBGRABitmap I can't do the .HorizontalFlip nor do .Resample(..., rmFineResample) and worser than that it is done inside critical section and slow down the network thread.

TL:DR:
How can I optimize it to get best fps?

What i'm using now: Lazarus SVN 1.3 (rev 46100), Ubuntu 12.04, Qt 4.6, BGRABitmap 7.2

Is there any tutorial on how do this in OpenGL in Qt? On Linux OpenGL demo is working only with GTK2, in Qt there is no implementation for OpenGL context.

lainz

  • Hero Member
  • *****
  • Posts: 4460
    • https://lainz.github.io/
Re: Fastest way to stretch and render to canvas a BGRA buffer
« Reply #1 on: October 20, 2014, 06:51:02 pm »
You can use

Code: [Select]
Bitmap.StretchPutImage
Instead of Resample.

and comment this line

Code: [Select]
//p^.alpha := Byte((buffer+3)^); //which is 255 allways;

circular

  • Hero Member
  • *****
  • Posts: 4195
    • Personal webpage
Re: Fastest way to stretch and render to canvas a BGRA buffer
« Reply #2 on: October 21, 2014, 12:27:05 am »
You don't need to copy the data in order to do some operations. If the data has no additional offset between scanlines, You can use TBGRAPtrBitmap from BGRADefaultBitmap unit.
Code: [Select]
ptrBmp := TBGRAPtrBitmap.Create(FWidth,FHeight,buffer);
ptrBmp.HorizontalFlip;
ptrBmp.Free;

You can also avoid to create the TBGRAPtrBitmap object each time by simply calling SetDataPtr with the new buffer.

When copying the data, in the case of an offset between scanlines, you don't need to specify each pixel or each channel on its own, especially if it is already in BGRA format. You can instead call Move and copy the whole line.
Conscience is the debugger of the mind

circular

  • Hero Member
  • *****
  • Posts: 4195
    • Personal webpage
Re: Fastest way to stretch and render to canvas a BGRA buffer
« Reply #3 on: October 21, 2014, 12:41:56 am »
@007: StretchPutImage is faster, yes, but it is only possible if the destination is another TBGRABitmap. However, it would still be a good idea to use it in order to avoid reallocating. This would be done by keeping formSizeBitmap, and replacing its content with StretchPutImage.
Conscience is the debugger of the mind

lainz

  • Hero Member
  • *****
  • Posts: 4460
    • https://lainz.github.io/
Re: Fastest way to stretch and render to canvas a BGRA buffer
« Reply #4 on: October 21, 2014, 11:13:04 am »
@circular

ok. Thanks for the info.

Shebuka

  • Sr. Member
  • ****
  • Posts: 427
Re: Fastest way to stretch and render to canvas a BGRA buffer
« Reply #5 on: November 11, 2014, 04:30:56 pm »
Here me again, thanks to your suggestions i've updated the function like this:

Code: [Select]
procedure TFrm_Preview.FormPaint(Sender: TObject);
var
  x, y: integer;
  p: PBGRAPixel;
  buffer: PChar;
  bufferAllign: Integer;
  frame: TRect;
  formSizeBitmap: TBGRABitmap;
begin
  if not (csDestroying in ComponentState) then
    if (Self.Visible) and (not Img_Overlay.Visible) and (FWidth > 0) and (FHeight > 0) then
    begin
      EnterCriticalSection(FCritSection);  //ACQUIRE

      if (FVideoBufferBitmap = nil) then
        FVideoBufferBitmap := TBGRABitmap.Create(FWidth, FHeight)
      else if (FVideoBufferBitmap.Width <> FWidth) or (FVideoBufferBitmap.Height <> FHeight) then
      begin
        FVideoBufferBitmap.Free;
        FVideoBufferBitmap := TBGRABitmap.Create(FWidth, FHeight)
      end;

      buffer := FVideoBuffer;
      bufferAllign := FStride - FWidth*4;
      frame := Rect(0, 0, Self.Width, Self.Height - Self.StBr_Name.Height);

      if (bufferAllign = 0) then
      begin
        if (FVideoBufferPtrBitmap = nil) then
          FVideoBufferPtrBitmap := TBGRAPtrBitmap.Create(FWidth, FHeight, buffer)
        else if (FVideoBufferPtrBitmap.Width <> FWidth) or (FVideoBufferPtrBitmap.Height <> FHeight) then
        begin
          FVideoBufferPtrBitmap.Free;
          FVideoBufferPtrBitmap := TBGRAPtrBitmap.Create(FWidth, FHeight, buffer)
        end
        else
          FVideoBufferPtrBitmap.SetDataPtr(buffer);

        FVideoBufferBitmap.PutImage(0, 0, FVideoBufferPtrBitmap, dmSet, 255);
      end
      else
      begin
        for y := 0 to FVideoBufferBitmap.Height-1 do
        begin
          p := FVideoBufferBitmap.Scanline[y];

          Move(buffer, p, FWidth*4);

          buffer += FWidth*4 + bufferAllign;
        end;
        FVideoBufferBitmap.InvalidateBitmap; // changed by direct access
      end;
      LeaveCriticalSection(FCritSection);   //RELEASE

      if (Self.Tag = TAG_PREVIEW_FRM) then
        FVideoBufferBitmap.HorizontalFlip;

      FVideoBufferBitmap.Draw(Canvas, frame, True);
    end;
end;

Do you see any other improvements I can make?

Thankyou,
Shebuka.

lagprogramming

  • Sr. Member
  • ****
  • Posts: 405
Re: Fastest way to stretch and render to canvas a BGRA buffer
« Reply #6 on: November 11, 2014, 06:38:58 pm »
   Don't expect much. You may want to ignore first two steps.
1)Removed the "x"variable because it appears useless.
2)Removed p: PBGRAPixel; variable because it's used only once  and replaced
  "p := FVideoBufferBitmap.Scanline[y];
   Move(buffer, p, FWidth*4);"
   with
   "Move(buffer, FVideoBufferBitmap.Scanline[y], FWidth*4);"
3) Replace "frame := Rect(0, 0, Self.Width, Self.Height - Self.StBr_Name.Height);" with
   frame.Left:=0;frame.Top:=0;frame.Right:=Self.Width;frame.Bottom:=Self.Height - Self.StBr_Name.Height;
4) Add variable "FWidthx4:integer;",
   add line "FWidthx4:=FWidth*4;" before "bufferAllign := FStride - FWidth*4;" and replace all following "FWidth*4" with FWidthx4(including the loop);

Code: [Select]
procedure TFrm_Preview.FormPaint(Sender: TObject);
var
  y: integer;
  FWidthx4:integer;
  buffer: PChar;
  bufferAllign: Integer;
  frame: TRect;
  formSizeBitmap: TBGRABitmap;
begin
  if not (csDestroying in ComponentState) then
    if (Self.Visible) and (not Img_Overlay.Visible) and (FWidth > 0) and (FHeight > 0) then
    begin
      EnterCriticalSection(FCritSection);  //ACQUIRE

      if (FVideoBufferBitmap = nil) then
        FVideoBufferBitmap := TBGRABitmap.Create(FWidth, FHeight)
      else if (FVideoBufferBitmap.Width <> FWidth) or (FVideoBufferBitmap.Height <> FHeight) then
      begin
        FVideoBufferBitmap.Free;
        FVideoBufferBitmap := TBGRABitmap.Create(FWidth, FHeight)
      end;

      buffer := FVideoBuffer;
      FWidthx4:=FWidth*4;
      bufferAllign := FStride - FWidthx4;
      //frame := Rect(0, 0, Self.Width, Self.Height - Self.StBr_Name.Height);
      frame.Left:=0;frame.Top:=0;frame.Right:=Self.Width;frame.Bottom:=Self.Height - Self.StBr_Name.Height;

      if (bufferAllign = 0) then
      begin
        if (FVideoBufferPtrBitmap = nil) then
          FVideoBufferPtrBitmap := TBGRAPtrBitmap.Create(FWidth, FHeight, buffer)
        else if (FVideoBufferPtrBitmap.Width <> FWidth) or (FVideoBufferPtrBitmap.Height <> FHeight) then
        begin
          FVideoBufferPtrBitmap.Free;
          FVideoBufferPtrBitmap := TBGRAPtrBitmap.Create(FWidth, FHeight, buffer)
        end
        else
          FVideoBufferPtrBitmap.SetDataPtr(buffer);

        FVideoBufferBitmap.PutImage(0, 0, FVideoBufferPtrBitmap, dmSet, 255);
      end
      else
      begin
        for y := 0 to FVideoBufferBitmap.Height-1 do
        begin
          //p := FVideoBufferBitmap.Scanline[y];
          //Move(buffer, p, FWidth*4);
          Move(buffer, FVideoBufferBitmap.Scanline[y], FWidthx4);

          buffer += FWidthx4 + bufferAllign;
        end;
        FVideoBufferBitmap.InvalidateBitmap; // changed by direct access
      end;
      LeaveCriticalSection(FCritSection);   //RELEASE

      if (Self.Tag = TAG_PREVIEW_FRM) then
        FVideoBufferBitmap.HorizontalFlip;

      FVideoBufferBitmap.Draw(Canvas, frame, True);
    end;
end;

 

TinyPortal © 2005-2018