Category Archives: Coding

When writing Cross-Library Code, Helpers can be your friend

Since XE2, I have been writing cross-platform (Windows, OSX, iOS, and Android) and “cross-library” (VCL and FMX) Delphi components.  My goal has always been to write as little unique platform or library code as possible (to reduce bugs and maintenance).  Sadly, Embarcadero (or Idera or whoever owns Delphi this week) did not put much emphasis on making code portable between VCL and FMX at first, though this has been improving with each version of Delphi since XE2.

For example, TVector, TMatrix and TPointF were not originally in the common run-time library (RTL) but in FMX only, which made no sense for code that had no library dependencies.  But though Embarcadero has been improving the RTL greatly, what about visual code?  If you use the end-point controls (TListBox, TEdit, etc), it is possible to mostly write code that uses them that would work for both VCL and FMX (e.g., ListBox1.Items.Add(‘Hello’)).  However, if you have to dive into using the Canvas, there has not been much, if any, progress made there.

For example, to write TPaintBox.OnPaint code for VCL, you might write code like this:

procedure TForm1.PaintBox1Paint(Sender: TObject);
var
 aCanvas: TCanvas;
begin
 aCanvas := (Sender as TPaintBox).Canvas;
 aCanvas.Font.Size := 20;
 aCanvas.TextOut(200,200,'Hello World!');
 aCanvas.Brush.Color := clRed;
 aCanvas.Pen.Width := 1;
 aCanvas.Rectangle(Rect(10,10,100,100));
 aCanvas.Brush.Color := clGreen;
 aCanvas.Pen.Width := 1;
 aCanvas.Ellipse(Rect(250,250,400,500));
 aCanvas.DrawFocusRect(Rect(250,250,400,500));
end;

Transferring this code to an FMX TPaintBox.OnPaint, we get a whole slew of errors:

[dcc32 Error] Unit2.pas(34): E2003 Undeclared identifier: 'TextOut'
[dcc32 Error] Unit2.pas(35): E2003 Undeclared identifier: 'Brush'
[dcc32 Error] Unit2.pas(35): E2003 Undeclared identifier: 'clRed'
[dcc32 Error] Unit2.pas(36): E2003 Undeclared identifier: 'Pen'
[dcc32 Error] Unit2.pas(37): E2003 Undeclared identifier: 'Rectangle'
[dcc32 Error] Unit2.pas(38): E2003 Undeclared identifier: 'Brush'
[dcc32 Error] Unit2.pas(38): E2003 Undeclared identifier: 'clGreen'
[dcc32 Error] Unit2.pas(39): E2003 Undeclared identifier: 'Pen'
[dcc32 Error] Unit2.pas(40): E2003 Undeclared identifier: 'Ellipse'
[dcc32 Error] Unit2.pas(41): E2003 Undeclared identifier: 'DrawFocusRect'

Unfortunately, the FMX TCanvas class does not have all the methods or properties that we have been used to with the VCL TCanvas.  At this point, what we usually do is start the laborious process of converting the code to FMX equivalents, e.g., Rectangle => FillRect, Brush => Fill, etc.  However, with class helpers we can make this process much less painful (though not always completely painless)

What are Class (Record) Helpers?

So what are class (record) helpers?  As the documentation states,

A class or a record helper is a type that - when associated with another class or a record - introduces additional method names and properties that may be used in the context of the associated type (or its descendants). Helpers are a way to extend a class without using inheritance, which is also useful for records that do not allow inheritance at all.

What this means is that it is a way to attach new methods and properties (but not new fields) to a class or record that can be used by calling code as if the new methods and properties were defined when the class was built.  These new methods and properties apply to the original class as well as any descendant classes as long as the class helper is in scope.

For example, to extend the VCL TBitmap class to have a Clear method, similar to the FMX TBitmap.Clear method, you can declare a class helper like this:

TBitmapHelper = class helper for TBitmap
public
  procedure Clear<span class="br0">(</span><span class="kw1">const</span> AColor<span class="sy1">:</span> TColor<span class="br0">)</span><span class="sy1">;</span> <span class="kw1">overload</span><span class="sy1">;
</span>  procedure Clear<span class="br0">(</span><span class="kw1">const</span> AColor<span class="sy1">:</span> TAlphaColor<span class="br0">)</span><span class="sy1">;</span> <span class="kw1">overload</span><span class="sy1">;
end;

[...]
procedure TBitmapHelper.Clear(const AColor: TColor);
begin
  Brush.Color := AColor;
  Brush.Style := bsSolid;
  Rectangle(Rect(0, 0, Width, Height));
end;

</span><span class="sy1">procedure TBitmapHelper.Clear(const AColor: TAlphaColor);
begin
  Brush.Color := RGB(TAlphaColorRec(AColor).R, TAlphaColorRec(AColor).G, TAlphaColorRec(AColor).B);
  Brush.Style := bsSolid;
  Rectangle(Rect(0, 0, Width, Height));
end;</span>

By adding this class helper for VCL TBitmap, you can now write:

Bitmap.Clear(clBlack);

Careful readers will notice that I added a procedure Clear( const AColor: TAlphaColor ) method.  This method gets to the purpose of this post.  By specifying a method with the exact same signature as the FMX TBitmap.Clear, your code that clears bitmaps can be written exactly the same with no changes whatsoever.  It can be a literal copy-and-paste.

Class (Record) Helpers for cross-library compatibility

Class (Record) Helpers are not intended to extend classes where you “own” and can modify the class code.  It is better in those cases to directly add the new methods and properties to the class itself.  However, when you do not own the code, class (record) helpers can be your friend and help you write portable code between VCL and FMX.  They can provide an elegant way to help write and maintain cross-library code, by either

  • Simplifying your code base that use common classes by adding new methods and properties to one library to achieve parity with the other library
  • Or, Attaching the same functionality to both libraries using the same code base

