def exec_compute_shader(self, shader_obj, shader_inputs, exec_size, workgroup_size=(16, 16, 1)):
        """ Executes a compute shader. The shader object should be a shader
        loaded with Shader.load_compute, the shader inputs should be a dict where
        the keys are the names of the shader inputs and the values are the
        inputs. The workgroup_size has to match the size defined in the
        compute shader """
        ntx = int(math.ceil(exec_size[0] / workgroup_size[0]))
        nty = int(math.ceil(exec_size[1] / workgroup_size[1]))
        ntz = int(math.ceil(exec_size[2] / workgroup_size[2]))

        nodepath = NodePath("shader")
        nodepath.set_shader(shader_obj)
        nodepath.set_shader_inputs(**shader_inputs)

        attr = nodepath.get_attrib(ShaderAttrib)
        Globals.base.graphicsEngine.dispatch_compute((ntx, nty, ntz), attr, Globals.base.win.gsg)
 Executes a compute shader. The shader object should be a shader
        loaded with Shader.load_compute, the shader inputs should be a dict where
        the keys are the names of the shader inputs and the values are the
        inputs. The workgroup_size has to match the size defined in the
        compute shader 
metadrive/render_pipeline/rpplugins/scattering/scattering_methods.py