def get_num_active(self, variable_type: gl.GLenum) -> int: """Get the number of active variables of the passed GL type. variable_type can be GL_ACTIVE_ATTRIBUTES, GL_ACTIVE_UNIFORMS, etc. """ num_active = gl.GLint(0) gl.glGetProgramiv(self.prog_id, variable_type, byref(num_active)) return num_active.value
def primitives_generated(self) -> int: """ How many primitives a vertex or geometry shader processed :type: int """ value = gl.GLint() gl.glGetQueryObjectiv(self._glo_primitives_generated, gl.GL_QUERY_RESULT, value) return value.value
def time_elapsed(self) -> int: """ The time elapsed in nanoseconds :type: int """ value = gl.GLint() gl.glGetQueryObjectiv(self._glo_time_elapsed, gl.GL_QUERY_RESULT, value) return value.value
def samples_passed(self) -> int: """ How many samples was written. These are per component (RGBA) :type: int """ value = gl.GLint() gl.glGetQueryObjectiv(self._glo_samples_passed, gl.GL_QUERY_RESULT, value) return value.value
def _introspect_attributes(self): """Introspect and store detailed info about an attribute""" # TODO: Ensure gl_* attributes are ignored num_attrs = gl.GLint() gl.glGetProgramiv(self._glo, gl.GL_ACTIVE_ATTRIBUTES, num_attrs) num_varyings = gl.GLint() gl.glGetProgramiv(self._glo, gl.GL_TRANSFORM_FEEDBACK_VARYINGS, num_varyings) # print(f"attrs {num_attrs.value} varyings={num_varyings.value}") for i in range(num_attrs.value): c_name = create_string_buffer(256) c_size = gl.GLint() c_type = gl.GLenum() gl.glGetActiveAttrib( self._glo, # program to query i, # index (not the same as location) 256, # max attr name size None, # c_length, # length of name c_size, # size of attribute (array or not) c_type, # attribute type (enum) c_name, # name buffer ) # Get the actual location. Do not trust the original order location = gl.glGetAttribLocation(self._glo, c_name) # print(c_name.value, c_size, c_type) type_info = GLTypes.get(c_type.value) # print(type_info) self._attributes.append( AttribFormat( c_name.value.decode(), type_info.gl_type, type_info.components, type_info.gl_size, location=location, )) # The attribute key is used to cache VertexArrays self.attribute_key = ":".join( f"{attr.name}[{attr.gl_type}/{attr.components}]" for attr in self._attributes)
def _introspect_uniform_blocks(self): active_uniform_blocks = gl.GLint(0) gl.glGetProgramiv(self._glo, gl.GL_ACTIVE_UNIFORM_BLOCKS, byref(active_uniform_blocks)) # print('GL_ACTIVE_UNIFORM_BLOCKS', active_uniform_blocks) for loc in range(active_uniform_blocks.value): index, size, name = self._query_uniform_block(loc) block = UniformBlock(self._glo, index, size, name) self._uniforms[name] = block
def link_program(program): gl.glLinkProgram(program) length = gl.GLint(0) gl.glGetProgramiv(program, gl.GL_INFO_LOG_LENGTH, ctypes.byref(length)) log_buffer = ctypes.create_string_buffer(length.value) gl.glGetProgramInfoLog(program, length, None, log_buffer) for line in log_buffer.value[:length.value].decode('ascii').splitlines(): print('GLSL: ' + line)
def _query_uniform_block(self, location: int) -> Tuple[int, int, str]: """Query active uniform block by retrieving the name and index and size""" # Query name u_size = gl.GLint() buf_size = 192 # max uniform character length u_name = create_string_buffer(buf_size) gl.glGetActiveUniformBlockName( self._glo, # program to query location, # location to query 256, # max size if the name u_size, # length u_name, ) # Query index index = gl.glGetUniformBlockIndex(self._glo, u_name) # Query size b_size = gl.GLint() gl.glGetActiveUniformBlockiv(self._glo, index, gl.GL_UNIFORM_BLOCK_DATA_SIZE, b_size) return index, b_size.value, u_name.value.decode()
def link_program(program_name): ''' link a glsl program and print error messages. ''' gl.glLinkProgram(program_name) success = gl.GLint(0) gl.glGetProgramiv(program_name, gl.GL_LINK_STATUS, ctypes.byref(success)) length = gl.GLint(0) gl.glGetProgramiv(program_name, gl.GL_INFO_LOG_LENGTH, ctypes.byref(length)) log_buffer = ctypes.create_string_buffer(length.value) gl.glGetProgramInfoLog(program_name, length, None, log_buffer) log_message = log_buffer.value[:length.value].decode('ascii').strip() if log_message: sys.stderr.write(log_message + '\n') if not success: raise ValueError('Linking of the shader program failed.')
def link(self): """ Main steps to link the program: 1. glLinkProgram: linke the shaders in the program to create an executable 2. glGetProgramiv: retrieve the link status 3. glGetProgramInfoLog: print the error log if link failed """ gl.glLinkProgram(self.program) link_status = gl.GLint(0) gl.glGetProgramiv(self.program, gl.GL_LINK_STATUS, ct.byref(link_status)) if not link_status: info_length = gl.GLint(0) gl.glGetProgramiv(self.program, gl.GL_INFO_LOG_LENGTH, ct.byref(info_length)) error_info = ct.create_string_buffer(info_length.value) gl.glGetProgramInfoLog(self.program, info_length, None, error_info) print(error_info.value)
def draw(self): previous_draw_mode = gl.GLint(0) gl.glGetIntegerv(gl.GL_POLYGON_MODE, byref(previous_draw_mode)) gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE) self.shader.use() gl.glBindVertexArray(self.array) # gl.glDrawElements(gl.GL_TRIANGLES, self.width * self.height * 6, gl.GL_UNSIGNED_INT, None) gl.glDrawElementsInstanced(gl.GL_TRIANGLES, Chunk.cells * 6, gl.GL_UNSIGNED_INT, None, self.instances) gl.glBindVertexArray(0) self.shader.clear() gl.glPolygonMode(gl.GL_FRONT_AND_BACK, previous_draw_mode.value)
def link(self): if self._shaders_empty: raise RuntimeError("No shaders attached to program") gl.glLinkProgram(self.gl_id) rc = gl.GLint(0) gl.glGetProgramiv(self.gl_id, gl.GL_LINK_STATUS, ctypes.byref(rc)) if not rc: gl.glGetProgramiv(self.gl_id, gl.GL_INFO_LOG_LENGTH, ctypes.byref(rc)) buffer = ctypes.create_string_buffer(rc.value) gl.glGetProgramInfoLog(self.gl_id, rc, None, buffer) raise GLObjectException(buffer.value.decode()) else: self._done_link = True
def compile_shader(shader_type, shader_source): shader_name = gl.glCreateShader(shader_type) src_buffer = ctypes.create_string_buffer(shader_source) buf_pointer = ctypes.cast(ctypes.pointer(ctypes.pointer(src_buffer)), ctypes.POINTER(ctypes.POINTER(ctypes.c_char))) length = ctypes.c_int(len(shader_source) + 1) gl.glShaderSource(shader_name, 1, buf_pointer, ctypes.byref(length)) gl.glCompileShader(shader_name) success = gl.GLint(0) gl.glGetShaderiv(shader_name, gl.GL_COMPILE_STATUS, ctypes.byref(success)) length = gl.GLint(0) gl.glGetShaderiv(shader_name, gl.GL_INFO_LOG_LENGTH, ctypes.byref(length)) log_buffer = ctypes.create_string_buffer(length.value) gl.glGetShaderInfoLog(shader_name, length, None, log_buffer) for line in log_buffer.value[:length.value].decode('ascii').splitlines(): print('GLSL: ' + line) assert success, 'Compiling of the shader failed.' return shader_name
def query_uniform(self, index: int) -> Tuple[str, int, int]: """Retrieve Uniform information at given location. Returns the name, the type as a GLenum (GL_FLOAT, ...) and the size. Size is greater than 1 only for Uniform arrays, like an array of floats or an array of Matrices. """ usize = gl.GLint() utype = gl.GLenum() buf_size = 192 uname = create_string_buffer(buf_size) gl.glGetActiveUniform(self.prog_id, index, buf_size, None, usize, utype, uname) return uname.value.decode(), utype.value, usize.value
def _log_error(self): log_length = gl.GLint(0) gl.glGetShaderiv(self.name, gl.GL_INFO_LOG_LENGTH, byref(log_length)) log_buffer = create_string_buffer(log_length.value) gl.glGetShaderInfoLog(self.name, log_length.value, None, log_buffer) self.logger.error("Error compiling GLSL (type %s) shader!", self.kind) self.logger.error("---Shader---") self.logger.error("\n".join( f"{i+1: 3d}: {line}" for i, line in enumerate( self.source.decode("ascii").splitlines()))) self.logger.error("---Message---") for line in log_buffer.value[:log_length.value].decode( 'ascii').splitlines(): self.logger.error('GLSL: ' + line) self.logger.error("------")
def useFBO(fbo): """Context manager for Framebuffer Object bindings. This function yields the framebuffer name as an integer. Parameters ---------- fbo :obj:`int` or :obj:`Framebuffer` OpenGL Framebuffer Object name/ID or descriptor. Yields ------- int OpenGL name of the framebuffer bound in the context. Returns ------- None Examples -------- # FBO bound somewhere deep in our code GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, someOtherFBO) ... # create a new FBO, but we have no idea what the currently bound FBO is fbo = createFBO() # use a context to bind attachments with bindFBO(fbo): attach(GL.GL_COLOR_ATTACHMENT0, colorTex) attach(GL.GL_DEPTH_ATTACHMENT, depthRb) attach(GL.GL_STENCIL_ATTACHMENT, depthRb) isComplete = gltools.isComplete() # someOtherFBO is still bound! """ prevFBO = GL.GLint() GL.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, ctypes.byref(prevFBO)) toBind = fbo.id if isinstance(fbo, Framebuffer) else int(fbo) GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, toBind) try: yield toBind finally: GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, prevFBO.value)
def getIntegerv(parName): """Get a single integer parameter value, return it as a Python integer. Parameters ---------- pName : :obj:`int' OpenGL property enum to query (e.g. GL_MAJOR_VERSION). Returns ------- int """ val = GL.GLint() GL.glGetIntegerv(parName, val) return int(val.value)
def __init__(self, source_file: str = None, source: str = None): self.name = gl.glCreateShader(self.kind) if source_file: self.source = open(source_file, "rb").read() else: self.source = source src_buffer = create_string_buffer(self.source) buf_pointer = cast(pointer(pointer(src_buffer)), POINTER(POINTER(c_char))) gl.glShaderSource(self.name, 1, buf_pointer, None) gl.glCompileShader(self.name) success = gl.GLint(0) gl.glGetShaderiv(self.name, gl.GL_COMPILE_STATUS, byref(success)) if not success.value: self._log_error() raise RuntimeError('Compiling of the shader failed.')
def _query_uniform(self, location: int) -> Tuple[str, int, int]: """Retrieve Uniform information at given location. Returns the name, the type as a GLenum (GL_FLOAT, ...) and the size. Size is greater than 1 only for Uniform arrays, like an array of floats or an array of Matrices. """ u_size = gl.GLint() u_type = gl.GLenum() buf_size = 192 # max uniform character length u_name = create_string_buffer(buf_size) gl.glGetActiveUniform( self._glo, # program to query location, # location to query buf_size, # size of the character/name buffer None, # the number of characters actually written by OpenGL in the string u_size, # size of the uniform variable u_type, # data type of the uniform variable u_name, # string buffer for storing the name ) return u_name.value.decode(), u_type.value, u_size.value
def refresh_font_texture(self): # save texture state last_texture = gl.GLint() gl.glGetIntegerv(gl.GL_TEXTURE_BINDING_2D, byref(last_texture)) width, height, pixels = self.io.fonts.get_tex_data_as_rgba32() if self._font_texture is not None: gl.glDeleteTextures(1, self._font_texture) self._font_texture = gl.GLuint() gl.glGenTextures(1, byref(self._font_texture)) gl.glBindTexture(gl.GL_TEXTURE_2D, self._font_texture) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixels) self.io.fonts.texture_id = self._font_texture gl.glBindTexture(gl.GL_TEXTURE_2D, cast((c_int*1)(last_texture), POINTER(c_uint)).contents) self.io.fonts.clear_tex_data()
def _introspect_uniforms(self): """Figure out what uniforms are available and build an internal map""" # Number of active uniforms in the program active_uniforms = gl.GLint(0) gl.glGetProgramiv(self._glo, gl.GL_ACTIVE_UNIFORMS, byref(active_uniforms)) # Loop all the active uniforms for index in range(active_uniforms.value): # Query uniform information like name, type, size etc. u_name, u_type, u_size = self._query_uniform(index) u_location = gl.glGetUniformLocation(self._glo, u_name.encode()) # Skip uniforms that may be in Uniform Blocks # TODO: We should handle all uniforms if u_location == -1: # print(f"Uniform {u_location} {u_name} {u_size} {u_type} skipped") continue u_name = u_name.replace("[0]", "") # Remove array suffix self._uniforms[u_name] = Uniform(self._glo, u_location, u_name, u_type, u_size)
def render(self, draw_data): # perf: local for faster access io = self.io display_width, display_height = io.display_size fb_width = int(display_width * io.display_fb_scale[0]) fb_height = int(display_height * io.display_fb_scale[1]) if fb_width == 0 or fb_height == 0: return draw_data.scale_clip_rects(*io.display_fb_scale) # backup GL state # todo: provide cleaner version of this backup-restore code last_program = gl.GLint() gl.glGetIntegerv(gl.GL_CURRENT_PROGRAM, byref(last_program)) last_texture = gl.GLint() gl.glGetIntegerv(gl.GL_TEXTURE_BINDING_2D, byref(last_texture)) last_active_texture = gl.GLint() gl.glGetIntegerv(gl.GL_ACTIVE_TEXTURE, byref(last_active_texture)) last_array_buffer = gl.GLint() gl.glGetIntegerv(gl.GL_ARRAY_BUFFER_BINDING, byref(last_array_buffer)) last_element_array_buffer = gl.GLint() gl.glGetIntegerv(gl.GL_ELEMENT_ARRAY_BUFFER_BINDING, byref(last_element_array_buffer)) last_vertex_array = gl.GLint() gl.glGetIntegerv(gl.GL_VERTEX_ARRAY_BINDING, byref(last_vertex_array)) last_blend_src = gl.GLint() gl.glGetIntegerv(gl.GL_BLEND_SRC, byref(last_blend_src)) last_blend_dst = gl.GLint() gl.glGetIntegerv(gl.GL_BLEND_DST, byref(last_blend_dst)) last_blend_equation_rgb = gl.GLint() gl.glGetIntegerv(gl.GL_BLEND_EQUATION_RGB, byref(last_blend_equation_rgb)) last_blend_equation_alpha = gl.GLint() gl.glGetIntegerv(gl.GL_BLEND_EQUATION_ALPHA, byref(last_blend_equation_alpha)) last_viewport = (gl.GLint*4)() gl.glGetIntegerv(gl.GL_VIEWPORT, last_viewport) last_scissor_box = (gl.GLint*4)() gl.glGetIntegerv(gl.GL_SCISSOR_BOX, last_scissor_box) last_enable_blend = gl.GLint() gl.glIsEnabled(gl.GL_BLEND, byref(last_enable_blend)) last_enable_cull_face = gl.GLint() gl.glIsEnabled(gl.GL_CULL_FACE, byref(last_enable_cull_face)) last_enable_depth_test = gl.GLint() gl.glIsEnabled(gl.GL_DEPTH_TEST, byref(last_enable_depth_test)) last_enable_scissor_test = gl.GLint() gl.glIsEnabled(gl.GL_SCISSOR_TEST, byref(last_enable_scissor_test)) gl.glEnable(gl.GL_BLEND) gl.glBlendEquation(gl.GL_FUNC_ADD) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) gl.glDisable(gl.GL_CULL_FACE) gl.glDisable(gl.GL_DEPTH_TEST) gl.glEnable(gl.GL_SCISSOR_TEST) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glViewport(0, 0, int(fb_width), int(fb_height)) ortho_projection = [ 2.0/display_width, 0.0, 0.0, 0.0, 0.0, 2.0/-display_height, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0 ] gl.glUseProgram(self._shader_handle) gl.glUniform1i(self._attrib_location_tex, 0) gl.glUniformMatrix4fv(self._attrib_proj_mtx, 1, gl.GL_FALSE, (gl.GLfloat * 16)(*ortho_projection)) gl.glBindVertexArray(self._vao_handle) for commands in draw_data.commands_lists: idx_buffer_offset = 0 gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._vbo_handle) # todo: check this (sizes) gl.glBufferData(gl.GL_ARRAY_BUFFER, commands.vtx_buffer_size * imgui.VERTEX_SIZE, c_void_p(commands.vtx_buffer_data), gl.GL_STREAM_DRAW) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self._elements_handle) # todo: check this (sizes) gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, commands.idx_buffer_size * imgui.INDEX_SIZE, c_void_p(commands.idx_buffer_data), gl.GL_STREAM_DRAW) # todo: allow to iterate over _CmdList for command in commands.commands: gl.glBindTexture(gl.GL_TEXTURE_2D, command.texture_id) # todo: use named tuple x, y, z, w = command.clip_rect gl.glScissor(int(x), int(fb_height - w), int(z - x), int(w - y)) if imgui.INDEX_SIZE == 2: gltype = gl.GL_UNSIGNED_SHORT else: gltype = gl.GL_UNSIGNED_INT gl.glDrawElements(gl.GL_TRIANGLES, command.elem_count, gltype, c_void_p(idx_buffer_offset)) idx_buffer_offset += command.elem_count * imgui.INDEX_SIZE # restore modified GL state gl.glUseProgram(cast((c_int*1)(last_program), POINTER(c_uint)).contents) gl.glActiveTexture(cast((c_int*1)(last_active_texture), POINTER(c_uint)).contents) gl.glBindTexture(gl.GL_TEXTURE_2D, cast((c_int*1)(last_texture), POINTER(c_uint)).contents) gl.glBindVertexArray(cast((c_int*1)(last_vertex_array), POINTER(c_uint)).contents) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, cast((c_int*1)(last_array_buffer), POINTER(c_uint)).contents) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, cast((c_int*1)(last_element_array_buffer), POINTER(c_uint)).contents) gl.glBlendEquationSeparate(cast((c_int*1)(last_blend_equation_rgb), POINTER(c_uint)).contents, cast((c_int*1)(last_blend_equation_alpha), POINTER(c_uint)).contents) gl.glBlendFunc(cast((c_int*1)(last_blend_src), POINTER(c_uint)).contents, cast((c_int*1)(last_blend_dst), POINTER(c_uint)).contents) if last_enable_blend: gl.glEnable(gl.GL_BLEND) else: gl.glDisable(gl.GL_BLEND) if last_enable_cull_face: gl.glEnable(gl.GL_CULL_FACE) else: gl.glDisable(gl.GL_CULL_FACE) if last_enable_depth_test: gl.glEnable(gl.GL_DEPTH_TEST) else: gl.glDisable(gl.GL_DEPTH_TEST) if last_enable_scissor_test: gl.glEnable(gl.GL_SCISSOR_TEST) else: gl.glDisable(gl.GL_SCISSOR_TEST) gl.glViewport(last_viewport[0], last_viewport[1], last_viewport[2], last_viewport[3]) gl.glScissor(last_scissor_box[0], last_scissor_box[1], last_scissor_box[2], last_scissor_box[3])
def binding(self) -> int: """int: Get or set the binding point for this uniform block""" binding = gl.GLint() gl.glGetActiveUniformBlockiv(self.glo, self.index, gl.GL_UNIFORM_BLOCK_BINDING, binding) return binding.value
def get_max_texture_size(): max_texture_size = gl.GLint() gl.glGetIntegerv(gl.GL_MAX_TEXTURE_SIZE, byref(max_texture_size)) return max_texture_size.value
def __init__( self, ctx: "Context", *, vertex_shader: str, fragment_shader: str = None, geometry_shader: str = None, tess_control_shader: str = None, tess_evaluation_shader: str = None, out_attributes: List[str] = None, ): """Create a Program. :param Context ctx: The context this program belongs to :param str vertex_shader: vertex shader source :param str fragment_shader: fragment shader source :param str geometry_shader: geometry shader source :param str tess_control_shader: tessellation control shader source :param str tess_evaluation_shader: tessellation evaluation shader source :param List[str] out_attributes: List of out attributes used in transform feedback. """ self._ctx = ctx self._glo = glo = gl.glCreateProgram() self._out_attributes = out_attributes or [] self._geometry_info = (0, 0, 0) self._attributes = [] # type: List[AttribFormat] #: Internal cache key used with vertex arrays self.attribute_key = "INVALID" # type: str self._uniforms: Dict[str, Uniform] = {} shaders = [(vertex_shader, gl.GL_VERTEX_SHADER)] if fragment_shader: shaders.append((fragment_shader, gl.GL_FRAGMENT_SHADER)) if geometry_shader: shaders.append((geometry_shader, gl.GL_GEOMETRY_SHADER)) if tess_control_shader: shaders.append((tess_control_shader, gl.GL_TESS_CONTROL_SHADER)) if tess_evaluation_shader: shaders.append( (tess_evaluation_shader, gl.GL_TESS_EVALUATION_SHADER)) shaders_id = [] for shader_code, shader_type in shaders: shader = Program.compile_shader(shader_code, shader_type) gl.glAttachShader(self._glo, shader) shaders_id.append(shader) # For now we assume varyings can be set up if no fragment shader if not fragment_shader: self._setup_out_attributes() Program.link(self._glo) if geometry_shader: geometry_in = gl.GLint() geometry_out = gl.GLint() geometry_vertices = gl.GLint() gl.glGetProgramiv(self._glo, gl.GL_GEOMETRY_INPUT_TYPE, geometry_in) gl.glGetProgramiv(self._glo, gl.GL_GEOMETRY_OUTPUT_TYPE, geometry_out) gl.glGetProgramiv(self._glo, gl.GL_GEOMETRY_VERTICES_OUT, geometry_vertices) self._geometry_info = ( geometry_in.value, geometry_out.value, geometry_vertices.value, ) # Flag shaders for deletion. Will only be deleted once detached from program. for shader in shaders_id: gl.glDeleteShader(shader) # Handle uniforms self._introspect_attributes() self._introspect_uniforms() self._introspect_uniform_blocks() self.ctx.stats.incr("program") weakref.finalize(self, Program._delete, self.ctx, shaders_id, glo)
def get_max_color_attachments(): """Get the maximum allow Framebuffer Color attachements""" number = gl.GLint() gl.glGetIntegerv(gl.GL_MAX_COLOR_ATTACHMENTS, number) return number.value