I’ll briefly discuss the second point to get it out of the way as it is not the focus of this blog post.  Class (Record) Helpers can help you add the same functionality to both libraries from one code base.  For example, say you wanted to give your developers a way to add a watermark to any bitmap for both VCL and FMX.  You can use a class helper to define an AddWatermark method to the TBitmap, for both VCL and FMX.  By carefully writing the method implementation, the same code could be used for both libraries (and then use #INCLUDE to scope the code for each library as discussed briefly in the digression part of this blog post and this Stack Overflow question)

Now onto the first point.  By using class helpers you can simplify your code base significantly by ensuring method signature and functional parity for the most part between the two libraries. For example, in our TPaintBox example above, the code can be significantly simplified by creating TCanvas helpers that add VCL TCanvas drawing functions to the FMX TCanvas and vice-versa.  By adding functions like DrawFocusRect, you can make the FMX TCanvas have methods exactly like the VCL TCanvas.  In this file, I have made class helpers for TBrushStroke and TCanvas.  Adding this file to the uses clause for my FMX PaintBox application, the errors have been significantly reduced.

[dcc32 Error] Unit2.pas(35): E2003 Undeclared identifier: 'clRed'
[dcc32 Error] Unit2.pas(38): E2003 Undeclared identifier: 'clGreen'

The way I fix these types of errors is that I define a clxRed and clxGreen that map to clRed/clGreen in VCL and TAlphaColorRec.Red/Green in FMX.  By changing the VCL/FMX code to use these constants, the code now looks like this and compiles in both libraries without changes:

procedure TForm1.PaintBox1Paint(Sender: TObject);
var
 aCanvas: TCanvas;
begin
 aCanvas := (Sender as TPaintBox).Canvas;
 aCanvas.Font.Size := 20;
 aCanvas.TextOut(200,200,'Hello World!');
 <strong>aCanvas.Brush.Color := clxRed;</strong>
 aCanvas.Pen.Width := 1;
 aCanvas.Rectangle(Rect(10,10,100,100));
 <strong>aCanvas.Brush.Color := clxGreen;</strong>
 aCanvas.Pen.Width := 1;
 aCanvas.Ellipse(Rect(250,250,400,500));
 aCanvas.DrawFocusRect(Rect(250,250,400,500));
end;

Limitations of Class (Record) Helpers

Note that the example paint code above was carefully contrived to avoid the limitations of class (record) helpers and the differences between the two library architectures 🙂  The biggest problems are

  • Class Helpers have no fields you can add to the original class, so you cannot imitate some functionality.  For example, the VCL TCanvas has the MoveTo and LineTo methods.  The LineTo method draws a line from the previous MoveTo or LineTo call to a new point.  Since we cannot store the last point, we cannot imitate these methods.  However, at least in this case, we can use class helpers for both libraries to introduce methods to provide the equivalent functionality.  For example, by creating a DrawLine method that takes the start and end point and using that method in both VCL and FMX.
  • If the functionality doesn’t exist, you cannot imitate it.  For example, the VCL TBrush has the Style property that specify different fill patterns.  You could sort of imitate this behavior by setting the FMX Fill.Kind to TBrushKind.Bitmap and using a bitmap as the pattern but it is not the same.  In this case, I decided the effort wasn’t worth it.
  • Simple types can be challenging. For example, in FMX the canvas coordinates are singles, TPointF, and TRectF.  In VCL, they are Integer, TPoint, and TRect.  To get the center point in FMX, you would write code like MyPoint.X := (Rect.Right – Right.Left) / 2 + Rect.Left.  This code produces a single, which in VCL needs to rounded to truncated.  You CAN get around even this though with a little work.  In the RSGraphics.pas unit (FMX.RS.Graphics in FMX) that is in the RiverSoftAVG Common Classes Library, there are TCanvasPoint types that map to TPoint for VCL and TPointF for FMX.  There are also functions like CanvasDiv, which in FMX does the division above but in VCL uses a DIV to keep everything in Integer.  You can get the RSGraphics.pas unit by installing the RiverSoftAVG Charting Component Suite (or if you are an owner of any RiverSoftAVG product) as the RiverSoftAVG Common Classes Library  comes with it. 
  • The SCOPEDENUMS directive used in FMX can be a real headache.  Converting code from (VCL): Canvas.Brush.Style := bsSolid becomes in FMX: Canvas.Fill.Kind := TBrushKind.Solid (or Canvas.Brush.Kind if you are using my class helper).  You can get around it by defining an enumeration in VCL that looks like the FMX version, but it is an annoyance.
  • There can be only one class (record) helper per class or record.  This is the toughest limitation of all if you encounter it, and there is not a lot you can do about it.  If it is an Embarcadero/Idera class helper, you can copy and paste the class helper functionality into your own class helper, but if it is a third-party’s code, you may be out of luck.

Well, that is the end of another long blog post, but I hope you think it was worth it and that it can help you in your porting of code between VCL and FMX.

Happy CodeSmithing!

 

XE8 and Radial Gradients in Windows FMX Applications

The Problem

(Update 2015-06-14: Note that the article below has been updated to set Radial Gradient Center Point and not GradientOriginOffset (or focal point) .  Setting GradientOriginOffset can skew the gradient and lead to incorrect results).

In Delphi XE8, Embarcadero broke (or perhaps more correctly, broke further) how radial gradients are rendered on Windows using their FMX.Canvas.D2D.pas file.  This issue affects anyone who uses a radial gradient and the center point/focal point to not be in the exact middle of the shape being rendered.  For the RiverSoftAVG SVG Component Library, SVG tend to define these types of gradients all the time so that is how I became aware of the regression.  Prior to XE8, the FMX.Canvas.D2D unit created a radial gradient brush whose center was modified by the Brush.Gradient.RadialTransform.RotationCenter.  This allowed the SVG library to correctly render radial gradients that had their center or focal point not equal to 50%, 50% (i.e., the center).  The screenshots below show the old pre-XE8 behavior:

Radial Gradient (Centered Origin 50%, 50%)

Radial Gradient (Centered Origin 50%, 50%)

Radial Gradient (Origin Down and Right 75%, 75%)

Radial Gradient (Origin Down and Right 75%, 75%)

In the screenshots above, we set up the top Ellipse’s gradient brushes using RotationCenter and copied the brush to the canvas to draw 2 additional ellipses:

procedure TForm24.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
var
 aSize: Single;
begin
 aSize := PaintBox1.Height*0.33;
 Canvas.Fill := Ellipse1.Fill;
 // draw upper left
 Canvas.FillEllipse(RectF(0,0,aSize,aSize), 1);
 // draw bottom right
 Canvas.FillEllipse(RectF(PaintBox1.Width-aSize,PaintBox1.Height-aSize,PaintBox1.Width,PaintBox1.Height), 1);
end;

The code from XE7 and before in the FMX.Canvas.D2D.pas looked like this:

rgradbrushprop.GradientOriginOffset := TD2D1Point2F(Point(0, 0));
 rgradbrushprop.Center := TD2D1Point2F(
 PointF(AGradient.RadialTransform.RotationCenter.X * RectWidth(ARect),
 AGradient.RadialTransform.RotationCenter.y * RectHeight(ARect)) + ARect.TopLeft);
 rgradbrushprop.RadiusX := RectWidth(ARect) / 2;
 rgradbrushprop.RadiusY := RectHeight(ARect) / 2;
 FTarget.CreateRadialGradientBrush(rgradbrushprop, nil, gradcol, ID2D1RadialGradientBrush(Result));

If you notice, Embarcadero set the center of the gradient to the center point of the rectangle (usually RotationCenter.Point := PointF(0.5, 0.5)) and translated the center point by the rectangle being drawn (by adding ARect.TopLeft). There were quite a few problems with this code:

  • First is a small cosmetic problem. Why was Embarcadero using RadialTransform.RotationCenter? Rotation Center seems a poor naming choice for the center of the gradient. Probably, the RadialTransform.Position would have been a better choice.
  • Why were they setting the Center of the gradient and not the GradientOriginOffset?
  • There is no means to set the radius of the gradient (and that is why the SVG library cannot set the radius of a gradient)

However, there were a couple of things sorta right about the code:

  • You could set the gradient focal point
  • The center of the gradient was specified by using a value from 0 to 1 and was translated by where the rectangle was, meaning that it scaled to the rectangle it was being drawn in. If you changed the rectangle size or location, the gradient would be drawn correctly.

In XE8, Embarcadero changed FMX.Canvas.D2D.pas to closely mirror their FMX.Canvas.GDIP.pas:

RadialGradBrushProp.GradientOriginOffset := TD2D1Point2F(TPointF.Create(0, 0));
 RadialGradBrushProp.Center := TD2D1Point2F(TPointF.Create(ARect.Width * 0.5, ARect.Height * 0.5));
 RadialGradBrushProp.RadiusX := ARect.Width / 2;
 RadialGradBrushProp.RadiusY := ARect.Height / 2;
 FTarget.CreateRadialGradientBrush(RadialGradBrushProp, nil, GradCol, ID2D1RadialGradientBrush(Result));
 UpdateBrushMatrix(Result, AGradient.RadialTransform.Matrix);

All modifications to the radial gradient now occur in the UpdateBrushMatrix which applies the AGradient.RadialTransform’s transformation matrix to the brush’s matrix. In some ways, this could be seen as an improvement as theoretically, you can modify the radial gradient how ever you like by using the RadialTransform.Matrix. However, there are some BIG problems with this approach:

  • You cannot set TTransformation.Matrix property directly. It is read-only. The TTransformation.Matrix property is generated by the class when you modify the Position, Scale, and Skew properties. But the Skew property is protected. You can hack the class to get to the Skew property but it is certainly not straightforward.
  • Even worse, you need to set the transformation matrix using absolute values, not proportional values between 0 and 1. To translate the gradient center point, you need to know the rectangle it will be drawn in the future to create the gradient brush. And because the gradient center was not offset by the ARect.TopLeft, you have to take that into account too.
  • Even worse than that, since you must use absolute values for the gradient, that gradient can only be used correctly with one rectangle or object. In addition, even if the gradient on a TEllipse or other shape is set correctly, if that TEllipse is moved or resized, the gradient is wrong. You cannot share that gradient with another rectangle.

The screenshots below show the problems with the new, XE8 behavior:

Radial Gradient in XE8 (Centered Origin 50%, 50%).  Note how the bottom right ellipse has lost its centered gradient because it is drawn in a rectangle that is not at the origin.

Radial Gradient in XE8 (Centered Origin 50%, 50%). Note how the bottom right ellipse has lost its centered gradient because it is drawn in a rectangle that is not at the origin.

Radial Gradient in XE8 (Origin Down Right 75%, 75%).  By clever manipulation of the transformation matrix, we set the focal point to (114,94), which works for the top Ellipse.  However, the same gradient does not work for either of the other ellipses since their rectangles are different.

Radial Gradient in XE8 (Origin Down Right 75%, 75%). By clever manipulation of the transformation matrix, we set the focal point to (114,94), which works for the top Ellipse. However, the same gradient does not work for either of the other ellipses since their rectangles are different.

Note that the FMX GDI+ never worked, but since that canvas was used rarely, this issue was ignored by the RSCL.

Interestingly, Embarcadero only made this change on Windows. Testing on OSX and Android reveals that the previous behavior, using RotationCenter, is still in effect. (Note XE8 is giving us problems deploying to iOS so we could not test its behavior)

What does this mean for the RiverSoftAVG SVG Component Library?

First, because there is no easy way to set the gradient center point or focal point correctly and even if we did, the behavior would break as soon as a SVG element was moved or resized, we are not going to change the code for how radial gradient’s are set at this time. Using the default Embarcadero XE8 DirectX2D code on Windows for radial gradient with a non-centered focal point, the gradient will be displayed incorrectly. On other platforms and earlier versions of Delphi, the radial gradient will work as well as it ever did.

The Solution (for Everyone)

However, you can get back proper rendering of Radial Gradients on Windows for your compiled applications by hacking the FMX.Canvas.D2D.pas file. Perform the following steps:

  • Copy FMX.Canvas.D2D.pas from Delphi’s source\fmx directory to your project’s directory
  • Modify the TCanvasD2D.CreateD2DGradientBrush method by replacing the entire “begin { Radial }” with
begin
 { Radial }
 for I := 0 to AGradient.Points.Count + Count - 1 do
 Grad[I].Position := 1 - Grad[I].Position;
 FTarget.CreateGradientStopCollection(@Grad[0], AGradient.Points.Count + Count, D2D1_GAMMA_2_2,
 D2D1_EXTEND_MODE_CLAMP, GradCol);
 // assume RotationCenter in range 0-1, modify the gradient origin offset
 RadialGradBrushProp.GradientOriginOffset := TD2D1Point2F(TPointF.Create(0, 0));
 // assume RotationCenter in range 0-1, translate gradient center by rectangle.TopLeft
 RadialGradBrushProp.Center := TD2D1Point2F(TPointF.Create(AGradient.RadialTransform.RotationCenter.X * ARect.Width,
 AGradient.RadialTransform.RotationCenter.Y * ARect.Height) + ARect.TopLeft);
 // bonus points, assume scale contains the percent of the radius to display
 // i.e., usually r=1 for the whole rectangle
 RadialGradBrushProp.RadiusX := AGradient.RadialTransform.Scale.X*(ARect.Width / 2);
 RadialGradBrushProp.RadiusY := AGradient.RadialTransform.Scale.Y*(ARect.Height / 2);
 FTarget.CreateRadialGradientBrush(RadialGradBrushProp, nil, GradCol, ID2D1RadialGradientBrush(Result));
// UpdateBrushMatrix(Result, M);
 GradCol := nil;
 end;

Alternatively, you can download the changed file.  As a bonus, the above code uses the AGradient.RadialTransform.Scale property as a proxy for setting the radius of the gradient (the RSCL has set the scale based on a SVG element’s radius since February 2015 but it is unused without the above hack for each platform and version of Delphi you are using).

Well, that’s it for now.  I hope this helps others.  Happy CodeSmithing!

Creating Icons and Launchers for Delphi Mobile Applications – Redux

About a year ago, I blogged about a handy little utility (EXE and source) I created for generating all of the mobile icons, spotlights, settings and launchers for Delphi Android and iOS applications.  At the time, there were 2 different platforms (iOS and Android), 3 different device types (iPhone, iPad, Android), and 7 different ratios (1:1, 1.33:1, etc) of icon/launcher graphic sizes all combining to require a whopping total of 28 different png files to be created.  GraphicsTab

Fast forward a year, there have been 2 updates to Delphi in the time (adding 2 more ratios and a total of 37 files).  Graham Murt kindly updated the utility for XE6/7 and added the great little feature of persisting your choices in an ini file between executions.  I extended his update a little bit for the Android Splash Images and persisting the rectangles as well and am re-releasing our changes back to you.   Please read the original blog post for instructions.  You can download the source and exe here.

Happy CodeSmithing!

Note: In some ways, this utility complements the XE Plus Pack – Release 17 that jed-software.com just released.  However, the utility takes another approach by using images you create in Photoshop or some other tool (allowing more flexibility) and then generates all the different ratios quickly and easily.

ZEROBASEDSTRINGS, Just Don’t

Ack!  I cannot believe that it has been 6 months since my last post!  I apologize for the long silence but I have been juggling 3 major upgrades (the RiverSoftAVG Charting Component Suite and 2 others I am not yet ready to blog about) and it has swamped my time completely.

This is a small blog post about ZEROBASEDSTRINGS, in my opinion a truly moronic change Embarcadero introduced in XE3 for mobile.  I know I have said that I feel sometimes like Embarcadero is out to get me, but here is a case where Embarcadero seems to be out to get everyone. 🙂  Jeroen provides an excellent little blog post about it and I recommend you read that first to understand the issue.

However, I would go farther than Jeroen.  My advice, never use ZEROBASEDSTRINGS. Even if you have only started using Object Pascal now and have no experience with 1 based strings.  There are millions of lines of Delphi code and examples out there, most of it based on the traditional 1-based strings.  You will use some of that code, if you haven’t already.  You will copy and paste some of that code.  Mixing and matching 0 based and 1 based strings is a recipe for disaster.  Eventually, if you do mobile, this change will bite you in the a**.  And it will probably be a very subtle bug that will take way too long to track down (I know I wasted a day on it 🙁 ).  The worst part is that your mobile app will work perfectly fine on desktop, where you probably do most of your testing.  It is only when you move to mobile that strange things will start happening.

The first thing I recommend you do whenever you create a new unit is add your company/copyright header and add a {$IFDEF NEXTGEN}{$ZEROBASEDSTRINGS OFF}{$ENDIF} right below it.  Alternatively, as a component developer, I have to support multiple Delphi versions.  As such, I have a RSDefines.inc file (defining things like DELPHIXE7ORHIGHER, etc) which is included in every unit.  In that unit, I have the following lines:

{$IFDEF NEXTGEN}
 {$ZEROBASEDSTRINGS OFF}
{$ENDIF}

You may want to do the same thing, which gets applied no matter which version of Delphi and which compiler (mobile, desktop, etc).

By just adopting this habit of always prefacing every unit with ZEROBASEDSTRINGS OFF, you never have to worry about whether you are using 0-based or 1-based strings.  You won’t have to surround any more of your code with IFDEFs or Lows/High as detailed in Jeroen’s post.

Of course, this is my opinion.  But, when it comes to ZEROBASEDSTRINGS, my advice is JUST DON’T.  Friends Don’t Let Friends Use Zero Based Strings 🙂 (in Delphi at least).  It will save you a lot of time and heartache later.

Happy CodeSmithing!

 

Of Paranoia and Enumerated Types

They say that just because you are paranoid, it doesn’t mean that someone is not out to get you.  I have never been paranoid, but I think I might start, as I think someone at Embarcadero is out to get me… or at least, out to get third party developers who create components for FMX 🙂  Supporting FMX components is simple masochistic fun, especially when you try to develop cross-library components (i.e., support VCL and FMX).  When I get especially frustrated, I start imagining that there is someone at Embarcadero, laughing maniacally every time a new release of Delphi and FMX come out.  It is hard to annoy your developer community as well as Embarcadero has unless you are truly trying. 🙂

Each version, Embarcadero likes to mix things up as they work to make FMX better, giving FMX component developers a perpetual thrill ride.  Not only do FMX component developers have to deal with bugs and slow performance, we have the joy of managing the constantly changing hierarchy, modified method names, and changed method signatures. Sometimes, seemingly just for giggles, types and classes will move to new units, giving FMX developers a fun whack-a-mole, find-that-class game with each release.  Supporting multiple versions of FMX from one code base is an IFDEF heaven. 🙂

All of which leads up to the point of this rant, I mean post, and one of my new pet peeves.  Enumerated types (and sets) are one of my favorite features of the Pascal language.  They are a joy to use: elegant and descriptive, beautifully type-safe while providing amazing power, especially with sets.  They are one of the features I miss the most when I have to go use another language.  However, it seems like Embarcadero is trying to ruin them for me, at least in FMX.

In FMX, every enumerated type is compiled with the SCOPEDENUMS directive on.  For those who don’t know, when an enumerated type is compiled with SCOPEDENUMS on, you have to type the enumerated type when you use it, e.g.,  this is not valid

MyControl.Align := alClient;

Instead, you are required to type:

MyControl.Align := TAlignLayout.alClient;

In my opinion, SCOPEDENUMS are one of the stupidest ideas that Embarcadero has ever adopted.  I know, I know, other languages have the same thing.  Well guess what, I like that you didn’t have to do that in Pascal.  What is the point of having a strongly-typed language if you have to do all the work for the compiler anyway?

For someone who tries to create code that works in both FMX and VCL, the SCOPEDENUMS directive is especially trying.  Either you have to type:

MyControl.Align := {$IFDEF FMX}TAlignLayout.{$ENDIF}alClient;

or

{$IFDEF FMX}
  TMyAlign = TAlignLayout;
{$ELSE}
  TMyAlign = TAlign;
{$ENDIF}
MyControl.Align := TMyAlign.alClient;

Digression: Note that the {$DEFINE FMX} is something I define to say if I am compiling for FMX or VCL., similar to what is discussed here.  In practice, I have one unit called FMX.RS.Something.pas that is defines the FMX and then includes the common unit, e.g.,

unit FMX.RS.Charts;
{$DEFINE FMX}
{$INCLUDE RSCharts.pas}

I found this solution on the forums one day, found the idea elegant, and use it extensively to make all RiverSoftAVG.com cross-library components.

But back to my rant… Embarcadero’s practice of using SCOPEDENUMS for FMX caused a lot of unnecessary code in my libraries.  I found the decision dumb, but I did the work, I could get around it, and life was good again…

…Until Delphi XE6.  In Delphi XE6, someone at Embarcedero decided to up the dumb level to 11.  🙁  In Delphi XE6, FMX enumerated types do not include the prefix!  TAlignLayout is no longer:

TAlignLayout = (alNone, alTop, alLeft, alRight, alBottom, alMostTop, alMostBottom, alMostLeft, alMostRight, alClient,alContents, alCenter, alVertCenter, alHorzCenter, alHorizontal, alVertical, alScale, alFit, alFitLeft, alFitRight);

but this:

 TAlignLayout = (None, Top, Left, Right, Bottom, MostTop, MostBottom, MostLeft, MostRight, Client, Contents, Center, VertCenter, HorzCenter, Horizontal, Vertical, Scale, Fit, FitLeft, FitRight);

 TAlignLayoutHelper = record helper for TAlignLayout
 const
 alNone = TAlignLayout.None deprecated 'Use TAlignLayout.None';
 alTop = TAlignLayout.Top deprecated 'Use TAlignLayout.Top';
 alLeft = TAlignLayout.Left deprecated 'Use TAlignLayout.Left';
 alRight = TAlignLayout.Right deprecated 'Use TAlignLayout.Right';
 alBottom = TAlignLayout.Bottom deprecated 'Use TAlignLayout.Bottom';
 alMostTop = TAlignLayout.MostTop deprecated 'Use TAlignLayout.MostTop';
 alMostBottom = TAlignLayout.MostBottom deprecated 'Use TAlignLayout.MostBottom';
 alMostLeft = TAlignLayout.MostLeft deprecated 'Use TAlignLayout.MostLeft';
 alMostRight = TAlignLayout.MostRight deprecated 'Use TAlignLayout.MostRight';
 alClient = TAlignLayout.Client deprecated 'Use TAlignLayout.Client';
 alContents = TAlignLayout.Contents deprecated 'Use TAlignLayout.Contents';
 alCenter = TAlignLayout.Center deprecated 'Use TAlignLayout.Center';
 alVertCenter = TAlignLayout.VertCenter deprecated 'Use TAlignLayout.VertCenter';
 alHorzCenter = TAlignLayout.HorzCenter deprecated 'Use TAlignLayout.HorzCenter';
 alHorizontal = TAlignLayout.Horizontal deprecated 'Use TAlignLayout.Horizontal';
 alVertical = TAlignLayout.Vertical deprecated 'Use TAlignLayout.Vertical';
 alScale = TAlignLayout.Scale deprecated 'Use TAlignLayout.Scale';
 alFit = TAlignLayout.Fit deprecated 'Use TAlignLayout.Fit';
 alFitLeft = TAlignLayout.FitLeft deprecated 'Use TAlignLayout.FitLeft';
 alFitRight = TAlignLayout.FitRight deprecated 'Use TAlignLayout.FitRight';
 end;

What’s the big deal you say?  After all, they provide backwards compatibility for using alClient, etc?  Ignoring that they just added a bunch of extra code for no reason (possibly bloating EXE sizes) and just wasted a record helper in order to provide backward compatibility for something they just broke, the big deal to me is that if you use TAlignLayout.alClient, you get the stupid deprecated warning, and warnings and hints are kind of a no-no when you are trying to develop quality components (for some reason, people get anxious 🙂 ).  A possibly even BIGGER DEAL (your mileage may vary) is that they broke compatibility with streaming in earlier versions of Delphi!  Once a form is saved in Delphi XE6, any alClient, alLeft, alRight, etc are converted to Client, Left, Right, etc making the form unusable in earlier versions of Delphi.  Aarrgh!  There are some truly choice words I said when I realized I completely broke a dialog in the IECS by saving it in Delphi XE6 (thankfully, I keep weekly backups).

Anyway, glad to get that off my chest.  Now, I am not truly paranoid, and I know that Embarcadero is not truly out to get me.  Eventually, my hope is that they will get FMX mature enough that you don’t see such core, breaking changes occurring each Delphi version.  I know in the grand scheme of things, I would rather have a stable, fast, working FMX than for them to worry about little ol’ developers like me.  However, if I ever walk past one of Embarcadero’s offices and hear an evil chuckle from within, I am not going to be held responsible for my actions.  🙂

Thank you for reading my rant.  Comments, as always, are welcome.

Happy CodeSmithing!

Performance Comparison from Delphi 2010 to XE6 PDF

The Performance Comparison from Delphi 2010 to XE6 series of blog posts has easily been the most popular ones I have ever done.  However, I know that it can be difficult to read as the blogging format is not really the best format for what is essentially an article.  I received a couple requests for a PDF version of the blog posts.  If you want to read the series as one big PDF (59 pages), you can download it here: Performance Comparison from Delphi 2010 to XE6.  It has been lightly edited from the blog to enhance the clarity and continuity.

Enjoy!  Happy CodeSmithing!

Performance Comparison from Delphi 2010 to Delphi XE6 (Conclusion)

The Last Chapter

With the release of Delphi XE6 and Embarcadero’s emphasis on Quality, Performance, and Stability (QPS), I wanted to see for myself the level of improvement, especially in performance.  Delphi XE6 is definitely faster and more responsive than the last few versions, especially in FMX, but I wanted to see if I could quantify the performance improvement. Before starting, I made some predictions about what I would see when comparing Delphi 2010-XE6.  Let’s see how I did:

  • EXE size will probably increase with every version of Delphi.  This turned out to be mostly true.  Each version of Delphi has increased the EXE size over the one before… EXCEPT this ignores the one proverbial elephant in the room, Delphi XE3.  Delphi XE3 massively increased EXE sizes, both in VCL and FMX.  Thankfully, Delphi XE4 reduced XE3’s excessive weight gain.  However, EXE size has been growing back to XE3 levels with every version since then.  It should be noted that VCL EXE sizes have been growing slower than FMX EXE sizes.
  • FMX executables will be larger than VCL executables.  This was definitely true.  This was completely expected as FMX controls are non-native controls (i.e., they don’t use OS level equivalents) so all the drawing and interaction code must be compiled into the executable.
  • FMX executables will be slower than VCL executables, though each new version of Delphi for a platform should improve.  This was mostly true.  Except for where the VCL version is using a software renderer for heavy graphics drawing vs the FMX hardware renderer, the FMX versions are slower than their VCL counterparts.  There was one outlier, when strings are added to a TMemo and BeginUpdate/EndUpdate have not been called, the Delphi XE6 FMX version was faster than the VCL feature.  However, the discrepancy is easily explained as the VCL TMemo draws every string as it is added while the FMX TMemo does not.  The part that is not completely true is that each new version of Delphi would be improving the FMX speed.  Speeds were all over the place, and earlier versions were sometimes significantly faster.  Delphi XE6 is definitely faster than XE5 though.
  • Win32 and Win64 compilation should be faster than other platforms.  This was true as well.  The Win32 and Win64 compilers (written by Embarcadero) are fastest.  However, the OSX version (also written by Embarcadero) is very close.  The iOS and Android versions are much slower, even when not counting the deployment step.
  • Windows FMX executables will be faster than other platforms’ FMX executables.  This was mostly true to true.  Since the test machines were not comparable, it was difficult to really test this.  However, when drawing the RSCL directly to the canvas, the OSX implementation was comparable to the Win32 implementation.  With the exception of the Clock, the OSX versions cleaned the Win32 versions clocks, being anywhere from 1.2x-4.2x faster.  It must be noted that the Windows box (Windows 7 64-bit Intel I7 930 @ 2.8 GHz CPU, ATI HD 5700 graphics card, and 6 GB RAM) and the Mac Mini (OSX 64-bit Intel I7 @ 2.3 GHz, integrated graphics, and 4GB RAM) are not exactly comparable.  Admittedly, the ATI HD 5700 is almost 5 years old now, but the Mac Mini uses integrated graphics.  Examining the outputs closely, there are slight differences in the output (see the dark lines in the Car).  Generally, however, the output from the OSX versions are excellent.  This hypothesis would need to be more rigorously investigated.

Comparing the competitors…

To make my final evaluation, I wanted to be as objective as possible and use concrete numbers.  I decided to ignore EXE size (Delphi 2010 wins! :-)) and compilation speed (more mixed but generally earlier versions of Delphi).  I concentrated solely on performance metrics.  This means I ignored features (Delphi XE6 wins!), number of platforms (Delphi XE6 wins), and stability (???? wins!) For each test application, I averaged the scores (e.g., the Hello World Listbox performance score is an average of the TListBox scores).  This does mean that the winner of an exponential test (e.g., 1000s of items in a TListBox) can win even if their performance with less items was not the best.  I then normalized the averages (e.g., the winner is equal to 1 and every other version is between 0 and 1 depending on their average).  Finally, I added each test score together to get a maximum score per platform.  For example, the VCL 32-bit score includes the normalized performance scores from the Hello World project (ListBox and Memo), the IECS Advanced Console and the IECS Basic Console (Max of 4 points).  The OSX score includes the normalized performance scores from the Hello World project (ListBox and Memo), the IECS Advanced Console, the IECS Basic Console, RSCL Drawing project, and the RSCL Primitives project (Max of 6 points).  In the graphs below, you can easily see how the normalized scores contributed to each Delphi version’s score. Note that my scores are just for the tests in this series of blog posts.  Other tests would give vastly different results.  This blog post is not meant to be a recommendation of one Delphi version over another.

