Storing/reading values from integer-typed textures.

Hi everybody.

I’m trying to utilize integer-based texture format for my volume textures, as it makes more sense to my application than loading [0,1]-normalized floats and then converting them back to integers through multiplication and rounding.

The thing is, it seems that every possible texture format works, except the ones I want to use. Once I set my texture format to R(8/16/32)_(U/S)INT, I can no longer read the data from it in a material.

This is my code for generating the texture -

void CreateVolumeTextureTransient(UVolumeTexture*& OutTexture, EPixelFormat PixelFormat, FIntVector Dimensions,
	uint8* BulkData, bool bUAVTargetable) {
	int BlockBytes = GPixelFormats[PixelFormat].BlockBytes;
	int TotalBytes = Dimensions.X * Dimensions.Y * Dimensions.Z * BlockBytes;

	UVolumeTexture* TransientTexture = 
		NewObject<UVolumeTexture>(
			GetTransientPackage(),
			NAME_None,
			RF_Transient
		);

	TransientTexture->SRGB = false;
	TransientTexture->NeverStream = true;
	TransientTexture->CompressionNone = true;

	TransientTexture->PlatformData = new FTexturePlatformData();
	TransientTexture->PlatformData->SizeX = Dimensions.X;
	TransientTexture->PlatformData->SizeY = Dimensions.Y;
	TransientTexture->PlatformData->NumSlices = Dimensions.Z;

	TransientTexture->PlatformData->PixelFormat = PixelFormat;

	// Allocate first mipmap.
	FTexture2DMipMap* Mip = new FTexture2DMipMap();

	TransientTexture->PlatformData->Mips.Add(Mip);
	Mip->BulkData.Lock(LOCK_READ_WRITE);
	void* Data = Mip->BulkData.Realloc(TotalBytes);

	if (BulkData) {
		FMemory::Memcpy(Data, BulkData, TotalBytes);
	}
	else {
		FMemory::Memset(Data, 0, TotalBytes);
	}
	Mip->BulkData.Unlock();

	TransientTexture->UpdateResource();
	OutTexture = TransientTexture;
}

And in the material, I use a custom node to read voxel values without interpolation, like so:

int LabelValue = Volume.Load(int4(xPos, yPos, zPos, 0)).r;
if (LabelValue > 0) return float4(1.0, 1.0, 1.0, 1.0);
else return float4(0.0, 0.0, 0.0, 0.0);

Now, if I use any floating-point format (G8, G16, R32F) and set inside any values that map to 1.0 or higher when read, I can see the expected white color in those spots of the Volume Texture. However, once I switch to e.g. PF_R32_INT, I keep getting straight zeroes from the texture Load(), no matter what I put in my data array when creating the texture.

I stepped through the UpdateResource RHI call-stack and everything points to the initial data really getting set into the texture.

Anybody has experienced this problem before? I’ll be making a custom compute shader tomorrow to try to check if the data actually gets transferred into the texture during creation, but I don’t have the willpower now.

Well, answering my own question:

Even though I’d expect the Load() function to return whatever format the underlying texture has specified, it does not. I couldn’t figure out what format exactly it returns, but it’s definitely not an int.
Without further ado, the solution is :

asint()

Yep.

int LabelValue = asint(Volume.Load(int4(xPos, yPos, zPos, 0)));

Works like it should. Of course, use asuint() for unsigned int versions.

I also tried this with R16_SINT and surprisingly, this also works (no need to take care of the 2’s complement representation when expanding from 16 to 32 bits).