GLES2 and GDNative, progress report #2

By: Thomas Herzog Feb 12, 2018

Introduction

Because of the big release there have been many GDNative related tasks that needed to be addressed. Apart from that, the month was mostly spent on implementing more 2D items in the renderer as well as working on getting custom shaders running.

Roadmap

Done January 2018

bring GDNative API into stable state

improve C++ bindings

add simple C++ GDNative demo

add line rendering

add ninepatch rendering

add polygon and GUI primitive rendering

start work on shader compiler

add circle rendering

Planned February 2018

meet with other developers at FOSDEM and GodotCon

implement more shader features

NativeScript 1.1 extension

Rust binding guidance

load meshes

render meshes

implement basic PBR

directional lights

Details about work in January 2018

bring GDNative API into stable state

With the release of Godot 3.0 being very close, the GDNative C API needed to be revisited one last time before any modifications would need to be done in a backwards-compatible way.

As the result of this revisiting a few things have changed in the API, most significant ones being:

changing String API to reduce memory allocations

adding read and write lock objects for PoolVector types

improve C++ bindings

The C++ bindings to GDNative make some use of templates to do various things. NativeScript requires, since it's a C API, that all methods registered to Godot from a library are implemented by a function with C linkage. The C++ bindings however use classes and instance-methods for increased usability.

So one of the use cases for templates is the creation of functions with C linkage at compile that, which wrap the actual C++ method pointer (not to be confused with function pointers...), unwrap the arguments that are passed in as godot_variants to the appropriate types and then call the actual C++ method with the associated object. (All of this happens here and here)

Previously, only basic Godot types were supported to be used as method parameters, making it harder to pass around and use objects with C++ classes attached to them. A change in the _ArgCast templates allows each pointer type to construct the object the way it wants, enabling the use of custom classes as parameters.

One kind of Object has special semantics in Godot, the Reference class. All classes inheriting from it can be reference counted. This is Godot's main mechanism for memory management.
To increase or decrease the reference count, the methods reference() and unreference() need to be called manually. Since this is bothersome, Godot has its own smart-pointer type called Ref, which references on copy and automatically dereferences when the smart pointer goes out of scope.

This functionality was replicated in the C++ bindings, but the translation from Godot-intern code into the external bindings had some unexpected problems - causing memory leaks.

add line rendering

As mentioned in the last progress report, 2D rendering is done by processing a series of "items" that tell the rendering backend which kind of 2D element should be rendered and in what way.

There were still quite a few item rendering implementations missing, one of them is basic line rendering.

The most obvious use case: underlining text.

2D Editor gizmos are another place where simple lines are used.

add ninepatch rendering

As seen in the previous screenshot, many UI elements looked "washed out" and stretched. The rendering command used by such an item is the CommandNinePatch.

A Ninepatch element is a (usually textured) rectangle that has a fixed margin for the borders. That's how the borders of a button look equally smooth when resizing the button - only the center gets freely scaled, the corners stay the same and the other parts of the border get only scaled in one dimension.

It's called Ninepatch because the rectangle gets split into nine sub-rectangles.

The GLES3 renderer only renders a single rectangle but feeds the fragment shader with the needed margin information. The fragment shader then calculates the associated UV coordinate for each fragment. The same approach shouldn't be used in GLES2, since some drivers work a lot better if no dependent texture reads are performed. (layman explanation: the UVs of a texture for a fragment should be known before the fragment shader actually executes)

Previously Godot issued 8 or 9 render calls for each sub-rectangle (8 if the center doesn't get rendered).
In the new GLES2 backened I decided to use a vertex + index array buffer instead to reduce the number of draw calls. For each vertex the fitting UVs get calculated before the draw call. Because of some stupid typos I had a hard time getting this right, but at least I got a nice UV-debug screenshot out of it.

Once this was all working, the elements rendered correctly.

A nice consequence of using a element array buffer for rendering instead of different draw calls is that the center rect rendering can be disabled and enabled with very little overhead.

add polygon and GUI primitive rendering

Even with Ninepatch elements rendering the Godot editor didn't really look like it should yet.

This is because the editor uses Items that use CommandPrimitive commands, which can be used to draw lines, triangles or rectangles with easy to set up custom UVs and colors.

With that done, the editor almost rendered correctly, except for some incorrect clipping. (which got fixed shortly after this screenshot got taken)

start work on shader compiler

This is done by using the Godot built-in shader parser and compiler which outputs an abstract syntax tree (AST) which can be used for further processing.

To support the Godot shader lanuage, the abstract syntax tree needs to be translated into the appropriate target language, in this case GLSL ES 2.0. This is done in the ShaderCompilerGLES2 class.

The _dump_node_code method is used to output a String for each kind of node in the AST. Recursively, this method is used to generate the GLSL code for the whole shader.

Because GLSL ES 2.0 is more limited than GLSL ES 3.0, some features need workarounds or just won't be supported.

For example, unsigned integers are not supported in GLSL ES 2.0, so as a workaround they will be converted to signed ints.

Many functions supported by GLSL ES 3.0 can't be used, such as the inverse() function for matrices, so either they will have to be re-implemented manually in code (without possible hardware support) or be plainly not supported at all, but this is still up for discussion.

Custom shaders are still in an early stage, but they already work inside the editor and in games and support hot-reloading just like with the GLES3 backend.

add circle rendering

One of the last few item command types unimplemented for 2D rendering was the CommandCircle. While the initial implementation had a fatal bug, it helped uncover a more hidden bug that caused problems in the editor UI later on.

Future

The big milestone of basic 3D rendering is still ahead and I'm very excited to get my hands dirty. A few things in the 2D rendering still need to be implemented, but overall the 2D experience should be pretty decent by now.

Seeing the code

If you are interested in the GLES2 related code, you can see all the commits in my fork on GitHub.