Now for the awards ceremony…

Overall Performance Score Comparison, Win32 VCL

Overall Performance Score Comparison, Win32 VCL

Surprisingly, Delphi XE6 wins the gold award in my performance tests for Win32 VCL.  The differences are extremely minor between all versions of Delphi so this coveted award could have gone to any of them 🙂  Delphi XE3 manages second place and a silver medal.  Delphi XE scores the third place bronze award.

Overall Performance Score Comparison, Win64 VCL

Overall Performance Score Comparison, Win64 VCL

For Win64 VCL (dropping Delphi 2010 and XE from the running), Delphi XE2 manages to barely eke out the gold award.  Delphi XE6 wins silver and XE5 bronze.

Overall Performance Score Comparison, Win32 VCL

Overall Performance Score Comparison, Win32 FMX

Moving to FMX, the performance differences get much larger.  Delphi XE3, despite its huge EXE sizes and poor Memo performance, produces the fastest FMX executables on Windows in these tests to win a gold medal.  Its ListBox performance is what really stands out.  Delphi XE4 makes a very strong second place showing for silver.  Bronze goes to Delphi XE6.

Overall Performance Score Comparison, Win64 FMX

Overall Performance Score Comparison, Win64 FMX

Going to 64-bit, Delphi XE3 again wins the gold with Delphi XE4 getting the silver.  Delphi XE6 comes again in third place, though its score improved noticeably over 32-bit.

