Announcement

Collapse
No announcement yet.

Calling UTexture2D::CreateTransient from a non-main thread?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    Calling UTexture2D::CreateTransient from a non-main thread?

    I am not sure if this is the correct category for this kind of question, but it seems to me the closest...

    There is this code that someone else wrote... He's not in our company anymore so I cannot ask him.

    There is separate thread that at a certain point calls UTexture2D::CreateTransient. Well is that even safe? I though that you can only manipulate UObjects from the game thread...

    Anyway, the reason I ask this question is that when I attempt to close the app when the thread is (probably) somewhere in CreateTransient function, it results in what seems to be a deadlock. The game thread calls a destructor of the class that owns the thread and waits for it to finish. But CreateTransient never returns. I suspect that the function may be calling some delayed AsyncTask from the game thread, but that one is (of course) stuck in the destructor of that class. Is this possible or is even the idea of calling the function from a non-game thread terribly wrong?

    Thanks!

    #2
    I'm fairly certain you can't create UObjects on other threads, they always need to be created on the game thread (but can be manipulated/read from on other threads if you know what you're doing).

    Surprised it doesn't crash immediatelly to be honest.

    Comment


      #3
      UTexture2D::CreateTransient is for run-time simulation memory. Your code is trying to access a memory which can generally is used for accessing world objects. This data functions is not reliable for saving information from it.
      If you want to access the data. Go to platformdata.Mip[] and get the uint8 byte array from there.

      I'm not sure if I answered your question. But that's the behavior I know about this method.


      Comment


        #4
        TheJamsh : That's what I thought then. Seems I will have to come up with some other solution...

        Fieol : No sorry, I don't get what you mean. The code looks like this:

        Code:
                texture = UTexture2D::CreateTransient(metaWidth, metaHeight);
                int32 height = texture->GetSizeY();
                int32 width = texture->GetSizeX();
        
                // Fill in the base mip for the texture we created
                uint8* mipData = (uint8*)texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
        
                for (int32 y = 0; y < height; y++)
                {
                    uint8* destinationPointer = &mipData[(height - 1 - y) * width * sizeof(FColor)];
        
                    for (int32 x = 0; x < width; x++)
                    {
                        *destinationPointer++ = inputFileStream.get();
                        *destinationPointer++ = inputFileStream.get();
                        *destinationPointer++ = inputFileStream.get();
                        *destinationPointer++ = inputFileStream.get();
                    }
                }
        So obviously it uses PlatformData->Mips. But what does that has to do with where the Texture is created?

        Thanks

        Comment


          #5
          Aros_Prince Here is the code:

          Code:
          IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
                  TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
                  TArray<uint8> ImageData = Response->GetContent();
          
                  if (FFileHelper::SaveArrayToFile(ImageData, *TestImageTxt, eFileManager, 0U))
                  {
                      UE_LOG(LogSmartWorldPro, Warning, TEXT("Tried Saving the File"));
                  }
          
                  if (FFileHelper::LoadFileToArray(ImageData, *TestImageTxt))
                  {
                      if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num()))
                      {
                          const TArray<uint8>* UncompressedBGRA = NULL;
                          if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA))
                          {
                              ImageTexPlane00 = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);
                              ImageTexPlane00->MipGenSettings = TMGS_NoMipmaps;
                              ImageTexPlane00->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
          
                              //We need to get the height and width so that we do Realloc in Image Texture  
                              int32 TexSizeX = ImageTexPlane00->PlatformData->Mips[0].SizeX;
                              int32 TexSizeY = ImageTexPlane00->PlatformData->Mips[0].SizeY;
          
                              //Find how the texture data is getting copied to Memcpy
                              uint8* TextureData = (uint8*)ImageTexPlane00->PlatformData->Mips[0].BulkData.Realloc(TexSizeX * TexSizeY * 4);
          
                              FMemory::Memcpy(TextureData, UncompressedBGRA->GetData(), UncompressedBGRA->Num());
          
                              ImageTexPlane00->PlatformData->Mips[0].BulkData.Unlock();
          
                              ImageTexPlane00->UpdateResource();
                         }
                      }
                   }

          Comment

          Working...
          X