So I simplified my test shader pass (still no idea what’s going wrong with it, but that is an issue for another time). This time I’ve tried creating a shader that reads from an input texture copies the pixels to an output texture.
Curiously, even though my input texture is declared in the pass parameters and created by the graph builder, I get the following error message:
"Ensure condition failed: Texture->HasBeenProduced() [File:D:/Build/++UE4/Sync/Engine/Source/Runtime/RenderCore/Private/RenderGraphValidation.cpp] [Line: 338]
Pass TestShaderPass has a dependency on the texture TestInputTexture that has never been produced."
Here is the code for running the pass:
FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(BufferSize, FTestShader::ThreadsPerGroupDimension);
ENQUEUE_RENDER_COMMAND(SceneDrawCompletion)
(&]
(FRHICommandListImmediate& CommandListImmediate)
{
FRDGBuilder GraphBuilder(CommandListImmediate);
FRDGTextureDesc InputTextureDesc = FRDGTextureDesc::Create2DDesc(BufferSize, EPixelFormat::PF_FloatRGBA, FClearValueBinding::BlackMaxAlpha,
TexCreate_None, TexCreate_ShaderResource, false);
FRDGTextureRef InputTexture = GraphBuilder.CreateTexture(InputTextureDesc, TEXT("TestInputTexture"));
FRDGTextureDesc OutputTextureDesc = FRDGTextureDesc::Create2DDesc(BufferSize, EPixelFormat::PF_FloatRGBA, FClearValueBinding::BlackMaxAlpha,
TexCreate_None, TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable, false);
FRDGTextureRef OutputTexture = GraphBuilder.CreateTexture(OutputTextureDesc, TEXT("TestOutputTexture"));
FTestShader::FParameters* PassParameters = GraphBuilder.AllocParameters<FTestShader::FParameters>();
PassParameters->InputTexture = InputTexture;
PassParameters->OutputTexture = GraphBuilder.CreateUAV(OutputTexture);
PassParameters->OutputTextureSize = BufferSize;
TShaderMapRef<FTestShader> TestShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
GraphBuilder.AddPass(RDG_EVENT_NAME("TestShaderPass"), PassParameters, ERDGPassFlags::Compute,
&](FRHICommandList& CommandList)
{
FTexture2DRHIRef InputTextureRHI = static_cast<FRHITexture2D*>(PassParameters->InputTexture->GetRHI());
uint32 Stride;
void* TextureData = RHILockTexture2D(InputTextureRHI, 0, EResourceLockMode::RLM_WriteOnly, Stride, false);
// Copy some data
RHIUnlockTexture2D(InputTextureRHI, 0, false);
FComputeShaderUtils::Dispatch(CommandList, *TestShader, *PassParameters, GroupCount);
});
GraphBuilder.Execute();
});
As well as the shader definitions:
#pragma once
#include "CoreMinimal.h"
#include "Shader.h"
#include "GlobalShader.h"
#include "ShaderParameterUtils.h"
#include "ShaderParameterStruct.h"
class FTestShader: public FGlobalShader
{
public:
static const int32 ThreadsPerGroupDimension = 32;
DECLARE_GLOBAL_SHADER(FTestShader)
SHADER_USE_PARAMETER_STRUCT(FTestShader, FGlobalShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float4>, InputTexture)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, OutputTexture)
SHADER_PARAMETER(FIntPoint, OutputTextureSize)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
static inline void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
};
IMPLEMENT_GLOBAL_SHADER(FTestShader, "/Shaders/Private/Test.usf", "main", SF_Compute);
And the shader itself:
Texture2D<float4> InputTexture;
RWTexture2D<float4> OutputTexture;
int2 OutputTextureSize;
[numthreads(32, 32, 1)]
void main( uint3 id : SV_DispatchThreadID )
{
OutputTexture[id.xy] = InputTexture[id.xy];
}
The pass runs correctly when the shader parameters only contain the UAV (I’ve tested this by having the shader use the pixel coordinates for red and green, and the texture shows as you’d expect when running the vis command). With just the one texture, I’m running into the same issue as before where the data the UAV refers to is invalid during the pass and the GetRHI() function throws an exception.
I thought that by having an additional parameter directly referencing a texture would force the graph builder to allow access to the RHI resource, but from the error message, it seems like that has the same problems.
My next idea is to try and bypass RDG resources altogether when trying to copy data to the buffer, and only use RDG textures for intermediate results of passes, though this seems like the incorrect method.