Overall Performance Score Comparison, OSX

Overall Performance Score Comparison, OSX

Switching to the Mac, Delphi XE4 wins its first gold medal.  Delphi XE6 sneaks past XE3 to win the silver.

Overall Performance Score Comparison, iOS

Overall Performance Score Comparison, iOS

Shifting to mobile, we are down to Delphi XE4, XE5, and XE6.  Delphi XE4 wins another gold medal for the iOS platform on its solid memo performance.  Delphi XE6 gets second place for the silver and XE5 trails for the bronze.

Overall Performance Score Comparison, Android

Overall Performance Score Comparison, Android

For our last platform, Android, Delphi XE6 wins handily, easily beating XE5.

Conclusion

So what does this mean?  How did Delphi XE6 perform on its mantra of “Quality, Performance, and Stability?”  As far as this series of performance blog posts are concerned, Delphi XE6 did neither as well as I hoped nor as bad as I feared.  It only outright won a platform twice (and one of those was by a whisker on Win32 VCL).  However, Delphi XE6 was a solid performer and competitive for all platforms.  Compared to its immediate successor (Delphi XE5), it is a huge advance in performance and nearly brings it back up to the levels of Delphi XE3 and XE4, both of which performed surprisingly well in FMX.

Overall Performance "Medal" Count (Gold=3, Silver=2, Bronze=1)

