OpenGL resource abstractions
Met.3D provides several abstractions over common OpenGL resource types.
These abstractions implement MAbstractGPUDataItem and can therefore be stored and lifetime-managed by OpenGL Resource management.
This page describes the currently available resource abstractions.
Each abstraction owns its corresponding OpenGL object.
The object is created during construction and automatically destroyed when the wrapper is deleted.
The implementations can be found in src/gxfw/gl.
Overview:
Resource |
Wrapper |
Purpose |
|---|---|---|
|
Textures objects |
|
|
Vertex attributes |
|
|
Element indices |
|
|
Uniform block data |
|
|
Large read / write GPU buffers |
|
Textures
OpenGL textures are implemented in GL::MTexture.
It allows you to create a texture of a certain size and format.
Though the constructor only creates the texture object, it does not yet allocate any memory.
The dimensions are only stored for resource management purposes until storage is allocated.
The texture can be bound to a texture unit for rendering via bindToTextureUnit(GLuint).
Additionally, bindToLastTextureUnit() can be used to bind to the last valid texture unit.
Image uploads, texture parameter configuration, and mipmap generation are performed using the standard OpenGL API.
A typical texture creation looks like the following:
1GLint width = 128;
2GLint height = 128;
3MTexture *texture = new MTexture("request_key", GL_TEXTURE_2D, GL_RGB8, width, height);
4
5// Register texture with resource management
6// ...
7
8// Configure texture parameters
9texture->bindToLastTextureUnit();
10
11glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
12glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
13glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
14glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
15
16// Upload texture data
17glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height,
18 0, GL_RGB, GL_UNSIGNED_BYTE, imgData);
Vertex buffers
Vertex buffers are a type of buffer that store vertex attributes, such as positions.
They are implemented as MVertexBuffer and MTypedVertexBuffer<Data, Type, Num> and its typedefs.
Data is the C++ data type that is stored in the vertex buffer, while Type is the OpenGL primitive type that it maps to.
Meanwhile, Num specifies how many values of OpenGL type Type are stored in one Data element.
Several typedefs are provided for common vertex buffer types, such as a MVector3DVertexBuffer for QVector3D.
While MVertexBuffer is the abstract base class for vertex buffers, providing ways to bind the vertex buffer,
the MTypedVertexBuffer<T> provides typed methods to reallocate, upload and update the vertex buffer.
It also provides information to the resource manager about the size of the vertex buffer.
When the number of vertices changes, call reallocate(...).
If only the contents change, while the size stays constant, call upload(...).
To only update a part of the buffer instead of the whole range, call update(...).
Generally prefer to use the typed vertex buffer implementation when storing references to vertex buffers you create. The abstract base class can be used when you do not know what kind of vertex buffer will be referenced by it.
The typed vertex buffer is typically used as follows:
1GLint numVertices = ...;
2auto *vbo = new MFloatVertexBuffer("request_key", numVertices); // Typedef for float typed vertex buffers
3
4// Register vbo with resource management
5// ...
6
7// Upload data
8QVector<float> vertexData = ...;
9vbo->upload(vertexData);
10
11// ...
12
13// Bind vbo before rendering at attribute location 0
14vbo->attachToVertexAttribute(0);
15
16// Draw
17// ...
Index buffers
Index buffers are used to store indices into vertex attributes to optimize drawing.
They are implemented as MIndexBuffer, which currently only allows storing of indices of type GLuint.
Index buffers only provide an upload() method. Calling it always reallocates the buffer, before uploading the supplied indices.
The amount of indices stored can be queried via count().
To bind the index buffer before drawing, call bindToElementArrayBuffer().
1GLint numIndices = ...;
2auto *ibo = new MIndexBuffer("request_key", numIndices);
3
4// Register ibo with resource management
5// ...
6
7// Upload indices
8QVector<GLuint> indices = ...;
9ibo->upload(indices);
10
11// ...
12
13// Bind index buffer before rendering
14ibo->bindToElementArrayBuffer();
15
16// Draw index buffer
17// ...
18glDrawElements(GL_TRIANGLES, ibo->count(), GL_UNSIGNED_INT, nullptr);
Uniform buffers
Uniform buffer objects are implemented as MUniformBufferObject<T>.
Uniform buffer objects allow related uniforms to be stored in a single buffer and updated with one upload operation.
It is templated based on the type that represents the structure of the uniform buffer.
Typically, you would define a struct on the C++ side that mirrors the byte layout of the GLSL side.
Use uploadData(...) to upload an instance of T to the uniform buffer.
To only update single members, use updateSubData<M>(...) where M is the type of the struct member.
Once ready for rendering, bind the uniform buffer via bind(GLuint).
A uniform buffer stays bound to the same binding location until another uniform buffer is bound to it in the same OpenGL context.
That makes them especially useful for global uniforms, like the scenes MVP matrix.
Typically, lower bindings are changed less frequently and are used for global data such as scene related uniforms.
When declaring a struct backing the uniform buffer, ensure that the C++ struct matches the shaders std140 layout rules.
More information can be found in the OpenGL 4.3 core specification on page 124.
1struct Buffer
2{
3 alignas(4) float someData; // alignas() is used to align the members according to the OpenGL specification.
4 alignas(16) float mvpMatrix[16];
5};
6
7// ...
8
9auto *ubo = new MUniformBufferObject<Buffer>("request_key");
10
11// Register ubo with resource management
12// ...
13
14// Create data to upload
15Buffer data;
16// ...
17
18ubo->uploadData(data, GL_STATIC_DRAW);
19
20// ...
21
22// Bind ubo before rendering
23ubo->bind(0);
24
25// Draw
26// ...
1#version 430
2
3// Buffer uses memory layout std140, with row-major matrices and bind location 0.
4layout (std140, row_major, binding=0) uniform Buffer
5{
6 float someData;
7 mat4 mvpMatrix;
8} buffer;
Shader storage buffers
Shader storage buffers are also buffer-backed objects in OpenGL, similar to uniform buffers.
They are implemented in MShaderStorageBufferObject.
Unlike uniform buffers, shader storage buffers are treated as raw byte storage and therefore only require an element size and element count.
If the amount of items in the shader storage buffer changes, you can call updateSize(GLuint) with the new element count.
To upload data, call upload(...).
To bind the buffer, use bindToIndex(GLuint) with the binding location of the shader storage buffer.
Note
Both uniform buffers and shader storage buffers are bound to certain binding locations. These are not shared between both buffer types, meaning a uniform buffer bound to 0 will not overwrite a shader storage buffer also bound to 0.
1// Create an SSBO with 100 floating point values.
2auto *ssbo = new MShaderStorageBufferObject("request_key", sizeof(float), 100);
3
4// Register SSBO with resource management
5// ...
6
7float data[100] = ...;
8ssbo->upload(data, GL_STATIC_DRAW);
9
10// ...
11
12// Bind SSBO before rendering
13ssbo->bindToIndex(0);
14
15// Draw
16// ...
1// Data uses memory layout 430 and shader storage buffer binding location 0
2layout (std430, binding=0) buffer Data
3{
4 float data[];
5};
Shader effects
Shader effects wrap one compiled GLFX effect file and exposes its shader programs.
They do map to basic OpenGL shader progams under the hood.
They are implemented in MShaderEffect.
See Shaders in Met.3D for writing and compiling shader effects.