Hello,
I’ve got a plugin that reads the landscape heightmap into a texture and then extracts the texture data.
It reads the heightmap into a texture but then trying to read its contents into CPU causes an access violation. I do not understand why - the memcpy is done on the render thread.
Creating the texture/RT
constexpr int gHeightmapSizeX = 1024;
constexpr int gHeightmapSizeY = 1024;
void FHeightmapToolModule::StartupModule()
{
...
_Heightmap = UTexture2D::CreateTransient( gHeightmapSizeX, gHeightmapSizeY, EPixelFormat::PF_G16 );
_Heightmap->AddToRoot();
_HeightMapRT = NewObject<UTextureRenderTarget2D>();
_HeightMapRT->AddToRoot();
}
Reading the landscape heightmap into the texture
void FHeightmapToolModule::UpdateTextureFromLandscape( UTexture2D& Heightmap )
{
UWorld* pWorld = GEditor->GetEditorWorldContext().World();
assert( pWorld );
TArray<AActor*> LandscapeActors;
UGameplayStatics::GetAllActorsOfClass( pWorld, ALandscape::StaticClass(), LandscapeActors );
assert( LandscapeActors.Num() == 1 );
ALandscape* pLandscapeActor = Cast<ALandscape>( LandscapeActors[ 0 ] );
const FTransform& LandscapeActorTransform = pLandscapeActor->GetTransform();
_HeightMapRT->InitCustomFormat( gHeightmapSizeX, gHeightmapSizeY, EPixelFormat::PF_G16, false );
pLandscapeActor->RenderHeightmap( FTransform::Identity, {}, _HeightMapRT );
_HeightMapRT->UpdateTexture( &Heightmap );
}
Trying to read the texture data into memory. Trigger on clicking the “Read Heightmap” button.
void FHeightmapToolModule::CopyHeightmapData()
{
UTexture2D& Heightmap = *_Heightmap;
struct FCopyBufferData {
UTexture2D* Texture;
TPromise<void> Promise;
TArray<FColor> DestBuffer;
};
using FCommandDataPtr = TSharedPtr<FCopyBufferData, ESPMode::ThreadSafe>;
FCommandDataPtr CommandData = MakeShared<FCopyBufferData, ESPMode::ThreadSafe>();
CommandData->Texture = &Heightmap;
CommandData->DestBuffer.SetNum( Heightmap.GetSizeX() * Heightmap.GetSizeY() );
{
ENQUEUE_RENDER_COMMAND( ReadHeightmapTexture )(
[CommandData]( FRHICommandListImmediate& RHICmdList )
{
auto Texture2DRHI = CommandData->Texture->GetResource()->GetTexture2DRHI();
uint32 DestPitch{ 0 };
void* MappedTextureMemory = RHILockTexture2D( Texture2DRHI, 0, EResourceLockMode::RLM_ReadOnly, DestPitch, false );
uint32 SizeX = CommandData->Texture->GetSizeX();
uint32 SizeY = CommandData->Texture->GetSizeY();
FMemory::Memcpy( CommandData->DestBuffer.GetData(), MappedTextureMemory, SizeX * SizeY * sizeof( FColor ) );
RHIUnlockTexture2D( Texture2DRHI, 0, false );
// signal completion of the operation
CommandData->Promise.SetValue();
} );
CommandData->Promise.GetFuture().Get();
}
}
It crashes on the memcpy() when trying to access the texture memory. Anyone got any clues why? The destination buffer is sized correctly.