Overall Performance “Medal” Count (Gold=3, Silver=2, Bronze=1)

If you do a medal count of the different platforms and give 3 points for Gold, 2 points for Silver, and 1 point for Bronze, Delphi XE6 racks up the most points.  Even if you ignore the Android platform, Delphi XE6 still did 1 point better than XE4.  If you then also ignored the iOS platform, Delphi XE6 would still tie with XE3 at 9 points.  Considering that it adds both iOS and Android platforms, seems to have fewer issues compared to Delphi XE3 and XE4, and vastly superior performance compared to Delphi XE5, I would say that Embarcadero achieved their goal. Almost a month and over 12,000 words later, I have finally finished this series of blog posts on the performance comparison from Delphi 2010 to Delphi XE6.  I hope you have enjoyed them.  It was a ton of work, but I think it was worth it.  Happy CodeSmithing!

Performance Comparison from Delphi 2010 to Delphi XE6 (Part 7b – RSCL Primitives)

RSCL Primitives Application, FMX, Win32

In our last blog post, we compared SVG drawing performance for the different versions of Delphi using low-level canvas routines.  However, the RiverSoftAVG SVG Component Library (RSCL) also contains a TRSSVGPanel component, which is able to generate FMX shape controls (TLayout and TPath mostly) descendants from an SVG to allow GUI interaction.  It was our thought that this could be useful for performance testing FMX primitives speed as these primitives are what controls are made of in the FMX library.  The RSCL converts the SVG elements listed in the last post into the following primitives:

