Slate resource preloading

Hi,

I have a working interface with Slate. However, I notice that the first loading of some brushes can be visible. For example, a button with a brush on SetHovered() will noticeably flash the first time you hover onto it. After that, it works like a charm.

I took a look at the GetResources() method, I listed all my brushes here. Is there anything more to do ?

The code here shows the beginning of my Construct() method.

	const FSButtonStyle* Helper= &StyleRef::Get().GetWidgetStyle<FSButtonStyle>("...");
	ButtonStyle.SetDisabled(ButtonStyleHelper->WidgetBackground);
	ButtonStyle.SetNormal(Helper->WidgetBackground);
	ButtonStyle.SetHovered(Helper->ActiveWidgetBackground);
	ButtonStyle.SetPressed(Helper->ActiveWidgetBackground);

	// ... 

	SNew(SButton).ButtonStyle(&ButtonStyle)

Now the FSButtonStyle code :

void FSButtonStyle::GetResources(TArray<const FSlateBrush*>& OutBrushes) const
{
	OutBrushes.Add(&WidgetBackground);
	OutBrushes.Add(&ActiveWidgetBackground);
}

UPROPERTY(EditAnywhere, Category = Background) FSlateBrush WidgetBackground;

UPROPERTY(EditAnywhere, Category = Background) FSlateBrush ActiveWidgetBackground;

What am I doing wrong ?

What size are your textures, and are they being tiled?

The code inside FSlateD3DTextureManager::CreateTextures handles loading the textures needed by a style set, and will also create a texture atlas for any textures smaller than 256x256 that are not being tiled by a brush.

The texture I use to reproduce the issue is never tiled and is 150x50 in size.

Wait, have you derived FSButtonStyle from FButtonStyle? If you have then you also need to call the parent version of GetResources:

void FSButtonStyle::GetResources(TArray<const FSlateBrush*>& OutBrushes) const
{
    FButtonStyle::GetResources(OutBrushes);
    OutBrushes.Add(&WidgetBackground);
    OutBrushes.Add(&ActiveWidgetBackground);
}

Also, widget styles are really designed to work via composition rather than inheritance, in the same way that Slate widgets are.

Could you also check the texture atlas (Windows → Development Tools → Debug Tools → Display Texture Atlases) and verify that the images used by your brushes exist in that texture.

As an aside, you’re also setting up your widget style in a slightly strange way. Really that should either be done via an asset in the editor (if it’s for a game) or via C++ (if it’s for the editor) - that said there’s nothing wrong with using C++ style sets for a game, providing you’re using a game specific style set.

Example C++ version.

In your style set:

Style->Set("TestButtonStyle", FButtonStyle()
	.SetDisabled(IMAGE_BRUSH("Tex1", FVector2D(150, 50)))
	.SetNormal(IMAGE_BRUSH("Tex1", FVector2D(150, 50)))
	.SetHovered(IMAGE_BRUSH("Tex1_Hover", FVector2D(150, 50)))
	.SetPressed(IMAGE_BRUSH("Tex1", FVector2D(150, 50)))
	);

When using your button:

SNew(SButton)
.ButtonStyle(FMyStyle::Get(), "TestButtonStyle")

I’ve made a test set-up with the above code and they appear in the atlas and don’t suffer from flickering.

Ah, I see, I thought you were passing FSButtonStyle here:

SNew(SButton).ButtonStyle(&ButtonStyle)

But that’s actually defined elsewhere. Is it part of a style-set, and is it relevant to this example, or is it only WidgetBackground and ActiveWidgetBackground that have an issue?

Wait, have you derived FSButtonStyle
from FButtonStyle?

No I don’t, the class inherits from FSlateWidgetStyle. Should I call the parent too ?

Could you also check the texture atlas
(Windows → Development Tools → Debug
Tools → Display Texture Atlases) and
verify that the images used by your
brushes exist in that texture.

I don’t see them, but I actually don’t see anything I created. I have backgrounds, borders, lots of stuff actually. None of it is showing up.
I also should say that the atlas window is larger than my 1080p screen, so I don’t see the menus or window title. I do see the border so I can confirm that the assets are not here.

As an aside, you’re also setting up
your widget style in a slightly
strange way. Really that should either
be done via an asset in the editor (if
it’s for a game) or via C++ (if it’s
for the editor).

Yeah, kind of wrong I know. I had just done the text thing (you know, the text style asset that can’t be used…) and I didn’t check, but I could actually just remove this class and use FButtonStyle directly.

Yeah, my example is unclear. What I am doing right now is actually copying the pointers to the slate brushes ; from the FSButtonStyle instance to a FButtonStyle.

It’s kind of ugly and I guess you’re going to tell me that it’s also what causes the issue, but I also have the same issue on another class. In this other class, I load a custom widget style and store it’s reference. Later, in a FSlateBrush* callback, I return one of the brushes to be used as a SBorder background, like you showed me in one of your posts.

Okay, so small update : I removed this entire class. Now I’m just passing a const FButtonStyle* to a SButton, very simple and clean. It still flickers.

