# How Impeller Works Around The Lack of Uniform Buffers in Open GL ES 2.0.

The Impeller Renderer API allows callers to specify uniform data in a discrete
buffer. Indeed, it is conventional for the higher level layers of the Impeller
stack to specify all uniform data for a render pass in a single buffer. This
jumbo buffer is allocated using a simple bump allocator on the host before being
transferred over to VRAM. The ImpellerC reflection engine generates structs with
the correct padding and alignment such that the caller can just populate uniform
struct members and `memcpy` them into the jumbo buffer, or use placement-new.
Placement-new is used in cases where device buffers can be memory mapped into
the client address space.

This works extremely well when using a modern rendering backend like Metal.
However, OpenGL ES 2.0 does not support uniform buffer objects. Instead, uniform
data must be specified to GL from the client side using the `glUniform*` family
of APIs. This poses a problem for the OpenGL backend implementation. From a view
(an offset and range) into a buffer pointing to uniform data, it must infer the
right uniform locations within a program object and bind uniform data at the
right offsets within the buffer view.

Since command generation is strongly typed, a pointer to metadata about the
uniform information is stashed along with the buffer view in the command stream.
This metadata is generated by the offline reflection engine part of ImpellerC.
The metadata is usually a collection of items the runtime would need to infer
the right `glUniform*` calls. An item in this collection would look like the
following:

```
struct ShaderStructMemberMetadata {
  ShaderType type; // the data type (bool, int, float, etc.)
  std::string name; // the uniform member name "frame_info.mvp"
  size_t offset;
  size_t size;
  size_t array_elements;
};
```

Using this mechanism, the runtime knows how to specify data from a buffer view
to GL. But, this is still not sufficient as the buffer bindings are not known
until after program link time.

To solve this issue, Impeller queries all active uniforms after program link
time using `glGet` with `GL_ACTIVE_UNIFORMS`. It then iterates over these
uniforms and notes their location using `glGetUniformLocation`. This uniform
location in the program is mapped to the reflection engine's notion of the
uniform location in the pipeline. This mapping is maintained in the pipeline
state generated once during the Impeller runtime setup. In this way, even though
there is no explicit notion of a pipeline state object in OpenGL ES, Impeller
still maintains one for this backend.

Since all commands in the command stream reference the pipeline state object
associated with the command, the render pass implementation in the OpenGL ES 2
backend can access the uniform bindings map and use that to bind uniforms using
the pointer to metadata already located next to the commands' uniform buffer
views.

And that’s it. This is convoluted in its implementation, but the higher levels
of the tech stack don’t have to care about not having access to uniform buffer
objects. Moreover, all the reflection happens offline and reflection information
is specified in the command stream via just a pointer. The uniform bindings map
is also generated just once during the setup of the faux pipeline state object.
This makes the whole scheme extremely low overhead. Moreover, in a modern
backend with uniform buffers, this mechanism is entirely irrelevant.
