from typing import TYPE_CHECKING
import weakref
from pyglet import gl
if TYPE_CHECKING:
from arcade.gl import Context
[docs]class Query:
"""
A query object to perform low level measurements of OpenGL rendering calls.
The best way to create a program instance is through :py:meth:`arcade.gl.Context.query`
Example usage::
query = ctx.query()
with query:
geometry.render(..)
print('samples_passed:', query.samples_passed)
print('time_elapsed:', query.time_elapsed)
print('primitives_generated:', query.primitives_generated)
"""
__slots__ = (
"_ctx",
"_glo_samples_passed",
"_glo_any_samples_passed",
"_glo_time_elapsed",
"_glo_primitives_generated",
"__weakref__",
)
def __init__(self, ctx: "Context"):
# TODO: Support querying a subset of these queries (faster)
# TODO: Evalute of this query should be included
# gl.GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
# gl.GL_ANY_SAMPLES_PASSED
self._ctx = ctx
self._glo_samples_passed = glo_samples_passed = gl.GLuint()
gl.glGenQueries(1, self._glo_samples_passed)
self._glo_time_elapsed = glo_time_elapsed = gl.GLuint()
gl.glGenQueries(1, self._glo_time_elapsed)
self._glo_primitives_generated = glo_time_elapsed = gl.GLuint()
gl.glGenQueries(1, self._glo_primitives_generated)
glos = [glo_samples_passed, glo_time_elapsed, glo_time_elapsed]
self.ctx.stats.incr("query")
if self._ctx.gc_mode == "auto":
weakref.finalize(self, Query.delete_glo, self._ctx, glos)
def __del__(self):
if self._ctx.gc_mode == "context_gc":
self._ctx.objects.append(self)
@property
def ctx(self) -> "Context":
"""
The context this query object belongs to
:type: :py:class:`arcade.gl.Context`
"""
return self._ctx
@property
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
@property
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
@property
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 __enter__(self):
gl.glBeginQuery(gl.GL_SAMPLES_PASSED, self._glo_samples_passed)
gl.glBeginQuery(gl.GL_TIME_ELAPSED, self._glo_time_elapsed)
gl.glBeginQuery(gl.GL_PRIMITIVES_GENERATED, self._glo_primitives_generated)
def __exit__(self, exc_type, exc_val, exc_tb):
gl.glEndQuery(gl.GL_SAMPLES_PASSED)
gl.glEndQuery(gl.GL_TIME_ELAPSED)
gl.glEndQuery(gl.GL_PRIMITIVES_GENERATED)
[docs] def delete(self):
"""
Destroy the underlying OpenGL resource.
Don't use this unless you know exactly what you are doing.
"""
Query.delete_glo(
self._ctx,
[
self._glo_samples_passed,
self._glo_time_elapsed,
self._glo_primitives_generated,
]
)
[docs] @staticmethod
def delete_glo(ctx, glos) -> None:
"""
Delete this query object. This is automatically called
when the object is garbage collected.
"""
if gl.current_context is None:
return
for glo in glos:
gl.glDeleteQueries(1, glo)
ctx.stats.decr("query")