gamnit :: GamnitProgramFromSource
Gamnit graphical program from the shaders source codeAttribute
that does not exist or that has been optimized out
Uniform
that does not exist or that has been optimized out
ShaderVariable
instances by their name
gamnit $ GamnitProgramFromSource
Gamnit graphical program from the shaders source codeAttribute
that does not exist or that has been optimized out
Uniform
that does not exist or that has been optimized out
gamnit :: programs $ NativeGLfloatArray
An array ofGLfloat
in C (GLfloat*
)
ShaderVariable
instances by their name
Serializable::inspect
to show more useful information
serialization :: serialization_core
Abstract services to serialize Nit objects to different formatscore :: union_find
union–find algorithm using an efficient disjoint-set data structuremore_collections :: more_collections
Highly specific, but useful, collections-related classes.accept_scroll_and_zoom
gamnit :: camera_control_android
Two fingers camera manipulation, pinch to zoom and slide to scrollgamnit :: camera_control_linux
Mouse wheel and middle mouse button to control cameraEulerCamera
and App::frame_core_draw
to get a stereoscopic view
# Services for graphical programs with shaders, attributes and uniforms
module programs
import display
import matrix
private import more_collections
# `Uniform` or `Attribute` of a `GamnitProgram`
class ShaderVariable
# The `GamnitProgram` to which `self` belongs
var program: GLProgram
# Name of `self` in the `program` source
var name: String
# Location of `self` in the compiled `program`
var location: Int
# Number of elements in this array (1 for scalars and more for vectors)
var size: Int
# Is `self` an active uniform or attribute in the `program`?
#
# If `false`, the variable may have been optimized out by the compiler.
fun is_active: Bool do return true
redef fun to_s do return "<{class_name} name:{name} location:{location} size:{size}"
end
# Inactive shader variable, either optimized out or simple absent from the program
#
# Returned by `GamnitProgram::uniforms` or `GamnitProgram::attributes` when
# the uniform has not been identified as active by the driver.
# Operations on instances of this class have no effects.
#
# Act as a compatibility when a program expect a uniform to exist even in
# a context where the driver's compiler may have optimized it out.
# You must be careful when receiving an `InactiveVariable` as it may also
# silence real program errors, such type in variable name.
abstract class InactiveVariable
super ShaderVariable
redef fun is_active do return false
end
# Shader attribute
#
# It will use either the `uniform` value or the data at `array_pointer` if
# and only if `array_enabled`.
class Attribute
super ShaderVariable
private var array_enabled_cache = false
# Is the array attribute enabled?
fun array_enabled: Bool do return array_enabled_cache
# Set whether to use the data at `array_pointer` over `uniform`.
fun array_enabled=(value: Bool)
do
if not is_active then return
glUseProgram program
self.array_enabled_cache = value
if value then
glEnableVertexAttribArray location
else glDisableVertexAttribArray location
end
# Define the `array` of vertex data
fun array(array: Array[Float], data_per_vertex: Int)
do
# TODO move this and native_float_array to a subclass specific to float
if not is_active then return
var native = native_float_array
if native == null or array.length > native.length then
if native != null then native.finalize
native = new GLfloatArray.from(array)
self.native_float_array = native
else
native.fill_from(array)
end
glVertexAttribPointer(location, data_per_vertex, gl_FLOAT, false, 0, native.native_array)
end
private var native_float_array: nullable GLfloatArray = null
end
# Shader attribute of GLSL type `float`
class AttributeFloat
super Attribute
# Set the uniform value to use when the vertex array is disabled
fun uniform(x: Float) do if is_active then glVertexAttrib1f(location, x)
end
# Shader attribute of GLSL type `vec2`
class AttributeVec2
super Attribute
# Set the uniform value to use when the vertex array is disabled
fun uniform(x, y: Float) do if is_active then glVertexAttrib2f(location, x, y)
end
# Shader attribute of GLSL type `vec3`
class AttributeVec3
super Attribute
# Set the uniform value to use when the vertex array is disabled
fun uniform(x, y, z: Float) do if is_active then glVertexAttrib3f(location, x, y, z)
end
# Shader attribute of GLSL type `vec4`
class AttributeVec4
super Attribute
# Set the uniform value to use when the vertex array is disabled
fun uniform(x, y, z, w: Float) do if is_active then glVertexAttrib4f(location, x, y, z, w)
end
# `Attribute` that does not exist or that has been optimized out
class InactiveAttribute
super InactiveVariable
super AttributeFloat
super AttributeVec2
super AttributeVec3
super AttributeVec4
end
# Shader uniform
class Uniform
super ShaderVariable
private fun uniform_1i(index, x: Int) `{ glUniform1i(index, x); `}
end
# Shader uniform of GLSL type `bool`
class UniformBool
super Uniform
# Set this uniform value
fun uniform(val: Bool) do uniform_1i(location, if val then 1 else 0)
end
# Shader uniform of GLSL type `int`
class UniformInt
super Uniform
# Set this uniform value
fun uniform(val: Int) do uniform_1i(location, val)
end
# Shader uniform of GLSL type `vec4`
class UniformFloat
super Uniform
# Set this uniform value
fun uniform(x: Float) do glUniform1f(location, x)
end
# Shader uniform of GLSL type `vec4`
class UniformVec2
super Uniform
# Set this uniform value
fun uniform(x, y: Float) do glUniform2f(location, x, y)
end
# Shader uniform of GLSL type `vec4`
class UniformVec3
super Uniform
# Set this uniform value
fun uniform(x, y, z: Float) do glUniform3f(location, x, y, z)
end
# Shader uniform of GLSL type `vec4`
class UniformVec4
super Uniform
# Set this uniform value
fun uniform(x, y, z, w: Float) do glUniform4f(location, x, y, z, w)
end
# Shader uniform of GLSL type `sampler2D`
class UniformSampler2D
super Uniform
# Set this uniform value
fun uniform(val: Int) do uniform_1i(location, val)
end
# Shader uniform of GLSL type `mat4`
class UniformMat4
super Uniform
private var native_matrix_cache: nullable NativeGLfloatArray = null
# Set this uniform value
fun uniform(matrix: Matrix)
do
var native = native_matrix_cache
if native == null then
native = new NativeGLfloatArray.matrix
self.native_matrix_cache = native
end
matrix.fill_native(native)
uniform_matrix_4f(location, 1, false, native)
end
private fun uniform_matrix_4f(index, count: Int, transpose: Bool, data: NativeGLfloatArray) `{
glUniformMatrix4fv(index, count, transpose, data);
`}
end
# `Uniform` that does not exist or that has been optimized out
class InactiveUniform
super InactiveVariable
super UniformBool
super UniformInt
super UniformFloat
super UniformSampler2D
super UniformVec2
super UniformVec3
super UniformVec4
super UniformMat4
redef fun is_active do return false
end
# Gamnit shader
abstract class Shader
# TODO add alternative init to load shaders from binary
# Index of this shader in OpenGL
private var gl_shader: GLShader is noinit
# Latest error raised by operations of this shader
var error: nullable Error = null
# Source code of this shader
var source: Text
# Low-level type of this shader
private fun gl_shader_type: GLShaderType is abstract
# Compile this shader and report any errors in the attribute `error`
fun compile
do
# Create
var gl_shader = glCreateShader(gl_shader_type)
if not glIsShader(gl_shader) then
self.error = new Error("Shader creation failed: {glGetError}")
return
end
self.gl_shader = gl_shader
glShaderSource(gl_shader, source.to_cstring)
# Compile
glCompileShader gl_shader
if not gl_shader.is_compiled then
self.error = new Error("Shader compilation failed: {glGetShaderInfoLog(gl_shader)}")
return
end
error = gammit_gl_error
end
# Has this shader been deleted?
var deleted = false
# Delete this shader and free its resources
fun delete
do
if deleted then return
glDeleteShader gl_shader
deleted = true
end
end
# Gamnit vertex shader
class VertexShader
super Shader
redef fun gl_shader_type do return gl_VERTEX_SHADER
end
# Gamnit fragment shader
class FragmentShader
super Shader
redef fun gl_shader_type do return gl_FRAGMENT_SHADER
end
# Gamnit graphical program
#
# Subclasses should implement both `vertex_shader` and `fragment_shader`.
abstract class GamnitProgram
# Vertex shader to attach to this program
fun vertex_shader: VertexShader is abstract
# Fragment shader to attach to this program
fun fragment_shader: FragmentShader is abstract
# Index to the OpenGL ES program, set by `compile_and_link`
private var gl_program: nullable GLProgram = null
# Last error raised by `compile_and_link`
var error: nullable Error = null is protected writable
# Compile the shaders, and this program, then link and report any errors
fun compile_and_link
do
# Get an index
var gl_program = glCreateProgram
if not glIsProgram(gl_program) then
self.error = new Error("Program creation failed: {glGetError.to_s}")
return
end
self.gl_program = gl_program
# Vertex shader
var vertex_shader = vertex_shader
vertex_shader.compile
if vertex_shader.error != null then
self.error = vertex_shader.error
return
end
# Fragment shader
var fragment_shader = fragment_shader
fragment_shader.compile
if fragment_shader.error != null then
self.error = fragment_shader.error
return
end
# Attach shaders
glAttachShader(gl_program, vertex_shader.gl_shader)
glAttachShader(gl_program, fragment_shader.gl_shader)
# Catch any errors up to here
var error = gammit_gl_error
if error != null then
self.error = error
return
end
# Link
glLinkProgram gl_program
if not gl_program.is_linked then
self.error = new Error("Linking failed: {glGetProgramInfoLog(gl_program)}")
return
end
# Fill the attribute and uniform lists
var n_attribs = glGetProgramiv(gl_program, gl_ACTIVE_ATTRIBUTES)
for a in [0..n_attribs[ do
var name = gl_program.active_attrib_name(a)
var size = gl_program.active_attrib_size(a)
var typ = gl_program.active_attrib_type(a)
var location = gl_program.attrib_location(name)
# FIXME location may be invalid at this point because
# attrib_location does not work with truncated names,
# as returned by `active_attrib_name`.
var attribute
if typ == gl_FLOAT then
attribute = new AttributeFloat(gl_program, name, location, size)
else if typ == gl_FLOAT_VEC2 then
attribute = new AttributeVec2(gl_program, name, location, size)
else if typ == gl_FLOAT_VEC3 then
attribute = new AttributeVec3(gl_program, name, location, size)
else if typ == gl_FLOAT_VEC4 then
attribute = new AttributeVec4(gl_program, name, location, size)
else
attribute = new Attribute(gl_program, name, location, size)
end
# TODO missing types
attributes[name] = attribute
end
var n_uniforms = glGetProgramiv(gl_program, gl_ACTIVE_UNIFORMS)
for a in [0..n_uniforms[ do
var name = gl_program.active_uniform_name(a)
var size = gl_program.active_uniform_size(a)
var typ = gl_program.active_uniform_type(a)
var location = gl_program.uniform_location(name)
var uniform
if typ == gl_BOOL then
uniform = new UniformBool(gl_program, name, location, size)
else if typ == gl_INT then
uniform = new UniformInt(gl_program, name, location, size)
else if typ == gl_SAMPLER_2D then
uniform = new UniformSampler2D(gl_program, name, location, size)
else if typ == gl_FLOAT then
uniform = new UniformFloat(gl_program, name, location, size)
else if typ == gl_FLOAT_VEC2 then
uniform = new UniformVec2(gl_program, name, location, size)
else if typ == gl_FLOAT_VEC3 then
uniform = new UniformVec3(gl_program, name, location, size)
else if typ == gl_FLOAT_VEC4 then
uniform = new UniformVec4(gl_program, name, location, size)
else if typ == gl_FLOAT_MAT4 then
uniform = new UniformMat4(gl_program, name, location, size)
else
uniform = new Uniform(gl_program, name, location, size)
end
# TODO missing types
uniforms[name] = uniform
end
end
# Diagnose possible problems with the shaders of the program
#
# Lists to the console inactive uniforms and attributes.
# These may not be problematic but they can help to debug the program.
fun diagnose
do
if gl_program == null then compile_and_link
print "# Diagnose {class_name}"
for k,v in uniforms do
if not v.is_active then print "* Uniform {v.name} is inactive"
end
for k,v in attributes do
if not v.is_active then print "* Attribute {v.name} is inactive"
end
end
# Attributes of this program organized by name
#
# Active attributes are gathered at `compile_and_link`.
# Upon request, inactive attributes are returned as a new `InactiveAttribute`.
var attributes = new AttributeMap(self)
# Uniforms of this program organized by name
#
# Active uniforms are gathered at `compile_and_link`.
# Upon request, inactive attributes are returned as a new `InactiveUniform`.
var uniforms = new UniformMap(self)
# Notify the GPU to use this program
fun use
do
var gl_program = gl_program
assert gl_program != null # TODO error not compiled, or compile it
glUseProgram gl_program
end
# Has this program been deleted?
var deleted = false
# Delete this program if it has not already been deleted
fun delete
do
if deleted then return
var gl_program = gl_program
if gl_program != null then glDeleteProgram gl_program
deleted = true
end
end
# Gamnit graphical program from the shaders source code
class GamnitProgramFromSource
super GamnitProgram
# Source code of the vertex shader
fun vertex_shader_source: Text is abstract
redef var vertex_shader = new VertexShader(vertex_shader_source) is lazy
# Source code of the fragment shader
fun fragment_shader_source: Text is abstract
redef var fragment_shader = new FragmentShader(fragment_shader_source) is lazy
end
# Map to organize `ShaderVariable` instances by their name
abstract class ShaderVariableMap[A: ShaderVariable]
super HashMap[String, A]
private var program: GamnitProgram
redef fun [](key)
do
# Alter the user specified name to fit the truncated name
var max_len = max_name_length - 1
if key isa Text and key.length > max_len then key = key.substring(0, max_len)
return super(key)
end
private fun max_name_length: Int is abstract
end
# Map to organize `Attribute` instances by their name
class AttributeMap
super ShaderVariableMap[Attribute]
redef fun provide_default_value(key) do
return new InactiveAttribute(program.gl_program.as(not null), "", -1, 0)
end
redef fun max_name_length do return glGetProgramiv(program.gl_program.as(not null), gl_ACTIVE_ATTRIBUTE_MAX_LENGTH)
redef fun [](key)
do
# Update the location of this attribute from the user specified name
var item = super
if key isa Text then item.location = program.gl_program.attrib_location(key.to_s)
return item
end
end
# Map to organize `Uniform` instances by their name
class UniformMap
super ShaderVariableMap[Uniform]
redef fun provide_default_value(key) do
return new InactiveUniform(program.gl_program.as(not null), "", -1, 0)
end
redef fun max_name_length do return glGetProgramiv(program.gl_program.as(not null), gl_ACTIVE_UNIFORM_MAX_LENGTH)
redef fun [](key)
do
var item = super
if key isa Text then item.location = program.gl_program.uniform_location(key.to_s)
return item
end
end
redef extern class NativeGLfloatArray
# Allocate a new matrix
new matrix `{ return malloc(4*4*sizeof(GLfloat)); `}
# Overwrite this matrix with the identity matrix
fun set_identity
do
for i in [0..4[ do
for j in [0..4[ do
matrix_set(i, j, if i == j then 1.0 else 0.0)
end
end
end
# Get the element at `x, y`
fun matrix_get(x, y: Int): Float `{ return self[y*4+x]; `}
# Set the element at `x, y`
fun matrix_set(x, y: Int, val: Float) `{ self[y*4+x] = val; `}
end
redef class Matrix
# Copy content of this matrix to a `NativeGLfloatArray`
fun fill_native(native: NativeGLfloatArray)
do
for i in [0..width[ do
for j in [0..height[ do
native.matrix_set(i, j, self[i, j])
end
end
end
end
private fun gammit_gl_error: nullable Error
do
var gl_error = glGetError
if gl_error == gl_NO_ERROR then return null
return new Error("GL error: {gl_error}")
end
lib/gamnit/programs.nit:15,1--602,3