Skip to content

Conversation

@daljit46
Copy link
Member

This PR supersedes #3096.

It introduces a new GPU compute abstraction built on top of WebGPU. See #3096 for general motivation and design philosophy. A notable change from that PR is now shaders are now required to be written in Slang, a new programming language created by NVidia (now under the umbrella of the Khronos group). This choice has been motivated by the fact that Slang provides many useful features like modules and generics for better code reusability and modularisation.

Some issues still need to be resolved (in future PRs):

  • Deal with the issue raised in Add option for canonical direct I/O layout #3108.
  • Add more tests, especially for the upload/download of MR::Image instances to/from GPU (currently the API only supports MR::Image<float>.
  • Create a real-world example that illustrates the use of the API.

One additional thing that this PR introduces is the addition of a new tcb::span class (see #3219); however, unlike for other third-party dependencies, the class has been directly added to the codebase rather than fetched via CMake. The hope is that this class will become redundant once we have access to C++20's std::span.

@daljit46 daljit46 self-assigned this Nov 26, 2025
@daljit46 daljit46 requested review from a team and removed request for a team November 26, 2025 13:02
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 83. Check the log or trigger a new build to see more.

Texture new_empty_texture(const TextureSpec &textureSpec) const;

[[nodiscard]]
Texture new_texture_from_host_memory(const TextureSpec &texture_desc, tcb::span<const float> src_memory_region) const;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'MR::GPU::ComputeContext::new_texture_from_host_memory' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name]

  Texture new_texture_from_host_memory(const TextureSpec &texture_desc, tcb::span<const float> src_memory_region) const;
          ^
Additional context

cpp/core/gpu/gpu.cpp:416: the definition seen here

Texture ComputeContext::new_texture_from_host_memory(const TextureSpec &texture_desc,
                        ^

cpp/core/gpu/gpu.h:295: differing parameters are named here: ('src_memory_region'), in definition: ('srcMemoryRegion')

  Texture new_texture_from_host_memory(const TextureSpec &texture_desc, tcb::span<const float> src_memory_region) const;
          ^

inline static std::unique_ptr<MR::GPU::ComputeContext> shared_context;

// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
MR::GPU::ComputeContext &context;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: member 'context' of type 'MR::GPU::ComputeContext &' is a reference [cppcoreguidelines-avoid-const-or-ref-data-members]

  MR::GPU::ComputeContext &context;
                           ^

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 58. Check the log or trigger a new build to see more.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 33. Check the log or trigger a new build to see more.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 33. Check the log or trigger a new build to see more.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 33. Check the log or trigger a new build to see more.

)slang";

const std::vector<float> host_data = {1.0F, 2.0F, 3.0F, 4.0F};
const std::vector<float> expected_data = {3.0F, 6.0F, 9.0F, 12.0F};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'expected_data' is not initialized [cppcoreguidelines-init-variables]

Suggested change
const std::vector<float> expected_data = {3.0F, 6.0F, 9.0F, 12.0F};
const std::vector<float> expected_data = 0 = {3.0F, 6.0F, 9.0F, 12.0F};

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Texture new_empty_texture(const TextureSpec &textureSpec) const;

[[nodiscard]]
Texture new_texture_from_host_memory(const TextureSpec &texture_desc, tcb::span<const float> src_memory_region) const;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'MR::GPU::ComputeContext::new_texture_from_host_memory' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name]

  Texture new_texture_from_host_memory(const TextureSpec &texture_desc, tcb::span<const float> src_memory_region) const;
          ^
Additional context

cpp/core/gpu/gpu.cpp:419: the definition seen here