Listing of SVG Primitives created for each SVG

Listing of SVG Primitives created for each SVG

Note that the loading, parsing, and building of the RSCL SVG elements and primitives are not tested in these tables.  Rather, the speed that the FMX library performs in refreshing the display of all the primitives that make up the SVG representation.

Comparison of averaged execution speed for the FMX RSCL Primitives application (Win32) with the baseline GDI+ VCL application, for Delphi XE2 to XE6

Comparison of averaged execution speed for the FMX RSCL Primitives application (Win32) with the baseline GDI+ VCL application, for Delphi XE2 to XE6

Before starting, we again did a sanity test to compare the FMX version to the VCL version. The VCL version uses GDI+, which is a software graphics library, so the hardware-optimized FMX TCanvas operations should be significantly faster.  Thankfully, that is what we see.  Every Delphi version of the FMX application was 4x-5x faster on average than the VCL application (XE6).

Comparison of execution speed for the FMX RSCL Primitives application (Win32) with Delphi XE2 to XE6

Comparison of execution speed for the FMX RSCL Primitives application (Win32) with Delphi XE2 to XE6

Zooming in on the primitives drawing performance for each individual SVG, Delphi XE2 dominates, drawing 2 SVGs fastest outright, tieing in drawing the Clock, and only 2/3 ms behind the leader drawing the Car SVG.  Delphi XE3 does especially poorly in performance.  Delphi XE6 comes in 4th place trailing XE2 by approximately 14%.

Ratio of Drawing vs FMX Primitives execution speed for RSCL applications in Delphi XE2 to XE6 (Win32)

Ratio of Drawing vs FMX Primitives execution speed for RSCL applications in Delphi XE2 to XE6 (Win32)

Comparing performance when drawing directly to the canvas versus using primitives, we see a mix of results.  The Clock and the Car are faster when using primitives, and the Watch and the Flag are faster when using the canvas directly.  Without more extensive profiling, it is difficult to say exactly why this is so.  If you remember the SVG element breakdown from the last post, the Clock and the Car have significantly more gradients and gradients stops and a lot fewer paths than the Watch and the Flag.  Since the RSCL caches Brushes and Pens after the first drawing call, the RSCL primitives do not have an advantage from the greater number of gradients.  It is probable that the FMX library bogs down as the number of primitives goes up.

*Note that Delphi XE3 had visual artifacts drawing the Car and the Watch.  

RSCL Primitives Application, FMX, Win64

Moving to 64-bits, Delphi XE2 again wins and is the only version to actually improve its average score from 32-bits.  It renders the Watch and Flag fastest, ties for the Clock, and trails the leader by 1.66 ms for the Car.  Delphi XE3 continues its poor performance.  Delphi XE6 is approximately 16% slower than Delphi XE2.

Comparison of execution speed for the FMX RSCL Primitives application (Win64) with Delphi XE2 to XE6

Comparison of execution speed for the FMX RSCL Primitives application (Win64) with Delphi XE2 to XE6

Ratio of Win64 to Win32 execution speed for RSCL Primitives applications in Delphi XE2 to XE6 (FMX)

Ratio of Win64 to Win32 execution speed for RSCL Primitives applications in Delphi XE2 to XE6 (FMX)

*Note that Delphi XE3 had visual artifacts drawing the Car and the Watch. 

RSCL Primitives Application, FMX, OSX

When we start testing using the OSX platform, the performance of all versions of Delphi suffer dramatically.  Unlike drawing directly to the canvas, the FMX primitives on OSX are very slow… for Delphi XE3, it is even slower than the non-hardware accelerated GDI+ version!

Comparison of execution speed for the FMX RSCL Primitives application (OSX) with Delphi XE2 to XE6

Comparison of execution speed for the FMX RSCL Primitives application (OSX) with Delphi XE2 to XE6

Ratio of Win32 to OSX execution speed for RSCL Primitives applications in Delphi XE2 to XE6 (FMX)

Ratio of Win32 to OSX execution speed for RSCL Primitives applications in Delphi XE2 to XE6 (FMX)

 

