Recent

Author Topic: FPC4Android branch  (Read 84364 times)

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3538
Re: FPC4Android branch
« Reply #30 on: April 07, 2012, 08:59:17 am »
Ok, this might be slightly off topic, but I'm curious about if there is a more efficient way to read from or write to the canvas than using Canvas.Pixels. Is there something similar to Canvas.Scanline or is it possible to use TRawImage for this? I'm thinking about using my own rasterization engine.

After working in my optimizations for the drawing code I have a more precise answer.

The fastest solution seams to be our scanline equivalent in TLazIntfImage:

TLazIntfImage(TLazCanvas(MyCanvas.Handle).Image).GetDataLineStart(Ycoordinate)

See: http://wiki.lazarus.freepascal.org/Developing_with_Graphics#Image_format_specific_example

But the problem is obvious: Then your code depends on the image format. But yep, it is the fastests way for sure.

meanderix

  • Jr. Member
  • **
  • Posts: 50
Re: FPC4Android branch
« Reply #31 on: April 07, 2012, 10:52:41 am »
Ok, this might be slightly off topic, but I'm curious about if there is a more efficient way to read from or write to the canvas than using Canvas.Pixels. Is there something similar to Canvas.Scanline or is it possible to use TRawImage for this? I'm thinking about using my own rasterization engine.

After working in my optimizations for the drawing code I have a more precise answer.

The fastest solution seams to be our scanline equivalent in TLazIntfImage:

TLazIntfImage(TLazCanvas(MyCanvas.Handle).Image).GetDataLineStart(Ycoordinate)

Thanks Felipe, I'm basically doing this right now in the TLCLBackend class.

This is what I'm using (it's not been checked in, because I don't know how reliable it is in all situations.):
Code: [Select]
procedure TransferBits(Src, Dst: TLazIntfImage; X, Y, XSrc, YSrc,
  SrcWidth, SrcHeight: Integer); overload;
var
  I, H: Integer;
  Depth: Integer;
  X1, Y1, X2, Y2, SrcLine, DstLine, CopyBytes: Integer;
  PSrc, PDst: PByte;
begin
  Depth := Src.DataDescription.Depth shr 3;
  SrcLine := Src.Width * Depth;
  DstLine := Dst.Width * Depth;
  PSrc := Src.PixelData;
  PDst := Dst.PixelData;
 
  if (X >= Dst.Width) or (Y >= Dst.Height) then Exit;
 
  X1 := X;
  Y1 := Y;
  X2 := Min(X + SrcWidth, Dst.Width);
  Y2 := Min(Y + SrcHeight, Dst.Height);
  CopyBytes := (X2 - X1) * Depth;
 
  Inc(PSrc, XSrc * Depth + SrcLine * YSrc);
  Inc(PDst, X * Depth + DstLine * Y);
  for I := 0 to SrcHeight - 1 do
  begin
    System.Move(PSrc^, PDst^, CopyBytes);
    Inc(PSrc, SrcLine);
    Inc(PDst, DstLine);
  end;
end;
 
procedure TransferBits(Src, Dst: TLazIntfImage); overload;
begin
  TransferBits(Src, Dst, 0, 0, 0, 0, Src.Width, Src.Height);
end;
 
function StretchMaskBlt(DestDC: HDC; X, Y, Width, Height: Integer;
  SrcDC: HDC; XSrc, YSrc, SrcWidth, SrcHeight: Integer; Mask: HBITMAP;
  XMask, YMask: Integer; Rop: DWORD): Boolean;
var
  SrcLazDC: TLazCanvas absolute SrcDC;
  DstLazDC: TLazCanvas absolute DestDC;
  SrcRect, DstRect, MaskRect: TRect;
begin
  Result := False;
 
  if (DstLazDC.Image is TLazIntfImage) and (Width = SrcWidth) and (Height = SrcHeight) then
    TransferBits(TLazIntfImage(SrcLazDC.Image), TLazIntfImage(DstLazDC.Image), X, Y, XSrc, YSrc, SrcWidth, SrcHeight)
  else
    DstLazDC.CanvasCopyRect(SrcLazDC, X, Y, XSrc, YSrc, SrcWidth, SrcHeight);
 
  Result := True;
end;
 
procedure TLCLBackend.DoPaint(ABuffer: TBitmap32; AInvalidRects: TRectList;
  ACanvas: TCanvas; APaintBox: TCustomPaintBox32);
var
  Src, Dst: TLazCanvas;
begin
  Dst := TLazCanvas(ACanvas.Handle);
  Src := TLazCanvas(Canvas.Handle);
  if Dst.Image is TLazIntfImage then
    TransferBits(TLazIntfImage(Src.Image), TLazIntfImage(Dst.Image))
  else
    Dst.CopyRect(0, 0, Src, Rect(0,0,Src.Width,Src.Height));
