12 GL Backend and Custom Backends - inanevin/LinaVG GitHub Wiki
LinaVG is distributed with two versions at the moment, GLBackend and NoBackend. NoBackend requires you to register your custom backend in your own application using LinaVG.
Default GL Backend
The default OpenGL backend modifies certain GL states before rendering, then restores them to the previous state. So ideally it shouldn't affect your application. Please note that LinaVG does not clear any framebuffer, so if you are using multiple frame buffers make sure that you clear your buffers accordingly before calling LinaVG::StartFrame().
Below are the modified OpenGL states, which are restored back after drawing:
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
This also allows you to draw the LinaVG on to a separate frame buffer if your application desires. It is also very important to note that even though OpenGL backend restores GL states, it doesn't restore VAO or currently bound GL_TEXTURE_2D, specifically, here are the commands after ending a frame:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
So as any other rendering loop will do, make sure you have proper binding/unbinding of your shaders/textures/buffers so that the next frame will correctly draw your own application alongside LinaVG.
Custom Backends
If you are not using OpenGL, you have to define your own backend. Before doing this, you need to make sure LinaVG does not try to use it's own OpenGL backend.
If you are linking against LinaVG using pre-built binaries
Just use the version distributed as "NoBackend".
If you are compiling the LinaVG project yourself to link against
Generate LinaVG project files by setting the CMake option LINAVG_BACKEND_GL
to OFF. This will make sure LinaVG is compiled without any OpenGL functionality. Open the project, re-compile & get your static binaries. Now you are ready to define your custom backend in your own project.
If you are compiling LinaVG along with your source code
Make sure you don't include BackendGL.hpp & .cpp files, they are not needed.
Make sure LINAVG_BACKEND_GL
is undefined in Core/Backend.hpp
Simply delete the define or:
#undef LINAVG_BACKEND_GL
After you get everything compiling successfully without any OpenGL functionality, check out the base backend class in include/Backends/BaseBackend.hpp. You need to create a backend class deriving from BaseBackend, implementing all pure virtuals. When your custom backend is ready, simply use:
LinaVG::Backend::BaseBackend::SetBackend(myBackend);
method to set your target backend class, BEFORE LinaVG::Initialize
call.
More about the backend functions
Your own backend needs to implement the LinaVG::Backend::BaseBackend interface.
// Inherited via BaseBackend
virtual bool Initialize() override;
virtual void Terminate() override;
virtual void StartFrame(int threadCount) override;
virtual void DrawGradient(LinaVG::GradientDrawBuffer* buf, int thread) override;
virtual void DrawTextured(LinaVG::TextureDrawBuffer* buf, int thread) override;
virtual void DrawDefault(LinaVG::DrawBuffer* buf, int thread) override;
virtual void DrawSimpleText(LinaVG::SimpleTextDrawBuffer* buf, int thread) override;
virtual void DrawSDFText(LinaVG::SDFTextDrawBuffer* buf, int thread) override;
virtual void EndFrame() override;
virtual void BufferFontTextureAtlas(int width, int height, int offsetX, int offsetY, unsigned char* data) override;
virtual void BindFontTexture(LinaVG::BackendHandle texture) override;
virtual void SaveAPIState() override;
virtual void RestoreAPIState() override;
virtual LinaVG::BackendHandle CreateFontTexture(int width, int height) override;
The functions are pretty self-explanatory, few crucial points:
Initialize
This function will be called when LinaVG::Initialize() is called. Implement your own init functionality here. The example OpenGL backend creates shaders, buffers, vaos, vbos etc. here.
StartFrame
Called when LinaVG::StartFrame
is called. threadCount
is whatever you pass inside LinaVG::StartFrame()
. Example OpenGL backend sets the draw state here, doesn't do any threading functionality. If you are multi-threading, you can resize your buffers according to the threadCount.
DrawXXX
This is where you will receive the calculated buffers from LinaVG. You can record draw commands here, or save the buffers and call custom rendering functions to dispatch draw calls all at once.
EndFrame
Called when LinaVG::EndFrame()
is called. Example OpenGL backend unbinds buffers and resets drawing state here.
Save/Restore API state
Called by the example GL backend internally to save/restore GL state, also called by the font loader in-between creating new texture atlases, in case you need to make state changes on your custom backend.
CreateFontTexture
This is where you are supposed to allocate a texture with the given size in the GPU and return a handle. The texture will be used for font atlas, so care about the filters and other sampler settings.
BindFontTexture
Handle you returned on the CreateFontTexture
will be given back to you here, where you can "bind" the texture for writing. When you call LoadFont, it will either create a new texture by calling CreateFontTexture
, or use a previously created one for atlas purposes. That's why this method exists. LinaVG does not necessarily create a texture per font you load.
BufferFontTextureAtlas
Buffers custom data into the texture given in the last BindFontTexture
call by width
& height
, as well as offsetX
and offsetY
. Used to buffer individual character glyphs into a font atlas. This is where you will write data to your texture.
CreateFontTexture
, BindFontTexture
and BufferFontTextureAtlas
Some remarks about the combination of Here is an example scenario:
LoadFont(myFont0)
- No atlasses,
CreateFontTexture
- return handle myTexture0 BindFontTexture(myTexture0)
BufferFontTexture()
-- now you are supposed to be buffering into myTexture0LoadFont(myFont1)
-- slightly bigger font- Atlas size is not sufficient enough,
CreateFontTexture
- return handle myTexture1 BindFontTexture(myTexture1)
BufferFontTexture()
-- now you are supposed to be buffering into myTexture1LoadFont(myFont2)
-- small font- Atlas that was created for the first time (myTexture0) has sufficient enough size.
BindFontTexture(myTexture0)
BufferFontTexture()
-- now you are supposed to be buffering into myTexture0