[
  {
    "path": ".gitignore",
    "content": "Binaries\nDerivedDataCache\nIntermediate\nSaved\n.vscode\n.vs\n*.VC.db\n*.opensdf\n*.opendb\n*.sdf\n*.sln\n*.suo\n*.xcodeproj\n*.xcworkspace"
  },
  {
    "path": "Config/DefaultEditor.ini",
    "content": "\r\n\r\n"
  },
  {
    "path": "Config/DefaultEngine.ini",
    "content": "[/Script/Engine.Engine]\r\n+ActiveGameNameRedirects=(OldGameName=\"TP_Blank\",NewGameName=\"/Script/CustomComputeShader\")\r\n+ActiveGameNameRedirects=(OldGameName=\"/Script/TP_Blank\",NewGameName=\"/Script/CustomComputeShader\")\r\n+ActiveClassRedirects=(OldClassName=\"TP_BlankGameModeBase\",NewClassName=\"CustomComputeShaderGameModeBase\")\r\n\r\n[/Script/HardwareTargeting.HardwareTargetingSettings]\r\nTargetedHardwareClass=Desktop\r\nAppliedTargetedHardwareClass=Desktop\r\nDefaultGraphicsPerformance=Maximum\r\nAppliedDefaultGraphicsPerformance=Maximum\r\n\r\n[/Script/EngineSettings.GameMapsSettings]\r\nEditorStartupMap=/Game/Maps/TestMap.TestMap\r\nGameDefaultMap=/Game/Maps/TestMap.TestMap\r\n\r\n"
  },
  {
    "path": "Config/DefaultGame.ini",
    "content": "\r\n[/Script/EngineSettings.GeneralProjectSettings]\r\nProjectID=098D2E764A73895DFD1D5EB20AB18FA8\r\n"
  },
  {
    "path": "CustomComputeShader.uproject",
    "content": "{\r\n\t\"FileVersion\": 3,\r\n\t\"EngineAssociation\": \"4.24\",\r\n\t\"Category\": \"\",\r\n\t\"Description\": \"\",\r\n\t\"Modules\": [\r\n\t\t{\r\n\t\t\t\"Name\": \"CustomComputeShader\",\r\n\t\t\t\"Type\": \"Runtime\",\r\n\t\t\t\"LoadingPhase\": \"Default\",\r\n\t\t\t\"AdditionalDependencies\": [\r\n\t\t\t\t\"Engine\"\r\n\t\t\t]\r\n\t\t},\r\n\t\t{\r\n\t\t\t\"Name\": \"CustomShadersDeclarations\",\r\n\t\t\t\"Type\": \"Runtime\",\r\n\t\t\t\"LoadingPhase\": \"PostConfigInit\",\r\n\t\t\t\"AdditionalDependencies\": [\r\n\t\t\t\t\"Engine\"\r\n\t\t\t]\r\n\t\t}\r\n\t]\r\n}"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Ayoub Khammassi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# CustomComputeShader\n\nA minimal Unreal Engine 4 porject for adding and using a compute shader. I published an article that covers the main steps of the process and I use this project as an example. \n\n## Modules:\n* **CustomComputeShader** : The primary game module\n* **CustomShadersDeclarations** : The game module that contains all the code for adding and using the compute shader\n\n##  Shaders:\n* **WhiteNoiseCS** : A simple compute shader that renders white noise to a texture\n\n## UE4 Version\nThis project was created and tested using **UE4.24**. Id you're using **UE4.25**, you'll need to include **/Engine/Public/Platform.ush** in your shader file in order for it to compile.\n\n"
  },
  {
    "path": "Shaders/Private/WhiteNoiseCS.usf",
    "content": "#include \"/Engine/Public/Platform.ush\"\r\nRWTexture2D<float> OutputTexture;\r\nfloat2 Dimensions;\r\nuint TimeStamp;\r\n\r\n\r\nfloat hash12(float2 p)\r\n{\r\n    float3 p3 = frac(float3(p.xyx) * .1031);\r\n    p3 += dot(p3, p3.yzx + 33.33);\r\n    return frac((p3.x + p3.y) * p3.z);\r\n}\r\n\r\n\r\n[numthreads(THREADGROUPSIZE_X, THREADGROUPSIZE_Y, THREADGROUPSIZE_Z)]\r\nvoid MainComputeShader(uint3 Gid : SV_GroupID, //atm: -, 0...256, - in rows (Y)        --> current group index (dispatched by c++)\r\n                       uint3 DTid : SV_DispatchThreadID, //atm: 0...256 in rows & columns (XY)   --> \"global\" thread id\r\n                       uint3 GTid : SV_GroupThreadID, //atm: 0...256, -,- in columns (X)      --> current threadId in group / \"local\" threadId\r\n                       uint GI : SV_GroupIndex)            //atm: 0...256 in columns (X)           --> \"flattened\" index of a thread within a group)\r\n{   \r\n    float2 p = float2(DTid.xy * TimeStamp);\r\n    float output = hash12(p);\r\n    \r\n    OutputTexture[DTid.xy] = output;\r\n}\r\n"
  },
  {
    "path": "Source/CustomComputeShader/CustomComputeShader.Build.cs",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\nusing UnrealBuildTool;\r\n\r\npublic class CustomComputeShader : ModuleRules\r\n{\r\n\tpublic CustomComputeShader(ReadOnlyTargetRules Target) : base(Target)\r\n\t{\r\n\t\tPCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;\r\n\t\r\n\t\tPublicDependencyModuleNames.AddRange(new string[] { \"Core\", \"CoreUObject\", \"Engine\", \"InputCore\" });\r\n\r\n\t\tPrivateDependencyModuleNames.AddRange(new string[] { \"CustomShadersDeclarations\" });\r\n\r\n\t\t// Uncomment if you are using Slate UI\r\n\t\t// PrivateDependencyModuleNames.AddRange(new string[] { \"Slate\", \"SlateCore\" });\r\n\t\t\r\n\t\t// Uncomment if you are using online features\r\n\t\t// PrivateDependencyModuleNames.Add(\"OnlineSubsystem\");\r\n\r\n\t\t// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Source/CustomComputeShader/CustomComputeShader.cpp",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\n#include \"CustomComputeShader.h\"\r\n#include \"Modules/ModuleManager.h\"\r\n#include \"Misc/Paths.h\"\r\n#include \"GlobalShader.h\"\r\n\r\nIMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, CustomComputeShader, \"CustomComputeShader\" );\r\n\r\n\r\n\r\n"
  },
  {
    "path": "Source/CustomComputeShader/CustomComputeShader.h",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\n#pragma once\r\n\r\n#include \"CoreMinimal.h\"\r\n"
  },
  {
    "path": "Source/CustomComputeShader/CustomComputeShaderGameModeBase.cpp",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\n\r\n#include \"CustomComputeShaderGameModeBase.h\"\r\n\r\n"
  },
  {
    "path": "Source/CustomComputeShader/CustomComputeShaderGameModeBase.h",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\n#pragma once\r\n\r\n#include \"CoreMinimal.h\"\r\n#include \"GameFramework/GameModeBase.h\"\r\n#include \"CustomComputeShaderGameModeBase.generated.h\"\r\n\r\n/**\r\n * \r\n */\r\nUCLASS()\r\nclass CUSTOMCOMPUTESHADER_API ACustomComputeShaderGameModeBase : public AGameModeBase\r\n{\r\n\tGENERATED_BODY()\r\n\t\r\n};\r\n"
  },
  {
    "path": "Source/CustomComputeShader/Private/WhiteNoiseConsumer.cpp",
    "content": "#include \"WhiteNoiseConsumer.h\"\r\n\r\n#include \"Kismet/GameplayStatics.h\"\r\n#include \"CustomShadersDeclarations/Private/ComputeShaderDeclaration.h\"\r\n\r\n// Sets default values\r\nAWhiteNoiseConsumer::AWhiteNoiseConsumer()\r\n{\r\n \t// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.\r\n\tPrimaryActorTick.bCanEverTick = true;\r\n\tRoot = CreateDefaultSubobject<USceneComponent>(TEXT(\"Root\"));\r\n\tRootComponent = Root;\r\n\r\n\tstatic_mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT(\"Static Mesh\"));\r\n\r\n\tTimeStamp = 0;\r\n}\r\n\r\n// Called when the game starts or when spawned\r\nvoid AWhiteNoiseConsumer::BeginPlay()\r\n{\r\n\tSuper::BeginPlay();\r\n\tFWhiteNoiseCSManager::Get()->BeginRendering();\r\n\r\n\r\n\t//Assuming that the static mesh is already using the material that we're targeting, we create an instance and assign it to it\r\n\tUMaterialInstanceDynamic* MID = static_mesh->CreateAndSetMaterialInstanceDynamic(0);\r\n\tMID->SetTextureParameterValue(\"InputTexture\", (UTexture*)RenderTarget);\r\n}\r\n\r\nvoid AWhiteNoiseConsumer::BeginDestroy()\r\n{\r\n\tFWhiteNoiseCSManager::Get()->EndRendering();\r\n\tSuper::BeginDestroy();\r\n\r\n}\r\n\r\n// Called every frame\r\nvoid AWhiteNoiseConsumer::Tick(float DeltaTime)\r\n{\r\n\tSuper::Tick(DeltaTime);\r\n\r\n\t//Update parameters\r\n\tFWhiteNoiseCSParameters parameters(RenderTarget);\r\n\tTimeStamp++;\r\n\tparameters.TimeStamp = TimeStamp;\r\n\tFWhiteNoiseCSManager::Get()->UpdateParameters(parameters);\r\n}\r\n\r\n// Called to bind functionality to input\r\nvoid AWhiteNoiseConsumer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)\r\n{\r\n\tSuper::SetupPlayerInputComponent(PlayerInputComponent);\r\n\r\n}\r\n\r\n"
  },
  {
    "path": "Source/CustomComputeShader/Public/WhiteNoiseConsumer.h",
    "content": "#pragma once\r\n\r\n#include \"CoreMinimal.h\"\r\n#include \"GameFramework/Pawn.h\"\r\n#include \"WhiteNoiseConsumer.generated.h\"\r\n\r\nUCLASS()\r\nclass CUSTOMCOMPUTESHADER_API AWhiteNoiseConsumer : public APawn\r\n{\r\n\tGENERATED_BODY()\r\n\r\n//Properties\r\npublic:\r\n\tUPROPERTY()\r\n\t\tUSceneComponent* Root;\r\n\r\n\tUPROPERTY(EditAnywhere)\r\n\t\tUStaticMeshComponent* static_mesh;\r\n\r\n\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ShaderDemo)\r\n\t\tclass UTextureRenderTarget2D* RenderTarget;\r\nprivate:\r\n\tuint32 TimeStamp;\r\npublic:\r\n\t// Sets default values for this pawn's properties\r\n\tAWhiteNoiseConsumer();\r\n\r\nprotected:\r\n\t// Called when the game starts or when spawned\r\n\tvirtual void BeginPlay() override;\r\n\r\n\tvirtual void BeginDestroy() override;\r\n\r\npublic:\t\r\n\t// Called every frame\r\n\tvirtual void Tick(float DeltaTime) override;\r\n\r\n\t// Called to bind functionality to input\r\n\tvirtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;\r\n\r\n};\r\n"
  },
  {
    "path": "Source/CustomComputeShader.Target.cs",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\nusing UnrealBuildTool;\r\nusing System.Collections.Generic;\r\n\r\npublic class CustomComputeShaderTarget : TargetRules\r\n{\r\n\tpublic CustomComputeShaderTarget( TargetInfo Target) : base(Target)\r\n\t{\r\n\t\tType = TargetType.Game;\r\n\t\tDefaultBuildSettings = BuildSettingsVersion.V2;\r\n\t\tExtraModuleNames.AddRange( new string[] { \"CustomComputeShader\", \"CustomShadersDeclarations\" } );\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Source/CustomComputeShaderEditor.Target.cs",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\nusing UnrealBuildTool;\r\nusing System.Collections.Generic;\r\n\r\npublic class CustomComputeShaderEditorTarget : TargetRules\r\n{\r\n\tpublic CustomComputeShaderEditorTarget( TargetInfo Target) : base(Target)\r\n\t{\r\n\t\tType = TargetType.Editor;\r\n\t\tDefaultBuildSettings = BuildSettingsVersion.V2;\r\n\t\tExtraModuleNames.AddRange( new string[] { \"CustomComputeShader\", \"CustomShadersDeclarations\" } );\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Source/CustomShadersDeclarations/CustomShadersDeclarations.Build.cs",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\nusing UnrealBuildTool;\r\n\r\npublic class CustomShadersDeclarations : ModuleRules\r\n{\r\n\tpublic CustomShadersDeclarations(ReadOnlyTargetRules Target) : base(Target)\r\n\t{\r\n\t\tPCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;\r\n\r\n\t\tPublicDependencyModuleNames.AddRange(new string[] { });\r\n\t\tPrivateDependencyModuleNames.AddRange(new string[]\r\n\t\t{\r\n\t\t\t\t\"Core\",\r\n\t\t\t\t\"CoreUObject\",\r\n\t\t\t\t\"Engine\",\r\n\t\t\t\t\"Renderer\",\r\n\t\t\t\t\"RenderCore\",\r\n\t\t\t\t\"RHI\",\r\n\t\t\t\t\"Projects\"\r\n\t\t});\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Source/CustomShadersDeclarations/Private/ComputeShaderDeclaration.cpp",
    "content": "#include \"ComputeShaderDeclaration.h\"\r\n\r\n#include \"GlobalShader.h\"\r\n#include \"ShaderParameterStruct.h\"\r\n#include \"RenderGraphUtils.h\"\r\n#include \"RenderTargetPool.h\"\r\n\r\n\r\n#include \"Modules/ModuleManager.h\"\r\n\r\n#define NUM_THREADS_PER_GROUP_DIMENSION 32\r\n\r\n/// <summary>\r\n/// Internal class thet holds the parameters and connects the HLSL Shader to the engine\r\n/// </summary>\r\nclass FWhiteNoiseCS : public FGlobalShader\r\n{\r\npublic:\r\n\t//Declare this class as a global shader\r\n\tDECLARE_GLOBAL_SHADER(FWhiteNoiseCS);\r\n\t//Tells the engine that this shader uses a structure for its parameters\r\n\tSHADER_USE_PARAMETER_STRUCT(FWhiteNoiseCS, FGlobalShader);\r\n\t/// <summary>\r\n\t/// DECLARATION OF THE PARAMETER STRUCTURE\r\n\t/// The parameters must match the parameters in the HLSL code\r\n\t/// For each parameter, provide the C++ type, and the name (Same name used in HLSL code)\r\n\t/// </summary>\r\n\tBEGIN_SHADER_PARAMETER_STRUCT(FParameters, )\r\n\t\tSHADER_PARAMETER_UAV(RWTexture2D<float>, OutputTexture)\r\n\t\tSHADER_PARAMETER(FVector2D, Dimensions)\r\n\t\tSHADER_PARAMETER(UINT, TimeStamp)\r\n\tEND_SHADER_PARAMETER_STRUCT()\r\n\r\npublic:\r\n\t//Called by the engine to determine which permutations to compile for this shader\r\n\tstatic bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)\r\n\t{\r\n\t\treturn IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);\r\n\t}\r\n\r\n\t//Modifies the compilations environment of the shader\r\n\tstatic inline void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)\r\n\t{\r\n\t\tFGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);\r\n\r\n\t\t//We're using it here to add some preprocessor defines. That way we don't have to change both C++ and HLSL code when we change the value for NUM_THREADS_PER_GROUP_DIMENSION\r\n\t\tOutEnvironment.SetDefine(TEXT(\"THREADGROUPSIZE_X\"), NUM_THREADS_PER_GROUP_DIMENSION);\r\n\t\tOutEnvironment.SetDefine(TEXT(\"THREADGROUPSIZE_Y\"), NUM_THREADS_PER_GROUP_DIMENSION);\r\n\t\tOutEnvironment.SetDefine(TEXT(\"THREADGROUPSIZE_Z\"), 1);\r\n\t}\r\n\r\n};\r\n\r\n// This will tell the engine to create the shader and where the shader entry point is.\r\n//                        ShaderType              ShaderPath             Shader function name    Type\r\nIMPLEMENT_GLOBAL_SHADER(FWhiteNoiseCS, \"/CustomShaders/WhiteNoiseCS.usf\", \"MainComputeShader\", SF_Compute);\r\n\r\n\r\n//Static members\r\nFWhiteNoiseCSManager* FWhiteNoiseCSManager::instance = nullptr;\r\n\r\n//Begin the execution of the compute shader each frame\r\nvoid FWhiteNoiseCSManager::BeginRendering()\r\n{\r\n\t//If the handle is already initalized and valid, no need to do anything\r\n\tif (OnPostResolvedSceneColorHandle.IsValid())\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\tbCachedParamsAreValid = false;\r\n\t//Get the Renderer Module and add our entry to the callbacks so it can be executed each frame after the scene rendering is done\r\n\tconst FName RendererModuleName(\"Renderer\");\r\n\tIRendererModule* RendererModule = FModuleManager::GetModulePtr<IRendererModule>(RendererModuleName);\r\n\tif (RendererModule)\r\n\t{\r\n\t\tOnPostResolvedSceneColorHandle = RendererModule->GetResolvedSceneColorCallbacks().AddRaw(this, &FWhiteNoiseCSManager::Execute_RenderThread);\r\n\t}\r\n}\r\n\r\n//Stop the compute shader execution\r\nvoid FWhiteNoiseCSManager::EndRendering()\r\n{\r\n\t//If the handle is not valid then there's no cleanup to do\r\n\tif (!OnPostResolvedSceneColorHandle.IsValid())\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\r\n\t//Get the Renderer Module and remove our entry from the ResolvedSceneColorCallbacks\r\n\tconst FName RendererModuleName(\"Renderer\");\r\n\tIRendererModule* RendererModule = FModuleManager::GetModulePtr<IRendererModule>(RendererModuleName);\r\n\tif (RendererModule)\r\n\t{\r\n\t\tRendererModule->GetResolvedSceneColorCallbacks().Remove(OnPostResolvedSceneColorHandle);\r\n\t}\r\n\r\n\tOnPostResolvedSceneColorHandle.Reset();\r\n}\r\n\r\n//Update the parameters by a providing an instance of the Parameters structure used by the shader manager\r\nvoid FWhiteNoiseCSManager::UpdateParameters(FWhiteNoiseCSParameters& params)\r\n{\r\n\tcachedParams = params;\r\n\tbCachedParamsAreValid = true;\r\n}\r\n\r\n\r\n/// <summary>\r\n/// Creates an instance of the shader type parameters structure and fills it using the cached shader manager parameter structure\r\n/// Gets a reference to the shader type from the global shaders map\r\n/// Dispatches the shader using the parameter structure instance\r\n/// </summary>\r\nvoid FWhiteNoiseCSManager::Execute_RenderThread(FRHICommandListImmediate& RHICmdList, class FSceneRenderTargets& SceneContext)\r\n{\r\n\t//If there's no cached parameters to use, skip\r\n\t//If no Render Target is supplied in the cachedParams, skip\r\n\tif (!(bCachedParamsAreValid && cachedParams.RenderTarget))\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\r\n\t//Render Thread Assertion\r\n\tcheck(IsInRenderingThread());\r\n\r\n\r\n\t//If the render target is not valid, get an element from the render target pool by supplying a Descriptor\r\n\tif (!ComputeShaderOutput.IsValid())\r\n\t{\r\n\t\tUE_LOG(LogTemp, Warning, TEXT(\"Not Valid\"));\r\n\t\tFPooledRenderTargetDesc ComputeShaderOutputDesc(FPooledRenderTargetDesc::Create2DDesc(cachedParams.GetRenderTargetSize(), cachedParams.RenderTarget->GetRenderTargetResource()->TextureRHI->GetFormat(), FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource | TexCreate_UAV, false));\r\n\t\tComputeShaderOutputDesc.DebugName = TEXT(\"WhiteNoiseCS_Output_RenderTarget\");\r\n\t\tGRenderTargetPool.FindFreeElement(RHICmdList, ComputeShaderOutputDesc, ComputeShaderOutput, TEXT(\"WhiteNoiseCS_Output_RenderTarget\"));\r\n\t}\r\n\t\r\n\t//Unbind the previously bound render targets\r\n\t//UnbindRenderTargets(RHICmdList);\r\n\r\n\t//Specify the resource transition, we're executing this in post scene rendering so we set it to Graphics to Compute\r\n\tRHICmdList.TransitionResource(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EGfxToCompute, ComputeShaderOutput->GetRenderTargetItem().UAV);\r\n\r\n\r\n\t//Fill the shader parameters structure with tha cached data supplied by the client\r\n\tFWhiteNoiseCS::FParameters PassParameters;\r\n\tPassParameters.OutputTexture = ComputeShaderOutput->GetRenderTargetItem().UAV;\r\n\tPassParameters.Dimensions = FVector2D(cachedParams.GetRenderTargetSize().X, cachedParams.GetRenderTargetSize().Y);\r\n\tPassParameters.TimeStamp = cachedParams.TimeStamp;\r\n\r\n\t//Get a reference to our shader type from global shader map\r\n\tTShaderMapRef<FWhiteNoiseCS> whiteNoiseCS(GetGlobalShaderMap(GMaxRHIFeatureLevel));\r\n\r\n\t//Dispatch the compute shader\r\n\tFComputeShaderUtils::Dispatch(RHICmdList, whiteNoiseCS, PassParameters,\r\n\t\tFIntVector(FMath::DivideAndRoundUp(cachedParams.GetRenderTargetSize().X, NUM_THREADS_PER_GROUP_DIMENSION),\r\n\t\t\tFMath::DivideAndRoundUp(cachedParams.GetRenderTargetSize().Y, NUM_THREADS_PER_GROUP_DIMENSION), 1));\r\n\r\n\t//Copy shader's output to the render target provided by the client\r\n\tRHICmdList.CopyTexture(ComputeShaderOutput->GetRenderTargetItem().ShaderResourceTexture, cachedParams.RenderTarget->GetRenderTargetResource()->TextureRHI, FRHICopyTextureInfo());\r\n\r\n}\r\n"
  },
  {
    "path": "Source/CustomShadersDeclarations/Private/ComputeShaderDeclaration.h",
    "content": "#pragma once\r\n\r\n#include \"CoreMinimal.h\"\r\n#include \"Runtime/Engine/Classes/Engine/TextureRenderTarget2D.h\"\r\n\r\n//This struct act as a container for all the parameters that the client needs to pass to the Compute Shader Manager.\r\nstruct  FWhiteNoiseCSParameters\r\n{\r\n\tUTextureRenderTarget2D* RenderTarget;\r\n\r\n\r\n\tFIntPoint GetRenderTargetSize() const\r\n\t{\r\n\t\treturn CachedRenderTargetSize;\r\n\t}\r\n\r\n\tFWhiteNoiseCSParameters() { }\r\n\tFWhiteNoiseCSParameters(UTextureRenderTarget2D* IORenderTarget)\r\n\t\t: RenderTarget(IORenderTarget)\r\n\t{\r\n\t\tCachedRenderTargetSize = RenderTarget ? FIntPoint(RenderTarget->SizeX, RenderTarget->SizeY) : FIntPoint::ZeroValue;\r\n\t}\r\n\r\nprivate:\r\n\tFIntPoint CachedRenderTargetSize;\r\npublic:\r\n\tuint32 TimeStamp;\r\n};\r\n\r\n\r\n/// <summary>\r\n/// A singleton Shader Manager for our Shader Type\r\n/// </summary>\r\nclass CUSTOMSHADERSDECLARATIONS_API FWhiteNoiseCSManager\r\n{\r\npublic:\r\n\t//Get the instance\r\n\tstatic FWhiteNoiseCSManager* Get()\r\n\t{\r\n\t\tif (!instance)\r\n\t\t\tinstance = new FWhiteNoiseCSManager();\r\n\t\treturn instance;\r\n\t};\r\n\r\n\t// Call this when you want to hook onto the renderer and start executing the compute shader. The shader will be dispatched once per frame.\r\n\tvoid BeginRendering();\r\n\r\n\t// Stops compute shader execution\r\n\tvoid EndRendering();\r\n\r\n\t// Call this whenever you have new parameters to share.\r\n\tvoid UpdateParameters(FWhiteNoiseCSParameters& DrawParameters);\r\n\t\r\nprivate:\r\n\t//Private constructor to prevent client from instanciating\r\n\tFWhiteNoiseCSManager() = default;\r\n\r\n\t//The singleton instance\r\n\tstatic FWhiteNoiseCSManager* instance;\r\n\r\n\t//The delegate handle to our function that will be executed each frame by the renderer\r\n\tFDelegateHandle OnPostResolvedSceneColorHandle;\r\n\r\n\t//Cached Shader Manager Parameters\r\n\tFWhiteNoiseCSParameters cachedParams;\r\n\r\n\t//Whether we have cached parameters to pass to the shader or not\r\n\tvolatile bool bCachedParamsAreValid;\r\n\r\n\t//Reference to a pooled render target where the shader will write its output\r\n\tTRefCountPtr<IPooledRenderTarget> ComputeShaderOutput;\r\npublic:\r\n\tvoid Execute_RenderThread(FRHICommandListImmediate& RHICmdList, class FSceneRenderTargets& SceneContext);\r\n};\r\n"
  },
  {
    "path": "Source/CustomShadersDeclarations/Private/CustomShadersDeclarations.cpp",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\n#include \"CustomShadersDeclarations.h\"\r\n#include \"Modules/ModuleManager.h\"\r\n#include \"Misc/Paths.h\"\r\n#include \"GlobalShader.h\"\r\n\r\nIMPLEMENT_GAME_MODULE( FCustomShadersDeclarationsModule, CustomShadersDeclarations);\r\n\r\n\r\nvoid FCustomShadersDeclarationsModule::StartupModule()\r\n{\r\n\t// Maps virtual shader source directory to actual shaders directory on disk.\r\n\tFString ShaderDirectory = FPaths::Combine(FPaths::ProjectDir(), TEXT(\"Shaders/Private\"));\r\n\tAddShaderSourceDirectoryMapping(\"/CustomShaders\", ShaderDirectory);\r\n}\r\n\r\nvoid FCustomShadersDeclarationsModule::ShutdownModule()\r\n{\r\n}\r\n\r\n"
  },
  {
    "path": "Source/CustomShadersDeclarations/Public/CustomShadersDeclarations.h",
    "content": "// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.\r\n\r\n#pragma once\r\n\r\n#include \"CoreMinimal.h\"\r\n\r\n#include \"Modules/ModuleInterface.h\"\r\n#include \"Modules/ModuleManager.h\"\r\n\r\n\r\n\r\nclass CUSTOMSHADERSDECLARATIONS_API FCustomShadersDeclarationsModule : public IModuleInterface\r\n{\r\npublic:\r\n\tstatic inline FCustomShadersDeclarationsModule& Get()\r\n\t{\r\n\t\treturn FModuleManager::LoadModuleChecked<FCustomShadersDeclarationsModule>(\"CustomShadersDeclarations\");\r\n\t}\r\n\r\n\tstatic inline bool IsAvailable()\r\n\t{\r\n\t\treturn FModuleManager::Get().IsModuleLoaded(\"CustomShadersDeclarations\");\r\n\t}\r\n\r\npublic:\r\n\tvirtual void StartupModule() override;\r\n\tvirtual void ShutdownModule() override;\r\n};\r\n"
  }
]