end;

I still think this is not the main bottleneck. There must be something else that is slowing things down. I'm wondering how the whole repaint process works -- if the PaintBox is subsequently painted onto the Form's canvas perhaps.
« Last Edit: April 07, 2012, 11:09:55 am by meanderix »

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3538
Re: FPC4Android branch
« Reply #32 on: April 09, 2012, 01:19:11 pm »
I still think this is not the main bottleneck. There must be something else that is slowing things down. I'm wondering how the whole repaint process works -- if the PaintBox is subsequently painted onto the Form's canvas perhaps.

Yes, each control has it's own bitmap surface, so they need to be painted into the form canvas to form the full image. In the past there was only 1 surface, but I changed to the current design because it allows us to buffer all controls, so the painting is much faster now for common cases like a fullscreen form where only 1 control was invalidate. Before everything had to be redrawn from zero, now we redraw only the invalidated control and merge all buffers.

You can test your theory by drawing in the Form OnPaint event instead of in a PaintBox. The form currently uses the general buffer, so in reality it is never buffered, but on the other hand whatever you draw there goes to the final image directly, without the need of an extra copy.

meanderix

  • Jr. Member
  • **
  • Posts: 50
Re: FPC4Android branch
« Reply #33 on: April 10, 2012, 12:24:35 am »
Wohoo! I'm finally getting quite acceptable framerates (~60 fps.)

I did two things:
  • I removed the local bitmap from LCLSurface.onDraw() and added it as a class field instead. I don't think this made a huge difference for the framerate, but most of the GC_EXTERNAL_ALLOC messages disappeared from the debug log.
  • I commented out DrawFormBackground() in Java_com_pascal_lclproject_LCLActivity_LCLDrawToBitmap(). This dramatically improved the framerate -- it went from 12 fps to 60 fps.
I'm now drawing the TBitmap32 buffer in TForm.OnPaint(), like you suggested and I think it would be really good if there was some option that would disable the background painting in order to not be constrained by the overhead of DrawFormBackground().

Btw, this is basically what I added to LCLSurface.onDraw():
Code: [Select]
  if ((formbuffer == null) || (formbuffer.getWidth() != lWidth) || (formbuffer.getHeight() != lHeight))
    formbuffer = Bitmap.createBitmap(lWidth, lHeight, Bitmap.Config.ARGB_8888);  
 LCLDrawToBitmap(lWidth, lHeight, formbuffer);
 canvas.drawBitmap(formbuffer, 0, 0, null);

Perhaps it's sufficient to set the buffer size in the constructor or in onCreate()?
« Last Edit: April 10, 2012, 12:28:05 am by meanderix »

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3538
Re: FPC4Android branch
« Reply #34 on: April 10, 2012, 08:12:57 am »
  • I removed the local bitmap from LCLSurface.onDraw() and added it as a class field instead. I don't think this made a huge difference for the framerate, but most of the GC_EXTERNAL_ALLOC messages disappeared from the debug log.

This is a great idea! I'll apply your changes on this today.

Quote
  • I commented out DrawFormBackground() in Java_com_pascal_lclproject_LCLActivity_LCLDrawToBitmap(). This dramatically improved the framerate -- it went from 12 fps to 60 fps.

This is very strange that this would make so much difference, the background is a flat color, so it should use my optimized drawing of rectangles which I introduced some days ago and be very fast.

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3538
Re: FPC4Android branch
« Reply #35 on: April 10, 2012, 10:49:54 am »
In rev 36700 I commited your solution to buffer the bitmap in Android with some small changes.

In the same rev I also added a new property which can be used like this:

Code: [Select]
uses customdrawnint;

CDWidgetset.DisableFormBackgroundDrawingProc := MyDisableFormBackgroundDrawingProc;

function TMyForm.MyDisableFormBackgroundDrawingProc(AForm: TCustomForm): Boolean;
begin
  Result := AForm = self; // disable drawing only for self
end;

If you set the result to true, then the form background will be completely skipped. Use with care!

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3538
Re: FPC4Android branch
« Reply #36 on: April 10, 2012, 12:05:08 pm »
In subsequent commits I made a couple of fixes for the DisableFormBackground... so use 36707 or superior

meanderix

  • Jr. Member
  • **
  • Posts: 50
Re: FPC4Android branch
« Reply #37 on: April 10, 2012, 04:27:39 pm »
This is very strange that this would make so much difference, the background is a flat color, so it should use my optimized drawing of rectangles which I introduced some days ago and be very fast.
I was probably using an earlier revision, so I should try to get the latest revision from svn.
« Last Edit: April 10, 2012, 04:32:04 pm by meanderix »

meanderix

  • Jr. Member
  • **
  • Posts: 50
