x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

HISMC performance issue: C++ vs Blueprints

Hi! I'm working on a simple hex tile map generator as part of a research for my dissertation. I successfully recreated the blueprint from this video with some minor tweaks to accommodate hexagonal tiles. I also used hierarchical static meshes as I read that they can handle LOD. Next step was to implement this in C++ so I created a C++ class and a blueprint based on it. I get the same result regarding where the tile instances are placed in the world, however there is a noticeable performance drop for the C++ class for the same amount of tiles (from 80 FPS for blueprint, to 2-5 FPS for C++). Also, increasing the number of tiles is quite fast for the Blueprint version (100x100 tiles just a slight delay), but for the C++ instance it starts choking for more than 25x25 tiles. Both instances are using construction script to add the tiles.

To be more precise, the performance suffers only in the editor, but not during the game play. I've checked the GPU profiler and apparently biggest chunk of time is spent on PostProcessSelectionOutlineBuffer. So I tested selecting nothing, selecting the Blueprint instance and then selecting C++ instance. Sure enough, when nothing is selected editor runs at >100 FPS, with the Blueprint instance selected it drops to ~80 FPS and then with the C++ instance selected it freezes, fills the RAM to 4 GB (my machine has 8 GB total), and after several seconds it responds with frame rate below 5 FPS. Deselecting the C++ instance takes as long as well, and it frees up the RAM (1.4 GB with nothing selected). I hope I'm doing something very wrong in my code and thus having this performance hit and that someone will catch what is wrong. I'm using UE 4.21.1 built from source code. All screenshots are bellow the code.

.h file:

 #pragma once
 
 #include "CoreMinimal.h"
 #include "GameFramework/Actor.h"
 #include "MapGenerator.generated.h"
 
 class UHierarchicalInstancedStaticMeshComponent;
 class TileData;
 
 // TODO: Reference - https://answers.unrealengine.com/questions/551199/dynamic-2d-array-using-tarray.html
 // A helper struct for creating a 2D array
 USTRUCT()
 struct FTileData {
     GENERATED_BODY()
 public:
     TArray<TileData*> ColumnArray;
     TileData* operator[] (int32 i) { return ColumnArray[i]; }
     void Add(TileData* m) { ColumnArray.Add(m); }
 };
 
 UCLASS()
 class ACO_NPC_BEHAVIOUR_API AMapGenerator : public AActor
 {
     GENERATED_BODY()
     
 public:    
     // Sets default values for this actor's properties
     AMapGenerator();
 
 protected:
     // Called when the game starts or when spawned
     virtual void BeginPlay() override;
 
     // Gets called when instance of this class is placed in the scene
     virtual void OnConstruction(const FTransform& Transform) override;
 
 public:    
     // Called every frame
     virtual void Tick(float DeltaTime) override;
     
     UPROPERTY(VisibleDefaultsOnly, Category = "Generator Setup")
     USceneComponent* Root;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup|Tile Types")
     UHierarchicalInstancedStaticMeshComponent* InstancedSMC_Generic;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup|Tile Types")
     UHierarchicalInstancedStaticMeshComponent* InstancedSMC_Plain;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup|Tile Types")
     UHierarchicalInstancedStaticMeshComponent* InstancedSMC_Water;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     UStaticMesh* TileStaticMesh;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     int32 Rows = 8;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     int32 Columns = 8;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     float TilePitch = 0.f;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     float TileYaw = 90.f;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     float TileRoll = 0.f;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     float TileScaleX = 1.f;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     float TileScaleY = 1.f;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     float TileScaleZ = 1.f;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     float TileLength = 200.f;
 
     UPROPERTY(EditAnywhere, Category = "Generator Setup")
     float TileHeight = 173.2051f;
 
     UPROPERTY(VisibleAnywhere, Category = "Generator Setup|Info")
     TArray<FTileData> TileDataSet;
 
     UPROPERTY(VisibleAnywhere, Category = "Generator Setup|Info")
     TMap<FString, FTileData> TileDataMap;
     
 };
 