Delphi XE2, XE3, and XE4 suffer the most coming to the OSX platform.  Delphi XE5 and XE6 make great showings in comparison.  Delphi XE5 is the fastest when using primitives by a large margin.  Delphi XE6 is in second place but about 18% slower than XE5.

*Note that Delphi XE3 had visual artifacts drawing the Car and the Watch. 

RSCL Primitives Application, FMX, iOS

Comparing Delphi XE4 through XE6 on mobile, Delphi XE5 maintains its performance advantage.  On iOS, Delphi XE5 is fastest using FMX primitives, with a 6%-7% speed advantage.  The Delphi XE6 numbers are very respectable.  With SVGs with more paths (Watch and Flag), Delphi XE6 manages to be faster than XE4.

Comparison of execution speed for the FMX RSCL Primitives application (iOS) with Delphi XE4 to XE6

Comparison of execution speed for the FMX RSCL Primitives application (iOS) with Delphi XE4 to XE6

*Delphi XE4 was last on iOS.  In addtion, there were minor visual artifacts (gradients) drawing the SVGs.

RSCL Primitives Application, FMX, Android

Our final test in this series of blog posts is on the Android platform.  Delphi XE5 wins the performance crown when using FMX primitives on Android, though it is a narrow victory.  Delphi XE6 loses by over 6% when drawing the Clock.  However, as the number of primitives go up, Delphi XE6 starts catching up and is less than 2% slower when drawing the most complex SVG (Watch).

Comparison of execution speed for the FMX RSCL Primitives application (Android) between Delphi XE5 and XE6

Comparison of execution speed for the FMX RSCL Primitives application (Android) between Delphi XE5 and XE6

Finally!  We are finished with our performance tests!  In the concluding blog post, I summarize the results and discuss how Delphi XE6 did.

Performance Comparison from Delphi 2010 to Delphi XE6 (Part 7 – RSCL)

RSCL Execution (Speed) Tests

Display of the SVG clock using FMX TCanvas operations in the iOS test app

Display of the SVG clock using FMX TCanvas operations in the iOS test app

Last week, we tested the various Delphi versions with sample applications using the Inference Engine Component Suite (IECS).  It provided interesting details for non-visual performance as well as a mix of non-visual and visual performance.  Our final tests are going to use the RiverSoftAVG SVG Component Library (RSCL) to test the graphics performance of the FMX Delphi versions, e.g., Delphi XE2 through XE6.  The FMX TCanvas is graphics hardware accelerated, using DirectX on Windows and OpenGL on OSX, iOS, and Android.  In addition, Delphi XE6 added DirectX 11 support so it will be interesting to see if that makes a difference.

Display of the SVG watch using FMX TCanvas operations in the iOS test app

Display of the SVG watch using FMX TCanvas operations in the iOS test app

About the RSCL Test Applications

There are actually two types of RSCL applications we are going to test.  The first type of RSCL applications will test the drawing speed of the FMX TCanvas routines by drawing SVGs.  They render SVG files using low-level canvas operations (gradients, paths, text, etc) and are available for VCL and FMX.  The VCL versions use GDI+ (which is a software renderer with comparable features to the FMX TCanvas) so they should provide a good baseline of the FMX DirectX and OpenGL hardware-based rendering.

Display of the SVG car using FMX TCanvas operations in the iOS test app

Display of the SVG car using FMX TCanvas operations in the iOS test app

The RSCL is also able to build SVGs using the FMX shape primitives (TRectangle, TPath, TText, etc); it should reveal any optimizations (or lack thereof) Embarcadero has been doing with the low-level primitives from which most FMX controls are built.

Display of the SVG flag using FMX shape primitives in the iOS test app

Display of the SVG flag using FMX shape primitives in the iOS test app

For simplicity with deploying mobile apps, all of the projects store the test SVGs inside a TClientDataSet in the app/application. The SVG applications are only testing drawing using low-level canvas operations or shape primitives; the loading, parsing, and building of the RSCL SVG elements and primitives are not tested.  There are four SVGs used in the tests:

  1. Clock
  2. Car
  3. Watch
  4. Flag

The following table breaks down the SVG elements that make up each SVG:

Listing of SVG Elements for each SVG

Listing of SVG Elements for each test SVG

Something to note is that these test applications should not be taken as an indicator of the pure TCanvas speed you can get in FMX.  The SVGs are incredibly complicated, with lots of paths, gradients, transparencies, CSS styles, etc.  The RSCL is saving canvas state before drawing each element and restoring canvas state after drawing each element.  Each SVG element has its own brush and pen (or Fill and Stroke in FMX terminology).  There are a lot of canvas operations occurring besides the pure filling of a graphics primitive (rectangle, ellipse, path, etc).

Another thing to note is that these drawing tests are not representative of the RSCL TRSSVGImage component performance.  The TRSSVGImage component draws an SVG once to a backscreen bitmap and then just draws the backscreen bitmap as needed when painting is called for.  These drawing tests deliberately use a TPaintBox and draws an SVG each time the TPaintBox is refreshed.  Even then, we removed the first drawing call from the performance times as the RSCL has to build the Brushes and Pens (Fills and Strokes) in the first drawing call but caches them for later drawing calls.

RSCL Drawing Application, FMX, Win32

Our first series of tests with the RiverSoftAVG SVG Component Library (RSCL) will be the RSCL Drawing application for Win32.  As mentioned earlier in the series, our test machine for Windows is a Windows 7 64-bit Intel I7 930 @ 2.8 GHz CPU, and 6 GB RAM.  The graphics card is an ATI HD5700 (not a top performer) using the latest AMD 14.1 drivers and with DirectX 11.  Note that no special effort was made to choose the TCanvas implementation; we let each Delphi version pick its default.

Comparison of averaged execution speed for the FMX RSCL Drawing application (Win32) with the baseline GDI+ VCL application, for Delphi XE2 to XE6

Comparison of averaged execution speed for the FMX RSCL Drawing application (Win32) with the baseline GDI+ VCL application, for Delphi XE2 to XE6

As a sanity test, we will first compare the FMX version to the VCL version.  Since the VCL version uses GDI+, which is a software graphics library, the hardware-optimized FMX TCanvas operations should be significantly faster.  Thankfully, that is what we see.  Every Delphi version of the FMX application was almost 4x faster on average than the VCL application (XE6).

Comparison of execution speed for the FMX RSCL Drawing application (Win32) with Delphi XE2 to XE6

Comparison of execution speed for the FMX RSCL Drawing application (Win32) with Delphi XE2 to XE6

Zooming in on the drawing performance for each individual SVG, we see that Delphi XE5 drew the Flag fastest, but that Delphi XE4 drew the Watch and Car the fastest.  Every version of Delphi drew the Clock in 7-8 ms (11x-12x faster than the GDI+ performance).  Overall, Delphi XE4 was the fastest in Win32.

*Note that Delphi XE3 had visual artifacts drawing the Car and the Watch.  

RSCL Drawing Application, FMX, Win64

Moving to 64-bit, every version of Delphi was faster than its 32-bit counterpart (running on Windows 7 64-bit).  This time, Delphi XE2 drew the Flag fastest.  Delphi XE6 drew the Watch fastest.  Delphi XE4 was fastest drawing the Car.  We have a 5-way tie for drawing the Clock at 7 ms.  Averaging the results, Delphi XE4 is again the winner for Win64.

Comparison of execution speed for the FMX RSCL Drawing application (Win64) with Delphi XE2 to XE6

Comparison of execution speed for the FMX RSCL Drawing application (Win64) with Delphi XE2 to XE6

