Implementation:Google deepmind Dm control Render Executor
| Knowledge Sources | |
|---|---|
| Domains | Rendering, Threading |
| Last Updated | 2026-02-15 04:00 GMT |
Overview
The render executor module implements thread-safe executors for offloading OpenGL rendering calls to the correct thread, ensuring OpenGL context thread-affinity requirements are met.
Description
This module provides an abstract BaseRenderExecutor with an execution_context() context manager and a call() method for dispatching rendering functions. Two concrete implementations handle single-threaded and multi-threaded scenarios.
PassthroughRenderExecutor executes calls directly on the calling thread with an RLock for mutual exclusion. This is suitable for single-threaded applications and backends like GLFW that require main-thread usage. OffloadingRenderExecutor maintains a dedicated thread via a ThreadPoolExecutor (acquired from a reusable _ThreadPoolExecutorPool) and dispatches all rendering calls to that thread using submit().result(). It handles the edge case where the offload thread calls back into its own executor (for example, when a weakref callback is triggered during an offloaded call) by using a _FakeLock to avoid deadlocks and executing directly instead of re-submitting.
Both executors support graceful terminate() with optional cleanup callables and maintain internal state tracking for locked and terminated conditions.
Usage
Use these executors when implementing OpenGL rendering contexts that need to enforce thread-affinity constraints. PassthroughRenderExecutor is appropriate for backends that must run on the main thread (GLFW, EGL). OffloadingRenderExecutor is used when rendering needs to be offloaded to a dedicated thread.
Code Reference
Source Location
- Repository: Google_deepmind_Dm_control
- File: dm_control/_render/executor/render_executor.py
- Lines: 1-218
Signature
class BaseRenderExecutor(metaclass=abc.ABCMeta):
def __init__(self):
def execution_context(self): # context manager
@property
def terminated(self):
@property
def thread(self): # abstract
def call(self, *args, **kwargs): # abstract
def terminate(self, cleanup_callable=None): # abstract
class PassthroughRenderExecutor(BaseRenderExecutor):
def __init__(self):
@property
def thread(self):
def call(self, func, *args, **kwargs):
def terminate(self, cleanup_callable=None):
class OffloadingRenderExecutor(BaseRenderExecutor):
def __init__(self):
@property
def thread(self):
def call(self, func, *args, **kwargs):
def terminate(self, cleanup_callable=None):
Import
from dm_control._render.executor.render_executor import PassthroughRenderExecutor
from dm_control._render.executor.render_executor import OffloadingRenderExecutor
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| func | callable | Yes (for call) |
The function to execute on the rendering thread |
| *args | any | No | Positional arguments to pass to func
|
| **kwargs | any | No | Keyword arguments to pass to func
|
| cleanup_callable | callable | No (for terminate) |
Optional function to call during termination cleanup |
Outputs
| Name | Type | Description |
|---|---|---|
| execution_context() | context manager | Yields the executor itself, allowing call() invocations within the context
|
| call() return | any | The return value of the dispatched function |
| terminated | bool | Whether this executor has been terminated |
| thread | threading.Thread or None | The thread associated with this executor |
Usage Examples
from dm_control._render.executor.render_executor import PassthroughRenderExecutor
# Single-threaded executor
executor = PassthroughRenderExecutor()
with executor.execution_context() as ctx:
ctx.call(some_opengl_function, arg1, arg2)
result = ctx.call(another_opengl_function)
# Terminate with cleanup
executor.terminate(cleanup_callable=my_cleanup_function)
from dm_control._render.executor.render_executor import OffloadingRenderExecutor
# Multi-threaded executor with dedicated rendering thread
executor = OffloadingRenderExecutor()
with executor.execution_context() as ctx:
# This call is dispatched to the dedicated thread
ctx.call(initialize_opengl_context)
pixels = ctx.call(render_scene, scene_data)
executor.terminate()