├── .gitignore ├── Clone.sh ├── HelloSlate ├── HelloSlate.Build.cs ├── HelloSlate.Target.cs ├── Private │ ├── HelloSlateApp.cpp │ ├── IOS │ │ └── IOSHelloSlateMain.cpp │ ├── Linux │ │ └── HelloSlateMainLinux.cpp │ ├── Mac │ │ └── HelloSlateMainMac.cpp │ └── Windows │ │ └── HelloSlateMainWindows.cpp ├── Public │ ├── HelloSlate.h │ └── HelloSlateApp.h └── Resources │ └── Windows │ └── HelloSlate.ico ├── HelloUECpp ├── HelloUECpp.Build.cs ├── HelloUECpp.Target.cs ├── Private │ ├── HelloUECpp.cpp │ └── HelloUECpp.h └── Resources │ └── Windows │ └── HelloUECpp.ico ├── LICENSE ├── MyBlankProgram ├── MyBlankProgram.Build.cs ├── MyBlankProgram.Target.cs ├── Private │ ├── MyBlankDerivedObject.cpp │ ├── MyBlankDerivedObject.h │ ├── MyBlankObject.cpp │ ├── MyBlankObject.h │ ├── MyBlankProgram.cpp │ ├── MyBlankProgram.h │ ├── MyEnum.cpp │ ├── MyEnum.h │ ├── MyInterface.cpp │ ├── MyInterface.h │ ├── MyLog.cpp │ ├── MyLog.h │ ├── MyStruct.cpp │ └── MyStruct.h └── Resources │ └── Windows │ └── MyBlankProgram.ico ├── README.md └── SnippetAsync ├── Private ├── PrimeNumberGraphTask.cpp ├── PrimeNumberGraphTask.h ├── PrimeNumberThread.cpp ├── PrimeNumberThread.h ├── RNGThread.cpp ├── RNGThread.h ├── SimpleAsync.h ├── SimpleAsyncTask.h ├── SimpleGraphTask.h ├── SimpleLockFree.h ├── SimpleProducerConsumer.h ├── SimpleQueuedWorker.h ├── SimpleThread.cpp ├── SimpleThread.h ├── SnippetAsync.cpp └── SnippetAsync.h ├── Resources └── Windows │ └── SnippetAsync.ico ├── SnippetAsync.Build.cs └── SnippetAsync.Target.cs /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /Clone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# -eq 0 ]; then 4 | echo "Usage1 : $0 Dst - Clone MyBlankProgram to Dst" 5 | echo "Usage2 : $0 Src Dst - Clone Src to Dst" 6 | exit 1 7 | fi 8 | 9 | SNIPPET_FROM=MyBlankProgram 10 | 11 | if [ $# -eq 1 ]; then 12 | SNIPPET_TO=$1 13 | elif [ $# -eq 2 ]; then 14 | SNIPPET_FROM=$1 15 | SNIPPET_TO=$2 16 | fi 17 | 18 | echo ">>>>>>> Clone ... $SNIPPET_FROM -> $SNIPPET_TO" 19 | 20 | rm -rf $SNIPPET_TO/ 21 | cp -Rf $SNIPPET_FROM $SNIPPET_TO/ 22 | 23 | # Rename 24 | for file in `find $SNIPPET_TO/`; do 25 | newfile=${file/${SNIPPET_FROM}/${SNIPPET_TO}} 26 | if [ "$file" != "$newfile" ]; then 27 | echo ">> $file -> $newfile" 28 | mv -v $file $newfile 29 | 30 | # Replace Content 31 | if [ "$file" != "${SNIPPET_TO}//Resources/Windows/MyBlankProgram.ico" ]; then 32 | sed -i "" "s/${SNIPPET_FROM}/${SNIPPET_TO}/g" $newfile 33 | fi 34 | fi 35 | done 36 | 37 | -------------------------------------------------------------------------------- /HelloSlate/HelloSlate.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | using UnrealBuildTool; 3 | 4 | public class HelloSlate : ModuleRules 5 | { 6 | public HelloSlate(ReadOnlyTargetRules Target) : base(Target) 7 | { 8 | PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Runtime/Launch/Public")); 9 | // PublicIncludePaths.Add("Runtime/Launch/Public"); 10 | 11 | PrivateDependencyModuleNames.AddRange( 12 | new string[] { 13 | "AppFramework", 14 | "Core", 15 | "ApplicationCore", 16 | "Projects", 17 | "Slate", 18 | "SlateCore", 19 | "StandaloneRenderer", 20 | "SourceCodeAccess", 21 | "WebBrowser", 22 | } 23 | ); 24 | 25 | PrivateIncludePathModuleNames.AddRange( 26 | new string[] { 27 | "SlateReflector", 28 | } 29 | ); 30 | 31 | DynamicallyLoadedModuleNames.AddRange( 32 | new string[] { 33 | "SlateReflector", 34 | } 35 | ); 36 | 37 | if (Target.Platform == UnrealTargetPlatform.Mac) 38 | { 39 | PrivateDependencyModuleNames.Add("XCodeSourceCodeAccess"); 40 | AddEngineThirdPartyPrivateStaticDependencies(Target, "CEF3"); 41 | } 42 | else if (Target.Platform == UnrealTargetPlatform.Win64) 43 | { 44 | PrivateDependencyModuleNames.Add("VisualStudioSourceCodeAccess"); 45 | } 46 | 47 | PrivateIncludePaths.Add("Runtime/Launch/Private"); // For LaunchEngineLoop.cpp include 48 | 49 | if (Target.Platform == UnrealTargetPlatform.IOS || Target.Platform == UnrealTargetPlatform.TVOS) 50 | { 51 | PrivateDependencyModuleNames.AddRange( 52 | new string [] { 53 | "NetworkFile", 54 | "StreamingFile" 55 | } 56 | ); 57 | } 58 | 59 | if (Target.IsInPlatformGroup(UnrealPlatformGroup.Linux)) 60 | { 61 | PrivateDependencyModuleNames.AddRange( 62 | new string[] { 63 | "UnixCommonStartup" 64 | } 65 | ); 66 | } 67 | 68 | // PublicDefinitions.Add("UE_ENGINE_DIRECTORY=/Users/david/UnrealEngine4.26/Engine/"); 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /HelloSlate/HelloSlate.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | [SupportedPlatforms(UnrealPlatformClass.Desktop)] 7 | public class HelloSlateTarget : TargetRules 8 | { 9 | public HelloSlateTarget(TargetInfo Target) : base(Target) 10 | { 11 | Type = TargetType.Program; 12 | LinkType = TargetLinkType.Monolithic; 13 | LaunchModuleName = "HelloSlate"; 14 | 15 | SolutionDirectory = "UESnippets"; 16 | 17 | ExtraModuleNames.Add("EditorStyle"); 18 | 19 | bBuildDeveloperTools = false; 20 | 21 | // HelloSlate doesn't ever compile with the engine linked in 22 | bCompileAgainstEngine = false; 23 | 24 | // We need CoreUObject compiled in as the source code access module requires it 25 | bCompileAgainstCoreUObject = true; 26 | 27 | // HelloSlate.exe has no exports, so no need to verify that a .lib and .exp file was emitted by 28 | // the linker. 29 | bHasExports = false; 30 | 31 | // Make sure to get all code in SlateEditorStyle compiled in 32 | bBuildDeveloperTools = true; 33 | 34 | // UnrealHeaderTool is a console application, not a Windows app (sets entry point to main(), instead of WinMain()) 35 | bIsBuildingConsoleApplication = false; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /HelloSlate/Private/HelloSlateApp.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "HelloSlateApp.h" 4 | #include "RequiredProgramMainCPPInclude.h" 5 | #include "Widgets/Testing/STestSuite.h" 6 | #include "ISourceCodeAccessModule.h" 7 | #include "Widgets/Testing/SPerfSuite.h" 8 | #include "Widgets/Docking/SDockTab.h" 9 | #include "SWebBrowser.h" 10 | #include "Framework/Application/SlateApplication.h" 11 | #include "IWebBrowserWindow.h" 12 | #include "IWebBrowserPopupFeatures.h" 13 | 14 | IMPLEMENT_APPLICATION(HelloSlate, "HelloSlate"); 15 | 16 | #define LOCTEXT_NAMESPACE "HelloSlate" 17 | 18 | namespace WorkspaceMenu 19 | { 20 | TSharedRef DeveloperMenu = FWorkspaceItem::NewGroup(LOCTEXT("DeveloperMenu", "Developer")); 21 | } 22 | 23 | 24 | int RunSimpleGUI(const TCHAR* CommandLine) 25 | { 26 | // start up the main loop 27 | GEngineLoop.PreInit(CommandLine); 28 | 29 | FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer()); 30 | 31 | // create a test window 32 | FGlobalTabmanager::Get()->SetApplicationTitle(LOCTEXT("AppTitle", "Hello Slate")); 33 | TSharedPtr SlateWin = SNew(SWindow) 34 | .Title(LOCTEXT("HelloSlate", "Hello Slate App!")) 35 | .ClientSize(FVector2D(900, 600)) 36 | .AutoCenter(EAutoCenter::None); 37 | 38 | FSlateApplication::Get().AddWindow(SlateWin.ToSharedRef()); 39 | 40 | while (!IsEngineExitRequested()) 41 | { 42 | FSlateApplication::Get().Tick(); 43 | FSlateApplication::Get().PumpMessages(); 44 | FPlatformProcess::Sleep(0.01); 45 | } 46 | FCoreDelegates::OnExit.Broadcast(); 47 | FSlateApplication::Shutdown(); 48 | FModuleManager::Get().UnloadModulesAtShutdown(); 49 | 50 | return 0; 51 | } 52 | 53 | int RunHelloSlate(const TCHAR* CommandLine) 54 | { 55 | // Set your engine dir 56 | GForeignEngineDir = TEXT("/Users/david/UnrealEngine/Engine/"); 57 | printf("Engine: > %s : %s : %s\n", UE_ENGINE_DIRECTORY, TCHAR_TO_ANSI(GForeignEngineDir), 58 | TCHAR_TO_ANSI(*FPaths::EngineDir())); 59 | 60 | 61 | return RunSimpleGUI(CommandLine); 62 | 63 | // start up the main loop 64 | GEngineLoop.PreInit(CommandLine); 65 | 66 | // Make sure all UObject classes are registered and default properties have been initialized 67 | ProcessNewlyLoadedUObjects(); 68 | 69 | // Tell the module manager it may now process newly-loaded UObjects when new C++ modules are loaded 70 | FModuleManager::Get().StartProcessingNewlyLoadedObjects(); 71 | 72 | // crank up a normal Slate application using the platform's standalone renderer 73 | FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer()); 74 | 75 | // Load the source code access module 76 | ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked( 77 | FName("SourceCodeAccess")); 78 | 79 | // Manually load in the source code access plugins, as standalone programs don't currently support plugins. 80 | #if PLATFORM_MAC 81 | IModuleInterface& XCodeSourceCodeAccessModule = FModuleManager::LoadModuleChecked( 82 | FName("XCodeSourceCodeAccess")); 83 | SourceCodeAccessModule.SetAccessor(FName("XCodeSourceCodeAccess")); 84 | #elif PLATFORM_WINDOWS 85 | IModuleInterface& VisualStudioSourceCodeAccessModule = FModuleManager::LoadModuleChecked( FName( "VisualStudioSourceCodeAccess" ) ); 86 | SourceCodeAccessModule.SetAccessor(FName("VisualStudioSourceCodeAccess")); 87 | #endif 88 | 89 | // set the application name 90 | FGlobalTabmanager::Get()->SetApplicationTitle(LOCTEXT("AppTitle", "Slate Viewer")); 91 | FModuleManager::LoadModuleChecked("SlateReflector").RegisterTabSpawner( 92 | WorkspaceMenu::DeveloperMenu); 93 | 94 | FGlobalTabmanager::Get()->RegisterNomadTabSpawner("WebBrowserTab", FOnSpawnTab::CreateStatic(&SpawnWebBrowserTab)) 95 | .SetDisplayName(LOCTEXT("WebBrowserTab", "Web Browser")); 96 | 97 | if (FParse::Param(FCommandLine::Get(), TEXT("perftest"))) 98 | { 99 | // Bring up perf test 100 | SummonPerfTestSuite(); 101 | } 102 | else 103 | { 104 | // Bring up the test suite. 105 | RestoreSlateTestSuite(); 106 | } 107 | 108 | 109 | #if WITH_SHARED_POINTER_TESTS 110 | SharedPointerTesting::TestSharedPointer(); 111 | SharedPointerTesting::TestSharedPointer(); 112 | #endif 113 | 114 | // loop while the server does the rest 115 | while (!IsEngineExitRequested()) 116 | { 117 | FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread); 118 | FStats::AdvanceFrame(false); 119 | FTicker::GetCoreTicker().Tick(FApp::GetDeltaTime()); 120 | FSlateApplication::Get().PumpMessages(); 121 | FSlateApplication::Get().Tick(); 122 | FPlatformProcess::Sleep(0); 123 | } 124 | 125 | FCoreDelegates::OnExit.Broadcast(); 126 | FSlateApplication::Shutdown(); 127 | FModuleManager::Get().UnloadModulesAtShutdown(); 128 | 129 | 130 | return 0; 131 | } 132 | 133 | namespace 134 | { 135 | TMap, TWeakPtr> BrowserWindowWidgets; 136 | 137 | bool HandleBeforePopup(FString Url, FString Target) 138 | { 139 | return false; // Allow any popup 140 | } 141 | 142 | bool HandleBrowserCloseWindow(const TWeakPtr& BrowserWindowPtr) 143 | { 144 | TSharedPtr BrowserWindow = BrowserWindowPtr.Pin(); 145 | if (BrowserWindow.IsValid()) 146 | { 147 | if (!BrowserWindow->IsClosing()) 148 | { 149 | // If the browser is not set to close, we tell the browser to close which will call back into this handler function. 150 | BrowserWindow->CloseBrowser(false); 151 | } 152 | else 153 | { 154 | const TWeakPtr* FoundBrowserWindowWidgetPtr = BrowserWindowWidgets.Find(BrowserWindow); 155 | if (FoundBrowserWindowWidgetPtr != nullptr) 156 | { 157 | TSharedPtr FoundBrowserWindowWidget = FoundBrowserWindowWidgetPtr->Pin(); 158 | if (FoundBrowserWindowWidget.IsValid()) 159 | { 160 | FoundBrowserWindowWidget->RequestDestroyWindow(); 161 | } 162 | BrowserWindowWidgets.Remove(BrowserWindow); 163 | return true; 164 | } 165 | } 166 | } 167 | 168 | return false; 169 | } 170 | 171 | bool HandleBrowserCreateWindow(const TWeakPtr& NewBrowserWindow, 172 | const TWeakPtr& PopupFeatures, 173 | TSharedPtr ParentWindow) 174 | { 175 | if (ParentWindow.IsValid()) 176 | { 177 | TSharedPtr PopupFeaturesSP = PopupFeatures.Pin(); 178 | check(PopupFeatures.IsValid()) 179 | 180 | const int PosX = PopupFeaturesSP->IsXSet() ? PopupFeaturesSP->GetX() : 100; 181 | const int PosY = PopupFeaturesSP->IsYSet() ? PopupFeaturesSP->GetY() : 100; 182 | const FVector2D BrowserWindowPosition(PosX, PosY); 183 | 184 | const int Width = PopupFeaturesSP->IsWidthSet() ? PopupFeaturesSP->GetWidth() : 800; 185 | const int Height = PopupFeaturesSP->IsHeightSet() ? PopupFeaturesSP->GetHeight() : 600; 186 | const FVector2D BrowserWindowSize(Width, Height); 187 | 188 | const ESizingRule SizeingRule = PopupFeaturesSP->IsResizable() 189 | ? ESizingRule::UserSized 190 | : ESizingRule::FixedSize; 191 | 192 | TSharedPtr NewBrowserWindowSP = NewBrowserWindow.Pin(); 193 | check(NewBrowserWindowSP.IsValid()) 194 | 195 | TSharedRef BrowserWindowWidget = 196 | SNew(SWindow) 197 | .Title(LOCTEXT("WebBrowserWindow_Title", "Web Browser")) 198 | .ClientSize(BrowserWindowSize) 199 | .ScreenPosition(BrowserWindowPosition) 200 | .AutoCenter(EAutoCenter::None) 201 | .SizingRule(SizeingRule) 202 | .SupportsMaximize(SizeingRule != ESizingRule::FixedSize) 203 | .SupportsMinimize(SizeingRule != ESizingRule::FixedSize) 204 | .HasCloseButton(true) 205 | .IsInitiallyMaximized(PopupFeaturesSP->IsFullscreen()) 206 | .LayoutBorder(FMargin(0)); 207 | 208 | // Setup browser widget. 209 | TSharedPtr BrowserWidget; 210 | { 211 | TSharedPtr Contents; 212 | BrowserWindowWidget->SetContent( 213 | SNew(SBorder) 214 | .VAlign(VAlign_Fill) 215 | .HAlign(HAlign_Fill) 216 | .Padding(0) 217 | [ 218 | SAssignNew(Contents, SVerticalBox) 219 | ]); 220 | 221 | Contents->AddSlot() 222 | [ 223 | SAssignNew(BrowserWidget, SWebBrowser, NewBrowserWindowSP) 224 | .ShowControls(PopupFeaturesSP->IsToolBarVisible()) 225 | .ShowAddressBar(PopupFeaturesSP->IsLocationBarVisible()) 226 | .OnCreateWindow(FOnCreateWindowDelegate::CreateStatic(&HandleBrowserCreateWindow, ParentWindow)) 227 | .OnCloseWindow(FOnCloseWindowDelegate::CreateStatic(&HandleBrowserCloseWindow)) 228 | ]; 229 | } 230 | 231 | // Setup some OnClose stuff. 232 | { 233 | struct FLocal 234 | { 235 | static void RequestDestroyWindowOverride(const TSharedRef& Window, 236 | TWeakPtr BrowserWindowPtr) 237 | { 238 | TSharedPtr BrowserWindow = BrowserWindowPtr.Pin(); 239 | if (BrowserWindow.IsValid()) 240 | { 241 | if (BrowserWindow->IsClosing()) 242 | { 243 | FSlateApplicationBase::Get().RequestDestroyWindow(Window); 244 | } 245 | else 246 | { 247 | // Notify the browser window that we would like to close it. On the CEF side, this will 248 | // result in a call to FWebBrowserHandler::DoClose only if the JavaScript onbeforeunload 249 | // event handler allows it. 250 | BrowserWindow->CloseBrowser(false); 251 | } 252 | } 253 | } 254 | }; 255 | 256 | BrowserWindowWidget->SetRequestDestroyWindowOverride( 257 | FRequestDestroyWindowOverride::CreateStatic(&FLocal::RequestDestroyWindowOverride, 258 | TWeakPtr(NewBrowserWindow))); 259 | } 260 | 261 | FSlateApplication::Get().AddWindowAsNativeChild(BrowserWindowWidget, ParentWindow.ToSharedRef()); 262 | BrowserWindowWidget->BringToFront(); 263 | FSlateApplication::Get().SetKeyboardFocus(BrowserWidget, EFocusCause::SetDirectly); 264 | 265 | BrowserWindowWidgets.Add(NewBrowserWindow, BrowserWindowWidget); 266 | return true; 267 | } 268 | return false; 269 | } 270 | } 271 | 272 | TSharedRef SpawnWebBrowserTab(const FSpawnTabArgs& Args) 273 | { 274 | return SNew(SDockTab) 275 | .Label(LOCTEXT("WebBrowserTab", "Web Browser")) 276 | .ToolTipText(LOCTEXT("WebBrowserTabToolTip", "Switches to the Web Browser to test its features.")) 277 | .TabRole(ETabRole::NomadTab) 278 | [ 279 | SNew(SWebBrowser) 280 | .OnCreateWindow(FOnCreateWindowDelegate::CreateStatic(&HandleBrowserCreateWindow, Args.GetOwnerWindow())) 281 | .OnCloseWindow(FOnCloseWindowDelegate::CreateStatic(&HandleBrowserCloseWindow)) 282 | .ParentWindow(Args.GetOwnerWindow()) 283 | ]; 284 | } 285 | 286 | #undef LOCTEXT_NAMESPACE 287 | -------------------------------------------------------------------------------- /HelloSlate/Private/IOS/IOSHelloSlateMain.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "HelloSlateApp.h" 4 | #include "IOS/IOSAppDelegate.h" 5 | #include "IOS/IOSCommandLineHelper.h" 6 | #include "IOS/SlateOpenGLESView.h" 7 | #include "Widgets/Testing/STestSuite.h" 8 | 9 | #import 10 | 11 | 12 | #define IOS_MAX_PATH 1024 13 | #define CMD_LINE_MAX 16384 14 | 15 | FString GSavedCommandLine; 16 | 17 | void FAppEntry::Suspend() 18 | { 19 | } 20 | 21 | void FAppEntry::Resume() 22 | { 23 | } 24 | 25 | void FAppEntry::SuspendTick() 26 | { 27 | } 28 | 29 | bool FAppEntry::IsStartupMoviePlaying() 30 | { 31 | return false; 32 | } 33 | 34 | 35 | void FAppEntry::PreInit(IOSAppDelegate* AppDelegate, UIApplication* Application) 36 | { 37 | // make a controller object 38 | AppDelegate.SlateController = [[SlateOpenGLESViewController alloc] init]; 39 | 40 | // property owns it now 41 | [AppDelegate.SlateController release]; 42 | 43 | // point to the GL view we want to use 44 | AppDelegate.RootView = [AppDelegate.SlateController view]; 45 | 46 | [AppDelegate.Window setRootViewController:AppDelegate.SlateController]; 47 | } 48 | 49 | 50 | void FAppEntry::PlatformInit() 51 | { 52 | } 53 | 54 | 55 | void FAppEntry::Init() 56 | { 57 | // start up the main loop 58 | GEngineLoop.PreInit(FCommandLine::Get()); 59 | 60 | // move it to this thread 61 | SlateOpenGLESView* View = (SlateOpenGLESView*)[IOSAppDelegate GetDelegate].RootView; 62 | [EAGLContext setCurrentContext:View.Context]; 63 | 64 | // crank up a normal Slate application using the platform's standalone renderer 65 | FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer()); 66 | 67 | // Bring up the test suite. 68 | { 69 | RestoreSlateTestSuite(); 70 | } 71 | 72 | #if WITH_SHARED_POINTER_TESTS 73 | SharedPointerTesting::TestSharedPointer< ESPMode::Fast >(); 74 | SharedPointerTesting::TestSharedPointer< ESPMode::ThreadSafe >(); 75 | #endif 76 | 77 | // loop while the server does the rest 78 | double LastTime = FPlatformTime::Seconds(); 79 | } 80 | 81 | 82 | void FAppEntry::Tick() 83 | { 84 | FSlateApplication::Get().PumpMessages(); 85 | FSlateApplication::Get().Tick(); 86 | 87 | // Sleep 88 | FPlatformProcess::Sleep( 0 ); 89 | } 90 | 91 | 92 | void FAppEntry::Shutdown() 93 | { 94 | FSlateApplication::Shutdown(); 95 | } 96 | 97 | 98 | int main(int argc, char *argv[]) 99 | { 100 | for (int32 Option = 1; Option < argc; Option++) 101 | { 102 | GSavedCommandLine += TEXT(" "); 103 | GSavedCommandLine += ANSI_TO_TCHAR(argv[Option]); 104 | } 105 | 106 | FIOSCommandLineHelper::InitCommandArgs(FString()); 107 | 108 | @autoreleasepool { 109 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([IOSAppDelegate class])); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /HelloSlate/Private/Linux/HelloSlateMainLinux.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "HelloSlateApp.h" 4 | #include "UnixCommonStartup.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | return CommonUnixMain(argc, argv, &RunHelloSlate); 9 | } 10 | -------------------------------------------------------------------------------- /HelloSlate/Private/Mac/HelloSlateMainMac.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "HelloSlateApp.h" 4 | #include "HAL/ExceptionHandling.h" 5 | #include "Mac/CocoaThread.h" 6 | 7 | static FString GSavedCommandLine; 8 | 9 | @interface UE4AppDelegate : NSObject 10 | { 11 | } 12 | 13 | @end 14 | 15 | @implementation UE4AppDelegate 16 | 17 | //handler for the quit apple event used by the Dock menu 18 | - (void)handleQuitEvent:(NSAppleEventDescriptor*)Event withReplyEvent:(NSAppleEventDescriptor*)ReplyEvent 19 | { 20 | [NSApp terminate:self]; 21 | } 22 | 23 | - (void) runGameThread:(id)Arg 24 | { 25 | FPlatformMisc::SetGracefulTerminationHandler(); 26 | FPlatformMisc::SetCrashHandler(nullptr); 27 | 28 | RunHelloSlate(*GSavedCommandLine); 29 | 30 | [NSApp terminate: self]; 31 | } 32 | 33 | - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)Sender; 34 | { 35 | if(!IsEngineExitRequested() || ([NSThread gameThread] && [NSThread gameThread] != [NSThread mainThread])) 36 | { 37 | RequestEngineExit(TEXT("applicationShouldTerminate")); 38 | return NSTerminateLater; 39 | } 40 | else 41 | { 42 | return NSTerminateNow; 43 | } 44 | } 45 | 46 | - (void)applicationDidFinishLaunching:(NSNotification *)Notification 47 | { 48 | //install the custom quit event handler 49 | NSAppleEventManager* appleEventManager = [NSAppleEventManager sharedAppleEventManager]; 50 | [appleEventManager setEventHandler:self andSelector:@selector(handleQuitEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; 51 | 52 | RunGameThread(self, @selector(runGameThread:)); 53 | } 54 | 55 | @end 56 | 57 | int main(int argc, char *argv[]) 58 | { 59 | for (int32 Option = 1; Option < argc; Option++) 60 | { 61 | GSavedCommandLine += TEXT(" "); 62 | FString Argument(ANSI_TO_TCHAR(argv[Option])); 63 | if (Argument.Contains(TEXT(" "))) 64 | { 65 | if (Argument.Contains(TEXT("="))) 66 | { 67 | FString ArgName; 68 | FString ArgValue; 69 | Argument.Split( TEXT("="), &ArgName, &ArgValue ); 70 | Argument = FString::Printf( TEXT("%s=\"%s\""), *ArgName, *ArgValue ); 71 | } 72 | else 73 | { 74 | Argument = FString::Printf(TEXT("\"%s\""), *Argument); 75 | } 76 | } 77 | GSavedCommandLine += Argument; 78 | } 79 | 80 | SCOPED_AUTORELEASE_POOL; 81 | [NSApplication sharedApplication]; 82 | [NSApp setDelegate:[UE4AppDelegate new]]; 83 | [NSApp run]; 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /HelloSlate/Private/Windows/HelloSlateMainWindows.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #include "HelloSlateApp.h" 5 | #include "Windows/WindowsHWrapper.h" 6 | 7 | 8 | /** 9 | * WinMain, called when the application is started 10 | */ 11 | int WINAPI WinMain( _In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR, _In_ int nCmdShow ) 12 | { 13 | // do the slate viewer thing 14 | RunHelloSlate(GetCommandLineW()); 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /HelloSlate/Public/HelloSlate.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "StandaloneRenderer.h" 8 | 9 | -------------------------------------------------------------------------------- /HelloSlate/Public/HelloSlateApp.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "ISlateReflectorModule.h" 7 | #include "HelloSlate.h" 8 | #include "Widgets/Docking/SDockTab.h" 9 | 10 | 11 | /** 12 | * Run the HelloSlate . 13 | */ 14 | int RunHelloSlate(const TCHAR* Commandline); 15 | 16 | /** 17 | * Spawn the contents of the web browser tab 18 | */ 19 | TSharedRef SpawnWebBrowserTab(const FSpawnTabArgs& Args); 20 | -------------------------------------------------------------------------------- /HelloSlate/Resources/Windows/HelloSlate.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-pp/UESnippets/a740f23307fef972c9d6c107ed23f6ff9cc4f0ea/HelloSlate/Resources/Windows/HelloSlate.ico -------------------------------------------------------------------------------- /HelloUECpp/HelloUECpp.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class HelloUECpp : ModuleRules 6 | { 7 | public HelloUECpp(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | 10 | PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Runtime/Launch/Public")); 11 | PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Runtime/Launch/Private")); // For LaunchEngineLoop.cpp include 12 | 13 | PrivateDependencyModuleNames.Add("Core"); 14 | PrivateDependencyModuleNames.Add("Projects"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /HelloUECpp/HelloUECpp.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | [SupportedPlatforms(UnrealPlatformClass.Desktop)] 7 | public class HelloUECppTarget : TargetRules 8 | { 9 | public HelloUECppTarget(TargetInfo Target) : base(Target) 10 | { 11 | Type = TargetType.Program; 12 | LinkType = TargetLinkType.Monolithic; 13 | LaunchModuleName = "HelloUECpp"; 14 | 15 | SolutionDirectory = "UESnippets"; 16 | 17 | // Lean and mean 18 | bBuildDeveloperTools = false; 19 | 20 | bCompileICU = true; 21 | 22 | // Never use malloc profiling in Unreal Header Tool. We set this because often UHT is compiled right before the engine 23 | // automatically by Unreal Build Tool, but if bUseMallocProfiler is defined, UHT can operate incorrectly. 24 | bUseMallocProfiler = false; 25 | 26 | // Editor-only data, however, is needed 27 | bBuildWithEditorOnlyData = true; 28 | 29 | // Currently this app is not linking against the engine, so we'll compile out references from Core to the rest of the engine 30 | bCompileAgainstEngine = false; 31 | bCompileAgainstCoreUObject = false; 32 | bCompileAgainstApplicationCore = false; 33 | 34 | // UnrealHeaderTool is a console application, not a Windows app (sets entry point to main(), instead of WinMain()) 35 | bIsBuildingConsoleApplication = true; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /HelloUECpp/Private/HelloUECpp.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma warning(disable: 4668) 4 | 5 | #include 6 | #include "HelloUECpp.h" 7 | #include "RequiredProgramMainCPPInclude.h" 8 | 9 | 10 | IMPLEMENT_APPLICATION(HelloUECpp, "HelloUECpp"); 11 | 12 | 13 | void Example_Logging() 14 | { 15 | UE_LOG(LogTemp, Display, TEXT("日志...")); 16 | UE_LOG(LogTemp, Warning, TEXT("日志...")); 17 | } 18 | 19 | int main(int argc, const char* argv[]) 20 | { 21 | FVector V1(1, 0, 0); 22 | FVector V2(0, 1, 0); 23 | 24 | float Value = V1 | V2; // Dot Product 25 | Value += 1; 26 | 27 | std::cout << "Hello UE C++! V1 * V2 = " << Value << std::endl; 28 | 29 | Example_Logging(); 30 | } 31 | -------------------------------------------------------------------------------- /HelloUECpp/Private/HelloUECpp.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /HelloUECpp/Resources/Windows/HelloUECpp.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-pp/UESnippets/a740f23307fef972c9d6c107ed23f6ff9cc4f0ea/HelloUECpp/Resources/Windows/HelloUECpp.ico -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 david++ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MyBlankProgram/MyBlankProgram.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class MyBlankProgram : ModuleRules 6 | { 7 | public MyBlankProgram(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Runtime/Launch/Public")); 10 | PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Runtime/Launch/Private")); // For LaunchEngineLoop.cpp include 11 | 12 | PublicDependencyModuleNames.AddRange(new string[] 13 | { 14 | "Core", 15 | "CoreUObject", 16 | "Projects", 17 | }); 18 | } 19 | } -------------------------------------------------------------------------------- /MyBlankProgram/MyBlankProgram.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | [SupportedPlatforms(UnrealPlatformClass.Desktop)] 7 | public class MyBlankProgramTarget : TargetRules 8 | { 9 | public MyBlankProgramTarget(TargetInfo Target) : base(Target) 10 | { 11 | Type = TargetType.Program; 12 | LinkType = TargetLinkType.Monolithic; 13 | LaunchModuleName = "MyBlankProgram"; 14 | 15 | SolutionDirectory = "UESnippets"; 16 | 17 | // Lean and mean 18 | bBuildDeveloperTools = false; 19 | 20 | // Never use malloc profiling in Unreal Header Tool. We set this because often UHT is compiled right before the engine 21 | // automatically by Unreal Build Tool, but if bUseMallocProfiler is defined, UHT can operate incorrectly. 22 | bUseMallocProfiler = false; 23 | 24 | // Editor-only data, however, is needed 25 | bBuildWithEditorOnlyData = true; 26 | 27 | // Currently this app is not linking against the engine, so we'll compile out references from Core to the rest of the engine 28 | bCompileAgainstEngine = false; 29 | bCompileAgainstCoreUObject = true; 30 | bCompileAgainstApplicationCore = false; 31 | 32 | // UnrealHeaderTool is a console application, not a Windows app (sets entry point to main(), instead of WinMain()) 33 | bIsBuildingConsoleApplication = true; 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyBlankDerivedObject.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "MyBlankDerivedObject.h" 5 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyBlankDerivedObject.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "CoreUObject/Public/UObject/Object.h" 7 | #include "MyBlankObject.h" 8 | #include "MyInterface.h" 9 | #include "MyBlankDerivedObject.generated.h" 10 | 11 | /** 12 | * 13 | */ 14 | UCLASS() 15 | class MYBLANKPROGRAM_API UMyBlankDerivedObject : public UMyBlankObject, public IMyInterface 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | UPROPERTY() 21 | float DerivedValue; 22 | 23 | UFUNCTION() 24 | void HelloDerived() 25 | { 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyBlankObject.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "MyBlankObject.h" 5 | #include "MyBlankDerivedObject.h" 6 | 7 | #include "BufferArchive.h" 8 | #include "FileHelper.h" 9 | #include "MemoryReader.h" 10 | #include "MyLog.h" 11 | #include "Paths.h" 12 | #include "CoreUObject/Public/CoreUObject.h" 13 | #include "CoreUObject/Public/Serialization/Formatters/JsonArchiveInputFormatter.h" 14 | #include "CoreUObject/Public/Serialization/Formatters/JsonArchiveOutputFormatter.h" 15 | #include "CoreUObject/Public/UObject/Object.h" 16 | 17 | FArchive& operator <<(FArchive& Ar, UMyBlankObject& MyObj) 18 | { 19 | Ar << MyObj.Health; 20 | Ar << MyObj.Ammo; 21 | Ar << MyObj.Location; 22 | return Ar; 23 | } 24 | 25 | void UMyBlankObject::ExampleUObject() 26 | { 27 | // Case1 28 | { 29 | UMyBlankObject* Obj = NewObject(); 30 | if (Obj) 31 | { 32 | Obj->Health = 120.0f; 33 | Obj->Ammo = 100; 34 | Obj->Location = FVector(20, 30, 40); 35 | 36 | Obj->SaveData(); 37 | } 38 | 39 | UMyBlankObject* Obj2 = NewObject(); 40 | if (Obj2) 41 | { 42 | Obj2->LoadData(); 43 | Obj2->Dump(); 44 | } 45 | } 46 | 47 | // Case2 48 | { 49 | UMyBlankObject* Obj = NewObject(); 50 | if (Obj) 51 | { 52 | Obj->Health = 240.0f; 53 | Obj->Ammo = 200; 54 | Obj->Location = FVector(20, 30, 40); 55 | 56 | Obj->SaveData2(); 57 | } 58 | 59 | UMyBlankObject* Obj2 = NewObject(); 60 | if (Obj2) 61 | { 62 | Obj2->LoadData2(); 63 | Obj2->Dump(); 64 | } 65 | } 66 | } 67 | 68 | // 69 | void UMyBlankObject::ExampleReflection() 70 | { 71 | // 获取UMyBlankObject的类型信息 72 | UClass* MyClass = UMyBlankObject::StaticClass(); 73 | UClass* MyClass2 = ::StaticClass(); 74 | 75 | // 获取UMyEnum的类型信息 76 | UEnum* MyEnum = ::StaticEnum(); 77 | UEnum* MyEnumOld = ::StaticEnum(); 78 | 79 | // 获取UMyStruct的类型信息 80 | UScriptStruct* MyStruct = ::StaticStruct(); 81 | UScriptStruct* MyStruct2 = FMyStruct::StaticStruct(); 82 | 83 | 84 | // 获取当前程序中所有的UClass/UEnum/UStriptStruct信息 85 | TArray Results; 86 | GetObjectsOfClass(UMyBlankObject::StaticClass(), Results); 87 | GetObjectsOfClass(UClass::StaticClass(), Results); 88 | GetObjectsOfClass(UEnum::StaticClass(), Results); 89 | GetObjectsOfClass(UScriptStruct::StaticClass(), Results); 90 | for (auto Obj : Results) 91 | { 92 | UClass* Class = Cast(Obj); 93 | if (Class) 94 | { 95 | UE_LOG(LogMy, Log, TEXT("UClass : %s"), *Class->GetFName().ToString()); 96 | } 97 | 98 | UEnum* Enum = Cast(Obj); 99 | if (Enum) 100 | { 101 | UE_LOG(LogMy, Log, TEXT("UEnum : %s"), *Enum->GetFName().ToString()); 102 | } 103 | 104 | UScriptStruct* Struct = Cast(Obj); 105 | if (Struct) 106 | { 107 | UE_LOG(LogMy, Log, TEXT("UScriptStruct : %s"), *Struct->GetFName().ToString()); 108 | } 109 | } 110 | 111 | // 根据类名字获取类定义(名字不带U) 112 | UClass* MyClass3 = FindObject(ANY_PACKAGE, TEXT("MyBlankObject")); 113 | if (MyClass3) 114 | { 115 | UE_LOG(LogMy, Log, TEXT("UMyBlankObject : %s, %s"), 116 | *MyClass3->GetFName().ToString(), 117 | (MyClass3 == UMyBlankObject::StaticClass() ? TEXT("true") : TEXT("false"))); 118 | } 119 | 120 | // 遍历字段 121 | { 122 | // Property 123 | for (TFieldIterator It(UMyBlankDerivedObject::StaticClass()); It; ++It) 124 | { 125 | FProperty* Property = *It; 126 | UE_LOG(LogMy, Log, TEXT("UMyBlankObject.%s"), *Property->GetName()); 127 | } 128 | 129 | // Function (without super class) 130 | for (TFieldIterator It(UMyBlankDerivedObject::StaticClass(), EFieldIteratorFlags::ExcludeSuper); It; 131 | ++It) 132 | { 133 | UFunction* Function = *It; 134 | UE_LOG(LogMy, Log, TEXT("UMyBlankObject.%s(), ParasNum=%d"), *Function->GetFName().ToString(), 135 | Function->NumParms); 136 | } 137 | 138 | // 实现的接口 139 | for (FImplementedInterface& Interface : UMyBlankDerivedObject::StaticClass()->Interfaces) 140 | { 141 | UE_LOG(LogMy, Log, TEXT("Interface Class :%s"), *Interface.Class->GetFName().ToString()); 142 | } 143 | 144 | // 遍历枚举 145 | // UEnum* MyEnum = StaticEnum(); 146 | if (MyEnum) 147 | { 148 | for (int I = 0; I < MyEnum->NumEnums(); ++I) 149 | { 150 | UE_LOG(LogMy, Log, TEXT("EMyEnum.%s = %d"), 151 | *MyEnum->GetNameByIndex(I).ToString(), 152 | MyEnum->GetValueByIndex(I)); 153 | } 154 | } 155 | 156 | // 遍历元数据 157 | #if WITH_METADATA 158 | UE_LOG(LogMy, Log, TEXT("EMyEnum-Meta ....")); 159 | UMetaData* MetaData = MyEnum->GetOutermost()->GetMetaData(); 160 | TMap* Metas = UMetaData::GetMapForObject(MyEnum); 161 | if (Metas) 162 | { 163 | for (auto & Pair : *Metas) 164 | { 165 | FName Key = Pair.Key; 166 | FString Value = Pair.Value; 167 | UE_LOG(LogMy, Log, TEXT("EMyEnum-Meta: %s-%s"), *Key.ToString(), *Value); 168 | } 169 | } 170 | #else 171 | UE_LOG(LogMy, Log, TEXT("EMyEnum-Meta - without ....")); 172 | #endif 173 | } 174 | 175 | // 查看继承关系 176 | { 177 | // 遍历继承关系 178 | TArray SuperClassNames; 179 | UClass* SuperClass = UMyBlankDerivedObject::StaticClass()->GetSuperClass(); 180 | while (SuperClass) 181 | { 182 | SuperClassNames.Add(SuperClass->GetName()); 183 | SuperClass = SuperClass->GetSuperClass(); 184 | } 185 | 186 | UE_LOG(LogMy, Log, TEXT("SuperClasss: UMyBlankDerivedObject->%s"), *FString::Join(SuperClassNames, TEXT("->"))); 187 | 188 | // 查看所有子类 189 | TArray Classes; 190 | GetDerivedClasses(UMyBlankObject::StaticClass(), Classes); 191 | for (auto Class : Classes) 192 | { 193 | UE_LOG(LogMy, Log, TEXT("MyBlankObject - Derived: %s"), *Class->GetName()); 194 | } 195 | } 196 | 197 | // 根据名获取Property & Function 198 | FProperty* HealthProp = UMyBlankDerivedObject::StaticClass()->FindPropertyByName(FName(TEXT("Health"))); 199 | if (HealthProp) 200 | { 201 | UE_LOG(LogMy, Log, TEXT("HealProp = %s"), *HealthProp->GetName()); 202 | } 203 | 204 | UFunction* HelloBPFunc = UMyBlankDerivedObject::StaticClass()->FindFunctionByName(FName(TEXT("Hello_Callable"))); 205 | if (HelloBPFunc) 206 | { 207 | UE_LOG(LogMy, Log, TEXT("HelloFunc = %s"), *HelloBPFunc->GetFName().ToString()); 208 | } 209 | 210 | UMyBlankDerivedObject* MyBlankDerivedObject = NewObject(); 211 | 212 | // 获取和设置属性 213 | { 214 | // Set methods - 1 215 | float* HealthPtr = HealthProp->ContainerPtrToValuePtr(MyBlankDerivedObject); 216 | *HealthPtr = 1111.0; 217 | 218 | 219 | // Set methods - 2 220 | FFloatProperty* HealthFloatProp = CastField(HealthProp); 221 | if (HealthFloatProp) 222 | { 223 | HealthFloatProp->SetPropertyValue(&MyBlankDerivedObject->Health, 2222.0f); 224 | } 225 | 226 | // Get methods - 1 227 | float Health1 = 0; 228 | Health1 = *HealthPtr; 229 | 230 | // Get methods - 2 231 | float Health2 = 0; 232 | if (HealthFloatProp) 233 | { 234 | Health2 = HealthFloatProp->GetPropertyValue(&MyBlankDerivedObject->Health); 235 | } 236 | 237 | UE_LOG(LogMy, Log, TEXT("Health = %f,%f,%f, HealthPropertyClass:%s"), 238 | MyBlankDerivedObject->Health, Health1, Health2, 239 | *HealthProp->GetClass()->GetName()); 240 | } 241 | 242 | // 调用函数 243 | { 244 | // Hello_Callable() - 无参数 245 | MyBlankDerivedObject->ProcessEvent(HelloBPFunc, nullptr); 246 | 247 | // Hello(FString) - 有参数 248 | UFunction* HelloFunc = UMyBlankDerivedObject::StaticClass()->FindFunctionByName(TEXT("Hello")); 249 | if (HelloFunc) 250 | { 251 | // Method1 - 直接用 252 | struct HelloParams 253 | { 254 | FString Value; 255 | }; 256 | HelloParams Params; 257 | Params.Value = TEXT("Unreal"); 258 | MyBlankDerivedObject->ProcessEvent(HelloFunc, &Params); 259 | } 260 | 261 | 262 | // Method2 - 封装下 263 | auto InvokeHello = [](UMyBlankObject* Object, FString Value) 264 | { 265 | UFunction* HelloFunc = UMyBlankDerivedObject::StaticClass()->FindFunctionByName(TEXT("Hello")); 266 | if (HelloFunc) 267 | { 268 | struct HelloParams 269 | { 270 | FString Value; 271 | }; 272 | HelloParams Params; 273 | Params.Value = Value; 274 | Object->ProcessEvent(HelloFunc, &Params); 275 | } 276 | }; 277 | 278 | InvokeHello(MyBlankDerivedObject, TEXT("Unreal!!")); 279 | 280 | // Method3 - 更加通用的封装(TODO: not work) 281 | // TTuple<> Returns; 282 | FString Param1 = TEXT("Unreal !!!"); 283 | // InvokeFunction(UMyBlankDerivedObject::StaticClass(), MyBlankDerivedObject, HelloFunc, Returns, Param1); 284 | } 285 | } 286 | 287 | void UMyBlankObject::Serialize2Json(FStructuredArchive& Ar) 288 | { 289 | FStructuredArchive::FRecord RootRecord = Ar.Open().EnterRecord(); 290 | 291 | RootRecord << SA_VALUE(TEXT("Health"), Health); 292 | RootRecord << SA_VALUE(TEXT("Ammo"), Ammo); 293 | FStructuredArchive::FRecord PosRecord = RootRecord.EnterField(SA_FIELD_NAME(TEXT("Position"))).EnterRecord(); 294 | PosRecord 295 | << SA_VALUE(TEXT("X"), Location.X) 296 | << SA_VALUE(TEXT("Y"), Location.Y) 297 | << SA_VALUE(TEXT("Z"), Location.Z); 298 | 299 | FStructuredArchive::FRecord StructRecord = RootRecord.EnterField(SA_FIELD_NAME(TEXT("StructVar"))). 300 | EnterRecord(); 301 | StructRecord << SA_VALUE(TEXT("Value"), StructVar.Value); 302 | } 303 | 304 | void UMyBlankObject::ExampleSerialization2Json() 305 | { 306 | FString SaveFile = FPaths::ProjectSavedDir() / TEXT("MyObject.json"); 307 | UE_LOG(LogMy, Log, TEXT("Save To: %s"), *SaveFile); 308 | 309 | // Save 310 | { 311 | TUniquePtr OutputAr(IFileManager::Get().CreateFileWriter(*SaveFile)); 312 | FJsonArchiveOutputFormatter Formatter(*OutputAr.Get()); 313 | FStructuredArchive Ar(Formatter); 314 | 315 | UMyBlankObject* Obj = NewObject(); 316 | if (Obj) 317 | { 318 | Obj->Health = 120.0f; 319 | Obj->Ammo = 100; 320 | Obj->Location = FVector(20, 30, 40); 321 | Obj->Serialize2Json(Ar); 322 | } 323 | 324 | Ar.Close(); 325 | } 326 | 327 | // Load 328 | { 329 | TUniquePtr OutputAr(IFileManager::Get().CreateFileReader(*SaveFile)); 330 | FJsonArchiveInputFormatter Formatter(*OutputAr.Get()); 331 | FStructuredArchive Ar(Formatter); 332 | 333 | UMyBlankObject* Obj = NewObject(); 334 | if (Obj) 335 | { 336 | Obj->Serialize2Json(Ar); 337 | Obj->Dump(); 338 | } 339 | 340 | Ar.Close(); 341 | } 342 | 343 | 344 | // Engine模块下 345 | #if 0 346 | FMyStruct MyStruct; 347 | MyStruct.Value = 100; 348 | TSharedPtr JsonObject = FJsonObjectConverter::UStructToJsonObject(&MyStruct); 349 | if (JsonObject) 350 | { 351 | } 352 | 353 | FString JsonStr; 354 | if (FJsonObjectConverter::UStructToFormattedJsonObjectString( 355 | FMyStruct::StaticStruct(), &MyStruct, JsonStr)) 356 | { 357 | UE_LOG(LogMy, Log, TEXT("Json:%s"), *JsonStr); 358 | } 359 | 360 | #endif 361 | } 362 | 363 | void UMyBlankObject::ExampleStructFieldProperty() 364 | { 365 | for (TFieldIterator It(UMyBlankDerivedObject::StaticClass()); It; ++It) 366 | { 367 | FProperty* Property = *It; 368 | UE_LOG(LogMy, Log, TEXT("UMyBlankObject.%s"), *Property->GetName()); 369 | 370 | 371 | FStructProperty* StructProp = CastField(Property); 372 | // if (StructProp && StructProp->GetFName() == FMyStruct::StaticStruct()->GetFName()) 373 | if (StructProp && StructProp->Struct == FMyStruct::StaticStruct()) 374 | { 375 | UE_LOG(LogMy, Log, TEXT("UMyBlankObject.%s is FMyStruct"), *Property->GetName()); 376 | } 377 | } 378 | } 379 | 380 | bool UMyBlankObject::SaveData() 381 | { 382 | FBufferArchive BinAr; 383 | BinAr << *this; 384 | 385 | FString SaveFile = FPaths::ProjectSavedDir() / TEXT("my_blankobj.bin"); 386 | bool Result = FFileHelper::SaveArrayToFile(BinAr, *SaveFile); 387 | if (!Result) 388 | { 389 | UE_LOG(LogMy, Error, TEXT("MyBlankObject Save Data filed: %s"), *SaveFile); 390 | return false; 391 | } 392 | 393 | BinAr.FlushCache(); 394 | BinAr.Empty(); 395 | 396 | UE_LOG(LogMy, Log, TEXT("MyBlankObject Save Data: %s"), *SaveFile); 397 | 398 | return Result; 399 | } 400 | 401 | void UMyBlankObject::LoadData() 402 | { 403 | TArray BinArray; 404 | FString SaveFile = FPaths::ProjectSavedDir() / TEXT("my_blankobj.bin"); 405 | if (!FFileHelper::LoadFileToArray(BinArray, *SaveFile)) 406 | { 407 | UE_LOG(LogMy, Error, TEXT("MyBlankObject Load Data filed")) 408 | return; 409 | } 410 | 411 | FMemoryReader BinAr(BinArray, true); 412 | BinAr.Seek(0); 413 | BinAr << *this; 414 | 415 | BinAr.FlushCache(); 416 | BinArray.Empty(); 417 | BinAr.Close(); 418 | } 419 | 420 | void UMyBlankObject::Dump() 421 | { 422 | UE_LOG(LogMy, Log, TEXT("Object: %f, %d, %s"), Health, Ammo, *Location.ToString()); 423 | UE_LOG(LogMy, Log, TEXT("StrutVar.Value = %f"), StructVar.Value); 424 | } 425 | 426 | bool UMyBlankObject::SaveData2() 427 | { 428 | FBufferArchive BinAr; 429 | this->Serialize(BinAr); 430 | 431 | FString SaveFile = FPaths::ProjectSavedDir() / TEXT("my_blankobj2.bin"); 432 | bool Result = FFileHelper::SaveArrayToFile(BinAr, *SaveFile); 433 | if (!Result) 434 | { 435 | UE_LOG(LogMy, Error, TEXT("MyBlankObject Save Data filed: %s"), *SaveFile); 436 | return false; 437 | } 438 | 439 | BinAr.FlushCache(); 440 | BinAr.Empty(); 441 | 442 | UE_LOG(LogMy, Log, TEXT("MyBlankObject Save Data: %s"), *SaveFile); 443 | 444 | return Result; 445 | } 446 | 447 | void UMyBlankObject::LoadData2() 448 | { 449 | TArray BinArray; 450 | FString SaveFile = FPaths::ProjectSavedDir() / TEXT("my_blankobj2.bin"); 451 | if (!FFileHelper::LoadFileToArray(BinArray, *SaveFile)) 452 | { 453 | UE_LOG(LogMy, Error, TEXT("MyBlankObject Load Data filed")) 454 | return; 455 | } 456 | 457 | FMemoryReader BinAr(BinArray, true); 458 | BinAr.Seek(0); 459 | Serialize(BinAr); 460 | 461 | BinAr.FlushCache(); 462 | BinArray.Empty(); 463 | BinAr.Close(); 464 | } 465 | 466 | 467 | void UMyBlankObject::Hello(FString Value) 468 | { 469 | UE_LOG(LogMy, Log, TEXT("Hello - %s"), *Value); 470 | } 471 | 472 | void UMyBlankObject::Hello_Callable() 473 | { 474 | UE_LOG(LogMy, Log, TEXT("Hello_Callable")); 475 | } 476 | 477 | void UMyBlankObject::Hello_Native_Implementation() 478 | { 479 | } 480 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyBlankObject.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "CoreUObject/Public/UObject/Object.h" 7 | #include "MyInterface.h" 8 | #include "MyStruct.h" 9 | #include "MyEnum.h" 10 | #include "CoreUObject/Public/UObject/UnrealType.h" 11 | #include "MyBlankObject.generated.h" 12 | 13 | 14 | /** 15 | * UCLASS 16 | */ 17 | UCLASS() 18 | class MYBLANKPROGRAM_API UMyBlankObject : public UObject 19 | { 20 | GENERATED_BODY() 21 | 22 | public: 23 | static void ExampleUObject(); 24 | static void ExampleReflection(); 25 | static void ExampleSerialization2Json(); 26 | static void ExampleStructFieldProperty(); 27 | 28 | // friend FArchive& operator <<(FArchive& Ar, UMyBlankObject& MyObj); 29 | 30 | float MemVarCPP; 31 | 32 | public: 33 | bool SaveData(); 34 | void LoadData(); 35 | void Dump(); 36 | 37 | bool SaveData2(); 38 | void LoadData2(); 39 | 40 | void Serialize2Json(FStructuredArchive& Ar); 41 | 42 | UPROPERTY() 43 | float Health; 44 | 45 | UPROPERTY() 46 | int32 Ammo; 47 | 48 | UPROPERTY() 49 | FVector Location; 50 | 51 | UPROPERTY() 52 | FMyStruct StructVar; 53 | 54 | UFUNCTION(BlueprintCallable, Category=MyBlank) 55 | void Hello(FString Value); 56 | 57 | /** C++实现、蓝图调用 */ 58 | UFUNCTION(BlueprintCallable, Category=MyBlank) 59 | void Hello_Callable(); 60 | 61 | //** C++实现默认版、蓝图可以重载实现 */ 62 | UFUNCTION(BlueprintNativeEvent, Category=MyBlank) 63 | void Hello_Native(); 64 | 65 | /** C++不实现、蓝图实现 */ 66 | UFUNCTION(BlueprintImplementableEvent, Category=MyBlank) 67 | void Hello_BPImplement(); 68 | }; 69 | 70 | // Invoke Function Template 71 | template 72 | inline void InvokeFunction(UClass* objClass, UObject* obj, UFunction* func, TTuple& outParams, 73 | TArgs&&... args) 74 | { 75 | objClass = obj != nullptr ? obj->GetClass() : objClass; 76 | UObject* context = obj != nullptr ? obj : objClass; 77 | uint8* outPramsBuffer = (uint8*)&outParams; 78 | 79 | if (func->HasAnyFunctionFlags(FUNC_Native)) //quick path for c++ functions 80 | { 81 | TTuple params(Forward(args)..., TReturns()...); 82 | context->ProcessEvent(func, ¶ms); 83 | //copy back out params 84 | for (TFieldIterator i(func); i; ++i) 85 | { 86 | FProperty* prop = *i; 87 | if (prop->PropertyFlags & CPF_OutParm) 88 | { 89 | void* propBuffer = prop->ContainerPtrToValuePtr(¶ms); 90 | prop->CopyCompleteValue(outPramsBuffer, propBuffer); 91 | outPramsBuffer += prop->GetSize(); 92 | } 93 | } 94 | return; 95 | } 96 | 97 | TTuple inParams(Forward(args)...); 98 | void* funcPramsBuffer = (uint8*)FMemory_Alloca(func->ParmsSize); 99 | uint8* inPramsBuffer = (uint8*)&inParams; 100 | 101 | for (TFieldIterator i(func); i; ++i) 102 | { 103 | FProperty* prop = *i; 104 | if (prop->GetFName().ToString().StartsWith("__")) 105 | { 106 | //ignore private param like __WolrdContext of function in blueprint funcion library 107 | continue; 108 | } 109 | void* propBuffer = prop->ContainerPtrToValuePtr(funcPramsBuffer); 110 | if (prop->PropertyFlags & CPF_OutParm) 111 | { 112 | prop->CopyCompleteValue(propBuffer, outPramsBuffer); 113 | outPramsBuffer += prop->GetSize(); 114 | } 115 | else if (prop->PropertyFlags & CPF_Parm) 116 | { 117 | prop->CopyCompleteValue(propBuffer, inPramsBuffer); 118 | inPramsBuffer += prop->GetSize(); 119 | } 120 | } 121 | 122 | context->ProcessEvent(func, funcPramsBuffer); //call function 123 | outPramsBuffer = (uint8*)&outParams; //reset to begin 124 | 125 | //copy back out params 126 | for (TFieldIterator i(func); i; ++i) 127 | { 128 | FProperty* prop = *i; 129 | if (prop->PropertyFlags & CPF_OutParm) 130 | { 131 | void* propBuffer = prop->ContainerPtrToValuePtr(funcPramsBuffer); 132 | prop->CopyCompleteValue(outPramsBuffer, propBuffer); 133 | outPramsBuffer += prop->GetSize(); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyBlankProgram.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #include "MyBlankProgram.h" 5 | #include "MyBlankObject.h" 6 | #include "MyLog.h" 7 | #include "RequiredProgramMainCPPInclude.h" 8 | #include "TextFilterUtils.h" 9 | 10 | DEFINE_LOG_CATEGORY_STATIC(LogMyBlankProgram, Log, All); 11 | 12 | IMPLEMENT_APPLICATION(MyBlankProgram, "MyBlankProgram"); 13 | 14 | void ExampleString() 15 | { 16 | FString String1 = TEXT("1-1-400 -80-0 | 2-1-800-20-0-0 | "); 17 | FString String2 = TEXT("1-1-400-80|2-1-800-20-0-0"); 18 | 19 | 20 | TArray EffectsArray; 21 | String1.Replace(TEXT(" "), TEXT("")).Replace(TEXT("\t"), TEXT("")).ParseIntoArray(EffectsArray, TEXT("|")); 22 | for (auto& EffectStr : EffectsArray) 23 | { 24 | UE_LOG(LogMyBlankProgram, Log, TEXT("[%s]"), *EffectStr); 25 | 26 | TArray Elements; 27 | EffectStr.ParseIntoArray(Elements, TEXT("-")); 28 | 29 | int Type = 0; 30 | int DurationPolicy = 0; 31 | int DurationMs = 0; 32 | TArray Coefficients; 33 | 34 | if (Elements.Num() > 0) Type = FCString::Atoi(*Elements[0]); 35 | if (Elements.Num() > 1) DurationPolicy = FCString::Atoi(*Elements[1]); 36 | if (Elements.Num() > 2) DurationMs = FCString::Atoi(*Elements[2]); 37 | 38 | UE_LOG(LogMyBlankProgram, Log, TEXT("effect:%d,%d,%d"), Type, DurationPolicy, DurationMs); 39 | } 40 | 41 | bool remove = true; 42 | // FString A = String1.TrimChar(TCHAR('|'), &remove); 43 | FString A = String1.Replace(TEXT(" "), TEXT("")); 44 | UE_LOG(LogMyBlankProgram, Log, TEXT("%s, %d"), *A, remove); 45 | 46 | 47 | FString Description = TEXT("物防+{X}%,持续{T}秒"); 48 | FText Result = FText::FormatNamed(FText::FromString(Description), TEXT("X"), 49 | FText::FromString(FString::FromInt(10))); 50 | UE_LOG(LogMyBlankProgram, Log, TEXT("%s"), *Result.ToString()); 51 | 52 | // 53 | // FName 54 | // 55 | FName UserName = TEXT("David"); 56 | FString UserNameStr = UserName.ToString(); 57 | FText UserNameText = FText::FromName(UserName); 58 | 59 | FName UserName2(FString(TEXT("David2"))); 60 | FName UserName3(UserNameText.ToString()); 61 | 62 | // case-insentive 63 | if (UserName == UserName3 && UserName.Compare(UserName3) == 0 && UserName == FName(TEXT("david"))) 64 | { 65 | UE_LOG(LogMyBlankProgram, Log, TEXT("FName - UserName:%s == UserName3:%s"), *UserName.ToString(), 66 | *UserName3.ToString()); 67 | } 68 | 69 | FName EmptyName; 70 | if (FName(TEXT("david"), FNAME_Find) != NAME_None && EmptyName == NAME_None) 71 | { 72 | UE_LOG(LogMyBlankProgram, Log, TEXT("FName - Exist Name in name table : %d,%d"), 73 | GetTypeHash(FName(TEXT("david"))), 74 | GetTypeHash(UserName)); 75 | } 76 | } 77 | 78 | void ExampleRegex() 79 | { 80 | // Simple Example 81 | FString TextStr(TEXT("ABCDEFGHIJKLMN")); 82 | FRegexPattern TestPattern(TEXT("C.+H")); 83 | FRegexMatcher TestMatcher(TestPattern, TextStr); 84 | if (TestMatcher.FindNext()) 85 | { 86 | int Begin = TestMatcher.GetMatchBeginning(); 87 | int End = TestMatcher.GetMatchEnding(); 88 | UE_LOG(LogMyBlankProgram, Log, TEXT("Find Matched : %d-%d -> %s"), 89 | TestMatcher.GetMatchBeginning(), TestMatcher.GetMatchEnding(), 90 | *TextStr.Mid(Begin, End - Begin)); 91 | } 92 | 93 | // Complex Example 94 | FString String2 = TEXT("1-1-400-80|2-1-800-20-0-0"); 95 | } 96 | 97 | void MainBody() 98 | { 99 | UE_LOG(LogMyBlankProgram, Display, TEXT("Hello World! by David !!!")); 100 | 101 | // ExampleLogging(); 102 | 103 | // ExampleString(); 104 | 105 | // ExampleRegex(); 106 | 107 | // UMyBlankObject::ExampleUObject(); 108 | // UMyBlankObject::ExampleReflection(); 109 | // UMyBlankObject::ExampleSerialization2Json(); 110 | // UMyBlankObject::ExampleStructFieldProperty(); 111 | } 112 | 113 | INT32_MAIN_INT32_ARGC_TCHAR_ARGV() 114 | { 115 | // Set your engine dir 116 | // GForeignEngineDir = TEXT("/Users/david/UnrealEngine/Engine/"); 117 | printf("Engine: > %s : %s : %s\n", UE_ENGINE_DIRECTORY, TCHAR_TO_ANSI(GForeignEngineDir), 118 | TCHAR_TO_ANSI(*FPaths::EngineDir())); 119 | 120 | GEngineLoop.PreInit(ArgC, ArgV); 121 | MainBody(); 122 | GEngineLoop.AppExit(); 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyBlankProgram.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyEnum.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "MyEnum.h" 5 | 6 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyEnum.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "MyEnum.generated.h" 7 | 8 | /** 9 | * UENUM 10 | */ 11 | UENUM() 12 | namespace EMyEnumOld 13 | { 14 | enum Type 15 | { 16 | Type1, 17 | Type2 18 | }; 19 | } 20 | 21 | 22 | UENUM(BlueprintType, meta=(EnumDisplayName="GetMyEnumDisplayName")) 23 | enum class EMyEnum : uint8 24 | { 25 | MyEnum_1 UMETA(DisplayName = "MyEnum_Item1"), 26 | MyEnum_2 UMETA(DisplayName = "MyEnum_Item2"), 27 | }; 28 | 29 | 30 | inline FText GetMyEnumDisplayName(int32 Val) 31 | { 32 | EMyEnum MyEnum = EMyEnum(Val); 33 | switch (MyEnum) 34 | { 35 | case EMyEnum::MyEnum_1: 36 | return FText::FromString(TEXT("MyEnumItem1")); 37 | case EMyEnum::MyEnum_2: 38 | return FText::FromString(TEXT("MyEnumItem2")); 39 | default: 40 | return FText::FromString(TEXT("MyEnumInvalid")); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyInterface.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "MyInterface.h" 5 | 6 | 7 | // Add default functionality here for any IMyInterface functions that are not pure virtual. 8 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyInterface.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "CoreUObject/Public/UObject/Interface.h" 7 | #include "MyInterface.generated.h" 8 | 9 | // This class does not need to be modified. 10 | UINTERFACE() 11 | class UMyInterface : public UInterface 12 | { 13 | GENERATED_BODY() 14 | }; 15 | 16 | /** 17 | * 18 | */ 19 | class MYBLANKPROGRAM_API IMyInterface 20 | { 21 | GENERATED_BODY() 22 | 23 | // Add interface functions to this class. This is the class that will be inherited to implement this interface. 24 | public: 25 | UFUNCTION(BlueprintImplementableEvent) 26 | void BPFunction() const; 27 | 28 | virtual void Hello() const 29 | { 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyLog.cpp: -------------------------------------------------------------------------------- 1 | #include "MyLog.h" 2 | 3 | DEFINE_LOG_CATEGORY(LogMy); 4 | 5 | void ExampleLogging() 6 | { 7 | // UE_LOG(LogMy, Fatal, TEXT("致命错误")); // 会直接Crash掉 8 | UE_LOG(LogMy, Error, TEXT("错误")); 9 | UE_LOG(LogMy, Warning, TEXT("警告")); 10 | UE_LOG(LogMy, Display, TEXT("显示")); 11 | UE_LOG(LogMy, Log, TEXT("Log ..")); 12 | UE_LOG(LogMy, Verbose, TEXT("Verbose Log ..")); 13 | UE_LOG(LogMy, VeryVerbose, TEXT("Very Verbose Log ...")); 14 | } 15 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyLog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | 5 | DECLARE_LOG_CATEGORY_EXTERN(LogMy, Verbose, All); 6 | 7 | extern void ExampleLogging(); -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyStruct.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "MyStruct.h" 5 | -------------------------------------------------------------------------------- /MyBlankProgram/Private/MyStruct.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "MyStruct.generated.h" 7 | /** 8 | * USTRUCT 9 | */ 10 | USTRUCT(BlueprintType) 11 | struct FMyStruct 12 | { 13 | GENERATED_BODY() 14 | 15 | UPROPERTY(BlueprintReadWrite) 16 | float Value; 17 | }; 18 | -------------------------------------------------------------------------------- /MyBlankProgram/Resources/Windows/MyBlankProgram.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-pp/UESnippets/a740f23307fef972c9d6c107ed23f6ff9cc4f0ea/MyBlankProgram/Resources/Windows/MyBlankProgram.ico -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UESnippets 2 | Unreal Engine Code Example & Snippets 3 | 4 | ```bash 5 | cd $GAMEPROJECT/Source 6 | git clone https://github.com/david-pp/UESnippets.git 7 | ``` 8 | 9 | 10 | 11 | ## Windows Platform 12 | 13 | Environment Values: 14 | 15 | ```bash 16 | UnrealEnginePath=E:\UnrealEngine-4.27.2 17 | ``` 18 | 19 | Build : 20 | 21 | ```bash 22 | # BuildProgram.bat 23 | @echo off 24 | set PROGRAME=%1 25 | %UnrealEnginePath%\Engine\Binaries\DotNET\UnrealBuildTool.exe %PROGRAME% Win64 Development -Project="%cd%\HelloUE.uproject" 26 | ``` 27 | 28 | ```bash 29 | BuildProgram.bat YourSnippet 30 | ``` 31 | -------------------------------------------------------------------------------- /SnippetAsync/Private/PrimeNumberGraphTask.cpp: -------------------------------------------------------------------------------- 1 | #include "PrimeNumberGraphTask.h" 2 | 3 | TArray FPrimeNumberTask::PrimeNumbers; 4 | FCriticalSection FPrimeNumberTask::PrimeNumbersMutex; 5 | FGraphEventArray FPrimeNumberTask::CompletionEvents; 6 | 7 | int32 FPrimeNumberTask::FindNextPrimeNumber() 8 | { 9 | //Last known prime number + 1 10 | PrimeNumbersMutex.Lock(); 11 | int32 TestPrime = PrimeNumbers.Last(); 12 | PrimeNumbersMutex.Unlock(); 13 | 14 | bool NumIsPrime = false; 15 | while (!NumIsPrime) 16 | { 17 | NumIsPrime = true; 18 | 19 | //Try Next Number 20 | TestPrime++; 21 | 22 | //Modulus from 2 to current number - 1 23 | for (int32 b = 2; b < TestPrime; b++) 24 | { 25 | if (TestPrime % b == 0) 26 | { 27 | NumIsPrime = false; 28 | break; 29 | } 30 | } 31 | } 32 | 33 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 34 | //Did another thread find this number already? 35 | PrimeNumbersMutex.Lock(); 36 | bool IsFound = PrimeNumbers.Contains(TestPrime); 37 | PrimeNumbersMutex.Unlock(); 38 | 39 | if (IsFound) 40 | { 41 | return FindNextPrimeNumber(); //recursion 42 | } 43 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 44 | 45 | //Success! 46 | return TestPrime; 47 | } 48 | -------------------------------------------------------------------------------- /SnippetAsync/Private/PrimeNumberGraphTask.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | class FPrimeNumberTask 6 | { 7 | public: 8 | // OUTPUT RESULTS OF TASKS 9 | static TArray PrimeNumbers; 10 | static FCriticalSection PrimeNumbersMutex; 11 | 12 | // This is the array of thread completions to know if all threads are done yet 13 | static FGraphEventArray CompletionEvents; 14 | 15 | static void FindPrimes(const uint32 TotalToFind) 16 | { 17 | PrimeNumbers.Empty(); 18 | PrimeNumbers.Add(2); 19 | PrimeNumbers.Add(3); 20 | 21 | //~~~~~~~~~~~~~~~~~~~~ 22 | //Add thread / task for each of total prime numbers to find 23 | //~~~~~~~~~~~~~~~~~~~~ 24 | CompletionEvents.Empty(); 25 | for (uint32 b = 0; b < TotalToFind; b++) 26 | { 27 | CompletionEvents.Add( 28 | TGraphTask::CreateTask(NULL, ENamedThreads::GameThread). 29 | ConstructAndDispatchWhenReady()); 30 | } 31 | } 32 | 33 | static bool TasksAreComplete() 34 | { 35 | //Check all thread completion events 36 | for (int32 Index = 0; Index < CompletionEvents.Num(); Index++) 37 | { 38 | if (!CompletionEvents[Index]->IsComplete()) 39 | { 40 | return false; 41 | } 42 | } 43 | return true; 44 | } 45 | 46 | public: 47 | FPrimeNumberTask() 48 | { 49 | } 50 | 51 | ~FPrimeNumberTask() 52 | { 53 | } 54 | 55 | FORCEINLINE static TStatId GetStatId() 56 | { 57 | RETURN_QUICK_DECLARE_CYCLE_STAT(FPrimeNumberTask, STATGROUP_TaskGraphTasks); 58 | } 59 | 60 | static ENamedThreads::Type GetDesiredThread() 61 | { 62 | return ENamedThreads::AnyThread; 63 | } 64 | 65 | static ESubsequentsMode::Type GetSubsequentsMode() 66 | { 67 | return ESubsequentsMode::TrackSubsequents; 68 | } 69 | 70 | void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 71 | { 72 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73 | PrimeNumber = FindNextPrimeNumber(); 74 | 75 | { 76 | FScopeLock Lock(&PrimeNumbersMutex); 77 | PrimeNumbers.Add(PrimeNumber); 78 | } 79 | 80 | // Please note you should not create, destroy, or modify UObjects here. 81 | // Do those sort of things after all thread are completed. 82 | 83 | // All calcs for making stuff can be done in the threads 84 | // But the actual making/modifying of the UObjects should be done in main game thread, 85 | // which is AFTER all tasks have completed :) 86 | Log("Find A Prime"); 87 | } 88 | 89 | protected: 90 | int32 FindNextPrimeNumber(); 91 | 92 | void Log(const char* Action) 93 | { 94 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 95 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 96 | UE_LOG(LogTemp, Display, TEXT("%s[%d] - %s, PrimeNumber:%d"), *CurrentThreadName, CurrentThreadId, 97 | ANSI_TO_TCHAR(Action), PrimeNumber); 98 | } 99 | 100 | int32 PrimeNumber; 101 | }; 102 | 103 | 104 | inline void Test_FindPrimeNumbers() 105 | { 106 | FPrimeNumberTask::FindPrimes(5000); //first 50,000 prime numbers 107 | 108 | while (true) 109 | { 110 | if (FPrimeNumberTask::TasksAreComplete()) 111 | { 112 | UE_LOG(LogTemp, Display, TEXT("Find Prime Number Task Are Complete !!")); 113 | 114 | for (int32 v = 0; v < FPrimeNumberTask::PrimeNumbers.Num(); v++) 115 | { 116 | UE_LOG(LogTemp, Display, TEXT("%d ~ %d"), v, FPrimeNumberTask::PrimeNumbers[v]); 117 | } 118 | 119 | break; 120 | } 121 | 122 | FPlatformProcess::Sleep(0.1); 123 | } 124 | 125 | UE_LOG(LogTemp, Display, TEXT("Over........")); 126 | } 127 | -------------------------------------------------------------------------------- /SnippetAsync/Private/PrimeNumberThread.cpp: -------------------------------------------------------------------------------- 1 | #include "PrimeNumberThread.h" 2 | 3 | //*********************************************************** 4 | //Thread Worker Starts as NULL, prior to being instanced 5 | // This line is essential! Compiler error without it 6 | FPrimeNumberThread* FPrimeNumberThread::Runnable = NULL; 7 | //*********************************************************** 8 | 9 | FPrimeNumberThread::FPrimeNumberThread(TArray& TheArray, const int32 IN_TotalPrimesToFind) 10 | : StopTaskCounter(0), PrimesFoundCount(0), TotalPrimesToFind(IN_TotalPrimesToFind) 11 | { 12 | //Link to where data should be stored 13 | PrimeNumbers = &TheArray; 14 | 15 | Thread = FRunnableThread::Create(this, TEXT("FPrimeNumberThread"), 0, TPri_BelowNormal); 16 | } 17 | 18 | FPrimeNumberThread::~FPrimeNumberThread() 19 | { 20 | delete Thread; 21 | Thread = NULL; 22 | } 23 | 24 | //Init 25 | bool FPrimeNumberThread::Init() 26 | { 27 | //Init the Data 28 | PrimeNumbers->Empty(); 29 | PrimeNumbers->Add(2); 30 | PrimeNumbers->Add(3); 31 | 32 | UE_LOG(LogTemp, Display, TEXT("Prime Number Thread Started!")); 33 | return true; 34 | } 35 | 36 | //Run 37 | uint32 FPrimeNumberThread::Run() 38 | { 39 | //Initial wait before starting 40 | FPlatformProcess::Sleep(0.03); 41 | 42 | //While not told to stop this thread 43 | //and not yet finished finding Prime Numbers 44 | while (StopTaskCounter.GetValue() == 0 && !IsFinished()) 45 | { 46 | PrimeNumbers->Add(FindNextPrimeNumber()); 47 | PrimesFoundCount++; 48 | 49 | 50 | UE_LOG(LogTemp, Display, TEXT("Prime Number : %d"), PrimeNumbers->Last()); 51 | 52 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 53 | //prevent thread from using too many resources 54 | // FPlatformProcess::Sleep(0.01); 55 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 56 | } 57 | 58 | //Run FPrimeNumberThread::Shutdown() from the timer in Game Thread that is watching 59 | //to see when FPrimeNumberThread::IsThreadFinished() 60 | 61 | return 0; 62 | } 63 | 64 | //stop 65 | void FPrimeNumberThread::Stop() 66 | { 67 | StopTaskCounter.Increment(); 68 | } 69 | 70 | FPrimeNumberThread* FPrimeNumberThread::JoyInit(TArray& TheArray, const int32 IN_TotalPrimesToFind) 71 | { 72 | //Create new instance of thread if it does not exist 73 | // and the platform supports multi threading! 74 | if (!Runnable && FPlatformProcess::SupportsMultithreading()) 75 | { 76 | Runnable = new FPrimeNumberThread(TheArray, IN_TotalPrimesToFind); 77 | } 78 | 79 | return Runnable; 80 | } 81 | 82 | void FPrimeNumberThread::EnsureCompletion() 83 | { 84 | Stop(); 85 | Thread->WaitForCompletion(); 86 | } 87 | 88 | void FPrimeNumberThread::Shutdown() 89 | { 90 | if (Runnable) 91 | { 92 | Runnable->EnsureCompletion(); 93 | delete Runnable; 94 | Runnable = NULL; 95 | } 96 | } 97 | 98 | bool FPrimeNumberThread::IsThreadFinished() 99 | { 100 | if (Runnable) return Runnable->IsFinished(); 101 | return true; 102 | } 103 | 104 | int32 FPrimeNumberThread::FindNextPrimeNumber() 105 | { 106 | //Last known prime number + 1 107 | int32 TestPrime = PrimeNumbers->Last(); 108 | 109 | bool NumIsPrime = false; 110 | while (!NumIsPrime) 111 | { 112 | NumIsPrime = true; 113 | 114 | //Try Next Number 115 | TestPrime++; 116 | 117 | //Modulus from 2 to current number - 1 118 | for (int32 b = 2; b < TestPrime; b++) 119 | { 120 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 121 | //prevent thread from using too many resources 122 | //FPlatformProcess::Sleep(0.01); 123 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 124 | 125 | if (TestPrime % b == 0) 126 | { 127 | NumIsPrime = false; 128 | break; 129 | } 130 | } 131 | } 132 | 133 | //Success! 134 | return TestPrime; 135 | } 136 | -------------------------------------------------------------------------------- /SnippetAsync/Private/PrimeNumberThread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | //~~~~~ Multi Threading ~~~ 6 | class FPrimeNumberThread : public FRunnable 7 | { 8 | private: 9 | /** Singleton instance, can access the thread any time via static accessor, if it is active! */ 10 | static FPrimeNumberThread* Runnable; 11 | 12 | /** Thread to run the worker FRunnable on */ 13 | FRunnableThread* Thread; 14 | 15 | /** The Data Ptr */ 16 | TArray* PrimeNumbers; 17 | 18 | 19 | /** Stop this thread? Uses Thread Safe Counter */ 20 | FThreadSafeCounter StopTaskCounter; 21 | 22 | //The actual finding of prime numbers 23 | int32 FindNextPrimeNumber(); 24 | 25 | int32 PrimesFoundCount; 26 | 27 | int32 TotalPrimesToFind; 28 | 29 | public: 30 | //Done? 31 | bool IsFinished() const 32 | { 33 | return PrimesFoundCount >= TotalPrimesToFind; 34 | } 35 | 36 | //~~~ Thread Core Functions ~~~ 37 | 38 | //Constructor / Destructor 39 | FPrimeNumberThread(TArray& TheArray, const int32 IN_PrimesToFindPerTick); 40 | 41 | virtual ~FPrimeNumberThread(); 42 | 43 | // Begin FRunnable interface. 44 | virtual bool Init(); 45 | virtual uint32 Run(); 46 | virtual void Stop(); 47 | // End FRunnable interface 48 | 49 | /** Makes sure this thread has stopped properly */ 50 | void EnsureCompletion(); 51 | 52 | public: 53 | //~~~ Starting and Stopping Thread ~~~ 54 | 55 | /* 56 | Start the thread and the worker from static (easy access)! 57 | This code ensures only 1 Prime Number thread will be able to run at a time. 58 | This function returns a handle to the newly started instance. 59 | */ 60 | static FPrimeNumberThread* JoyInit(TArray& TheArray, const int32 IN_TotalPrimesToFind); 61 | 62 | /** Shuts down the thread. Static so it can easily be called from outside the thread context */ 63 | static void Shutdown(); 64 | 65 | static bool IsThreadFinished(); 66 | }; 67 | -------------------------------------------------------------------------------- /SnippetAsync/Private/RNGThread.cpp: -------------------------------------------------------------------------------- 1 | #include "RNGThread.h" 2 | 3 | FRNGThread::FRNGThread(int Count, int minNumber, int maxNumber, int chunkCount) 4 | { 5 | m_Kill = false; 6 | m_Pause = false; 7 | 8 | //Initialize FEvent (as a cross platform (Confirmed Mac/Windows)) 9 | m_semaphore = FGenericPlatformProcess::GetSynchEventFromPool(false);; 10 | 11 | m_MinInt = minNumber; 12 | m_MaxInt = maxNumber; 13 | 14 | m_chunkCount = chunkCount; 15 | 16 | m_amount = Count; 17 | m_RandomVecs.Reserve(m_amount); 18 | 19 | Thread = FRunnableThread::Create(this, TEXT("FRNGThread"), 0, TPri_BelowNormal); 20 | } 21 | 22 | FRNGThread::~FRNGThread() 23 | { 24 | if (m_semaphore) 25 | { 26 | //Cleanup the FEvent 27 | FGenericPlatformProcess::ReturnSynchEventToPool(m_semaphore); 28 | m_semaphore = nullptr; 29 | } 30 | 31 | if (Thread) 32 | { 33 | //Cleanup the worker thread 34 | delete Thread; 35 | Thread = nullptr; 36 | } 37 | } 38 | 39 | bool FRNGThread::Init() 40 | { 41 | //Init the Data 42 | m_RandomVecs.Empty(); 43 | return true; 44 | } 45 | 46 | uint32 FRNGThread::Run() 47 | { 48 | //Initial wait before starting 49 | FPlatformProcess::Sleep(0.03); 50 | 51 | while (!m_Kill) 52 | { 53 | if (m_Pause) 54 | { 55 | //FEvent->Wait(); will "sleep" the thread until it will get a signal "Trigger()" 56 | m_semaphore->Wait(); 57 | 58 | if (m_Kill) 59 | { 60 | return 0; 61 | } 62 | } 63 | else 64 | { 65 | //Create temporal array (chunk) 66 | TArray ChunkArray; 67 | ChunkArray.Reserve(m_chunkCount); 68 | 69 | //Calculate random vectors and put them to the temporal array 70 | //I did it so we won't lock/unlock FCritical section each time we generating a new FVector (Locking and Unlocking is somewhat expensive). 71 | for (int i = 0; i < m_chunkCount; i++) 72 | { 73 | FVector RandomVec; 74 | RandomVec.X = (float)FMath::RandRange((int)m_MinInt, (int)m_MaxInt); 75 | RandomVec.Y = (float)FMath::RandRange((int)m_MinInt, (int)m_MaxInt); 76 | RandomVec.Z = 0; 77 | ChunkArray.Emplace(RandomVec); 78 | } 79 | 80 | //Critical section: 81 | m_mutex.Lock(); 82 | //We are locking our FCriticalSection so no other thread will access it 83 | //And thus it is a thread-safe access now 84 | 85 | //Append the temporal array to the Actual storage array/ 86 | m_RandomVecs.Append(ChunkArray); 87 | //Get array size 88 | int num = m_RandomVecs.Num(); 89 | 90 | //Unlock FCriticalSection so other threads may use it. 91 | m_mutex.Unlock(); 92 | 93 | //Pause Condition - if we RandomVectors contains more vectors than m_amount we shall pause the thread to release system resources. 94 | if (num > m_amount) 95 | { 96 | m_Pause = true; 97 | } 98 | //A little sleep between the chunks (So CPU will rest a bit -- (may be omitted)) 99 | FPlatformProcess::Sleep(0.1); 100 | } 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | void FRNGThread::PauseThread() 107 | { 108 | m_Pause = true; 109 | } 110 | 111 | void FRNGThread::ContinueThread() 112 | { 113 | m_Pause = false; 114 | 115 | if (m_semaphore) 116 | { 117 | //Here is a FEvent signal "Trigger()" -> it will wake up the thread. 118 | m_semaphore->Trigger(); 119 | } 120 | } 121 | 122 | void FRNGThread::Stop() 123 | { 124 | m_Kill = true; //Thread kill condition "while (!m_Kill){...}" 125 | m_Pause = false; 126 | 127 | if (m_semaphore) 128 | { 129 | //We shall signal "Trigger" the FEvent (in case the Thread is sleeping it shall wake up!!) 130 | m_semaphore->Trigger(); 131 | } 132 | } 133 | 134 | //Use this method to kill the thread!! 135 | void FRNGThread::EnsureCompletion() 136 | { 137 | Stop(); 138 | 139 | if (Thread) 140 | { 141 | Thread->WaitForCompletion(); 142 | } 143 | } 144 | 145 | //if the array is not yet ready we will generate the vector on the caller thread. 146 | FORCEINLINE FVector GenerateRandomVecInRange(int min, int max) 147 | { 148 | FVector WanderingPoint(0, 0, 0); 149 | WanderingPoint.X = (float)FMath::RandRange((int)min, (int)max); 150 | WanderingPoint.Y = (float)FMath::RandRange((int)min, (int)max); 151 | WanderingPoint.Z = (float)FMath::RandRange((int)min, (int)max); 152 | return WanderingPoint; 153 | } 154 | 155 | bool FRNGThread::IsThreadPaused() 156 | { 157 | return (bool)m_Pause; 158 | } 159 | 160 | FVector FRNGThread::GetRandomVector() 161 | { 162 | //Here we are retrieving the Vector from our storage array in a thread safe manner 163 | //Despite this is a member method of this class it will be called from another thread (most likely from the GameThread) (This is by the way true for each public member methods except the "Run()" method) - So we must ensure the thread safety! 164 | //Critical section: 165 | FScopeLock Lock(&m_mutex); 166 | int lastIndex = m_RandomVecs.Num() - 1; 167 | if (lastIndex < 0) 168 | { 169 | //The array is not ready yet :-0 170 | return GenerateRandomVecInRange(m_MinInt, m_MaxInt); 171 | } 172 | FVector vec2ret; 173 | vec2ret = m_RandomVecs[lastIndex]; 174 | m_RandomVecs.RemoveAt(lastIndex); 175 | 176 | //Some automation: if we have less than 10% random FVectors in our array we will UnPause the thread. (maybe omitted). 177 | if (m_RandomVecs.Num() < m_amount / 10) 178 | { 179 | ContinueThread(); 180 | } 181 | //return random vector to the caller. 182 | return vec2ret; 183 | } 184 | -------------------------------------------------------------------------------- /SnippetAsync/Private/RNGThread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | class FRNGThread : public FRunnable 6 | { 7 | public: 8 | //Constructor 9 | FRNGThread(int Count = 50000, int minNumber = 0, int maxNumber = 1000, int chunkCount = 20); 10 | 11 | //Destructor 12 | ~FRNGThread(); 13 | 14 | //Use this method to kill the thread!! 15 | void EnsureCompletion(); 16 | 17 | //Pause the thread 18 | void PauseThread(); 19 | 20 | //Continue/UnPause the thread 21 | void ContinueThread(); 22 | 23 | //FRunnable interface. 24 | virtual bool Init(); 25 | virtual uint32 Run(); 26 | virtual void Stop(); 27 | 28 | bool IsThreadPaused(); 29 | 30 | FVector GetRandomVector(); 31 | 32 | private: 33 | //Thread to run the worker FRunnable on 34 | FRunnableThread* Thread; 35 | 36 | FCriticalSection m_mutex; 37 | FEvent* m_semaphore; 38 | 39 | int m_chunkCount; 40 | int m_amount; 41 | int m_MinInt; 42 | int m_MaxInt; 43 | 44 | //As the name states those members are Thread safe 45 | FThreadSafeBool m_Kill; 46 | FThreadSafeBool m_Pause; 47 | 48 | //The array is a private member because we don't want to allow anyone from outside the class to access it since we want to ensure a thread safety. 49 | TArray m_RandomVecs; 50 | }; 51 | -------------------------------------------------------------------------------- /SnippetAsync/Private/SimpleAsync.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | inline void AsyncLog(const char* Action) 6 | { 7 | int32 ThreadId = FPlatformTLS::GetCurrentThreadId(); 8 | FString ThreadName = FThreadManager::Get().GetThreadName(ThreadId); 9 | UE_LOG(LogTemp, Display, TEXT("%s[%d], %s"), *ThreadName, ThreadId, ANSI_TO_TCHAR(Action)); 10 | } 11 | 12 | // Future & Promise 13 | inline void Test_FuturePromise() 14 | { 15 | TPromise Promise; 16 | TFuture Future = Promise.GetFuture(); 17 | 18 | // running in any threads 19 | FFunctionGraphTask::CreateAndDispatchWhenReady([&Promise]() 20 | { 21 | FPlatformProcess::Sleep(3); 22 | 23 | AsyncLog("do the promise"); 24 | Promise.SetValue(true); 25 | }); 26 | 27 | // waiting for the promise 28 | UE_LOG(LogTemp, Display, TEXT("waiting for the promise...")); 29 | // Future.Wait(); 30 | Future.WaitFor(FTimespan::FromSeconds(5)); 31 | // Future.WaitUntil(FDateTime(2022, 1, 1)); 32 | 33 | if (Future.IsReady()) 34 | { 35 | UE_LOG(LogTemp, Display, TEXT("keep the promise, future is %d"), Future.Get()); 36 | } 37 | else 38 | { 39 | Promise.SetValue(false); 40 | UE_LOG(LogTemp, Display, TEXT("break the promise")); 41 | } 42 | } 43 | 44 | ///////////////////////////////////////////////////////////////////////////////// 45 | 46 | inline void Test_FuturePromise2() 47 | { 48 | // promise with callback 49 | TPromise Promise([]() 50 | { 51 | AsyncLog("the promise is set !"); 52 | }); 53 | 54 | TFuture Future = Promise.GetFuture(); 55 | 56 | // running in any threads 57 | FFunctionGraphTask::CreateAndDispatchWhenReady([&Promise]() 58 | { 59 | FPlatformProcess::Sleep(3); 60 | 61 | AsyncLog("do the promise"); 62 | Promise.SetValue(true); 63 | })->Wait(ENamedThreads::GameThread); 64 | } 65 | 66 | TFuture DoSthAsync(int Value) 67 | { 68 | TPromise Promise; 69 | TFuture Future = Promise.GetFuture(); 70 | 71 | class FGraphTaskSimple 72 | { 73 | public: 74 | // CAUTION!: Must not use references in the constructor args; use pointers instead if you need by reference 75 | FGraphTaskSimple(TPromise&& ThePromise, int TheValue) 76 | : Promise(MoveTemp(ThePromise)), FutureValue(TheValue) 77 | { 78 | } 79 | 80 | FORCEINLINE TStatId GetStatId() const 81 | { 82 | RETURN_QUICK_DECLARE_CYCLE_STAT(FGraphTaskSimple, STATGROUP_TaskGraphTasks); 83 | } 84 | 85 | static ENamedThreads::Type GetDesiredThread() 86 | { 87 | return ENamedThreads::AnyThread; 88 | } 89 | 90 | static ESubsequentsMode::Type GetSubsequentsMode() 91 | { 92 | return ESubsequentsMode::FireAndForget; 93 | } 94 | 95 | void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 96 | { 97 | AsyncLog("DoSthAsync.... Begin"); 98 | FPlatformProcess::Sleep(3); 99 | AsyncLog("DoSthAsync.... Done"); 100 | 101 | Promise.SetValue(FutureValue); 102 | } 103 | 104 | private: 105 | TPromise Promise; 106 | int FutureValue; 107 | }; 108 | 109 | TGraphTask::CreateTask().ConstructAndDispatchWhenReady(MoveTemp(Promise), MoveTemp(Value)); 110 | 111 | return MoveTemp(Future); 112 | } 113 | 114 | inline void Test_FuturePromise3() 115 | { 116 | // usage1: get 117 | { 118 | auto Future = DoSthAsync(100); 119 | 120 | // todo other ... 121 | FPlatformProcess::Sleep(1); 122 | UE_LOG(LogTemp, Display, TEXT("Do something else ..")); 123 | 124 | // waiting for the value 125 | int Value = Future.Get(); 126 | UE_LOG(LogTemp, Display, TEXT("Value = %d"), Value); 127 | } 128 | 129 | // usage2 : then 130 | DoSthAsync(1) 131 | .Then([](TFuture Future) -> int 132 | { 133 | AsyncLog("then1 .. "); 134 | UE_LOG(LogTemp, Display, TEXT("Value = %d"), Future.Get()); 135 | return 2; 136 | }) 137 | .Then([](TFuture Future) 138 | { 139 | AsyncLog("then2 .. "); 140 | UE_LOG(LogTemp, Display, TEXT("Value = %d"), Future.Get()); 141 | }) 142 | .Wait(); 143 | 144 | AsyncLog("Over...."); 145 | } 146 | 147 | ///////////////////////////////////////////////////////////////////////////////// 148 | 149 | int SimpleAsyncFunc() 150 | { 151 | AsyncLog("SimpleAsyncFunc .... Begin"); 152 | FPlatformProcess::Sleep(1); 153 | AsyncLog("SimpleAsyncFunc .... Over"); 154 | return 123; 155 | } 156 | 157 | inline void Test_Async1() 158 | { 159 | auto Future = Async(EAsyncExecution::TaskGraph, SimpleAsyncFunc); 160 | 161 | // todo other ... 162 | FPlatformProcess::Sleep(1); 163 | UE_LOG(LogTemp, Display, TEXT("Do something else ..")); 164 | 165 | // waiting for the value 166 | int Value = Future.Get(); 167 | UE_LOG(LogTemp, Display, TEXT("Value = %d"), Value); 168 | } 169 | 170 | inline void Test_Async2() 171 | { 172 | // using global function 173 | Async(EAsyncExecution::Thread, SimpleAsyncFunc); 174 | 175 | // using task 176 | // TUniqueFunction Task = SimpleTestFunc; 177 | TUniqueFunction Task = []() 178 | { 179 | AsyncLog("Lambda1 .... Begin"); 180 | FPlatformProcess::Sleep(1); 181 | AsyncLog("Lambda1 .... Over"); 182 | return 123; 183 | }; 184 | 185 | Async(EAsyncExecution::Thread, MoveTemp(Task)); 186 | 187 | // using inline lambda 188 | Async(EAsyncExecution::Thread, []() 189 | { 190 | AsyncLog("Inline Lambda .... Begin"); 191 | FPlatformProcess::Sleep(1); 192 | AsyncLog("Inline Lambda .... Over"); 193 | }); 194 | 195 | // with completion callback 196 | Async(EAsyncExecution::ThreadPool, 197 | []() 198 | { 199 | AsyncLog("Inline Lambda2 .... Begin"); 200 | FPlatformProcess::Sleep(1); 201 | AsyncLog("Inline Lambda2 .... Over"); 202 | }, 203 | []() 204 | { 205 | AsyncLog("Inline Lambda2 .... Completion Callback"); 206 | }); 207 | 208 | // Execute a given function asynchronously using a separate thread. 209 | AsyncThread([]() 210 | { 211 | AsyncLog("AsyncThread Function .... Begin"); 212 | FPlatformProcess::Sleep(1); 213 | AsyncLog("AsyncThread Function .... Over"); 214 | }); 215 | 216 | // Execute a given function asynchronously on the specified thread pool. 217 | AsyncPool(*GThreadPool, []() 218 | { 219 | AsyncLog("AsyncPool Function .... Begin"); 220 | FPlatformProcess::Sleep(1); 221 | AsyncLog("AsyncPool Function .... Over"); 222 | }); 223 | 224 | // Executing code asynchronously on the Task Graph. 225 | AsyncTask(ENamedThreads::AnyThread, []() 226 | { 227 | AsyncLog("AsyncTask Function .... Begin"); 228 | FPlatformProcess::Sleep(1); 229 | AsyncLog("AsyncTask Function .... Over"); 230 | }); 231 | } 232 | 233 | ///////////////////////////////////////////////////////////////////////////////// 234 | 235 | inline void Test_Parallel() 236 | { 237 | // parallel 238 | ParallelFor(100, [](int32 Index) 239 | { 240 | int32 ThreadId = FPlatformTLS::GetCurrentThreadId(); 241 | FString ThreadName = FThreadManager::Get().GetThreadName(ThreadId); 242 | UE_LOG(LogTemp, Display, TEXT("%s[%d],Parallel Task, Index:%d"), *ThreadName, ThreadId, Index); 243 | }); 244 | 245 | // done 246 | UE_LOG(LogTemp, Display, TEXT("Over....")); 247 | } -------------------------------------------------------------------------------- /SnippetAsync/Private/SimpleAsyncTask.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | class SimpleExampleTask : public FNonAbandonableTask 6 | { 7 | friend class FAsyncTask; 8 | 9 | int32 ExampleData; 10 | float WorkingTime; 11 | 12 | public: 13 | SimpleExampleTask(int32 InExampleData, float TheWorkingTime = 1) 14 | : ExampleData(InExampleData), WorkingTime(TheWorkingTime) 15 | { 16 | } 17 | 18 | ~SimpleExampleTask() 19 | { 20 | Log(__FUNCTION__); 21 | } 22 | 23 | void DoWork() 24 | { 25 | // do the work... 26 | FPlatformProcess::Sleep(WorkingTime); 27 | Log(__FUNCTION__); 28 | } 29 | 30 | FORCEINLINE TStatId GetStatId() const 31 | { 32 | RETURN_QUICK_DECLARE_CYCLE_STAT(ExampleAsyncTask, STATGROUP_ThreadPoolAsyncTasks); 33 | } 34 | 35 | /////////////////////// 36 | 37 | 38 | void Log(const char* Action) 39 | { 40 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 41 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 42 | UE_LOG(LogTemp, Display, TEXT("%s[%d] - %s, ExampleData=%d"), *CurrentThreadName, CurrentThreadId, 43 | ANSI_TO_TCHAR(Action), ExampleData); 44 | } 45 | }; 46 | 47 | inline void Test_SimpleTask_1() 48 | { 49 | // start an example job, running in GThreadPool 50 | (new FAutoDeleteAsyncTask(1000))->StartBackgroundTask(); 51 | 52 | // do an example job now, on this thread 53 | (new FAutoDeleteAsyncTask(2000))->StartSynchronousTask(); 54 | } 55 | 56 | inline void Test_SimpleTask_2(bool bForceOnThisThread) 57 | { 58 | //start an example job 59 | FAsyncTask* MyTask = new FAsyncTask(1000); 60 | 61 | if (bForceOnThisThread) 62 | MyTask->StartSynchronousTask(); 63 | else 64 | MyTask->StartBackgroundTask(); 65 | 66 | //to just do it now on this thread 67 | //Check if the task is done : 68 | bool IsDone = MyTask->IsDone(); 69 | UE_LOG(LogTemp, Display, TEXT("Is Done : %d"), IsDone); 70 | 71 | //Spinning on IsDone is not acceptable( see EnsureCompletion ), but it is ok to check once a frame. 72 | //Ensure the task is done, doing the task on the current thread if it has not been started, waiting until completion in all cases. 73 | MyTask->EnsureCompletion(); 74 | delete MyTask; 75 | } 76 | 77 | inline void Test_SimpleTask_3() 78 | { 79 | using FSimpleExampleAsyncTask = FAsyncTask; 80 | 81 | int TaskCount = 20; 82 | TArray Tasks; 83 | 84 | // create a bunch of tasks 85 | for (int i = 0; i < TaskCount; ++i) 86 | { 87 | FSimpleExampleAsyncTask* MyTask = new FSimpleExampleAsyncTask(i + 1, 3); 88 | if (MyTask) 89 | { 90 | MyTask->StartBackgroundTask(); 91 | Tasks.Add(MyTask); 92 | } 93 | } 94 | 95 | // Do some other work 96 | FPlatformProcess::Sleep(5); 97 | 98 | // If not done, cancel it 99 | TArray RemainTasks; 100 | for (auto Task : Tasks) 101 | { 102 | if (Task->IsDone()) 103 | { 104 | UE_LOG(LogTemp, Display, TEXT("Done ..............")); 105 | delete Task; 106 | } 107 | else 108 | { 109 | if (Task->Cancel()) 110 | { 111 | UE_LOG(LogTemp, Display, TEXT("Cancel ..............")); 112 | delete Task; 113 | } 114 | else 115 | { 116 | UE_LOG(LogTemp, Display, TEXT("Still Working ..............")); 117 | RemainTasks.Add(Task); 118 | } 119 | } 120 | } 121 | Tasks.Reset(); 122 | 123 | // waiting for the remain tasks 124 | for (auto Task : RemainTasks) 125 | { 126 | UE_LOG(LogTemp, Display, TEXT("EnsureCompletion ..............")); 127 | Task->EnsureCompletion(); 128 | delete Task; 129 | } 130 | 131 | UE_LOG(LogTemp, Display, TEXT("Over ..............")); 132 | } 133 | -------------------------------------------------------------------------------- /SnippetAsync/Private/SimpleGraphTask.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Core.h" 3 | 4 | 5 | class FGraphTaskSimple 6 | { 7 | public: 8 | // CAUTION!: Must not use references in the constructor args; use pointers instead if you need by reference 9 | FGraphTaskSimple(const TCHAR* TheName, int InSomeArgument, float InWorkingTime = 1.0f) 10 | : TaskName(TheName), SomeArgument(InSomeArgument), WorkingTime(InWorkingTime) 11 | { 12 | // Usually the constructor doesn't do anything except save the arguments for use in DoWork or GetDesiredThread. 13 | Log(__FUNCTION__); 14 | } 15 | 16 | ~FGraphTaskSimple() 17 | { 18 | // you will be destroyed immediately after you execute. Might as well do cleanup in DoWork, but you could also use a destructor. 19 | Log(__FUNCTION__); 20 | } 21 | 22 | FORCEINLINE TStatId GetStatId() const 23 | { 24 | RETURN_QUICK_DECLARE_CYCLE_STAT(FGraphTaskSimple, STATGROUP_TaskGraphTasks); 25 | } 26 | 27 | static ENamedThreads::Type GetDesiredThread() 28 | { 29 | return ENamedThreads::AnyThread; 30 | } 31 | 32 | static ESubsequentsMode::Type GetSubsequentsMode() 33 | { 34 | return ESubsequentsMode::FireAndForget; 35 | } 36 | 37 | void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 38 | { 39 | // The arguments are useful for setting up other tasks. 40 | // Do work here, probably using SomeArgument. 41 | FPlatformProcess::Sleep(WorkingTime); 42 | Log(__FUNCTION__); 43 | 44 | // MyCompletionGraphEvent->DontCompleteUntil( 45 | // TGraphTask::CreateTask(NULL, CurrentThread).ConstructAndDispatchWhenReady()); 46 | } 47 | 48 | public: 49 | FString TaskName; 50 | 51 | int SomeArgument; 52 | 53 | float WorkingTime; 54 | 55 | void Log(const char* Action) 56 | { 57 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 58 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 59 | UE_LOG(LogTemp, Display, TEXT("%s@%s[%d] - %s, SomeArgument=%d"), *TaskName, *CurrentThreadName, 60 | CurrentThreadId, 61 | ANSI_TO_TCHAR(Action), SomeArgument); 62 | } 63 | }; 64 | 65 | 66 | class FTask : public FGraphTaskSimple 67 | { 68 | public: 69 | using FGraphTaskSimple::FGraphTaskSimple; 70 | 71 | FORCEINLINE TStatId GetStatId() const 72 | { 73 | RETURN_QUICK_DECLARE_CYCLE_STAT(FGraphTask, STATGROUP_TaskGraphTasks); 74 | } 75 | 76 | static ENamedThreads::Type GetDesiredThread() 77 | { 78 | return ENamedThreads::AnyThread; 79 | } 80 | 81 | static ESubsequentsMode::Type GetSubsequentsMode() 82 | { 83 | return ESubsequentsMode::TrackSubsequents; 84 | } 85 | }; 86 | 87 | 88 | // Only One Task - Auto delete 89 | inline void Test_GraphTask_Simple() 90 | { 91 | // construct and dispatch, when the work is done, deleted automatically 92 | TGraphTask::CreateTask(). 93 | ConstructAndDispatchWhenReady(TEXT("SimpleTask1"), 10000, 3); 94 | 95 | // hold and unlock to run 96 | TGraphTask* Task = TGraphTask::CreateTask().ConstructAndHold( 97 | TEXT("SimpleTask2"), 20000); 98 | if (Task) 99 | { 100 | // unlock to run 101 | UE_LOG(LogTemp, Display, TEXT("Task is Unlock to Run...")); 102 | Task->Unlock(); 103 | Task = nullptr; 104 | } 105 | } 106 | 107 | // Only One Task - Waiting in gamethread 108 | inline void Test_GraphTask_Simple1() 109 | { 110 | // task completes after it's waited for 111 | FGraphEventRef Event = FFunctionGraphTask::CreateAndDispatchWhenReady([]() 112 | { 113 | UE_LOG(LogTemp, Display, TEXT("Main task")); 114 | FPlatformProcess::Sleep(5.f); // pause for a bit to let waiting start 115 | } 116 | ); 117 | check(!Event->IsComplete()); 118 | Event->Wait(ENamedThreads::GameThread); 119 | UE_LOG(LogTemp, Display, TEXT("Over1 ...")); 120 | 121 | // tasks 122 | FGraphEventArray Tasks; 123 | for (int i = 0; i < 10; ++i) 124 | { 125 | Tasks.Add(FFunctionGraphTask::CreateAndDispatchWhenReady([i]() 126 | { 127 | UE_LOG(LogTemp, Display, TEXT("Task %d"), i); 128 | })); 129 | } 130 | FTaskGraphInterface::Get().WaitUntilTasksComplete(MoveTemp(Tasks), ENamedThreads::GameThread); 131 | UE_LOG(LogTemp, Display, TEXT("Over2 ...")); 132 | } 133 | 134 | 135 | // TaskA -> TaskB -> TaskC 136 | inline void Test_GraphTask_Simple2() 137 | { 138 | UE_LOG(LogTemp, Display, TEXT("Start ......")); 139 | 140 | FGraphEventRef TaskA, TaskB, TaskC; 141 | 142 | // TaskA 143 | { 144 | TaskA = TGraphTask::CreateTask().ConstructAndDispatchWhenReady(TEXT("TaksA"), 1, 3); 145 | } 146 | 147 | // TaskA -> TaskB 148 | { 149 | FGraphEventArray Prerequisites; 150 | Prerequisites.Add(TaskA); 151 | TaskB = TGraphTask::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady(TEXT("TaksB"), 2, 2); 152 | } 153 | 154 | 155 | // TaskB -> TaskC 156 | { 157 | FGraphEventArray Prerequisites{TaskB}; 158 | TaskC = TGraphTask::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady(TEXT("TaksC"), 3, 1); 159 | } 160 | 161 | 162 | UE_LOG(LogTemp, Display, TEXT("Construct is Done ......")); 163 | 164 | 165 | UE_LOG(LogTemp, Display, TEXT("TaskA is Done : %d"), TaskA->IsComplete()); 166 | UE_LOG(LogTemp, Display, TEXT("TaskB is Done : %d"), TaskA->IsComplete()); 167 | UE_LOG(LogTemp, Display, TEXT("TaskC is Done : %d"), TaskC->IsComplete()); 168 | 169 | // Waiting until TaskC is Done 170 | // FTaskGraphInterface::Get().WaitUntilTaskCompletes(TaskC); 171 | // Or. 172 | TaskC->Wait(); 173 | 174 | UE_LOG(LogTemp, Display, TEXT("TaskA is Done : %d"), TaskA->IsComplete()); 175 | UE_LOG(LogTemp, Display, TEXT("TaskB is Done : %d"), TaskA->IsComplete()); 176 | UE_LOG(LogTemp, Display, TEXT("TaskC is Done : %d"), TaskC->IsComplete()); 177 | 178 | UE_LOG(LogTemp, Display, TEXT("Over ......")); 179 | } 180 | 181 | inline void Test_GraphTask_Simple2_Funciton() 182 | { 183 | UE_LOG(LogTemp, Display, TEXT("Start ......")); 184 | 185 | auto TaskA = FFunctionGraphTask::CreateAndDispatchWhenReady( 186 | [](ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 187 | { 188 | auto TaskB = FFunctionGraphTask::CreateAndDispatchWhenReady( 189 | [](ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 190 | { 191 | auto TaskC = FFunctionGraphTask::CreateAndDispatchWhenReady( 192 | [](ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 193 | { 194 | FPlatformProcess::Sleep(1); 195 | UE_LOG(LogTemp, Display, TEXT("TaskC is Done")); 196 | }); 197 | 198 | FPlatformProcess::Sleep(2); 199 | UE_LOG(LogTemp, Display, TEXT("TaskB is Done")); 200 | }); 201 | 202 | FPlatformProcess::Sleep(1); 203 | UE_LOG(LogTemp, Display, TEXT("TaskA is Done")); 204 | }); 205 | 206 | TaskA->Wait(ENamedThreads::GameThread); 207 | UE_LOG(LogTemp, Display, TEXT("Over ......")); 208 | } 209 | 210 | // 211 | // -> TaskB -> TaskC -> 212 | // / \ 213 | // TaskA -> TaskD ---------------> TaskE 214 | // 215 | inline void Test_GraphTask_Simple3() 216 | { 217 | FGraphEventRef TaskA, TaskB, TaskC, TaskD, TaskE; 218 | 219 | TaskA = TGraphTask::CreateTask().ConstructAndDispatchWhenReady(TEXT("TaksA"), 1, 1); 220 | 221 | { 222 | FGraphEventArray Prerequisites; 223 | Prerequisites.Add(TaskA); 224 | TaskB = TGraphTask::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady(TEXT("TaksB"), 1, 1); 225 | } 226 | 227 | { 228 | FGraphEventArray Prerequisites; 229 | Prerequisites.Add(TaskB); 230 | TaskC = TGraphTask::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady(TEXT("TaksC"), 1, 1); 231 | } 232 | 233 | { 234 | FGraphEventArray Prerequisites; 235 | Prerequisites.Add(TaskA); 236 | TaskD = TGraphTask::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady(TEXT("TaksD"), 1, 3); 237 | } 238 | 239 | { 240 | FGraphEventArray Prerequisites {TaskC, TaskD}; 241 | TaskE = TGraphTask::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady(TEXT("TaksE"), 1, 1); 242 | } 243 | 244 | UE_LOG(LogTemp, Display, TEXT("Construct is Done ......")); 245 | 246 | TaskE->Wait(); 247 | 248 | UE_LOG(LogTemp, Display, TEXT("Over ......")); 249 | } 250 | 251 | 252 | // 253 | // TaskA -> 254 | // | -> NullTask(Gather/Fence) 255 | // TaskB -> 256 | // 257 | inline void Test_GraphTask_NullTask() 258 | { 259 | auto TaskA = TGraphTask::CreateTask().ConstructAndDispatchWhenReady(TEXT("TaskA"), 1, 2); 260 | auto TaskB = TGraphTask::CreateTask().ConstructAndDispatchWhenReady(TEXT("TaskB"), 2, 1); 261 | 262 | 263 | FGraphEventRef CompleteEvent; 264 | { 265 | DECLARE_CYCLE_STAT(TEXT("FNullGraphTask.Gather"), 266 | STAT_FNullGraphTask_Gather, 267 | STATGROUP_TaskGraphTasks); 268 | 269 | FGraphEventArray Prerequisites; 270 | Prerequisites.Add(TaskA); 271 | Prerequisites.Add(TaskB); 272 | CompleteEvent = TGraphTask::CreateTask(&Prerequisites).ConstructAndDispatchWhenReady( 273 | GET_STATID(STAT_FNullGraphTask_Gather), ENamedThreads::GameThread); 274 | } 275 | 276 | 277 | UE_LOG(LogTemp, Display, TEXT("Construct is Done ......")); 278 | 279 | CompleteEvent->Wait(); 280 | 281 | UE_LOG(LogTemp, Display, TEXT("Over ......")); 282 | } 283 | 284 | 285 | // FReturnGraphTask 286 | inline void Test_GraphTask_ReturnTask() 287 | { 288 | // Stop the RHI Thread (using IsRHIThreadRunning() is unreliable since RT may be stopped) 289 | if (FTaskGraphInterface::IsRunning() && FTaskGraphInterface::Get(). 290 | IsThreadProcessingTasks(ENamedThreads::RHIThread)) 291 | { 292 | DECLARE_CYCLE_STAT(TEXT("Wait For RHIThread Finish"), STAT_WaitForRHIThreadFinish, STATGROUP_TaskGraphTasks); 293 | FGraphEventRef QuitTask = TGraphTask::CreateTask(nullptr, ENamedThreads::GameThread). 294 | ConstructAndDispatchWhenReady(ENamedThreads::RHIThread); 295 | FTaskGraphInterface::Get().WaitUntilTaskCompletes(QuitTask, ENamedThreads::GameThread_Local); 296 | } 297 | } 298 | 299 | //FTriggerEventGraphTask 300 | // a task that triggers an event(operating system Event object) 301 | 302 | 303 | // FSimpleDelegateGraphTask 304 | // for simple delegate based tasks.This is less efficient than a custom task, doesn’t provide the task arguments, doesn’t allow specification of the current thread, etc. 305 | 306 | // FDelegateGraphTask 307 | // class for more full featured delegate based tasks.Still less efficient than a custom task, but provides all of the args. 308 | inline void Test_GraphTask_Delegate() 309 | { 310 | // Simple Delegate 311 | FSimpleDelegateGraphTask::CreateAndDispatchWhenReady( 312 | FSimpleDelegateGraphTask::FDelegate::CreateLambda([]() 313 | { 314 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 315 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 316 | UE_LOG(LogTemp, Display, TEXT("%s[%d] - Simple Delegate"), *CurrentThreadName, CurrentThreadId); 317 | }), 318 | TStatId() 319 | ); 320 | 321 | 322 | // Delegate 323 | FTaskGraphInterface::Get().WaitUntilTaskCompletes( 324 | FDelegateGraphTask::CreateAndDispatchWhenReady( 325 | FDelegateGraphTask::FDelegate::CreateLambda( 326 | [](ENamedThreads::Type InCurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 327 | { 328 | FPlatformProcess::Sleep(3); 329 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 330 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 331 | UE_LOG(LogTemp, Display, TEXT("%s[%d] - Delegate, %d"), *CurrentThreadName, CurrentThreadId, 332 | InCurrentThread); 333 | }), 334 | TStatId() 335 | ), 336 | ENamedThreads::GameThread 337 | ); 338 | 339 | UE_LOG(LogTemp, Display, TEXT("Over ......")); 340 | } 341 | 342 | 343 | // FFunctionGraphTask 344 | inline void Test_GraphTask_Function() 345 | { 346 | FFunctionGraphTask::CreateAndDispatchWhenReady([]() 347 | { 348 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 349 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 350 | UE_LOG(LogTemp, Display, TEXT("%s[%d] - Fuction with Void"), *CurrentThreadName, CurrentThreadId); 351 | }, TStatId()); 352 | 353 | 354 | FFunctionGraphTask::CreateAndDispatchWhenReady( 355 | [](ENamedThreads::Type InCurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 356 | { 357 | FPlatformProcess::Sleep(3); 358 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 359 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 360 | UE_LOG(LogTemp, Display, TEXT("%s[%d] - Function with parameters, %d"), *CurrentThreadName, CurrentThreadId, 361 | InCurrentThread); 362 | }, TStatId())->Wait(ENamedThreads::GameThread); 363 | 364 | UE_LOG(LogTemp, Display, TEXT("Over ......")); 365 | } 366 | -------------------------------------------------------------------------------- /SnippetAsync/Private/SimpleLockFree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | 6 | class FMyData 7 | { 8 | public: 9 | FMyData(const int& V) : Value(V) 10 | { 11 | } 12 | 13 | int Value; 14 | }; 15 | 16 | 17 | inline void Test_LockFree_LIFO() 18 | { 19 | TLockFreePointerListLIFO ThreadSafeStack; 20 | 21 | TArray> Futures; 22 | const int ThreadNum = 10; 23 | 24 | // Create N threads 25 | for (int i = 0; i < ThreadNum; ++i) 26 | { 27 | TFuture Future = Async(EAsyncExecution::Thread, [&ThreadSafeStack, i]() 28 | { 29 | // Push in a thread 30 | ThreadSafeStack.Push(new FMyData(i)); 31 | 32 | // FPlatformProcess::Sleep(2); 33 | int32 ThreadId = FPlatformTLS::GetCurrentThreadId(); 34 | FString ThreadName = FThreadManager::Get().GetThreadName(ThreadId); 35 | UE_LOG(LogTemp, Display, TEXT("%s[%d], Push, %d"), *ThreadName, ThreadId, i); 36 | }); 37 | 38 | Futures.Add(MoveTemp(Future)); 39 | } 40 | 41 | // Waiting for thread done 42 | while (true) 43 | { 44 | bool IsAllThreadDone = true; 45 | for (auto& Future : Futures) 46 | { 47 | if (!Future.IsReady()) 48 | IsAllThreadDone = false; 49 | } 50 | 51 | if (IsAllThreadDone) break; 52 | } 53 | 54 | 55 | // Dump all in main thread 56 | while (FMyData* Data = ThreadSafeStack.Pop()) 57 | { 58 | int32 ThreadId = FPlatformTLS::GetCurrentThreadId(); 59 | FString ThreadName = FThreadManager::Get().GetThreadName(ThreadId); 60 | UE_LOG(LogTemp, Display, TEXT("%s[%d], Dump - %d"), *ThreadName, ThreadId, Data->Value); 61 | delete Data; 62 | } 63 | 64 | UE_LOG(LogTemp, Display, TEXT("Over .....")); 65 | } 66 | 67 | inline void Test_LockFree_FIFO() 68 | { 69 | TLockFreePointerListFIFO ThreadSafeList; 70 | 71 | TArray> Futures; 72 | const int ThreadNum = 10; 73 | 74 | // Create N threads 75 | for (int i = 0; i < ThreadNum; ++i) 76 | { 77 | TFuture Future = Async(EAsyncExecution::Thread, [&ThreadSafeList, i]() 78 | { 79 | // Push in a thread 80 | ThreadSafeList.Push(new FMyData(i)); 81 | 82 | // FPlatformProcess::Sleep(2); 83 | int32 ThreadId = FPlatformTLS::GetCurrentThreadId(); 84 | FString ThreadName = FThreadManager::Get().GetThreadName(ThreadId); 85 | UE_LOG(LogTemp, Display, TEXT("%s[%d], Push, %d"), *ThreadName, ThreadId, i); 86 | }); 87 | 88 | Futures.Add(MoveTemp(Future)); 89 | } 90 | 91 | // Waiting for thread done 92 | while (true) 93 | { 94 | bool IsAllThreadDone = true; 95 | for (auto& Future : Futures) 96 | { 97 | if (!Future.IsReady()) 98 | IsAllThreadDone = false; 99 | } 100 | 101 | if (IsAllThreadDone) break; 102 | } 103 | 104 | 105 | // Dump all in main thread 106 | while (FMyData* Data = ThreadSafeList.Pop()) 107 | { 108 | int32 ThreadId = FPlatformTLS::GetCurrentThreadId(); 109 | FString ThreadName = FThreadManager::Get().GetThreadName(ThreadId); 110 | UE_LOG(LogTemp, Display, TEXT("%s[%d], Dump - %d"), *ThreadName, ThreadId, Data->Value); 111 | delete Data; 112 | } 113 | 114 | UE_LOG(LogTemp, Display, TEXT("Over .....")); 115 | } -------------------------------------------------------------------------------- /SnippetAsync/Private/SimpleProducerConsumer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | #include 5 | 6 | // 7 | // Tests: TaskGraphTask.cpp 8 | // 9 | 10 | // 11 | // TQueue Examples 12 | // 13 | struct FMyItem 14 | { 15 | FMyItem(uint32 TheId = 0, const FString& TheName = TEXT("Item")) 16 | : Id(TheId), Name(TheName) 17 | { 18 | } 19 | 20 | uint32 Id; 21 | FString Name; 22 | }; 23 | 24 | static TQueue ItemsQueue; 25 | 26 | inline void Test_Queue() 27 | { 28 | // Single Producer 29 | Async(EAsyncExecution::Thread, []() 30 | { 31 | for (uint32 Id = 1; ; Id++) 32 | { 33 | FPlatformProcess::Sleep(1); 34 | ItemsQueue.Enqueue(FMyItem(Id, "Item")); 35 | UE_LOG(LogTemp, Display, TEXT("Produce: %d,%s"), Id, TEXT("Item")); 36 | } 37 | }); 38 | 39 | // Single Consumer 40 | Async(EAsyncExecution::Thread, []() 41 | { 42 | while (true) 43 | { 44 | if (!ItemsQueue.IsEmpty()) 45 | { 46 | FMyItem Item; 47 | ItemsQueue.Dequeue(Item); 48 | UE_LOG(LogTemp, Display, TEXT("Consume: %d,%s"), Item.Id, *Item.Name); 49 | } 50 | } 51 | }); 52 | } 53 | 54 | // 55 | // Multiple Producer, Multiple Consumer Examples 56 | // 57 | template 58 | class TQueueMPMC 59 | { 60 | public: 61 | TQueueMPMC() 62 | { 63 | ItemsCount = 0; 64 | Items.Reserve(QueueSize); 65 | } 66 | 67 | void Enqueue(const ItemType& Item) 68 | { 69 | // Block if full 70 | if (ItemsCount == QueueSize) 71 | { 72 | UE_LOG(LogTemp, Display, TEXT("Enque-Waiting....")); 73 | FullEvent->Wait(); 74 | } 75 | 76 | // Push 77 | ItemsMutex.Lock(); 78 | Items.Push(Item); 79 | ItemsCount = Items.Num(); 80 | ItemsMutex.Unlock(); 81 | 82 | // Notify is not empty anymore 83 | if (ItemsCount >= 1) 84 | { 85 | EmptyEvent->Trigger(); 86 | } 87 | } 88 | 89 | ItemType Dequeue() 90 | { 91 | // Block if empty 92 | if (ItemsCount == 0) 93 | { 94 | UE_LOG(LogTemp, Display, TEXT("Dequeue-Waiting....")); 95 | EmptyEvent->Wait(); 96 | } 97 | 98 | // Pop Item 99 | ItemType Item; 100 | ItemsMutex.Lock(); 101 | if (Items.Num() > 0) { Item = Items.Pop(); } else { Item = ItemType(); } 102 | ItemsCount = Items.Num(); 103 | ItemsMutex.Unlock(); 104 | 105 | // Notify is not full anymore 106 | if (ItemsCount == (QueueSize - 1)) 107 | FullEvent->Trigger(); 108 | 109 | return Item; 110 | } 111 | 112 | private: 113 | FEventRef FullEvent; 114 | FEventRef EmptyEvent; 115 | TAtomic ItemsCount; 116 | FCriticalSection ItemsMutex; 117 | TArray Items; 118 | }; 119 | 120 | struct MPMCTest 121 | { 122 | static TQueueMPMC QueueMPMC; 123 | 124 | static void Producer(int ItemID = 0) 125 | { 126 | int Item = ItemID; 127 | while (true) 128 | { 129 | // produce item 130 | Item++; 131 | // enqueue, if is full, waiting for consumer 132 | QueueMPMC.Enqueue(Item); 133 | } 134 | } 135 | 136 | static void Consumer() 137 | { 138 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 139 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 140 | 141 | while (true) 142 | { 143 | // dequeue, if is empty, waiting for producer 144 | int Item = QueueMPMC.Dequeue(); 145 | // consume item 146 | UE_LOG(LogTemp, Display, TEXT("Consumer@%s, Item=%d"), *CurrentThreadName, Item); 147 | } 148 | } 149 | }; 150 | 151 | inline void Test_MPMC() 152 | { 153 | const int NumProducer = 5; 154 | const int NumConsumer = 3; 155 | 156 | // Multiple Producer 157 | for (int i = 0; i < NumProducer; ++i) 158 | { 159 | Async(EAsyncExecution::Thread, [i]() 160 | { 161 | MPMCTest::Producer(i * 1000000); 162 | }); 163 | } 164 | 165 | // Multiple Consumer 166 | for (int i = 0; i < NumConsumer; ++i) 167 | { 168 | Async(EAsyncExecution::Thread, &MPMCTest::Consumer); 169 | } 170 | } 171 | 172 | // 173 | // Double Buffering Examples 174 | // 175 | template 176 | class TDoubleBuffer 177 | { 178 | public: 179 | TDoubleBuffer(uint32 Capacity = (uint32)-1) 180 | : MaxCapacity(Capacity) 181 | { 182 | WriteBuffer = new TArray(); 183 | ReadBuffer = new TArray(); 184 | } 185 | 186 | ~TDoubleBuffer() 187 | { 188 | delete WriteBuffer; 189 | delete ReadBuffer; 190 | } 191 | 192 | bool Enqueue(const ItemType& Item) 193 | { 194 | FScopeLock Lock(&SwapMutex); 195 | if ((uint32)WriteBuffer->Num() > MaxCapacity) 196 | return false; 197 | 198 | WriteBuffer->Push(Item); 199 | return true; 200 | } 201 | 202 | bool Dequeue(ItemType& Item) 203 | { 204 | if (ReadBuffer->Num() == 0) 205 | { 206 | // Swap Read&Write Buffer 207 | FScopeLock Lock(&SwapMutex); 208 | Swap(WriteBuffer, ReadBuffer); 209 | if (ReadBuffer->Num() == 0) 210 | return false; 211 | } 212 | 213 | if (ReadBuffer->Num() > 0) 214 | { 215 | Item = ReadBuffer->Pop(); 216 | return true; 217 | } 218 | 219 | return false; 220 | } 221 | 222 | private: 223 | uint32 MaxCapacity; 224 | FCriticalSection SwapMutex; 225 | TArray* WriteBuffer; 226 | TArray* ReadBuffer; 227 | }; 228 | 229 | inline void Test_DoubleBuffer() 230 | { 231 | TDoubleBuffer DoubleBuffer; 232 | 233 | std::atomic bStop{false}; 234 | // Producer Thread 235 | auto IOThread = Async(EAsyncExecution::Thread, [&bStop, &DoubleBuffer]() 236 | { 237 | FRandomStream Rand; 238 | Rand.GenerateNewSeed(); 239 | while (!bStop) 240 | { 241 | // Produce a Object 242 | DoubleBuffer.Enqueue(Rand.GetUnsignedInt()); 243 | } 244 | }); 245 | 246 | // Consumer Thread(Main) 247 | const uint32 MaxConsuming = 100000; 248 | for (uint32 i = 0; i < MaxConsuming;) 249 | { 250 | uint32 Item; 251 | if (DoubleBuffer.Dequeue(Item)) 252 | { 253 | ++i; 254 | UE_LOG(LogTemp, Display, TEXT("Consumer %u, Item=%u"), i, Item); 255 | } 256 | } 257 | 258 | bStop = true; 259 | IOThread.Wait(); 260 | 261 | UE_LOG(LogTemp, Display, TEXT("OVer....")); 262 | } 263 | -------------------------------------------------------------------------------- /SnippetAsync/Private/SimpleQueuedWorker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | 5 | class FSimpleQueuedWorker : public IQueuedWork 6 | { 7 | public: 8 | FSimpleQueuedWorker(const FString& Name) : WorkerName(Name) 9 | { 10 | Log(__FUNCTION__); 11 | } 12 | 13 | virtual ~FSimpleQueuedWorker() override 14 | { 15 | Log(__FUNCTION__); 16 | } 17 | 18 | virtual void DoThreadedWork() override 19 | { 20 | FPlatformProcess::Sleep(0.2); 21 | Log(__FUNCTION__); 22 | 23 | // Finish the task, delete the worker 24 | delete this; 25 | } 26 | 27 | virtual void Abandon() override 28 | { 29 | Log(__FUNCTION__); 30 | 31 | // Abandon the task, delete the worker 32 | delete this; 33 | } 34 | 35 | void Log(const char* Action) 36 | { 37 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 38 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 39 | UE_LOG(LogTemp, Display, TEXT("%s@%s[%d] - %s"), 40 | *WorkerName, *CurrentThreadName, CurrentThreadId, ANSI_TO_TCHAR(Action)); 41 | } 42 | 43 | public: 44 | FString WorkerName; 45 | }; 46 | 47 | 48 | inline void Test_SimpleQueuedWorker() 49 | { 50 | // Create A Thread Pool 51 | FQueuedThreadPool* Pool = FQueuedThreadPool::Allocate(); 52 | Pool->Create(5, 0, TPri_Normal, TEXT("SimpleThreadPool")); 53 | 54 | int WokerNum = 100; 55 | for (int i = 0; i < WokerNum; ++i) 56 | { 57 | FString Name = TEXT("Worker") + FString::FromInt(i); 58 | 59 | // Worker will be deleted when the job is done 60 | Pool->AddQueuedWork(new FSimpleQueuedWorker(Name)); 61 | } 62 | 63 | // Ticks 64 | int TickCount = 20; 65 | for (int i = 0; i < TickCount; ++i) 66 | { 67 | // Consume 68 | UE_LOG(LogTemp, Display, TEXT("Tick[%d] ........ "), i); 69 | FPlatformProcess::Sleep(0.1); 70 | } 71 | 72 | // Destroy, if work is not done, call Abandon 73 | Pool->Destroy(); 74 | delete Pool; 75 | } 76 | -------------------------------------------------------------------------------- /SnippetAsync/Private/SimpleThread.cpp: -------------------------------------------------------------------------------- 1 | #include "SimpleThread.h" 2 | #include "SimpleProducerConsumer.h" 3 | 4 | TQueueMPMC MPMCTest::QueueMPMC; -------------------------------------------------------------------------------- /SnippetAsync/Private/SimpleThread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core.h" 4 | #include "Misc/ScopeRWLock.h" 5 | 6 | class FSimpleThread : public FRunnable 7 | { 8 | public: 9 | FSimpleThread(const FString& TheName) : Name(TheName) 10 | { 11 | RunnableThread = FRunnableThread::Create(this, *Name); 12 | Log(__FUNCTION__); 13 | } 14 | 15 | virtual ~FSimpleThread() override 16 | { 17 | if (RunnableThread) 18 | { 19 | RunnableThread->WaitForCompletion(); 20 | delete RunnableThread; 21 | RunnableThread = nullptr; 22 | Log(__FUNCTION__); 23 | } 24 | } 25 | 26 | virtual bool Init() override 27 | { 28 | Log(__FUNCTION__); 29 | return true; 30 | } 31 | 32 | virtual uint32 Run() override 33 | { 34 | while (!bStop) 35 | { 36 | FPlatformProcess::Sleep(1); 37 | Log(__FUNCTION__); 38 | } 39 | return 0; 40 | } 41 | 42 | virtual void Exit() override 43 | { 44 | Log(__FUNCTION__); 45 | } 46 | 47 | 48 | virtual void Stop() override 49 | { 50 | bStop = true; 51 | if (RunnableThread) 52 | RunnableThread->WaitForCompletion(); 53 | } 54 | 55 | void Log(const char* Action) 56 | { 57 | uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); 58 | FString CurrentThreadName = FThreadManager::Get().GetThreadName(CurrentThreadId); 59 | 60 | if (RunnableThread) 61 | { 62 | UE_LOG(LogTemp, Display, TEXT("%s@%s[%d] - %s,%d, %s"), *Name, *CurrentThreadName, CurrentThreadId, 63 | *RunnableThread->GetThreadName(), 64 | RunnableThread->GetThreadID(), ANSI_TO_TCHAR(Action)); 65 | } 66 | else 67 | { 68 | UE_LOG(LogTemp, Display, TEXT("%s@%s[%d] - %s,%d, %s"), *Name, *CurrentThreadName, CurrentThreadId, 69 | TEXT("NULL"), 0, ANSI_TO_TCHAR(Action)); 70 | } 71 | } 72 | 73 | public: 74 | FString Name; 75 | FRunnableThread* RunnableThread = nullptr; 76 | FThreadSafeBool bStop; 77 | }; 78 | 79 | 80 | class ThreadSafeArray 81 | { 82 | public: 83 | int32 GetValue(int32 Index) 84 | { 85 | FScopeLock Lock(&CS); 86 | return Values[Index]; 87 | } 88 | 89 | void AppendValue(int32 Value) 90 | { 91 | CS.Lock(); 92 | Values.Add(Value); 93 | CS.Unlock(); 94 | } 95 | 96 | private: 97 | FCriticalSection CS; 98 | TArray Values; 99 | }; 100 | 101 | 102 | class ThreadSafeArray2 103 | { 104 | public: 105 | int32 GetValue(int32 Index) 106 | { 107 | FRWScopeLock ScopeLock(ValuesLock, SLT_ReadOnly); 108 | return Values[Index]; 109 | } 110 | 111 | void AppendValue(int32 Value) 112 | { 113 | ValuesLock.WriteLock(); 114 | Values.Add(Value); 115 | ValuesLock.WriteUnlock(); 116 | } 117 | 118 | private: 119 | FRWLock ValuesLock; 120 | TArray Values; 121 | }; 122 | 123 | 124 | ///////////////////////////////////////////////////////////////////////////////// 125 | 126 | #define SAFE_DELETE(Ptr) if (Ptr) { delete Ptr; Ptr = nullptr; } 127 | 128 | inline void DumpAllThreads(const char* Log) 129 | { 130 | FThreadManager::Get().ForEachThread( 131 | [=](uint32 ThreadID, FRunnableThread* Thread) 132 | { 133 | UE_LOG(LogTemp, Display, TEXT("%s: %s,%u"), ANSI_TO_TCHAR(Log), *Thread->GetThreadName(), ThreadID); 134 | }); 135 | } 136 | 137 | inline void Test_SimpleThread() 138 | { 139 | // Create Threads 140 | FSimpleThread* SimpleThread1 = new FSimpleThread(TEXT("SimpleThread1")); 141 | FSimpleThread* SimpleThread2 = new FSimpleThread(TEXT("SimpleThread2")); 142 | 143 | DumpAllThreads(__FUNCTION__); 144 | 145 | // Ticks 146 | int TickCount = 100; 147 | for (int i = 0; i < TickCount; ++i) 148 | { 149 | // Consume 150 | UE_LOG(LogTemp, Display, TEXT("Tick[%d] ........ "), i); 151 | FPlatformProcess::Sleep(0.1); 152 | } 153 | 154 | // Stop Thread 155 | SimpleThread1->Stop(); 156 | SimpleThread2->Stop(); 157 | 158 | // Destroy Threads 159 | SAFE_DELETE(SimpleThread1); 160 | SAFE_DELETE(SimpleThread2); 161 | } 162 | 163 | inline void Test_Atomic() 164 | { 165 | TAtomic Counter; 166 | Counter ++; // Atomic increment -> FPlatformAtomics::InterlockedIncrement 167 | if (Counter.Load()) // Atomic read -> FPlatformAtomics::AtomicRead 168 | { 169 | } 170 | 171 | FThreadSafeCounter Counter2; 172 | Counter2.Increment(); // FPlatformAtomics::InterlockedIncrement 173 | Counter2.Decrement(); // FPlatformAtomics::InterlockedDecrement 174 | if (Counter2.GetValue() == 0) // FPlatformAtomics::AtomicRead 175 | { 176 | } 177 | } 178 | 179 | 180 | inline void Test_Event1() 181 | { 182 | FEvent* SyncEvent = nullptr; 183 | 184 | Async(EAsyncExecution::Thread, [&SyncEvent]() 185 | { 186 | FPlatformProcess::Sleep(3); 187 | if (SyncEvent) 188 | { 189 | SyncEvent->Trigger(); 190 | UE_LOG(LogTemp, Display, TEXT("Trigger .....")); 191 | } 192 | }); 193 | 194 | SyncEvent = FPlatformProcess::GetSynchEventFromPool(true); 195 | SyncEvent->Wait((uint32)-1); 196 | FPlatformProcess::ReturnSynchEventToPool(SyncEvent); 197 | 198 | UE_LOG(LogTemp, Display, TEXT("Over .....")); 199 | } 200 | 201 | inline void Test_Event2() 202 | { 203 | FEventRef SyncEvent(EEventMode::AutoReset); 204 | 205 | FEvent* Event = SyncEvent.operator->(); 206 | Async(EAsyncExecution::Thread, [Event]() 207 | { 208 | FPlatformProcess::Sleep(3); 209 | Event->Trigger(); 210 | UE_LOG(LogTemp, Display, TEXT("Trigger .....")); 211 | }); 212 | 213 | SyncEvent->Wait((uint32)-1); 214 | UE_LOG(LogTemp, Display, TEXT("Over .....")); 215 | } 216 | 217 | inline void Test_Event() 218 | { 219 | // waiting.. 220 | { 221 | FScopedEvent SyncEvent; 222 | 223 | Async(EAsyncExecution::Thread, [&SyncEvent]() 224 | { 225 | FPlatformProcess::Sleep(3); 226 | SyncEvent.Trigger(); 227 | UE_LOG(LogTemp, Display, TEXT("Trigger .....")); 228 | }); 229 | } 230 | 231 | UE_LOG(LogTemp, Display, TEXT("Over .....")); 232 | } 233 | 234 | ////////////////////////////////////////////////// 235 | 236 | // #include 237 | 238 | inline void Test_OpenMP() 239 | { 240 | static long num_rects = 1000000; 241 | 242 | // pi = Int(4/(1+x^2)) 243 | double mid, height, width, sum = 0; 244 | int i = 0; 245 | double area = 0; 246 | width = 1. / (double)num_rects; 247 | 248 | #pragma omp parallel for private(mid, height, width) reduce(+:sum) 249 | for (i = 0; i < num_rects; i++) 250 | { 251 | mid = (i + 0.5) * width; 252 | height = 4.0 / (1. + mid * mid); 253 | sum += height; 254 | } 255 | 256 | area = width * sum; 257 | UE_LOG(LogTemp, Display, TEXT("Pi is : %f"), area); 258 | } 259 | 260 | 261 | ////////////////////////////////////////////////// -------------------------------------------------------------------------------- /SnippetAsync/Private/SnippetAsync.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #include "SnippetAsync.h" 5 | 6 | #include "RequiredProgramMainCPPInclude.h" 7 | #include "Async/Async.h" 8 | #include "Async/AsyncWork.h" 9 | #include 10 | #include 11 | 12 | #include "ParallelFor.h" 13 | 14 | #include "SimpleThread.h" 15 | #include "SimpleQueuedWorker.h" 16 | #include "SimpleAsyncTask.h" 17 | #include "SimpleGraphTask.h" 18 | #include "SimpleAsync.h" 19 | 20 | #include "RNGThread.h" 21 | #include "PrimeNumberThread.h" 22 | #include "PrimeNumberGraphTask.h" 23 | 24 | #include "SimpleLockFree.h" 25 | #include "SimpleProducerConsumer.h" 26 | 27 | 28 | DEFINE_LOG_CATEGORY_STATIC(LogSnippetAsync, Log, All); 29 | 30 | IMPLEMENT_APPLICATION(SnippetAsync, "SnippetAsync"); 31 | 32 | 33 | //////////////////////////////////////////////////// 34 | 35 | void TestParallel() 36 | { 37 | int32 MaxEntries = 500; 38 | ParallelFor(MaxEntries, [](int32 CurrIdx) 39 | { 40 | // Your implementation may fetch/store results based on the CurrIdx, but 41 | // for simplicity we just have some dummy "expensive" calculation here. 42 | int32 Sum = CurrIdx; 43 | for (int32 Idx = 0; Idx < 1000 * 10; ++Idx) 44 | { 45 | Sum += FMath::Sqrt(1234.56f); 46 | } 47 | 48 | uint32 ThreadId = FPlatformTLS::GetCurrentThreadId(); 49 | const FString& ThreadName = FThreadManager::GetThreadName(ThreadId); 50 | 51 | UE_LOG(LogTemp, Display, TEXT("sum ~ %d, thread:%d,%s"), Sum, 52 | FPlatformTLS::GetCurrentThreadId(), *ThreadName); 53 | }); 54 | } 55 | 56 | ////////////////////////////////////////////////// 57 | 58 | void TestRNGThread() 59 | { 60 | // Create RNG Thread 61 | FRNGThread* RNG = new FRNGThread(50, 0, 100, 5); 62 | 63 | // Consume Random Vector from RNG Thread 64 | int TickCount = 100; 65 | for (int i = 0; i < TickCount; ++i) 66 | { 67 | // Consume 68 | FVector V = RNG->GetRandomVector(); 69 | 70 | bool IsPaused = RNG->IsThreadPaused(); 71 | UE_LOG(LogSnippetAsync, Display, TEXT("Random Vector[%d] : %s, FRNGThread Paused : %d"), i, *V.ToString(), 72 | IsPaused); 73 | FPlatformProcess::Sleep(0.1); 74 | } 75 | 76 | // Destroy RNG Thread 77 | if (RNG) 78 | { 79 | RNG->EnsureCompletion(); 80 | delete RNG; 81 | RNG = nullptr; 82 | } 83 | 84 | UE_LOG(LogSnippetAsync, Display, TEXT("RNG Thread is Over !!")); 85 | } 86 | 87 | void TestPrimeNumberThreads() 88 | { 89 | TArray PrimeNumbers; 90 | FPrimeNumberThread::JoyInit(PrimeNumbers, 5000); 91 | 92 | while (!FPrimeNumberThread::IsThreadFinished()) 93 | { 94 | UE_LOG(LogSnippetAsync, Display, TEXT("FPrimeNumberThread Thread is Working...")); 95 | FPlatformProcess::Sleep(0.1); 96 | } 97 | 98 | FPrimeNumberThread::Shutdown(); 99 | UE_LOG(LogSnippetAsync, Display, TEXT("FPrimeNumberThread Thread is Over !!")); 100 | } 101 | 102 | 103 | INT32_MAIN_INT32_ARGC_TCHAR_ARGV() 104 | { 105 | GEngineLoop.PreInit(ArgC, ArgV); 106 | UE_LOG(LogSnippetAsync, Display, TEXT("Hello World! by David !!")); 107 | 108 | // Test_SimpleThread(); 109 | // Test_Event(); 110 | // Test_SimpleQueuedWorker(); 111 | // Test_SimpleTask_1(); 112 | // Test_SimpleTask_2(true); 113 | // Test_SimpleTask_2(false); 114 | // Test_SimpleTask_3(); 115 | 116 | 117 | // Test_GraphTask_Simple(); 118 | // Test_GraphTask_Simple1(); 119 | // Test_GraphTask_Simple2(); 120 | // Test_GraphTask_Simple2_Funciton(); 121 | // Test_GraphTask_Simple3(); 122 | // Test_GraphTask_NullTask(); 123 | // Test_GraphTask_ReturnTask(); 124 | // Test_GraphTask_Delegate(); 125 | // Test_GraphTask_Function(); 126 | 127 | // Test_FuturePromise(); 128 | // Test_FuturePromise2(); 129 | // Test_FuturePromise3(); 130 | // Test_Async1(); 131 | // Test_Async2(); 132 | Test_Parallel(); 133 | 134 | 135 | // TestRNGThread(); 136 | // TestPrimeNumberThreads(); 137 | // Test_FindPrimeNumbers(); 138 | 139 | // Test_OpenMP(); 140 | // Test_LockFree_LIFO(); 141 | // Test_LockFree_FIFO(); 142 | 143 | // Test_Queue(); 144 | // Test_MPMC(); 145 | // Test_DoubleBuffer(); 146 | 147 | // FPlatformProcess::Sleep(60); 148 | std::cin.get(); 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /SnippetAsync/Private/SnippetAsync.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /SnippetAsync/Resources/Windows/SnippetAsync.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-pp/UESnippets/a740f23307fef972c9d6c107ed23f6ff9cc4f0ea/SnippetAsync/Resources/Windows/SnippetAsync.ico -------------------------------------------------------------------------------- /SnippetAsync/SnippetAsync.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SnippetAsync : ModuleRules 6 | { 7 | public SnippetAsync(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | 10 | PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Runtime/Launch/Public")); 11 | PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Runtime/Launch/Private")); // For LaunchEngineLoop.cpp include 12 | 13 | PrivateDependencyModuleNames.Add("Core"); 14 | PrivateDependencyModuleNames.Add("Projects"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SnippetAsync/SnippetAsync.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | [SupportedPlatforms(UnrealPlatformClass.Desktop)] 7 | public class SnippetAsyncTarget : TargetRules 8 | { 9 | public SnippetAsyncTarget(TargetInfo Target) : base(Target) 10 | { 11 | Type = TargetType.Program; 12 | LinkType = TargetLinkType.Monolithic; 13 | LaunchModuleName = "SnippetAsync"; 14 | 15 | SolutionDirectory = "UESnippets"; 16 | 17 | // Lean and mean 18 | bBuildDeveloperTools = false; 19 | 20 | // Never use malloc profiling in Unreal Header Tool. We set this because often UHT is compiled right before the engine 21 | // automatically by Unreal Build Tool, but if bUseMallocProfiler is defined, UHT can operate incorrectly. 22 | bUseMallocProfiler = false; 23 | 24 | // Editor-only data, however, is needed 25 | bBuildWithEditorOnlyData = true; 26 | 27 | // Currently this app is not linking against the engine, so we'll compile out references from Core to the rest of the engine 28 | bCompileAgainstEngine = false; 29 | bCompileAgainstCoreUObject = false; 30 | bCompileAgainstApplicationCore = false; 31 | 32 | // UnrealHeaderTool is a console application, not a Windows app (sets entry point to main(), instead of WinMain()) 33 | bIsBuildingConsoleApplication = true; 34 | } 35 | } 36 | --------------------------------------------------------------------------------