Ratio of Win64 to Win32 execution speed for RSCL Drawing apps in Delphi XE2 to XE6 (FMX)

Ratio of Win64 to Win32 execution speed for RSCL Drawing apps in Delphi XE2 to XE6 (FMX)

Comparing the ratio of performance of Win64/Win32, Delphi XE2 and XE6 saw the largest performance gain when moving to Win64.  It is difficult to see if Delphi XE6 gained anything with its DirectX 11 compatibility.

*Note that Delphi XE3 had visual artificacts drawing the Car and the Watch.  

RSCL Drawing Application, FMX, OSX

Things get much more interesting when we move to Mac OSX.  The FMX programs are using OpenGL instead of DirectX.  Delphi XE2 is fastest drawing the Car and ties with XE3 for drawing the Clock.  Delphi XE5 is fastest drawing the Watch; XE3 gets another tie when drawing the Flag.  Delphi XE6’s showing in OSX is not so impressive at first glance, as it is dead last with the two least complicated SVG objects (Clock and Flag) and in the back of the pack with the two more complicated SVG objects.  However, it should be noted that the XE6 is only 5.5 ms (Clock), 3 ms (Car), 4 ms (Watch), and 4 ms (Flag).  Averaging the results, Delphi XE2 is the winner for OSX.

Comparison of execution speed for the FMX RSCL Drawing application (OSX) with Delphi XE2 to XE6

Comparison of execution speed for the FMX RSCL Drawing application (OSX) with Delphi XE2 to XE6

Ratio of Win32 to OSX execution speed for RSCL Drawing apps in Delphi XE2 to XE6 (FMX)

Ratio of Win32 to OSX execution speed for RSCL Drawing apps in Delphi XE2 to XE6 (FMX)

Display of the SVG Clock using FMX TCanvas operations in the OSX test application

Display of the SVG Clock using FMX TCanvas operations in the OSX test application

Display of the SVG Watch using FMX TCanvas operations in the OSX test application

Display of the SVG Watch using FMX TCanvas operations in the OSX test application

The really interesting thing is how fast the OSX implementation is compared to the Win32 implementation.  With the exception of the Clock, the OSX versions clean the Win32 versions clocks (pun unintended 🙂 ), being anywhere from 1.2x-4.2x faster.  It must be noted that the Windows box and the Mac Mini are not exactly comparable.  The Mac Mini is an OSX 64-bit Intel I7 @ 2.3 GHz with 4GB RAM.  Admittedly, the ATI HD 5700 is almost 5 years old now, but the Mac Mini uses integrated graphics.  Examining the outputs closely, there are slight differences in the output (see the dark lines in the Car).  Generally, however, the output from the OSX versions are excellent.

Display of the SVG car using FMX TCanvas operations in the OSX test app

Display of the SVG car using FMX TCanvas operations in the OSX test app

Display of the SVG Flag using FMX TCanvas operations in the OSX test application

Display of the SVG Flag using FMX TCanvas operations in the OSX test application

*Note that Delphi XE3 had visual artificacts drawing the Car and the Watch.  

RSCL Drawing Application, FMX, iOS

Moving to mobile, our drawing times get much slower.  This is expected as the mobile hardware is much slower than the desktop hardware.  For testing iOS apps, we used an iPod Touch with 32GB.  For testing Android apps, we used a Nexus 7 (2013) with 32GB.

Comparison of execution speed for RSCL Drawing App (iOS) from Delphi XE4 to XE6

Comparison of execution speed for RSCL Drawing App (iOS) from Delphi XE4 to XE6

The interesting thing is that Delphi XE5 is the clear winner on iOS in drawing the SVGS.  It is fastest drawing three of the SVGs and only 26 ms (approx. 4%) slower than XE4 when drawing the Car.  Delphi XE6 is the slowest in drawing in iOS.

*Note that Delphi XE4 had minor visual artificacts (gradients) drawing on iOS.  

RSCL Drawing Application, FMX, Android

On an Android device, Delphi XE5 maintains its speed lead over Delphi XE6.  Delphi XE5 is 4-10% faster than Delphi XE6 drawing the SVGs.

Comparison of execution speed for RSCL Drawing App (Android) between Delphi XE5 and XE6

Comparison of execution speed for RSCL Drawing App (Android) between Delphi XE5 and XE6

In the next blog post, we will finish our performance tests with the RSCL Primitives application tests…

Performance Comparison from Delphi 2010 to Delphi XE6 (Part 6c – IECS Mobile App)

IECS Mobile App Execution (Speed) Tests

On the previous two pages, we looked at the Inference Engine Component Suite (IECS) Advanced Console speed tests and the IECS Basic Console speed tests on Win32, Win64, and OSX.  Our final IECS Speed tests are with the IECS Mobile app running on iOS and Android.  The IECS Mobile app provides the simple GUI console for executing expert systems on iOS and Android.  This app executes expert systems and can optionally send output to a TMemo.  It is much closer to the Basic Console than to the Advanced Console so we expect to see similar performance patterns to the IECS Basic Console.

IECS Mobile App, FMX, iOS

Since Delphi XE4, Delphi has supported deploying to iOS devices.  Note that the Delphi XE4 app was compiled with debug.  Delphi XE5 and XE6 can deploy release versions directly to the device without requiring certificates.  However, this was not true with Delphi XE4 but as we will see, it doesn’t seem to have slowed the app down.  As with the Hello World Mobile App, we tested our mobile app on an iPod Touch.

Comparison of execution speed for IECS Mobile App (iOS) between Delphi XE4 and XE6

Comparison of execution speed for IECS Mobile App (iOS) from Delphi XE4 to XE6

Delphi XE6 makes a very good showing on iOS.  It manages the fastest non-visual speed (no updates), being a 1/4 second faster than XE5 and over 2.5 seconds faster than XE4.  With live updates turned on, Delphi XE6 puts in a respectable second place finish.  Delphi XE4 dominates with live updates and finishes in 14.1 seconds compared to 18.2 seconds with XE6 and compared to the embarrassingly awful 50.3 seconds with XE5.

IECS Mobile App, FMX, Android

Since Delphi XE5, Delphi has supported deploying to Android devices.  In Android, Delphi XE5 has to plead no contest again.  While Delphi XE6 is only approximately 9% faster with no updates, once the TMemo is turned on, Delphi XE6 is twice as fast as Delphi XE5.

Comparison of execution speed for IECS Mobile App (Android) between Delphi XE5 and XE6

Comparison of execution speed for IECS Mobile App (Android) between Delphi XE5 and XE6

Coming up next week…

Well, that is it for this week.  The three IECS test applications showed the strength of Delphi XE4 and Delphi XE6.  In our final post, we are going to perform speed tests using the RiverSoftAVG SVG Component Library(RSCL).  The RSCL will only test Delphi XE2 through XE6 and will concentrate on FMX.  However, it renders SVG files using low-level canvas operations (gradients, paths, text, etc) and is available for VCL and FMX.  It uses GDI+ for VCL (which is a software renderer with comparable features to the FMX TCanvas) so it should provide a good test of the FMX DirectX and OpenGL hardware-based rendering.  The RSCL is also able to build SVGs using the FMX shape primitives (TRectangle, TPath, TText, etc); it should reveal any optimizations (or lack thereof) Embarcadero has been doing with the low-level primitives from which most FMX controls are built.

That’s it for this week.  I hope you are finding these blog posts informative.  Until next time, Happy CodeSmithing!