Re: FPC4Android branch
« Reply #38 on: April 10, 2012, 04:31:35 pm »
If you set the result to true, then the form background will be completely skipped. Use with care!
Thanks Felipe. Would it be possible to erase the background by posting an LM_ERASEBKGND message instead?

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3538
Re: FPC4Android branch
« Reply #39 on: April 10, 2012, 04:47:37 pm »
Thanks Felipe. Would it be possible to erase the background by posting an LM_ERASEBKGND message instead?

Not unless we decide that the background can only be a flat color, which is not really true. The LCL does not have enough information to draw the form background unless it is a flat color.

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3538
Re: FPC4Android branch
« Reply #40 on: April 10, 2012, 05:11:23 pm »
Also, it would be nice if you could make a screenshot or photo showing Graphics32 in Android and upload it to the Lazarus wiki to this section:

http://wiki.lazarus.freepascal.org/Custom_Drawn_Interface#Screenshots

meanderix

  • Jr. Member
  • **
  • Posts: 50
Re: FPC4Android branch
« Reply #41 on: April 17, 2012, 12:35:46 pm »
Also, it would be nice if you could make a screenshot or photo showing Graphics32 in Android and upload it to the Lazarus wiki to this section:

http://wiki.lazarus.freepascal.org/Custom_Drawn_Interface#Screenshots

Just uploaded a screenshot of VPR rendering an SVG image on my Android phone. :)

meanderix

  • Jr. Member
  • **
  • Posts: 50
Re: FPC4Android branch
« Reply #42 on: April 22, 2012, 08:15:02 pm »
Just upgraded to ICS (4.0.3) on my phone and now I'm getting the following error when I try to run the app:
Quote
D/dalvikvm(25265): Added shared lib /data/data/com.pascal.lcltest/lib/liblclapp.so 0x414d1498
I/lclapp  (25265): JNI_OnLoad called
I/lclapp  (25265): Reading our Activity Class
I/lclapp  (25265): JNI_OnLoad finished
I/lclapp  (25265): LCLOnCreate called by LCLActivity.onCreate
W/dalvikvm(25265): JNI WARNING: GetLongField for field 'lclxdpi' of expected type J, got I
W/dalvikvm(25265):              in Lcom/pascal/lcltest/LCLActivity;.LCLOnCreate:(Lcom/pascal/lcltest/LCLActivity;)I (GetLongField)
I/dalvikvm(25265): "main" prio=5 tid=1 NATIVE
I/dalvikvm(25265):   | group="main" sCount=0 dsCount=0 obj=0x40c34460 self=0x129d0
I/dalvikvm(25265):   | sysTid=25265 nice=0 sched=0/0 cgrp=default handle=1074975944
I/dalvikvm(25265):   | schedstat=( 349002463 58555830 198 ) utm=28 stm=6 core=0
I/dalvikvm(25265):   at com.pascal.lcltest.LCLActivity.LCLOnCreate(Native Method)
I/dalvikvm(25265):   at com.pascal.lcltest.LCLActivity.onCreate(LCLActivity.java:246)
I/dalvikvm(25265):   at android.app.Activity.performCreate(Activity.java:4465)
I/dalvikvm(25265):   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1052)
I/dalvikvm(25265):   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1932)
I/dalvikvm(25265):   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1993)
I/dalvikvm(25265):   at android.app.ActivityThread.access$600(ActivityThread.java:127)
I/dalvikvm(25265):   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1159)
I/dalvikvm(25265):   at android.os.Handler.dispatchMessage(Handler.java:99)
I/dalvikvm(25265):   at android.os.Looper.loop(Looper.java:137)
I/dalvikvm(25265):   at android.app.ActivityThread.main(ActivityThread.java:4507)
I/dalvikvm(25265):   at java.lang.reflect.Method.invokeNative(Native Method)
I/dalvikvm(25265):   at java.lang.reflect.Method.invoke(Method.java:511)
I/dalvikvm(25265):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
I/dalvikvm(25265):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
I/dalvikvm(25265):   at dalvik.system.NativeStart.main(Native Method)
I/dalvikvm(25265):
E/dalvikvm(25265): VM aborting
F/libc    (25265): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1)

I'll try to do some further debugging.

meanderix

  • Jr. Member
  • **
  • Posts: 50
Re: FPC4Android branch
« Reply #43 on: April 24, 2012, 06:10:05 pm »
I'll try to do some further debugging.

Ok, I fixed it by replacing javaEnvRef^^.GetLongField with javaEnvRef^^.GetIntField for any instances of lclxdpi, lclydpi, screenwidth and screenheight in customdrawnwinapi_android.inc.

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3538
Re: FPC4Android branch
« Reply #44 on: April 24, 2012, 06:18:20 pm »
Great catch! That's the advantage of more eyeballs looking at the code =) I substituted all GetLongField calls to GetIntField calls as all of them one accessing our own fields which are of type int. Please test with revision 37016

 

TinyPortal © 2005-2018