Okay, thanks.

So nothing from your style set is showing in the atlas? You’ve definitely called FSlateStyleRegistry::RegisterSlateStyle(*StylePtr)? (I’m sure you have since I remember helping you with an issue related to style set registration and module reloading, but I’d just like to double check).

If so, could you check FSlateRHIResourceManager::LoadStyleResources and verify that you’re seeing the resources you expect when it’s called for your style. From there you should be able to see it create/load/atlas the textures.

Okay. Are you able to provide me the exact textures you’re using? I’ll take a look with them tomorrow to see if there’s anything obvious I can see.

If you’d rather not post them publicly, I can provide my email address for you to send them to - if so I’ll PM you on the forum as I’d rather not post that on here :slight_smile:

Yeah I called it, I just checked, along with the LoadStyleResources. So after a fresh debug build, it looks like every method that should be called is called, including the GetResources of my faulty style, one of those who flicker.

Here they are, both the “normal” brush used on deactivated/normal states, and the “active” brush when hovering and pressing.

4062-button_normal.png

4064-button_normal_active.png

On a side note, I have the issue with at least another set like this one that I know of.

I’ve tried those images but I still see them get loaded and atlassed correctly, and they render without any flickering from visibly being loaded.

I’ve attached my test case for you to take a look at. If you could give it a go and let me know if you see the same issue with it, that would be great.

It’s the bare minimum to keep it small enough for AnswerHub, so:

  1. Create a new project called “MyTestFirstPerson” from the “Code First Person” template, making sure to include starter content.
  2. Place the zip file in the root of the project (where the .uproject file is) and extract it, overwriting any files that you’re prompted for.
  3. Build the “MyTestFirstPerson” and run the editor. If you PIE you should see your button in the top center of the viewport. You should also see the button textures in the texture atlas.

The files of interest would be MyTestFirstPersonStyle.cpp and MyTestFirstPersonHUD.cpp as these are what set-up and use the button style.

Thanks a lot for doing all of this !

So your test case works, of course. I took a look at what we’re not doing the same way, and your style class has different initialization than ShooterGame’s (FSlateStyleSet vs FSlateGameResources).

Before :

TSharedRef StyleRef =
FSlateGameResources::New(FName(“MyStyle”),
“/Game/Slate”, “/Game/Slate”);

After :

TSharedRef StyleRef =
MakeShareable(new
FSlateStyleSet(“MyStyle”));
StyleRef->SetContentRoot(FPaths::GameContentDir()
/ “Slate”);

But I can’t try it because this breaks all my asset paths. I can’t load a single style anymore :

Unable to find Slate Widget Style
‘/Styles/SS_Panel_Default’. Using
FSWindowStyle defaults instead.

I don’t get it. I am using a Slate folder in Content, with subfolders (Styles, Brushes, etc). Then I call GetWidgetStyle with, here, “Styles/SS_Panel_Default” as parameter, Content/Slate/Styles/SS_Panel_Default being the actual full path.

Any idea ? Is this related ? Should I try to load from PNGs instead of styles ?

Okay so it’s probably not the method name since the gameresource constructor calls the gamestyle constructor.

But I have another idea. In your sample, you load the PNGs directly in the style class. I don’t load anything in my class, I load the styles in the Construct() method. Here is the typical code :

ButtonStyle = MyGameStyle::Get().GetWidgetStyle(FName(*InArgs._StyleName));

ChildSlot [
SAssignNew(PhysicalButton, SButton)
.ButtonStyle(ButtonStyle) ];

So maybe that’s the issue ? The style is not referenced until late in the gameplay, so how could the atlas be constructed ?

Ah, right. I just used that as an example since I spend more time in editor code than game code, so I’m more familiar with it.

I changed the FSlateStyleSet to an FSlateGameResources, and there was still no flickering.

I then added an asset for a button widget style and set it up as I had in code, updating my SButton to use this style. This flickered, and doesn’t appear in the atlas. I assume this is how you’re using the button style (as an asset)?

It seems only those textures loaded from disk are atlased. I’ll check this with someone and get back to you.

As an aside, SLATE_STYLE_ARGUMENT has syntactic sugar for taking either a const ISlateStyle* or const ISlateStyle& and a FName. This is the preferred form when setting a style from a style set (for your case above).

.ButtonStyle(MyGameStyle::Get(), InArgs._StyleName)

Yeah, I am using assets.

Thanks !

MattK has taken a look at this and found a bug in the Slate resource loading that was leading to a one frame delay between requesting the texture, and the texture actually being used.

This fix will be in 4.2, however it’s small, and if you’re building from source you can try it out yourself before then.

Open up SlateRHIResourceManager.cpp (UE4\Engine\Source\Runtime\SlateRHIRenderer\Private).

Inside the constructor for FDynamicTextureResource, add the following line:

Proxy->Resource = RHIRefTexture;

Inside FSlateRHIResourceManager::InitializeDynamicTextureResource, remove the following line:

TextureResource->Proxy->Resource = TextureResource->RHIRefTexture;