How do renderers create textures
When talking about the UImGui::Texture
class, renderers
must create their own classes that derive from the
UImGui::GenericTexture
abstract class.
However, GenericTexture
and GenericRenderer
do not depend on each other. This produces following side effects:
- Custom renderers can decide whether to implement a texture backend(some applications might prefer their own custom abstractions)
- Custom renderers based on OpenGL, Vulkan and/or WebGPU can decide to reuse the existing texture creation workflows
- Custom renderers can easily mix and match texture and renderer backends
This is why in the RendererData
struct one can see that
there are 2 members of the RendererType
enum. One for the
renderer, and one for the texture backend.
By default the rendererType
and the
textureRendererType
fields are set to be equal to each
other. In order for your renderer to reuse one of the existing texture
renderer backends use
UImGui::Renderer::data().textureRendererType
to set it to
another type before creating your texture.
The GenericTexture class
The UImGui::GenericTexture
abstract class looks like
this:
class GenericTexture
{
public:
// Event Safety - Any time
virtual void init(TextureData& dt, String location, bool bFiltered) noexcept = 0;
// Event Safety - Post-begin
virtual uintptr_t get(TextureData& dt) noexcept = 0;
// Event Safety - Post-begin
virtual void load(TextureData& dt, void* data, FVector2 size, uint32_t depth, bool bFreeImageData,
const TFunction<void(void*)>& freeFunc) noexcept = 0;
template<TextureFormat format>
static bool saveToFile(TextureData& dt, const String location, const TextureFormat fmt = format, const uint8_t jpegQuality = 100);
// Cleans up the image data
// Event Safety - All initiated
virtual void clear(TextureData& dt) noexcept = 0;
virtual ~GenericTexture() noexcept = default;
protected:
friend class Texture;
static void beginLoad(TextureData& dt, void** data, FVector2& size) noexcept;
static void endLoad(TextureData& dt, void* data, bool bFreeImageData, const TFunction<void(void*)>& freeFunc) noexcept;
static void defaultInit(TextureData& dt, String location, bool bFiltered) noexcept;
static void defaultClear(TextureData& dt) noexcept;
};
If you compare it to the normal UImGui::Texture
class,
you'll discover that most of the functions map 1:1, except that most
functions in the GenericTexture
class get a
TextureData&
as their first argument. This is to ensure
that derivations based on GenericTexture
are always
code-only, since the same backend instance is used between all
textures.
The TextureData
struct looks like this:
struct TextureData
{
;
String filename;
FVector2 sizebool bFiltered;
int channels;
uintptr_t id;
void* data;
// This stores the string location for the internal C storage system
size_t storageIndex;
;
CustomSaveFunction customSaveFunction
// Data for each custom texture renderer backend
void* context;
size_t contextSize;
};
The context
and contextSize
members are
used for storing any additional data that the custom texture backend
instance may need.
All other members are part of what all textures store as their data. For more info, refer to the Textures entry.
Examples
Here is a pseudo-example that showcases how custom texture backends are generally implemented:
class PseudoTexture final : public UImGui::GenericTexture
{
public:
virtual void init(UImGui::TextureData& dt, const UImGui::String location, const bool bFiltered) noexcept override
{
(dt, location, bFiltered);
defaultInit}
virtual void load(UImGui::TextureData& dt, void* data, UImGui::FVector2 size, uint32_t depth, bool bFreeImageData,
const TFunction<void(void*)>& freeFunc) noexcept override
{
(dt, &data, size);
beginLoad// Load using your renderer API here
(dt, data, bFreeImageData, freeFunc);
endLoad}
virtual uintptr_t get(UImGui::TextureData& dt) noexcept override
{
return dt.id;
}
virtual void clear(UImGui::TextureData& dt) noexcept override
{
// Clear with your renderer API here
.id = 0;
dt(dt);
defaultClear}
virtual ~TestOpenGLTexture() noexcept override = default;
};
To then use the PseudoTexture
backend, create an
instance of it and assign its address to the customTexture
field of the InitInfo
struct.
More examples can be found in the following documentation entry.
C API
The C API for the GenericTexture
class looks like
this:
* UImGui_CGenericTexture_make(
UImGui_CGenericTexture,
UImGui_CGenericTexture_InitFun init,
UImGui_CGenericTexture_GetFun get,
UImGui_CGenericTexture_LoadFun load
UImGui_CGenericTexture_Clear clear);
void UImGui_CGenericTexture_free(UImGui_CGenericTexture* texture);
As you can see, the callback functions provided to the
UImGui_CGenericTexture_make()
function are equivalent to
the ones in the C++ class.
These function pointer types are implemented like this:
typedef void UImGui_CGenericTexture;
typedef void(*UImGui_CGenericTexture_VoidFun)(UImGui_TextureData*);
typedef void(*UImGui_CGenericTexture_InitFun)(UImGui_TextureData*, UImGui_String, bool);
typedef uintptr_t(*UImGui_CGenericTexture_GetFun)(UImGui_TextureData*);
typedef void(*UImGui_CGenericTexture_LoadFun)(UImGui_TextureData*, void*, UImGui_FVector2, uint32_t, bool);
typedef UImGui_CGenericTexture_VoidFun UImGui_CGenericTexture_Clear;
Event safety
All functions in the UImGui::GenericTexture
class or C
API are rated with the same event safety as their counterparts in the
UImGui::Texture
class.
- Home
- Beginner content
- Install guide
- Creating and using the UI components
- The Instance
- The Init Info struct
- Textures
- Logging
- Unicode support
- Additional features
- Client-side bar
- Custom type definitions
- Memory management
- C API development
- Config files and Folders
- Interfaces
- Internal Event safety
- Customising the build system
- Modules system
- Collaborating with others
- Advanced content
- Developer and contributor resources
- Misc