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

[Impeller] exploit content context options' perfect hash function. #56360

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <utility>

#include "fml/trace_event.h"
#include "impeller/base/strings.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/core/texture_descriptor.h"
Expand Down
86 changes: 36 additions & 50 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,47 +315,27 @@ struct ContentContextOptions {
bool wireframe = false;
bool is_for_rrect_blur_clear = false;

struct Hash {
constexpr uint64_t operator()(const ContentContextOptions& o) const {
static_assert(sizeof(o.sample_count) == 1);
static_assert(sizeof(o.blend_mode) == 1);
static_assert(sizeof(o.sample_count) == 1);
static_assert(sizeof(o.depth_compare) == 1);
static_assert(sizeof(o.stencil_mode) == 1);
static_assert(sizeof(o.primitive_type) == 1);
static_assert(sizeof(o.color_attachment_pixel_format) == 1);

return (o.is_for_rrect_blur_clear ? 1llu : 0llu) << 0 |
(o.wireframe ? 1llu : 0llu) << 1 |
(o.has_depth_stencil_attachments ? 1llu : 0llu) << 2 |
(o.depth_write_enabled ? 1llu : 0llu) << 3 |
// enums
static_cast<uint64_t>(o.color_attachment_pixel_format) << 8 |
static_cast<uint64_t>(o.primitive_type) << 16 |
static_cast<uint64_t>(o.stencil_mode) << 24 |
static_cast<uint64_t>(o.depth_compare) << 32 |
static_cast<uint64_t>(o.blend_mode) << 40 |
static_cast<uint64_t>(o.sample_count) << 48;
}
};

struct Equal {
constexpr bool operator()(const ContentContextOptions& lhs,
const ContentContextOptions& rhs) const {
return lhs.sample_count == rhs.sample_count &&
lhs.blend_mode == rhs.blend_mode &&
lhs.depth_write_enabled == rhs.depth_write_enabled &&
lhs.depth_compare == rhs.depth_compare &&
lhs.stencil_mode == rhs.stencil_mode &&
lhs.primitive_type == rhs.primitive_type &&
lhs.color_attachment_pixel_format ==
rhs.color_attachment_pixel_format &&
lhs.has_depth_stencil_attachments ==
rhs.has_depth_stencil_attachments &&
lhs.wireframe == rhs.wireframe &&
lhs.is_for_rrect_blur_clear == rhs.is_for_rrect_blur_clear;
}
};
constexpr uint64_t ToKey() const {
static_assert(sizeof(sample_count) == 1);
static_assert(sizeof(blend_mode) == 1);
static_assert(sizeof(sample_count) == 1);
static_assert(sizeof(depth_compare) == 1);
static_assert(sizeof(stencil_mode) == 1);
static_assert(sizeof(primitive_type) == 1);
static_assert(sizeof(color_attachment_pixel_format) == 1);

return (is_for_rrect_blur_clear ? 1llu : 0llu) << 0 |
(wireframe ? 1llu : 0llu) << 1 |
(has_depth_stencil_attachments ? 1llu : 0llu) << 2 |
(depth_write_enabled ? 1llu : 0llu) << 3 |
// enums
static_cast<uint64_t>(color_attachment_pixel_format) << 8 |
static_cast<uint64_t>(primitive_type) << 16 |
static_cast<uint64_t>(stencil_mode) << 24 |
static_cast<uint64_t>(depth_compare) << 32 |
static_cast<uint64_t>(blend_mode) << 40 |
static_cast<uint64_t>(sample_count) << 48;
}

void ApplyToPipelineDescriptor(PipelineDescriptor& desc) const;
};
Expand Down Expand Up @@ -771,15 +751,15 @@ class ContentContext {
struct Hash {
std::size_t operator()(const RuntimeEffectPipelineKey& key) const {
return fml::HashCombine(key.unique_entrypoint_name,
ContentContextOptions::Hash{}(key.options));
key.options.ToKey());
}
};

struct Equal {
constexpr bool operator()(const RuntimeEffectPipelineKey& lhs,
const RuntimeEffectPipelineKey& rhs) const {
return lhs.unique_entrypoint_name == rhs.unique_entrypoint_name &&
ContentContextOptions::Equal{}(lhs.options, rhs.options);
lhs.options.ToKey() == rhs.options.ToKey();
}
};
};
Expand Down Expand Up @@ -810,7 +790,13 @@ class ContentContext {

void Set(const ContentContextOptions& options,
std::unique_ptr<PipelineHandleT> pipeline) {
pipelines_[options] = std::move(pipeline);
uint64_t p_key = options.ToKey();
for (const auto& [key, pipeline] : pipelines_) {
if (key == p_key) {
return;
}
}
pipelines_.push_back(std::make_pair(p_key, std::move(pipeline)));
}

void SetDefault(const ContentContextOptions& options,
Expand All @@ -833,8 +819,11 @@ class ContentContext {
}

PipelineHandleT* Get(const ContentContextOptions& options) const {
if (auto found = pipelines_.find(options); found != pipelines_.end()) {
return found->second.get();
uint64_t p_key = options.ToKey();
for (const auto& [key, pipeline] : pipelines_) {
if (key == p_key) {
return pipeline.get();
}
}
return nullptr;
}
Expand All @@ -850,10 +839,7 @@ class ContentContext {

private:
std::optional<ContentContextOptions> default_options_;
std::unordered_map<ContentContextOptions,
std::unique_ptr<PipelineHandleT>,
ContentContextOptions::Hash,
ContentContextOptions::Equal>
std::vector<std::pair<uint64_t, std::unique_ptr<PipelineHandleT>>>
pipelines_;

Variants(const Variants&) = delete;
Expand Down
8 changes: 4 additions & 4 deletions impeller/entity/entity_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2170,16 +2170,16 @@ TEST_P(EntityTest, DecalSpecializationAppliedToMorphologyFilter) {
// set of options, we can just compare benchmarks.
TEST_P(EntityTest, ContentContextOptionsHasReasonableHashFunctions) {
ContentContextOptions opts;
auto hash_a = ContentContextOptions::Hash{}(opts);
auto hash_a = opts.ToKey();

opts.blend_mode = BlendMode::kColorBurn;
auto hash_b = ContentContextOptions::Hash{}(opts);
auto hash_b = opts.ToKey();

opts.has_depth_stencil_attachments = false;
auto hash_c = ContentContextOptions::Hash{}(opts);
auto hash_c = opts.ToKey();

opts.primitive_type = PrimitiveType::kPoint;
auto hash_d = ContentContextOptions::Hash{}(opts);
auto hash_d = opts.ToKey();

EXPECT_NE(hash_a, hash_b);
EXPECT_NE(hash_b, hash_c);
Expand Down