.cpp file:

 // Fill out your copyright notice in the Description page of Project Settings.
 
 #include "MapGenerator.h"
 
 // Game includes
 #include "Public/TileData.h"
 
 // Engine includes
 #include "Engine/StaticMesh.h"
 #include "Components/SceneComponent.h"
 #include "Components/HierarchicalInstancedStaticMeshComponent.h"
 
 // Sets default values
 AMapGenerator::AMapGenerator()
 {
      // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
     PrimaryActorTick.bCanEverTick = true;
 
     Root = CreateDefaultSubobject<USceneComponent>(TEXT("RootSceneComponent"));
     if (!SetRootComponent(Root))
     {
         UE_LOG(LogTemp, Error, TEXT("Unable to set the Root Component in MapGenerator.cpp!"));
     }
 
     InstancedSMC_Generic = CreateDefaultSubobject<UHierarchicalInstancedStaticMeshComponent>(TEXT("HierarchicalInstancedSMC_Generic"));
     InstancedSMC_Generic->AttachToComponent(Root, FAttachmentTransformRules::KeepRelativeTransform);
 
     InstancedSMC_Plain = CreateDefaultSubobject<UHierarchicalInstancedStaticMeshComponent>(TEXT("HierarchicalInstancedSMC_Plain"));
     InstancedSMC_Plain->AttachToComponent(Root, FAttachmentTransformRules::KeepRelativeTransform);
 
     InstancedSMC_Water = CreateDefaultSubobject<UHierarchicalInstancedStaticMeshComponent>(TEXT("HierarchicalInstancedSMC_Water"));
     InstancedSMC_Water->AttachToComponent(Root, FAttachmentTransformRules::KeepRelativeTransform);
 
 
 }
 
 // Called when the game starts or when spawned
 void AMapGenerator::BeginPlay()
 {
     Super::BeginPlay();
 
 }
 
 // Called every frame
 void AMapGenerator::Tick(float DeltaTime)
 {
     Super::Tick(DeltaTime);
 
 }
 
 // Construction script
 void AMapGenerator::OnConstruction(const FTransform& Transform)
 {
     Super::OnConstruction(Transform);
 
     // Null-checks
     if (InstancedSMC_Generic == nullptr
         || InstancedSMC_Plain == nullptr
         || InstancedSMC_Water == nullptr
         || Root == nullptr) return;
 
 
     //Register all the components
     RegisterAllComponents();
 
     // Hierarchical Static Mesh Component setup
     InstancedSMC_Generic->CreationMethod = EComponentCreationMethod::UserConstructionScript;
     InstancedSMC_Plain->CreationMethod = EComponentCreationMethod::UserConstructionScript;
     InstancedSMC_Water->CreationMethod = EComponentCreationMethod::UserConstructionScript;
 
     InstancedSMC_Generic->ClearInstances();
     InstancedSMC_Plain->ClearInstances();
     InstancedSMC_Water->ClearInstances();
 
     InstancedSMC_Generic->SetFlags(RF_Transactional);
     InstancedSMC_Plain->SetFlags(RF_Transactional);
     InstancedSMC_Water->SetFlags(RF_Transactional);
 
     const auto tileHeight = TileLength * FMath::Sqrt(3) / 2;
     const auto tileOffsetX = 3 * TileLength / 4;
     const auto tileOffsetY = tileHeight / 2;
     
     for (auto i = 0; i < Rows; i++)
     {
         for (auto j = 0; j < Columns; j++)
         {
             FVector location;
             location.X = GetRootComponent()->GetComponentLocation().X + j * tileOffsetX;
             location.Y = GetRootComponent()->GetComponentLocation().Y + i * tileHeight;
             if (j % 2 != 0) { location.Y = location.Y - tileOffsetY; }
             location.Z = GetRootComponent()->GetComponentLocation().Z;
 
             FTransform transform(
                 FRotator(TilePitch, TileYaw, TileRoll),
                 location,
                 FVector(TileScaleX, TileScaleY, TileScaleZ)
             );
 
             if (i % 3 == 0 && j % 3 == 0)
             {
                 InstancedSMC_Generic->AddInstanceWorldSpace(transform);
             }
             else if (i % 5 == 0 && j % 5 == 0)
             {
                 InstancedSMC_Water->AddInstanceWorldSpace(transform);
             }
             else if (i % 7 == 0 && j % 7 == 0)
             {
                 // No tiles
             }
             else
             {
                 InstancedSMC_Plain->AddInstanceWorldSpace(transform);
             }
 
             // TODO: maybe replace this array with DataTable
             // TileDataSet[i].Add(NewTileData);
         }
     }
 
     // UE_LOG(LogTemp, Warning, TEXT("Total instances: %d"),
     //     InstancedSMC_Generic->GetInstanceCount() 
     //     + InstancedSMC_Plain->GetInstanceCount() 
     //     + InstancedSMC_Water->GetInstanceCount()
     // );
 
 }


GPU profiler: alt text

No selection: alt text

Blueprint selection: alt text

C++ selection: alt text

EDIT: crucial section of the pure blueprint - the beginning of it is just calculating hex specific stuff alt text

Product Version: UE 4.21
Tags:
gpu-profiler.png (154.8 kB)
cpp-selection.jpg (114.4 kB)
bp-selection.jpg (111.3 kB)
noselection.jpg (119.0 kB)
blueprint.jpg (337.5 kB)
more ▼

asked Jan 22 '19 at 03:06 PM in C++ Programming

avatar image

CiborgCG
16 6 5

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

1 answer: sort voted first

With no luck in solving the performance issue in the editor when the C++ based blueprint is selected, I turned to searching for a workaround until some feedback on this comes through. The idea is simple, how to modify values in a blueprint and have them reflected to any of its instances in the level. Luckily, user DEDRICK had a solution to that here.

So, for now the C++ version works OK as long as its instance in the level is not selected. Instead having a child blueprint will behave nicely. Which leads me to ask why we have this behaviour with pure blueprint instance straight out of the box, but with C++ based blueprints this is not the case?

more ▼

answered Feb 01 '19 at 09:45 PM

avatar image

CiborgCG
16 6 5

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question