Skip to content

Cloudy-table texture refactor (Cool Refactor 2/3)#510

Open
mabruzzo wants to merge 14 commits into
cholla-hydro:devfrom
mabruzzo:texture-refactor
Open

Cloudy-table texture refactor (Cool Refactor 2/3)#510
mabruzzo wants to merge 14 commits into
cholla-hydro:devfrom
mabruzzo:texture-refactor

Conversation

@mabruzzo
Copy link
Copy Markdown
Collaborator

@mabruzzo mabruzzo commented Apr 28, 2026

To be reviewed after #509 is merged. This is the second in a sequence of 3 PRs to refactor and standardize cooling recipes and components.


Overview

This refactors the cloudy-table texture logic.

The headline change is the creation of the cool_component::CloudyHeatAndCool class. In slightly more detail:

  • this class holds logic factored out from CoolRecipeCloudy
  • CoolRecipeCloudy and CoolRecipeCloudyAndPhotoHeating have now both been refactored to wrap this type (with this change, a cooling recipe NEVER wraps another cooling recipe)

Other Changes

Start using SharedHandle<cudaTextureObject_t>

As part of this PR, I started to make use of SharedHandle<cudaTextureObject_t> inside of cool_component::CloudyHeatAndCool. For context, I introduced the SharedHandle<HandleT> class template within #509. Rather than track 2 raw cudaTextureObject_t instances within the class (for the cooling & heating tables), we now track 2 SharedHandle<cudaTextureObject_t> instances.

  • Essentially, a SharedHandle<cudaTextureObject_t> instance wraps cudaTextureObject_t handle and uses it to exercise ownership over the associated texture.
  • You can get access to the underlying handle by calling the get() method.
  • Importantly, SharedHandle<cudaTextureObject_t> models shared ownership
    • When you make a copy of a given instance of SharedHandle<cudaTextureObject_t> (e.g. by copying a cool_component::CloudyHeatAndCool that tracked that instance) the new copy and the original instance share ownership of the associated texture.
    • When all owners release ownership1 the texture is deleted2.
  • Importantly, you can seamlessly pass a SharedHandle<cudaTextureObject_t> directly to a GPU kernel

All of this allowed us to stop tracking the cudaTextureObject_t as global variables and to actually avoid memory leaks (previously we never actually free-ed the memory). The only way to avoid the memory leaks (something we want to do in tests) in a composable manner (i.e. where we can directly pass the cool_component::CloudyHeatAndCool to a global function) and without move-semantics (more about that in the next paragraph) is if we did some kind of crude reference counting. If we are going to reference count, we might as well use a SharedHandle<cudaTextureObject_t>. (As an added bonus, we can avoid making new global variables for each kind of texture we may want to use).

I spent a bunch of time going down a dead-end where I tried giving cool_component::CloudyHeatAndCool the move-semantics of a std::unique_ptr, but the following 2 factors convinced me not to do it

  1. I had forgotten that doing this meant we would have to stop wrapping CoolingUpdateExecutor<CoolingRecipe> in a std::function<void(Grid3D &)>. This is surmountable: we could either use something like C++23's std::move_only_function<void(Grid3D &)> OR we could create a custom abstract base class and store CoolingUpdateExecutor<CoolingRecipe> in a subclass
  2. I had concerns that other cholla contributors would find move semantics to be confusing. Furthermore, passing a cool_component::CloudyHeatAndCool directly to a global function becomes tricky. We either:
    • must unwrap the underlying texture object and explicitly pass it as a separate argument to the global function (this would make it hard to compose cooling recipes out of multiple distinct components)
    • Or, we could make the act of passing a cool_component::CloudyHeatAndCool to a global function act as an exception of move-semantics. While inelegant, I'm not ideologically opposed. I just don't think its a good idea since I'm already concerned about people being intimidated by move-semantics.

Other stuff

I also got the the legacy test-logic from load_cloudy_texture.cu, the Test_Cloudy_Textures() and Test_Cloudy_Speed() functions, running in the automated test suite. I disabled the former (to avoid printing lots of lines).

NOTE: they don't actually check the computed values.

Footnotes

  1. A SharedHandle<cudaTextureObject_t> instance releases ownership over a texture if it the instance goes out of scope (i.e. the destructor gets called), calling the reset() method, or it takes on ownership of a different texture. The last case occurs if you have 2 shared handles (sh_A & sh_B) that don't own the same handle and you perform some kind of assignment (e.g. sh_A = sh_B;) or swap the contents (e.g. sh_A.swap(sh_B));

  2. The precise deletion logic is specified by a callback that is provided when you construct a shared handle

Comment thread src/cooling/load_cloudy_texture.h Outdated
Comment thread src/cooling/load_cloudy_texture.h Outdated
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