1.17.0.1
Prysm
Preloaded Images

Table of Contents

Texture preloading is a feature of Prysm that allows developers to manually load and decode textures (which Cohtml would normally do). Texture preloading also allows you to specify a content rectangle where the actual image is. This can be used if the texture has padding or is part of a texture atlas.

This lets you achieve the following:

  • Improve the loading time of pages by preparing the textures in advance instead of when Cohtml asks for them.
  • Reduce memory footprint when the engine and the UI share textures, by having the texture stored in GPU memory only once, instead of twice (once for the engine and once for Cohtml).
  • Use custom image formats.

Preloading steps

Note
Cohtml expects all textures to have premultiplied alpha, keep that in mind when loading the images (unless the default shaders / backends are replaced).
  1. Prepare the images, by loading them and keeping a reference to the loaded texture.
    void PreloadTexture()
    {
        for (auto& textureURL : texturesToPreload)
        {
            preloadedTextures[textureURL] = LoadTexture(textureURL);
        }
    }
    
  2. Override cohtml::IAsyncResourceHandler::OnResourceRequest in the resource handler. Here's a pseudocode implementation:
    virtual void MyResponseHandler::OnResourceRequest(
        const cohtml::IAsyncResourceRequest* request,
        cohtml::IAsyncResourceResponse* response) override
    {
        std::string url = request->GetURL();
        if (MyApp::IsPreloaded(url))
        {
            auto texture = MyApp::GetPreloadedTexture(url);
    
            // Provide texture information to Cohtml
            cohtml::IAsyncResourceResponse::UserImageData data;
            data.Width = texture->Width;
            data.Height = texture->Height;
            data.ContentRectX = 0;
            data.ContentRectY = 0;
            data.ContentRectWidth = data.Width;
            data.ContentRectHeight = data.Height;
            data.Format = texture->Format;
            data.Texture = texture->Resource;
            response->ReceiveUserImage(data);
            response->Finish(cohtml::IAsyncResourceResponse::Status::Success);
        }
        else
        {
            // This texture isn't loaded yet, let Cohtml read the raw file.
            auto rawData = MyApp::GetFileBytes(url);
    
            if (!rawData)
            {
                // Signal Cohtml that the resource load failed
                response->Finish(cohtml::IAsyncResourceResponse::Failure);
            }
            // We allocate space for the raw data in Cohtml
            auto destPtr = response->GetSpace(rawData.size());
    
            // Copy file bytes
            MyApp::CopyData(destPtr, rawData, rawData.size());
    
            response->Finish(cohtml::IAsyncResourceResponse::Success);
        }
    }
    
  3. Modify the rendering backends to notify the engine about the texture usage. When WrapUserTexture is called, you should notify your engine that the texture is being used by Cohtml. The engine must keep the texture alive, while it is being used by Cohtml.
void MyBackend::WrapUserTexture(void* userObject,
const Texture2D& description,
Texture2DObject object)
{
// userObject contains whatever you have passed to
// cohtml::IAsyncResourceResponse::UserImageData::Texture
auto userTexture = (OpaqueUserTexture*) userObject;
userTexture->IsUserTexture = true
// Notify the engine about the texture being used
MyApp::TextureBeingUsed(userTexture->Resource);
m_Textures.insert(std::make_pair(object, userTexture));
}

When DestroyTexture is called, you should notify your engine that the texture is not used anymore.

Note
DestroyTexture will be called when the texture is not referenced by any HTML element or JS object.
void MyBackend::DestroyTexture(Texture2DObject texture)
{
auto t = m_Textures.find(texture);
if (t != m_Textures.end())
{
if (!t->second.IsUserTexture)
{
glDeleteTextures(1, &t->second.Name); CHECK_GL_ERROR;
}
else
{
MyApp::TextureReleased(t);
}
m_Textures.erase(t);
}
}

Lifetime of preloaded resources

Note
References to the preloaded resources will be released after they are not referenced anymore by any HTML element or JS object. Removing an element which is using a preloaded resource from the DOM tree does not mean that the reference to the resource will be released immediately.

Reference flow when using preloaded resources as background-image:

  1. Create an element with a style which has preloaded resource for a background-image.
  2. When the element's style is evaluated, the style will add a reference to the resource.
  3. The reference will be removed when:
    • The background-image in the element's style is changed.
    • The style of the element is removed or the whole element is removed from the DOM tree.
Note
Elements which are detached from the tree does not have styles.

Lifetime flow in <img>:

  1. Create <img> element with a preloaded resource specified by the src attribute.
  2. The element will add a reference to the resource.
  3. The reference will be removed when:
    • The src attribute is changed.
    • The Garbage Collector destroys the element.
Note
Non-user images work the same way, but they are cached internally by our SDK. They are going to be freed when the cache reaches the configured thresholds.