Failing to set texture pixel data at runtime, why?

I’m struggling to create a new texture at runtime and not have it contain gabrage data. I have simplified my problem into the following few lines of code:

	// Create texture and set to all black
	UTexture2D* Tex = UTexture2D::CreateTransient(16, 16);
	auto BulkData = Tex->GetPlatformData()->Mips[0].BulkData;
	auto Data = BulkData.Lock(LOCK_READ_WRITE);
	FMemory::Memset(Data, 0, 4*16*16); // Default format is 32-bit BGRA

	int breakpoint = 0;

	BulkData.Unlock();
	Tex->UpdateResource();

	// Read data back
	auto BulkData2 = Tex->GetPlatformData()->Mips[0].BulkData;
	auto Data2 = BulkData2.Lock(LOCK_READ_ONLY);

	int breakpoint2 = 0;

	BulkData2.Unlock();

As you can see I am writing all 0’s into the texture’s mip 0, expecting it to come out all black. Indeed, setting a breakpoint at int breakpoint line I can inspect the memory at the address Data and confirm that it is all 0’s.

However when I then try to read the data back from the texture and examine it in the debugger at line int breakpoint2, the debugger shows that not only is the address of Data not equal to Data2 as I would expect, but indeed Data2 is filled with random garbage! And indeed looking at the texture in the editor, it has a bunch of random colors and is definitelly not all black. What am I missing?

Unreal Engine 5.1.1 in case that is relevant in any way.
Running on main thread inside an actors BeginPlay method.

Okay, well I saw that there is a different way to create textures that seems to work for me, going along the lines of:

	// Create 2x2 texture with red, green, blue, white texels

	FColor Data[4] = {FColor::Red, FColor::Green, FColor::Blue, FColor::White};
	MyTexture = Cast<UTexture2D>(NewObject<UObject>(GetTransientPackage(), UTexture2D::StaticClass(), NAME_None, RF_Transient));
	MyTexture->Source.Init(2, 2, 1, 1, TSF_BGRA8, reinterpret_cast<uint8*>(Data));
	MyTexture->UpdateResource();

This one seems to work for me – for now at least.

I will however not be closing this topic until someone can provide an explanation to me what is the difference between the above two approaches, and why one of them works while the other one doesn’t.

P.S.: There is another weird issue where IsValid(MyTexture) returns true in C++ immediatelly after the code above, however in a blueprint that same call to IsValid will return false for this 1 frame until becoming valid on the next. Great.

It looks like the Source field only exists within #if WITH_EDITORONLY_DATA, so it’s probably not an option at runtime.

1 Like