1. 09 Dec, 2020 1 commit
    • Nikhila Ravi's avatar
      Non square image rasterization for meshes · d07307a4
      Nikhila Ravi authored
      Summary:
      There are a couple of options for supporting non square images:
      1) NDC stays at [-1, 1] in both directions with the distance calculations all modified by (W/H). There are a lot of distance based calculations (e.g. triangle areas for barycentric coordinates etc) so this requires changes in many places.
      2) NDC is scaled by (W/H) so the smallest side has [-1, 1]. In this case none of the distance calculations need to be updated and only the pixel to NDC calculation needs to be modified.
      
      I decided to go with option 2 after trying option 1!
      
      API Changes:
      - Image size can now be specified optionally as a tuple
      
      TODO:
      - add a benchmark test for the non square case.
      
      Reviewed By: jcjohnson
      
      Differential Revision: D24404975
      
      fbshipit-source-id: 545efb67c822d748ec35999b35762bce58db2cf4
      d07307a4
  2. 30 Nov, 2020 1 commit
    • Georgia Gkioxari's avatar
      taubin smoothing · 112959e0
      Georgia Gkioxari authored
      Summary: Taubin Smoothing for filtering meshes and making them smoother. Taubin smoothing is an iterative approach.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D24751149
      
      fbshipit-source-id: fb779e955f1a1f6750e704f1b4c6dfa37aebac1a
      112959e0
  3. 18 Nov, 2020 1 commit
    • Georgia Gkioxari's avatar
      move icp_data.pth to tests/data · 5fb63b45
      Georgia Gkioxari authored
      Summary: Move icp_data.pth to tests/data
      
      Reviewed By: bottler
      
      Differential Revision: D25012575
      
      fbshipit-source-id: 9252d2eeca9141c82ad3bf9d3e3331a2eab5203b
      5fb63b45
  4. 11 Nov, 2020 1 commit
  5. 10 Nov, 2020 2 commits
    • Christoph Lassner's avatar
      Fix flaky CircleCI test. · fb2763dc
      Christoph Lassner authored
      Summary: This fixes issues with `pulsar.test.TestDepth` that we are encountering on CircleCI. The ID equality test is removed, which seems to give different results on different hardware (which is okay, because the exact order of spheres can slightly vary if they're close due to numerical instabilities). The depth map validity test stays in place.
      
      Reviewed By: bottler
      
      Differential Revision: D24840776
      
      fbshipit-source-id: 2f38ea4880abf202c84d2987fdd71a84c5ef3b05
      fb2763dc
    • Jeremy Reizenstein's avatar
      pulsar build and CI changes · d220ee2f
      Jeremy Reizenstein authored
      Summary:
      Changes to CI and some minor fixes now that pulsar is part of pytorch3d. Most significantly, add CUB to CI builds.
      
      Make CUB_HOME override the CUB already in cudatoolkit (important for cuda11.0 which uses cub 1.9.9 which pulsar doesn't work well with.
      Make imageio available for testing.
      Lint fixes.
      Fix some test verbosity.
      Avoid use of atomicAdd_block on older GPUs.
      
      Reviewed By: nikhilaravi, classner
      
      Differential Revision: D24773716
      
      fbshipit-source-id: 2428356bb2e62735f2bc0c15cbe4cff35b1b24b8
      d220ee2f
  6. 05 Nov, 2020 1 commit
    • Dave Schnizlein's avatar
      Add MeshRendererWithFragments class to also return fragments after rendering · 83fef0a5
      Dave Schnizlein authored
      Summary: Users want to be able to obtain the depth from the renderer. Current work-around requires running the rasterizer and extra time. This change creates a new renderer class that also returns the fragments from the rasterizer.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D24432381
      
      fbshipit-source-id: 6552e8a6bfee646791afb34bdb7452fbc4094aed
      83fef0a5
  7. 04 Nov, 2020 1 commit
    • Christoph Lassner's avatar
      Example and test updates. · b6be3b95
      Christoph Lassner authored
      Summary: This commit performs pulsar example and test refinements. The examples are fully adjusted to adhere to PEP style guide and additional comments are added.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D24723391
      
      fbshipit-source-id: 6d289006f080140159731e7f3a8c98b582164f1a
      b6be3b95
  8. 03 Nov, 2020 2 commits
    • Christoph Lassner's avatar
      pulsar interface unification. · 960fd6d8
      Christoph Lassner authored
      Summary:
      This diff builds on top of the `pulsar integration` diff to provide a unified interface for the existing PyTorch3D point renderer and Pulsar. For more information about the pulsar backend, see the release notes and the paper (https://arxiv.org/abs/2004.07484). For information on how to use the backend, see the point cloud rendering notebook and the examples in the folder docs/examples.
      
      The unified interfaces are completely consistent. Switching the render backend is as easy as using `renderer = PulsarPointsRenderer(rasterizer=rasterizer).to(device)` instead of `renderer = PointsRenderer(rasterizer=rasterizer, compositor=compositor)` and adding the `gamma` parameter to the forward function. All PyTorch3D camera types are supported as far as possible; keyword arguments are properly forwarded to the camera. The `PerspectiveCamera` and `OrthographicCamera` require znear and zfar as additional parameters for the forward pass.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D21421443
      
      fbshipit-source-id: 4aa0a83a419592d9a0bb5d62486a1cdea9d73ce6
      960fd6d8
    • Christoph Lassner's avatar
      pulsar integration. · b19fe1de
      Christoph Lassner authored
      Summary:
      This diff integrates the pulsar renderer source code into PyTorch3D as an alternative backend for the PyTorch3D point renderer. This diff is the first of a series of three diffs to complete that migration and focuses on the packaging and integration of the source code.
      
      For more information about the pulsar backend, see the release notes and the paper (https://arxiv.org/abs/2004.07484). For information on how to use the backend, see the point cloud rendering notebook and the examples in the folder `docs/examples`.
      
      Tasks addressed in the following diffs:
      * Add the PyTorch3D interface,
      * Add notebook examples and documentation (or adapt the existing ones to feature both interfaces).
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D23947736
      
      fbshipit-source-id: a5e77b53e6750334db22aefa89b4c079cda1b443
      b19fe1de
  9. 30 Oct, 2020 1 commit
    • Dave Schnizlein's avatar
      Update cameras to accept projection matrix as input · 36fb257e
      Dave Schnizlein authored
      Summary: To initialize the Cameras class currently we require the principal point, focal length and other parameters to be specified from which we calculate the intrinsic matrix. In some cases the matrix might be directly available e.g. from a dataset and the associated metadata for an image.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D24489509
      
      fbshipit-source-id: 1b411f19c5f6c8074bcfbf613f3339d5e242c119
      36fb257e
  10. 29 Oct, 2020 1 commit
    • Jeremy Reizenstein's avatar
      remove texture_vis test · 0e5f4f76
      Jeremy Reizenstein authored
      Summary: This recently added test is sensitive to the version of PIL because of different algorithms to draw ellipses/circles. Remove it as there is no obvious safe way to test this. Replace with a test for the underlying centres_for_image().
      
      Reviewed By: theschnitz
      
      Differential Revision: D24622465
      
      fbshipit-source-id: e46d7384df491c71ac87ba8bbbce89507ac40080
      0e5f4f76
  11. 27 Oct, 2020 1 commit
    • Jeremy Reizenstein's avatar
      images for debugging TexturesUV · aa4cc0ad
      Jeremy Reizenstein authored
      Summary: New methods to directly plot a TexturesUV map with its used points, using PIL and matplotlib.
      
      Reviewed By: gkioxari
      
      Differential Revision: D23782968
      
      fbshipit-source-id: 692970857b5be13a35a3175dc82ac03963a73555
      aa4cc0ad
  12. 21 Oct, 2020 1 commit
    • Jeremy Reizenstein's avatar
      axis_angle representation of rotations · c93c4dd7
      Jeremy Reizenstein authored
      Summary: We can represent a rotation as a vector in the axis direction, whose length is the rotation anticlockwise in radians around that axis.
      
      Reviewed By: gkioxari
      
      Differential Revision: D24306293
      
      fbshipit-source-id: 2e0f138eda8329f6cceff600a6e5f17a00e4deb7
      c93c4dd7
  13. 16 Oct, 2020 1 commit
  14. 15 Oct, 2020 2 commits
    • Jeremy Reizenstein's avatar
      matrix_to_quaternion corner case · 4d52f9fb
      Jeremy Reizenstein authored
      Summary: Issue #119. The function `sqrt(max(x, 0))` is not convex and has infinite gradient at 0, but 0 is a subgradient at 0. Here we implement it in such a way as to give 0 as the gradient.
      
      Reviewed By: gkioxari
      
      Differential Revision: D24306294
      
      fbshipit-source-id: 48d136faca083babad4d64970be7ea522dbe9e09
      4d52f9fb
    • John Reese's avatar
      apply black 20.8b1 formatting update · 2d397236
      John Reese authored
      Summary:
      allow-large-files
      
      black_any_style
      
      Reviewed By: zertosh
      
      Differential Revision: D24325133
      
      fbshipit-source-id: b4afe80d1e8b2bc993f4b8e3822c02964df47462
      2d397236
  15. 07 Oct, 2020 1 commit
    • Nikhila Ravi's avatar
      Fix texture atlas for objs which only have material properties · f5383a7e
      Nikhila Ravi authored
      Summary:
      Fix for GitHub issue #381.
      
      The example mesh provided in the issue only had material properties but no texture image. The current implementation of texture atlassing generated an atlas using both the material properties and the texture image but only worked if there was a texture image and associated vertex uv coordinates. I have now modified the texture atlas creation so that it doesn't require an image and can work with materials which only have material properties.
      
      Reviewed By: gkioxari
      
      Differential Revision: D24153068
      
      fbshipit-source-id: 63e9d325db09a84b336b83369d5342ce588a9932
      f5383a7e
  16. 06 Oct, 2020 2 commits
    • Georgia Gkioxari's avatar
      add texture vertex sampling functionality to textures · e651a429
      Georgia Gkioxari authored
      Summary: Enhance every texture type with `faces_verts_textures_packed` that allows users to query the texture of each vertex in mesh
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D24058778
      
      fbshipit-source-id: 19d0e3a244fa96aae462c47bf52e07dfd3b7c6f0
      e651a429
    • Georgia Gkioxari's avatar
      extend sample_points_from_meshes with texture · 327bd2b9
      Georgia Gkioxari authored
      Summary:
      Enhanced `sample_points_from_meshes` with texture sampling
      
      * This new feature is used to return textures corresponding to the sampled points in `sample_points_from_meshes`
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D24031525
      
      fbshipit-source-id: 8e5d8f784cc38aa391aa8e84e54423bd9fad7ad1
      327bd2b9
  17. 01 Oct, 2020 1 commit
    • Patrick Labatut's avatar
      Fix a few linting warnings · 8219a52c
      Patrick Labatut authored
      Summary: Fix a few linting warnings
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D20720810
      
      fbshipit-source-id: c5b6a25fdd7971cc8743b54bbe162464a874071d
      8219a52c
  18. 24 Sep, 2020 1 commit
    • Nikhila Ravi's avatar
      Support for moving the renderer to a new device · 956d3a01
      Nikhila Ravi authored
      Summary:
      Support for moving all the tensors of the renderer to another device by calling `renderer.to(new_device)`
      
      Currently the `MeshRenderer`, `MeshRasterizer` and `SoftPhongShader` (and other shaders) are all of type `nn.Module` which already supports easily moving tensors of submodules (defined as class attributes) to a different device. However the class attributes of the rasterizer and shader (e.g. cameras, lights, materials), are of type `TensorProperties`, not nn.Module so we need to explicity create a `to` method to move these tensors to device. Note that the `TensorProperties` class already has a `to` method so we only need to call `cameras.to(device)` and don't need to worry about the internal tensors.
      
      The other option is of course making these other classes (cameras, lights etc) also of type nn.Module.
      
      Reviewed By: gkioxari
      
      Differential Revision: D23885107
      
      fbshipit-source-id: d71565c442181f739de4d797076ed5d00fb67f8e
      956d3a01
  19. 23 Sep, 2020 1 commit
    • andrijazz's avatar
      resouces filenames with spaces (#358) · b1eee579
      andrijazz authored
      Summary:
      I'm constantly encountering 3D models with resources that have spaces in their filenames (especially on Windows) and therefore they can't be loaded in pytorch3d. Let me know what you think.
      
      Thanks
      
      Pull Request resolved: https://github.com/facebookresearch/pytorch3d/pull/358
      
      Reviewed By: bottler
      
      Differential Revision: D23798492
      
      Pulled By: nikhilaravi
      
      fbshipit-source-id: 4d85b7ee05339486d2e5ef53a531f8e6052251c5
      b1eee579
  20. 21 Sep, 2020 1 commit
    • Jeremy Reizenstein's avatar
      save_ply binary · 197f1d62
      Jeremy Reizenstein authored
      Summary:
      Make save_ply save to binary instead of ascii. An option makes the previous functionality available. save_ply's API accepts a stream, but this is undocumented; that stream must now be a binary stream not a text stream.
      
      Avoiding warnings about making tensors from immutable numpy arrays.
      
      Possible performance improvement when reading binary files.
      
      Fix reading zero-length binary lists.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D22333118
      
      fbshipit-source-id: b423dfd3da46e047bead200255f47a7707306811
      197f1d62
  21. 19 Sep, 2020 1 commit
  22. 14 Sep, 2020 1 commit
    • Amitav Baruah's avatar
      Add background color support to compositors · 872ff8c7
      Amitav Baruah authored
      Summary: Support rendering different color backgrounds for pointclouds for both compositors
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D23611043
      
      fbshipit-source-id: ab029650d51349340372c5bd66700e6577d48851
      872ff8c7
  23. 10 Sep, 2020 1 commit
    • Amitav Baruah's avatar
      Fix look_at corner case · eb517dd7
      Amitav Baruah authored
      Summary: When the camera is vertically oriented, calculating the look_at x-axis (also known as the "right" vector) does not succeed, resulting in the x-axis being placed at the origin. Adds a check to correctly calculate the x-axis if this case occurs.
      
      Reviewed By: nikhilaravi, sbranson
      
      Differential Revision: D23511859
      
      fbshipit-source-id: ee5145cdbecdbe2f7c7d288588bd0899480cb327
      eb517dd7
  24. 09 Sep, 2020 1 commit
    • Steve Branson's avatar
      Fix softmax_rgb_blend() when mesh is outside zfar · f8ea5906
      Steve Branson authored
      Summary:
      This fixes two small issues with blending.py:softmax_rgb_blend():
        1) zfar and znear attributes are propagated from the camera settings instead of just using default settings of znear=1.0 and zfar=100.0
        2) A check is added to prevent arithmetic overflow in softmax_rgb_blend()
      
      This is a fix in response to https://github.com/facebookresearch/pytorch3d/issues/334
      where meshes rendererd using a SoftPhongShader with faces_per_pixel=1 appear black.  This only occurs when the scale of the mesh is large (vertex values > 100, where 100 is the default value of zfar).  This fix allows the caller to increase the value of cameras.zfar to match the scale of her/his mesh.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D23517541
      
      fbshipit-source-id: ab8631ce9e5f2149f140b67b13eff857771b8807
      f8ea5906
  25. 03 Sep, 2020 1 commit
    • David Novotny's avatar
      Camera alignment · 316b7778
      David Novotny authored
      Summary:
      adds `corresponding_cameras_alignment` function that estimates a similarity transformation between two sets of cameras.
      
      The function is essential for computing camera errors in SfM pipelines.
      
      ```
      Benchmark                                                   Avg Time(μs)      Peak Time(μs) Iterations
      --------------------------------------------------------------------------------
      CORRESPONDING_CAMERAS_ALIGNMENT_10_centers_False                32219           36211             16
      CORRESPONDING_CAMERAS_ALIGNMENT_10_centers_True                 32429           36063             16
      CORRESPONDING_CAMERAS_ALIGNMENT_10_extrinsics_False              5548            8782             91
      CORRESPONDING_CAMERAS_ALIGNMENT_10_extrinsics_True               6153            9752             82
      CORRESPONDING_CAMERAS_ALIGNMENT_100_centers_False               33344           40398             16
      CORRESPONDING_CAMERAS_ALIGNMENT_100_centers_True                34528           37095             15
      CORRESPONDING_CAMERAS_ALIGNMENT_100_extrinsics_False             5576            7187             90
      CORRESPONDING_CAMERAS_ALIGNMENT_100_extrinsics_True              6256            9166             80
      CORRESPONDING_CAMERAS_ALIGNMENT_1000_centers_False              32020           37247             16
      CORRESPONDING_CAMERAS_ALIGNMENT_1000_centers_True               32776           37644             16
      CORRESPONDING_CAMERAS_ALIGNMENT_1000_extrinsics_False            5336            8795             94
      CORRESPONDING_CAMERAS_ALIGNMENT_1000_extrinsics_True             6266            9929             80
      --------------------------------------------------------------------------------
      ```
      
      Reviewed By: shapovalov
      
      Differential Revision: D22946415
      
      fbshipit-source-id: 8caae7ee365b304d8aa1f8133cf0dd92c35bc0dd
      316b7778
  26. 25 Aug, 2020 2 commits
    • Jeremy Reizenstein's avatar
      amalgamate meshes with texture into a single scene · 909dc835
      Jeremy Reizenstein authored
      Summary:
      Add a join_scene method to all the textures to allow the join_mesh function to include textures. Rename the join_mesh function to join_meshes_as_scene.
      
      For TexturesAtlas, we now interpolate if the user attempts to have the resolution vary across the batch. This doesn't look great if the resolution is already very low.
      
      For TexturesUV, a rectangle packing function is required, this does something simple.
      
      Reviewed By: gkioxari
      
      Differential Revision: D23188773
      
      fbshipit-source-id: c013db061a04076e13e90ccc168a7913e933a9c5
      909dc835
    • Jeremy Reizenstein's avatar
      align_corners and padding for TexturesUV · e25ccab3
      Jeremy Reizenstein authored
      Summary:
      Allow, and make default, align_corners=True for texture maps. Allow changing the padding_mode and set the default to be "border" which produces more logical results. Some new documentation.
      
      The previous behavior corresponds to padding_mode="zeros" and align_corners=False.
      
      Reviewed By: gkioxari
      
      Differential Revision: D23268775
      
      fbshipit-source-id: 58d6229baa591baa69705bcf97471c80ba3651de
      e25ccab3
  27. 22 Aug, 2020 3 commits
    • Eduardo Henrique Arnold's avatar
      Fix look_at_view_transform when object location is not (0,0,0) (#230) · d0cec028
      Eduardo Henrique Arnold authored
      Summary:
      The look_at_view_transform did not give the correct results when the object location `at` was not (0,0,0).
      
      The problem was on computing the cameras' location in world's coordinate `C`. It only took into account the camera position from spherical angles, but ignored the object location in the world's coordinate system. I simply modified the C tensor to take into account the object's location which is not necessarily in the origin.
      
      I ran unit tests and all but 4 failed with the same error message: `RuntimeError: CUDA error: invalid device ordinal`. However the same happens before this patch, so I believe these errors are unrelated.
      
      Pull Request resolved: https://github.com/facebookresearch/pytorch3d/pull/230
      
      Reviewed By: gkioxari
      
      Differential Revision: D23278126
      
      Pulled By: nikhilaravi
      
      fbshipit-source-id: c06e891bc46de8222325ee7b37aa43cde44648e8
      d0cec028
    • Nikhila Ravi's avatar
      Texture loading and rendering in ShapeNetCore and R2N2 data loaders · 778383ee
      Nikhila Ravi authored
      Summary:
      - Add support for loading textures from ShapeNet Obj files as a texture atlas.
      - Support textured rendering of shapenet models
      
      Reviewed By: gkioxari
      
      Differential Revision: D23141143
      
      fbshipit-source-id: 26eb81758d4cdbd6d820b072b58f5c6c08cb90bc
      778383ee
    • Nikhila Ravi's avatar
      Tutorials textures updates and fix bug in extending meshes with uv textures · 90f6a005
      Nikhila Ravi authored
      Summary:
      Found a bug in extending textures with vertex uv coordinates. This was due to the padded -> list conversion of vertex uv coordinates i.e.                 The number of vertices in the mesh and in verts_uvs can differ
      e.g. if a vertex is shared between 3 faces, it can
      have up to 3 different uv coordinates. Therefore we cannot convert directly from padded to list using _num_verts_per_mesh
      
      Reviewed By: bottler
      
      Differential Revision: D23233595
      
      fbshipit-source-id: 0c66d15baae697ead0bdc384f74c27d4c6539fc9
      90f6a005
  28. 21 Aug, 2020 3 commits
    • Jeremy Reizenstein's avatar
      Fix batching bug from TexturesUV packed ambiguity, other textures tidyup · 9a50cf80
      Jeremy Reizenstein authored
      Summary:
      faces_uvs_packed and verts_uvs_packed were only used in one place and the definition of the former was ambiguous. This meant that the wrong coordinates could be used for meshes other than the first in the batch. I have therefore removed both functions and build their common result inline. Added a test that a simple batch of two meshes is rendered consistently with the rendering of each alone. This test would have failed before.
      
      I hope this fixes https://github.com/facebookresearch/pytorch3d/issues/283.
      
      Some other small improvements to the textures code.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D23161936
      
      fbshipit-source-id: f99b560a46f6b30262e07028b049812bc04350a7
      9a50cf80
    • Steve Branson's avatar
      Temporary fix for mesh rasterization bug for traingles partially behind the camera · 9aaba048
      Steve Branson authored
      Summary: A triangle is culled if any vertex in a triangle is behind the camera.  This fixes incorrect rendering of triangles that are partially behind the camera, where screen coordinate calculations are strange.  It doesn't work for triangles that are partially behind the camera but still intersect with the view frustum.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D22856181
      
      fbshipit-source-id: a9cbaa1327d89601b83d0dfd3e4a04f934a4a213
      9aaba048
    • Georgia Gkioxari's avatar
      camera refactoring · 57a22e73
      Georgia Gkioxari authored
      Summary:
      Refactor cameras
      * CamerasBase was enhanced with `transform_points_screen` that transforms projected points from NDC to screen space
      * OpenGLPerspective, OpenGLOrthographic -> FoVPerspective, FoVOrthographic
      * SfMPerspective, SfMOrthographic -> Perspective, Orthographic
      * PerspectiveCamera can optionally be constructred with screen space parameters
      * Note on Cameras and coordinate systems was added
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D23168525
      
      fbshipit-source-id: dd138e2b2cc7e0e0d9f34c45b8251c01266a2063
      57a22e73
  29. 17 Aug, 2020 2 commits
    • Georgia Gkioxari's avatar
      detach for meshes, pointclouds, textures · 7f2f95f2
      Georgia Gkioxari authored
      Summary: Add `detach` for Meshes, Pointclouds, Textures
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D23070418
      
      fbshipit-source-id: 68671124ce114c4495d7ef3c944c9aac3d0db2d8
      7f2f95f2
    • Nikhila Ravi's avatar
      Softmax blending small fix · 5852b74d
      Nikhila Ravi authored
      Summary:
      Small fix to the softmax blending function.
      
      To avoid overflow in the exponential for the softmax, the exponent is shifted by the maximum value. In the final calculation of the color there is a weighted sum between the pixel color and the background color - in order for the sum to be correct, the background color also needs to be handled in the same way witt the shifted exponent.
      
      Reviewed By: gkioxari
      
      Differential Revision: D23148301
      
      fbshipit-source-id: 86066586ee7d3ce7bd4a2076b12ce191fbd151a7
      5852b74d
  30. 07 Aug, 2020 1 commit
    • Luya Gao's avatar
      Return R2N2 voxel coordinates · 63ba74f1
      Luya Gao authored
      Summary: Return R2N2's voxel coordinates.
      
      Reviewed By: nikhilaravi
      
      Differential Revision: D22462530
      
      fbshipit-source-id: a995cfa0957b2561eb3b0f4591cb1db42170bc68
      63ba74f1