-
Notifications
You must be signed in to change notification settings - Fork 2.4k
[FEATURE] Integrating Madrona Batch Renderer #1416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Create tuples directly in BatchRender.render() Check input_types in Camera.set_pose() with set Update comments in batch_render.py and scene.py
* Improve math formulas and comments * Simplify the math to do y-forward quaternion transform * Black format scene.py and camera.py * Refactor ImageExporter to handle RGB and Depth separately with partial function * Black format image_exporter.py and batch_renderer.py
* Genesis now passes render_options to Madrona to specify the which outputs to generate, including RGB and Depth for now * Black format batch_renderer.py
…tiple environments (#4) * Fix transform issue with camera.set_pose by supporting batch transform with z_up_to_R Optimize quaternion transform of camera with Taichi kernel * Format files with black * Optimize R_to_quat
97306db
to
8964649
Compare
Create tuples directly in BatchRender.render() Check input_types in Camera.set_pose() with set Update comments in batch_render.py and scene.py
* Improve math formulas and comments * Simplify the math to do y-forward quaternion transform * Black format scene.py and camera.py * Refactor ImageExporter to handle RGB and Depth separately with partial function * Black format image_exporter.py and batch_renderer.py
* Genesis now passes render_options to Madrona to specify the which outputs to generate, including RGB and Depth for now * Black format batch_renderer.py
… wrong. It uses nenvs as index to be passed to attach. With the new implementation, index is not required. With batch rendering, cameras attached to links can be updated for each environment.
8964649
to
ca30205
Compare
for nenv, cam in enumerate(cams): | ||
cam.attach(robot.get_link("hand"), T, nenv) | ||
for cam in cams: | ||
cam.attach(robot.get_link("hand"), T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing camera index as env index is wrong.
The new camera attach implementation in this PR allows attaching a camera to the same link and update the camera location to follow the position of the link in each environment independently, so env index is not needed any more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you add new tests for madrona batched renderer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we can start with some some tests for API, not necessarily the correctness test of rendering results
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried, but it's more work than I thought. The test failed probably because the container needs to be updated due to extra dependencies. I think it's better to do it in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added one test case for batch renderer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to do it in a separate PR.
Note that we don't merge any new feature without accompanying unit tests. No exceptions.
But it seems you added one. Even if it only runs locally on your machine, this is fine for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new camera attach implementation in this PR allows attaching a camera to the same link and update the camera location to follow the position of the link in each environment independently.
This was not the case before? From what I understand, it does not make sense anymore to create multiple cameras or is this still necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not about multiple cameras.
My implementation could, for each camera, update the camera location to follow the link it is attaching to in all environments, but the current implementation added by some one 2 weeks ago only support one environment, by specifying an env_idx.
For example, the position of a link could be at different location in different environment. My implementation can move camera to different locations in different environments. The camera transforms are updated in batch.
I didn't know how the current implementation got approved. It is not a good implementation.
I added this feature per request from @YilingQiao
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I merged that one as a temporay solution for pyrender. You can replace it with a better solution.
""" | ||
self._attached_link = rigid_link | ||
self._attached_offset_T = offset_T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Someone implemented a attaching camera 2 weeks ago, but the implementation is limited to only attach to the location of one environment, so this implementation is removed with the implementation in this PR, which allows attaching cameras to the same rigid link and update their location independently in each environment.
genesis/vis/visualizer.py
Outdated
self._rasterizer.update_scene() | ||
self._rasterizer.render_camera(self._cameras[0]) | ||
if not self._use_batch_renderer: | ||
self._rasterizer.update_scene() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AnisB I moved the update_scene() you added in a previous PR under the if-statement, since when batch renderer is enabled, _rasterizer, which is the legacy pyrender renderer, will not be used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is the way to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skipped _rasterizer.render_camera()
when batch renderer is enabled, as we discussed.
return _np_z_up_to_R(z, up, out) | ||
|
||
|
||
def _tc_z_up_to_R(z, up=None, out=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added back the torch implementation.
Will do a thorough benchmark to find out which is faster.
- Numba + CPU->GPU memory copy
- Torch tensor + all GPU memory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you check whether the 2 implementation were matching?
Did you do the benchmark that you suggested?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are asking the whether the implementation of tc version matches the implementation of your new np version, the answer is no, but the tc version is based on the previous implementation and expanded it into the 2-dimension case. I didn't rewrite the algorithm.
I think we discussed this earlier and agreed that I can use the previously implemented torch version for this PR. That implementation was already tested and approved in the PR under genesis-company/Genesis.
The benchmarking will be done later on.
59a534f
to
b3de3a9
Compare
README.md
Outdated
@@ -172,6 +172,8 @@ Genesis's development has been made possible thanks to these open-source project | |||
- [libccd](https://github.com/danfis/libccd): Reference for collision detection. | |||
- [PyRender](https://github.com/mmatl/pyrender): Rasterization-based renderer. | |||
- [LuisaCompute](https://github.com/LuisaGroup/LuisaCompute) and [LuisaRender](https://github.com/LuisaGroup/LuisaRender): Ray-tracing DSL. | |||
- [Madrona](https://github.com/shacklettbp/madrona): Madrona batch renderer | |||
- [Madrona-mjx](https://github.com/shacklettbp/madrona_mjx): Madrona MJX integration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we put the in the same line?
Madrona and Madrona-mjx: Batch renderer backend
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I could
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
for nenv, cam in enumerate(cams): | ||
cam.attach(robot.get_link("hand"), T, nenv) | ||
for cam in cams: | ||
cam.attach(robot.get_link("hand"), T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you add new tests for madrona batched renderer?
from genesis.utils.misc import tensor_to_array | ||
|
||
|
||
class FrameImageExporter: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when you add batched rendering test, can you also test this function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and a fairly detailed documentation (I would expect at least 4-5 lines).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
for nenv, cam in enumerate(cams): | ||
cam.attach(robot.get_link("hand"), T, nenv) | ||
for cam in cams: | ||
cam.attach(robot.get_link("hand"), T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we can start with some some tests for API, not necessarily the correctness test of rendering results
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should write a unit test in this PR. It will only pass locally since Madrona must be installed manually, so you need add the decorator @pytest.mark.xfail(reason="gs-madrona must be built and installed manually for now.")
.
This way, once the python wheels are ready, I will just have to enable it systematically!
.gitmodules
Outdated
@@ -7,3 +7,6 @@ | |||
[submodule "genesis/ext/ParticleMesher"] | |||
path = genesis/ext/ParticleMesher | |||
url = https://github.com/ACMLCZH/ParticleMesher | |||
[submodule "genesis/ext/gs-madrona"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think gs-madrona
should be a submodules. Python wheels will be compiled and distributed on PyPI completely independently from Genesis. gs-madrona
will then be added to the Python requirements of Genesis simulator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@YilingQiao Do you agree to remove gs-madrona as a submodule?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
scene.build(n_envs=N_ENVS) | ||
|
||
# Create an image exporter | ||
exporter = FrameImageExporter(OUTPUT_DIR) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a comment to explain what this frame image exporter does and why you are using it instead of the usual start|stop_recording
public API? This would be helpful for practitioners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could. Basically this does batch exporting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but why it works differently for batch rendering?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comments to explain the reason to use FrameImageExporter for batch exporting
Good idea. I'll add one |
genesis/engine/scene.py
Outdated
Whether to force render the scene. | ||
|
||
Returns: | ||
A tuple of tensors of shape (n_envs, H, W, 3) if rgb is not None, otherwise a list of tensors of shape (n_envs, H, W, 1) if depth is not None. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
120 chars linewidth limit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Split into 2 lines
genesis/ext/gs-madrona
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this submodule. It will be managed differently. On the long run we should get rid of all these submodules, it is not a good approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed. Yes, we should remove LuisaRender as well.
Added a test case using batch renderer and marked xfail. Tested locally |
@@ -148,3 +148,6 @@ dmypy.json | |||
|
|||
# Pyre type checker | |||
.pyre/ | |||
|
|||
# Batch renderer output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this change. I don't think it should be the default. It is the responsibility of the user to choose a folder outside the repository if desired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll push the change
tests/test_render.py
Outdated
@pytest.mark.parametrize("n_steps", [1, 1000]) | ||
@pytest.mark.required | ||
@pytest.mark.xfail(reason="gs-madrona must be built and installed manually for now.") | ||
def test_madrona_batch_rendering(use_rasterizer, render_all_cameras, n_envs, n_steps): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about just update test_batched_offscreen_rendering
to run for both pyrender
and gs-madrona
, and add 2 fixed cameras in the scene to make the test more comprehensive?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Let me see how I could do it.
I also found that test_batched_offscreen_rendering should be updated. I just went through the test case. It was not written using cameras and environments properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I update test_batched_offscreen_rendering() , I'll have to make it temporarily xfail. How about we have 2 separate test functions for now. When the wheels are ready, and the test case for batched camera can pass, we merge them into one.
exporter = FrameImageExporter(export_dir) | ||
|
||
# Only verify functionality works without crashes and output dimension matches for now | ||
# To verify whether the output is correct pixel-wise, we need to use a more sophisticated test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not enough. See unit test test_batched_offscreen_rendering
. You should check that the standard deviation for each frame is large enough, that 2 frames that are supposed to match are truly matching, and that 2 frames that are supposed to be different are truly different (easy if you have 2 cameras at different locations).
Having good extensive unit tests is more important than adding new features. We do not merge PRs if their unit tests are not comprehensive enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was hesitant of adding something to compare mean and std for an image because I don't think this is the right way to compare rendering results. It might work as some point, but it is far from ideal, because rendering result may change very frequently for a system under development.
A rendering test system should not be part of CI but not part of the unit test framework. It should compare images pixel by pixel and show the percentage of differences, and allow users to update golden images when necessary.
But anyway, since we don't have such a system yet, I'm OK to just compare mean and std here.
This PR migrates all Madrona batch rendering changes from https://github.com/genesis-company/Genesis, which is temporary to the official repo. All the code has already been reviewed before except for a few changes I commented in this PR to resolve merge conflicts. See this link for previous PRs.
Description
This PR add support of batch rendering for Genesis.
Batch Renderer
[New API] Enable batch renderer
Default to false
[New API] Batch render -
Scene.render_all_cameras()
. A tuple of 4-d arrays will be returned by this function, with len() of the tuple being the number of cameras, first dimension of each tensor being environment/world. Right now only rgb and depth is support. Normal and segmentation output will be supported later[New Behavior] Single-camera render -
Camera.render()
still works, and it will return a tensor with output only from the camera from which render() is called. In the case of n_envs >0, the first dimension is the environment. WhenCamera.render()
is called, actuallyScene.batch_render()
will be invoked internally, with outputs from all cameras cached, which means ifCamera.render()
is later called from another camera, the output will be returned immediately without any additional rendering. This behavior may change later to only render for the requesting camera.Scene
Lights
Scene.add_light()
with a new signature to add non-mesh light. Thisadd_light()
can be used to add point lights, spot lights and directional lights, which are supported by the lighting model of the batch renderer.add_light()
functions now, with different signatures. If BatchRenderer is used, only the new add_light() can be used, otherwise, there will be a warning and lights won’t be added.add_light()
can be used, otherwise, there will be a warning and lights won’t be added.add_light()
might be renamed toadd_mesh_light()
in future and be supported in the ray tracing pipeline in the batch renderer, but right now only directional lights, point lights and spot lights are supported.Shadow
castshadow=true
. The raytracer pipeline of batch rendering can cast shadow from all lights, while the rasterizer pipeline can only cast shadow from the first light added with shadow casting enabled.Camera
Scene.add_camera()
function, but aperture and focus_dis is not supported right now.env_idx
toCamera.set_pose()
to specify the indices of environments to update camera transforms.Related Issue
Resolves /issues/1232
Resolves /issues/21
How Has This Been / Can This Be Tested?
Tests have been done with example script single_franka.py and single_franka_batch_render.py, to cover combinations of
Screenshots (if appropriate):
Checklist:
Submitting Code Changes
section of CONTRIBUTING document.