[SOLVED] Write Pixels to a Grayscale (PF_G8) texture resource?

I need to send a pre-sized array of normalized floats to a material, and rather than using scalar or vector parameters the best way to do this would be to send the data via a Texture. I’ve got as far as creating the texture (I believe), and sending it to the material. The material samples it as it needs to.

This is where I’m at so far:



		// Create Data Buffer
		// Don't ever save it
		UTexture2D* NewDataBuffer = UTexture2D::CreateTransient(MAX_CHARGE_ABILITIES, 1, PF_G8);
		ASSERTV(NewDataBuffer != nullptr, TEXT("Invalid Data Buffer"));

		NewDataBuffer->Filter = TextureFilter::TF_Nearest;
		NewDataBuffer->SRGB = false;
		NewDataBuffer->CompressionSettings = TextureCompressionSettings::TC_Grayscale;
		NewDataBuffer->CompressionNoAlpha = true;
		NewDataBuffer->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
		NewDataBuffer->AddressX = TextureAddress::TA_Clamp;
		NewDataBuffer->AddressY = TextureAddress::TA_Clamp;

		// Actually creates the GPU resource?
		NewDataBuffer->UpdateResource();
		NewDataBuffer->RefreshSamplerStates();


What I want to do now is ‘set’ the pixels to the values in an array of floats. It’s so I can pack a bunch of progress bars into a single material.



                TArray<float> NormalizedFloats;
		NormalizedFloats.SetNum(MAX_CHARGE_ABILITIES);

		NormalizedFloats[0] == ...


I’ve seen lots of examples around, but they all seem very overcomplex for what I want to do. How can write that array of values to the texture?

1 Like

Getting closer I think, but this doesn’t compile. Any clues?



			// Enqueue Render Command to update texture resource
			if (ProgressDataBuffer)
			{
				ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(void, FTexture2DRHIRef, InputTexture, ProgressDataBuffer->Resource->TextureRHI, TArray<uint8>, InputValues, ChargeRatios,
					{
						uint32 DestStride;
						uint32* DestBuffer = (uint32*)RHILockTexture2D(InputTexture, 0, RLM_WriteOnly, DestStride, false, true);
						uint8* SourceBuffer = new uint8[MAX_CHARGE_ABILITIES];
						for (int32 Idx = 0; Idx < MAX_CHARGE_ABILITIES; Idx++)
						{
							SourceBuffer[Idx] = InputValues[Idx];
						}

						FMemory::Memcpy(DestBuffer, SourceBuffer, MAX_CHARGE_ABILITIES);
						RHIUnlockTexture2D(InputTexture, 0, false, true);
						delete] SourceBuffer;
					}
				);
			}




error C2664: 'UECGame_PieMenu::Update::EURCMacro_void::EURCMacro_void(UECGame_PieMenu::Update::EURCMacro_void &&)': cannot convert argument 1 from 'FRHITexture' to 'const TRefCountPtr<FRHITexture2D> &'
Reason: cannot convert from 'FRHITexture' to 'const FTexture2DRHIRef'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called


Alright finally solved it!

Full code:

Initialization:



	// Create Data Buffer
		// Don't ever save it
		UTexture2D* NewDataBuffer = UTexture2D::CreateTransient(MAX_CHARGE_ABILITIES, 1, PF_G8);
		ASSERTV(NewDataBuffer != nullptr, TEXT("Invalid Data Buffer"));

		NewDataBuffer->Filter = TextureFilter::TF_Nearest;
		NewDataBuffer->SRGB = false;
		NewDataBuffer->CompressionSettings = TextureCompressionSettings::TC_Grayscale;
		NewDataBuffer->CompressionNoAlpha = true;
		NewDataBuffer->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
		NewDataBuffer->AddressX = TextureAddress::TA_Clamp;
		NewDataBuffer->AddressY = TextureAddress::TA_Clamp;

		// Actually creates the GPU resource?
		NewDataBuffer->UpdateResource();
		NewDataBuffer->RefreshSamplerStates();

		ProgressDataBuffer = NewDataBuffer;
		if (RootBorderDMI)
		{
			RootBorderDMI->SetTextureParameterValue(TEXT("ProgressData"), ProgressDataBuffer);
			RootBorderDMI->SetScalarParameterValue(TEXT("DataPixelsU"), MAX_CHARGE_ABILITIES);
		}


Update Loop:




			TArray<uint8> ChargeRatios;
			ChargeRatios.SetNumZeroed(MAX_CHARGE_ABILITIES);

			// Enqueue Render Command to update texture resource
			if (ProgressDataBuffer)
			{
				FTexture2DRHIParamRef TexParam = ProgressDataBuffer->Resource->TextureRHI->GetTexture2D();
				ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(void, FTexture2DRHIParamRef, InputTexture, TexParam, TArray<uint8>, InputValues, ChargeRatios,
					{
						uint32 DestStride;
						uint32* DestBuffer = (uint32*)RHILockTexture2D(InputTexture, 0, RLM_WriteOnly, DestStride, false, true);
						FMemory::Memcpy(DestBuffer, InputValues.GetData(), MAX_CHARGE_ABILITIES);
						RHIUnlockTexture2D(InputTexture, 0, false, true);
					}
				);
			}


Result:
acaec68427279f37d4b0a48821f1124e606b1e59.jpeg

Are you updating the texture in the tick function?

It’s updated every frame yeah, not via tick but similar method.