3

I have a ViewPort3D element in a multi-device application form that being filled with a large number of TRectangle3D elements (anywhere from 1 to 10000) with LightMaterialSource applied to them, which all need to be rendered dynamically since I'm also rotating the camera using the following procedure:

procedure TForm3.Viewport3D1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
var
  I: IViewport3D;
begin
  if ssRight in shift then
  begin
    I:=ViewPort3D1;
    with tdummy(I.CurrentCamera.Parent) do RotationAngle.X:=RotAng.X - Y;
    with tdummy(I.CurrentCamera.Parent.Parent) do RotationAngle.Y:=RotAng.Y + X;
  end;
end;

However the performance of the ViewPort3D begins to drop noticeably when the number of rectangles rendered approaches at least several dozen. The camera rotation gets slower and more unresponsive the more rectangles are added to the viewport up to the point of becoming a slideshow.

Is there a way to improve performance of the ViewPort3D without deleting said rectangles?

I've tried using setting the Multisample property to "none": ViewPort3d1.Context.SetMultisample(TMultisample.None) as well as removing MaterialSource from all the rectangles. While it did help a little with the performance, it didn't solve the problem entirely.

2 Answers2

1

10k drawed cube

For 10k in my tests

Context.Draw/Fill.cube

Drawing in the render event is much more efficient, but the performance drops significantly when the camera angle is changed.

TRectangle3D Creating 10k pieces turns into serious performance problems after 2k created and showed on windows.

if visible=false is set after TRectangle3D create, rendering is completed at very high speed,

however, when visible=true, there is a serious performance degradation when the camera angle is changed.

As far as I can see, the reason for the slowdown is the operations on the CPU, that is, the gpu is not the part that slows down here, but when I examine the codes, there is a constant high amount of event type message notifications going to on mousemove to objects.

my suggestion is if a lot of visible objects are going to be used, if objects are out of camera view, hide objects(visible=false), hide objects with a loop at each mousemove event, see if objects are in camera area or not, this will help performance.

There is not only 10k object problem here, it has many shortcomings. basically this one can be used as a simple drawing 3d engine for simple jobs.

As far as I know, in Java, just like the viewport3d object, we can add the unity engine graphical window to a form in a java application,

I don't know if unity engine graphics window can be added to delphi, u can request the support from embercadero, but for advanced graphics it would be more logical to use engines like unreal unity proffesional optimized engines.

  • Unfortunately, objects almost never go outside of camera view, so hiding them won't help, although that is a good idea. I've noticed, that when i rotate the camera, the GPU usage can spike up to 50% in task manager (GPU installed in that PC is quite beefy too), while CPU rests at regular 8%. – Crockinline Sep 19 '21 at 10:37
  • What is your processor model? how many cpu cores does it have? For example, if you have 12 cores, 100% CPU usage, 100 / 12 = 8.33 per core cpu usage, If your application does not exceed 8.33 in CPU usage, it means that the code is running on a single thread, which is the key point that limits performance, that is, the bottleneck. please check its. –  Oct 04 '21 at 16:19
0

Each high level fmx 3d object make a "drawcall." (wich reprocess all, from cpu (mesh preparation) to gpu (for diplay) -> You always have to minimize drawcall count.

So, displaying 10000 FMX rectangles is never a solution. :)

You have to make only 1 Tmesh descendant, witch will draw your 10000 "hand-made" rectangles in one call.

Please see TMesh.Data (data.points and data.triangleindice) to undestand how to draw a 3d object in direct mode. Or, more simply, see how "TPlane" (in source) is built.

As a general plan, and as a basic "3d making" approach, making "complex" 3d FMX object is possible, but you have to deal with vertices/indexes to draw much possible things in one call.

As an exemple, here a is the code to realize it :

put TMesh on your viewport, and call this proc :


Procedure PopulateMesh_RectangleMap(aMesh : TMesh; const xCount : integer = 100; const yCount : integer=100; const rectWidth : single = 2.0; const rectHeight : single= 1.0);
var lv,li : integer;
    lsx, lsy, xpos, ypos : single;
    i,j : integer;
    a,b,c,d : TPoint3d; //4 corner of a rect.
begin
  Assert(assigned(aMesh));

  lsx := rectWidth;
  lsy := rectHeight;
  li := 0;
  lv := 0;

  //mem. allocation.
  aMesh.Data.Clear;
  aMesh.Data.VertexBuffer.Length := 4 * xCount * yCount;     //4 vertices by rect, need k*k rects.
  aMesh.Data.IndexBuffer.Length := 6 * xCount * yCount;     // 6 indices for 2 triangles desc (to make 1 rect) for k*k rects.

  for i := 0 to xcount-1 do
  for j := 0 to ycount-1 do begin

    xpos := -xcount/2 + i + i*lsx;
    ypos := -ycount/2 + j + j*lsy;

    a := point3d(xpos - lsx/2,ypos - lsy/2,0);
    b := point3d(xpos + lsx/2,ypos - lsy/2,0);
    c := point3d(xpos + lsx/2,ypos + lsy/2,0);
    d := point3d(xpos - lsx/2,ypos + lsy/2,0);

    aMesh.Data.VertexBuffer.Vertices[li+0] := a;
    aMesh.Data.VertexBuffer.Vertices[li+1] := b;
    aMesh.Data.VertexBuffer.Vertices[li+2] := c;
    aMesh.Data.VertexBuffer.Vertices[li+3] := d;

    aMesh.Data.IndexBuffer.Indices[lv+0] := li+0;
    aMesh.Data.IndexBuffer.Indices[lv+1] := li+1;
    aMesh.Data.IndexBuffer.Indices[lv+2] := li+2;
    aMesh.Data.IndexBuffer.Indices[lv+3] := li+2;
    aMesh.Data.IndexBuffer.Indices[lv+4] := li+3;
    aMesh.Data.IndexBuffer.Indices[lv+5] := li+0;

    inc(li,4);
    inc(lv,6);
  end;
  aMesh.Data.BoundingBoxNeedsUpdate; //We touch data. Update Mesh container.
  aMesh.Width := lsx*3;              //keep proportion.
  aMesh.Height := lsy*3;
  aMesh.Repaint;
end;
Vincent
  • 21
  • 1
  • 3