patrikhuber / eigen-natvis-imagewatch

ImageWatch natvis visualiser file for Eigen matrices
MIT License
8 stars 2 forks source link

Custom row/col strides #1

Open patrikhuber opened 2 years ago

patrikhuber commented 2 years ago

Preserving the content of the MS Developer Community post here, in case Microsoft ever moves platform again and it goes offline.

Image Watch and Eigen support (custom row/col strides)

TLDR: I'm writing a custom natvis file for the Image Watch plugin trying to properly support Eigen matrices, but there's a problem with displaying ColMajor matrices, and potentially how the row and col strides are handled in Image Watch. The natvis file is here: https://github.com/patrikhuber/eigen-natvis-imagewatch/blob/master/Eigen.natvis

Details: Matrices with Eigen::RowMajor are displayed correctly - E.g. a Eigen::Matrix<double, 2, 3, Eigen::RowMajor> shows as a 2x3 matrix in ImageWatch (2 rows, 3 columns). However, ColMajor matrices (which is Eigen's default) are being shown transposed - so an Eigen::Matrix<double, 2, 3> or Eigen::Matrix<double, 2, 3, Eigen::ColMajor> would be shown as 3 cols and 2 rows ("width"=2, "height"=3).

Note that currently, the natvis file specifies width and height conditionally: https://github.com/patrikhuber/eigen-natvis-imagewatch/blob/master/Eigen.natvis#L26-L30 I think this is wrong. width should always be equal to cols, and height always be equal to rows. Memory storage order has nothing to do with width and height of the matrix or image. An Eigen::Matrix<double, 2, 3, StorageOrder> always has 2 rows (height) and 3 cols (width), no matter if StorageOrder is Col or RowMajor. This can be changed but it doesn't fix problem:

That all means we need to fiddle with the strides. Luckily, in theory, ImageWatch supports lists of strides, i.e. "For planar images, this can be a semicolon-delimited list of strides for each plane." (from https://imagewatch.azurewebsites.net/ImageWatchHelp/ImageWatchHelp.htm). It doesn't say though if the row-stride or col-stride comes first and what kind of stride it actually expects (e.g. further down in the documentation they talk about "row padding", and "all strides equal, therefore [stride] is simply ncols". But it wouldn't make sense that all strides are equal in that example. The stride along the first dimension should be ncols, and the stride along the second dimension should be 1 .

So: I took this 2x3 matrix and basically tried every (or hopefully every...) combination of values, but none of them worked. Also, if you change "{$T3sizeof($T1)}" to "{$T3sizeof($T1)}, {$T3sizeof($T1)}", "{$T3sizeof($T1)}, {sizeof($T1)}" or "{$T3*sizeof($T1)}, {1}", neither of them works (matrix is always shown as [invalid]), which is weird as according to the documentation if one sets only one value there, then "all strides are equal", so at least one of these should work.

This could be me making a mistake somewhere (or having a misunderstanding of some of these concepts about strides) but the documentation also doesn't make it easier.


And a test case from the issue:

    Eigen::Matrix<double, 2, 3, Eigen::ColMajor> test_colmajor; // displays the matrix transposed
    test_colmajor(0, 0) = 1.0f;
    test_colmajor(0, 1) = 2.0f;
    test_colmajor(0, 2) = 3.0f;
    test_colmajor(1, 0) = 4.0f;
    test_colmajor(1, 1) = 5.0f;
    test_colmajor(1, 2) = 6.0f;
    Eigen::Matrix<double, 2, 3, Eigen::RowMajor> test_rowmajor; // displays the matrix correctly.
    test_rowmajor(0, 0) = 1.0f;
    test_rowmajor(0, 1) = 2.0f;
    test_rowmajor(0, 2) = 3.0f;
    test_rowmajor(1, 0) = 4.0f;
    test_rowmajor(1, 1) = 5.0f;
    test_rowmajor(1, 2) = 6.0f;

Now open the Image Watch window and see that test_colmajor gets displayed transposed. You can now start fiddling with the Eigen.natvis file and change the strides (and width/height) as I described in my post above, for example change the strides to a list of strides as I described and how the documentation says, and you should see the same inconsistencies to the documentation that I'm seeing and the result will always be an image showing [invalid]. I'm also happy for you to contact me via email to discuss more details.

chuzhixing commented 8 months ago

According to [1], @flipd(img): flip img diagonally (matrix transpose)。 We could use @flipd(img) at image watch to visualize the eigen matrix of col major layout. flip

I tried transposing matrix in Natvis as follows, but it didn't work. <Item Name="[data]">(m_storage.transpose().m_data)</Item>

According to [2], Natvis expressions don't allow function evaluation or side effects.

References [1] https://imagewatch.azurewebsites.net/ImageWatchHelp/ImageWatchHelp.htm (Image Watch Help) [2] https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2022