Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@jonahwilliams
Copy link
Contributor

The backend specific sampler libraries hold a strong reference to the native sampler objects and never clear this cache. As a result of this, we don't theoretically need rendering commands to increment a shared_ptr ref count - instead the sampler library can provide the Sampler object as a const ref and guarantee that it continues to be valid.

This allows us to reduce the amount of refcount ops for commands that use samplers.

Additionally, the sampler library uses nullptr as a sentinel for failing to construct a sampler object. Since sampler already has an isValid member that is checked - we can replace this with a specific invalid object subtype.


/// @brief A sampler subclass to be returned in the result of a lost device
// or other failure to allocate a sampler object.
class InvalidSampler : public Sampler {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not a huge fan of creating an object to represent what essentially a null value. I would prefer that instead of passing in a const Sampler& sampler we passed in const std::unique_ptr<Sampler>& sampler. Then we can keep the semantics all the same.

I think IsValid() is an anti-pattern. We shouldn't have invalid objects. It starts polluting your code where you have to check for validity all the time. Or it doesn't and you just have potential bugs everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't use unique_ptr and have a global cache of samplers.

Copy link
Member

Choose a reason for hiding this comment

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

You already are switching to a unique_ptr but you are dereferencing it below. All you have to do is not dereference this and pass the reference to the unique_ptr.


/// @brief A sampler subclass to be returned in the result of a lost device
// or other failure to allocate a sampler object.
class InvalidSampler : public Sampler {
Copy link
Member

Choose a reason for hiding this comment

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

You already are switching to a unique_ptr but you are dereferencing it below. All you have to do is not dereference this and pass the reference to the unique_ptr.

auto found = samplers_.find(desc);
if (found != samplers_.end()) {
return found->second;
return *found->second.get();
Copy link
Member

Choose a reason for hiding this comment

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

Right here, this is a no-no. You are transforming a std::unique_ptr to a reference. References are non-null, std::unique_ptr are nullable. If you return the reference to the unique_ptr you avoid that class of bugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We cannot return a reference to a unique_ptr though?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(from a hash map, specifically)

Copy link
Member

Choose a reason for hiding this comment

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

Yea, you can. Depending on the method you might have to do that trick where you have a static nullptr std::unique_ptr that you return the reference to sometimes.

Copy link
Member

Choose a reason for hiding this comment

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

Here's an example:

#include <map>
#include <memory>

class Foo {};

static const std::unique_ptr<Foo> kNullFoo;

const std::unique_ptr<Foo>& find(const std::map<int, std::unique_ptr<Foo>>& map) {
  const auto& it = map.find(123);
  if (it == map.end()) {
    return kNullFoo;
  } else {
    return it->second;
  }
}
int main() {
  std::map<int, std::unique_ptr<Foo>> map;

  map.insert(std::make_pair(123, std::make_unique<Foo>()));

  const std::unique_ptr<Foo>& value = find(map);

  return 0;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pushed it up

Copy link
Member

Choose a reason for hiding this comment

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

You have to make it return a const std::unique_ptr<const Sampler>& to fix what you have right now. I'm not sure if a const Sampler will work for you. I don't know why that is off the bat

--- a/impeller/renderer/backend/vulkan/sampler_library_vk.cc
+++ b/impeller/renderer/backend/vulkan/sampler_library_vk.cc
@@ -16,9 +16,9 @@ SamplerLibraryVK::SamplerLibraryVK(
 
 SamplerLibraryVK::~SamplerLibraryVK() = default;
 
-static const std::unique_ptr<Sampler> kNullSampler = nullptr;
+static const std::unique_ptr<const Sampler> kNullSampler = nullptr;
 
-static const std::unique_ptr<Sampler>& find_thing(
+static const std::unique_ptr<const Sampler>& find_thing(
     const SamplerMap& map,
     const SamplerDescriptor& desc) {
   const auto& it = map.find(desc);

Copy link
Member

Choose a reason for hiding this comment

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

(since you were using a const Sampler& it should be good)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OH DUH

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OKAY! I DID IT

Thanks @gaaclarke I would have never figured that out!

@jonahwilliams jonahwilliams changed the title [Impeller] use const ref for Sampler type. [Impeller] use const std::unique_ptr ref for Sampler type. Jan 23, 2024
SampledImageSlot slot;
TextureResource texture;
std::shared_ptr<const Sampler> sampler;
const std::unique_ptr<const Sampler>& sampler;
Copy link
Member

Choose a reason for hiding this comment

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

This is potentially a dangling pointer now. What guarantees do we have that this reference will remain valid for the lifetime of the TextureAndSampler?

If we can be confident that it will remain valid, we should add documentation. If we aren't so sure I think maybe we should go back to std::shared_ptr but keep all your work converting the arguments to references. That would eliminate all those shared_ptr copies, except this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The platform sampler libraries store the sampler in a unordered_map (which has stable references even when it reallocates) and never evicts them. This should keep the references valid for the lifetime of the program.

Copy link
Member

Choose a reason for hiding this comment

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

This should keep the references valid for the lifetime of the program.

The lifetime of the Context, right? Can you add a comment for this please.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I think a comment right here at the field would be appropriate too since looking at this code peoples spider sense tingles for dangling pointers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code is almost done being deleted :)

Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

lgtm!

@jonahwilliams jonahwilliams added autosubmit Merge PR when tree becomes green via auto submit App and removed autosubmit Merge PR when tree becomes green via auto submit App labels Jan 24, 2024
@auto-submit auto-submit bot merged commit 4bc368e into flutter:main Jan 24, 2024
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Jan 24, 2024
…142157)

flutter/engine@0b9fce3...4bc368e

2024-01-24 [email protected] [Impeller] use const std::unique_ptr ref for Sampler type. (flutter/engine#49974)
2024-01-24 [email protected] Roll Skia from c353c00a4dcb to 9ea7cb490804 (1 revision) (flutter/engine#50005)
2024-01-24 [email protected] Replace Fuchsia logging macros (FX_LOG*) with FML logging (flutter/engine#49970)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

autosubmit Merge PR when tree becomes green via auto submit App e: impeller

Projects

No open projects
Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants