Shared View Render Targets

Overview

In cases where there are multiple cohtml::Views, the memory/performance overhead of the UTextureRenderTarget2Ds that will be needed could potentially become far from negligible, because each View uses its own render target texture. This can impact memory when the textures are very high resolution, in addition to impacting performance because each render target texture will have to be managed individually (for example, during resizes).

In such scenarios where GPU memory needs to be reduced and/or achieve better performance, Gameface allows multiple cohtml::Views to share a single render target texture between them.

How to use

When a FCohtmlViewWrapper is created, normally it would also automatically request the creation of a FCohTexture of type ViewRenderTargetTexture and from then on, this FCohTexture will be tied to the lifetime of the FCohtmlViewWrapper. This is done through the FCohTexture::AcquireAutoManagedTexture API, which makes the internal FCohTextureCollector “collect” the created FCohTexture and provide a raw pointer to it.

In order to make multiple FCohtmlViewWrappers share the same FCohTexture (and thus the used render target textures), two things need to be done:

  • You have to subscribe to the ICohtmlPlugin::OnProvideUITextureForViewWrapper event, which will be fired when an FCohtmlViewWrapper is created, just before it has to create its FCohTexture, allowing you to instead provide it with a FCohTexture that you have created on your own.
  • To bypass the FCohTextureCollector lifetime management, we’ve provided a FCohTexture::AcquireManuallyManagedTexture API, which returns a shared pointer to the created FCohTexture.

Doing the above steps will enable you to determine if you want to provide a manually-managed FCohTexture or an auto-managed FCohTexture based on some custom conditions. The code would look something like this:

	bool bSomeCustomCondition = false;
	TSharedPtr<FCohTexture> ManuallyManagedCohTexture = FCohTexture::AcquireManuallyManagedTexture(ECohTextureType::ViewRenderTargetTexture);
	ICohtmlPlugin::Get().OnProvideUITextureForViewWrapper.BindLambda([ManuallyManagedCohTexture, bSomeCustomCondition]() {
		// Use the FCohTexture that will be manually managed or the FCohTextureCollector-managed one
		return bSomeCustomCondition ? ManuallyManagedCohTexture : FCohTexture::AcquireAutoManagedTexture(ECohTextureType::ViewRenderTargetTexture);
	});

For a more complete example of how similar code can work, we’ve created a Shared Render Targets map sample that you can check out.

Resizing

There are a few things to note when deciding to share render targets between Views:

  • If you have a UI element that will cover the size of your viewport and an in-world UI element which won’t be the same size as the viewport, but they share the same render target texture, you won’t be able to have both of them displayed at the same time without having visual artifacts, due to the size mismatch.
    • Instead, it is recommended to either show only one of those components at a time, and when showing the other, ensure that you are also manually resizing the FCohtmlViewWrapper to the expected size (this may potentially also require a clear of the render target texture). This process is showcased in the Shared Render Targets sample.
    • An even better approach would be to split your shared render targets based on the types of components. For example, share only render targets between UI elements of the same size (e.g., in-world Views), so that you won’t have to resize each time you switch between UI elements that will draw to a given render target.

How Gameface Views work with textures

While Unreal’s render target class is technically UTextureRenderTarget2D, what Gameface is interested in is the underlying FRHITexture and ensuring its lifetime. This is why Gameface’s systems work with FCohTexture primarily. This doesn’t only go for render target textures that will be used by a cohtml::View, but also extends to every other Unreal texture type that is supported.

When a Gameface UI element is created (be it Component-based or UMG-based, there are two main things that happen:

  • Each UI element will create a FCohtmlViewWrapper, which, as the name implies, is a class wrapper around the cohtml::View which will render your HTML page.
  • To render things, a cohtml::View requires a render target texture. Such a texture is automatically created by the FCohtmlViewWrapper when it is initialized. Internally, this render target texture is wrapped in a FCohTexture class whose main purpose is to work with Unreal APIs to retrieve a reference to the UTextureRenderTarget2D’s FRHITexture, in order to prevent Unreal from deleting it too early, while Gameface is still in the process using it in its rendering pipeline.
    • This FCohTexture’s lifetime is tied to a FCohtmlViewWrapper’s lifetime, but it is not managed by it. That is the job of the FCohTextureCollector singleton class.

Limitations

  • Displaying UI elements that share the same render target texture and size at the same time is possible, however, if all Views draw over the same section of the texture, you will see visual errors due to the overlap.
    • For such a scenario to work (given two Views let’s say) you would need to only draw to one section of the texture with one View, and another section of the texture with the other View. This behavior would very closely resemble our Surface Partitioning feature, so perhaps that would be a better solution depending on the case.