Texture ComputeContext::new_texture_from_host_memory(const TextureSpec &texture_desc,
                        ^

cpp/core/gpu/gpu.h:295: differing parameters are named here: ('src_memory_region'), in definition: ('srcMemoryRegion')

  Texture new_texture_from_host_memory(const TextureSpec &texture_desc, tcb::span<const float> src_memory_region) const;
          ^

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

void find_bindings_in_type_layout(slang::TypeLayoutReflection *typeLayout,
std::unordered_map<std::string, ReflectedBindingInfo> &bindings);

void find_bindings_in_variable_layout(slang::VariableLayoutReflection *varLayout,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'find_bindings_in_variable_layout' is within a recursive call chain [misc-no-recursion]

void find_bindings_in_variable_layout(slang::VariableLayoutReflection *varLayout,
     ^
Additional context

cpp/core/gpu/slangcodegen.cpp:124: example recursive call chain, starting from function 'find_bindings_in_type_layout'

void find_bindings_in_type_layout(slang::TypeLayoutReflection *typeLayout,
     ^

cpp/core/gpu/slangcodegen.cpp:133: Frame #1: function 'find_bindings_in_type_layout' calls function 'find_bindings_in_variable_layout' here:

      find_bindings_in_variable_layout(typeLayout->getFieldByIndex(i), bindings);
      ^

cpp/core/gpu/slangcodegen.cpp:119: Frame #2: function 'find_bindings_in_variable_layout' calls function 'find_bindings_in_type_layout' here:

    find_bindings_in_type_layout(varLayout->getTypeLayout(), bindings);
    ^

cpp/core/gpu/slangcodegen.cpp:119: ... which was the starting point of the recursive call chain; there may be other cycles

    find_bindings_in_type_layout(varLayout->getTypeLayout(), bindings);
    ^

@daljit46 daljit46 force-pushed the webgpu branch 2 times, most recently from 4e205c0 to 24b7532 Compare November 27, 2025 14:25
This a generic GPGPU compute abstraction built on top of WebGPU. To run
operations on the GPU, shaders need to be written using the Slang
programming language.

See https://dawn.googlesource.com/dawn
See https://shader-slang.org/
DOWNLOAD_EXTRACT_TIMESTAMP is only available on CMake >=3.24. This
change make CMake ignore the option on older versions.
We also set CMP0135 policy behaviour to NEW to fix warnings.
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 40. Check the log or trigger a new build to see more.

inline static std::unique_ptr<MR::GPU::ComputeContext> shared_context;

// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
MR::GPU::ComputeContext &context;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: member 'context' of type 'int &' is a reference [cppcoreguidelines-avoid-const-or-ref-data-members]

  MR::GPU::ComputeContext &context;
                           ^


TEST_F(GPUTest, MakeEmptyBuffer) {
const size_t buffer_element_count = 1024;
const Buffer<uint32_t> buffer = context.new_empty_buffer<uint32_t>(buffer_element_count);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'buffer' is not initialized [cppcoreguidelines-init-variables]

Suggested change
const Buffer<uint32_t> buffer = context.new_empty_buffer<uint32_t>(buffer_element_count);
const Buffer<uint32_t> buffer = 0 = context.new_empty_buffer<uint32_t>(buffer_element_count);

TEST_F(GPUTest, BufferFromHostMemory) {
std::vector<int32_t> host_data = {1, 2, 3, 4, 5};

const Buffer<int32_t> buffer = context.new_buffer_from_host_memory<int32_t>(tcb::span<const int32_t>(host_data));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'buffer' is not initialized [cppcoreguidelines-init-variables]

Suggested change
const Buffer<int32_t> buffer = context.new_buffer_from_host_memory<int32_t>(tcb::span<const int32_t>(host_data));
const Buffer<int32_t> buffer = 0 = context.new_buffer_from_host_memory<int32_t>(tcb::span<const int32_t>(host_data));


TEST_F(GPUTest, BufferFromHostMemoryVoidPtr) {
std::vector<float> host_data = {1.0F, 2.5F, -3.0F};
const Buffer<float> buffer =

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'buffer' is not initialized [cppcoreguidelines-init-variables]

Suggested change
const Buffer<float> buffer =
const Buffer<float> buffer = 0 =

std::vector<uint32_t> region3 = {6, 7, 8, 9};

const std::vector<tcb::span<const uint32_t>> regions = {region1, region2, region3};
const Buffer<uint32_t> buffer = context.new_buffer_from_host_memory<uint32_t>(regions);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'buffer' is not initialized [cppcoreguidelines-init-variables]

Suggested change
const Buffer<uint32_t> buffer = context.new_buffer_from_host_memory<uint32_t>(regions);
const Buffer<uint32_t> buffer = 0 = context.new_buffer_from_host_memory<uint32_t>(regions);

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 40. Check the log or trigger a new build to see more.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

@Lestropie
Copy link
Member

$ ./build/bin/mrtrix-unit-tests.exe --gtest_filter=GPUTest*
Running main() from C:/msys64/home/rob/src/mrtrix3/build/_deps/googletest-src/googletest/src/gtest_main.cc
Note: Google Test filter = GPUTest*
[==========] Running 16 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 16 tests from GPUTest
[ RUN      ] GPUTest.MakeEmptyBuffer
[       OK ] GPUTest.MakeEmptyBuffer (0 ms)
[ RUN      ] GPUTest.BufferFromHostMemory
[       OK ] GPUTest.BufferFromHostMemory (0 ms)
[ RUN      ] GPUTest.BufferFromHostMemoryVoidPtr
[       OK ] GPUTest.BufferFromHostMemoryVoidPtr (0 ms)
[ RUN      ] GPUTest.BufferFromHostMemoryMultipleRegions
[       OK ] GPUTest.BufferFromHostMemoryMultipleRegions (0 ms)
[ RUN      ] GPUTest.WriteToBuffer
[       OK ] GPUTest.WriteToBuffer (0 ms)
[ RUN      ] GPUTest.WriteToBufferWithOffset
[       OK ] GPUTest.WriteToBufferWithOffset (0 ms)
[ RUN      ] GPUTest.EmptyTexture
[       OK ] GPUTest.EmptyTexture (0 ms)
[ RUN      ] GPUTest.KernelWithInlineShader
[       OK ] GPUTest.KernelWithInlineShader (38 ms)
[ RUN      ] GPUTest.ShaderConstants
[       OK ] GPUTest.ShaderConstants (11 ms)
[ RUN      ] GPUTest.ShaderEntryPointArgs
[       OK ] GPUTest.ShaderEntryPointArgs (21 ms)
[ RUN      ] GPUTest.CopyBufferToBuffer_Full
[       OK ] GPUTest.CopyBufferToBuffer_Full (0 ms)
[ RUN      ] GPUTest.CopyBufferToBuffer_Partial
[       OK ] GPUTest.CopyBufferToBuffer_Partial (0 ms)
[ RUN      ] GPUTest.CopyBufferToBuffer_SourceOutOfRangeThrows
[       OK ] GPUTest.CopyBufferToBuffer_SourceOutOfRangeThrows (0 ms)
[ RUN      ] GPUTest.CopyBufferToBuffer_DestinationOutOfRangeThrows
[       OK ] GPUTest.CopyBufferToBuffer_DestinationOutOfRangeThrows (0 ms)
[ RUN      ] GPUTest.ClearBuffer
[       OK ] GPUTest.ClearBuffer (0 ms)
[ RUN      ] GPUTest.DownloadBufferAsVector
[       OK ] GPUTest.DownloadBufferAsVector (0 ms)
[----------] 16 tests from GPUTest (131 ms total)

[----------] Global test environment tear-down
[==========] 16 tests from 1 test suite ran. (657 ms total)
[  PASSED  ] 16 tests.

@daljit46
Copy link
Member Author

daljit46 commented Dec 1, 2025

Thanks, that deepens the mystery of why the CI tests are not executing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants