chore: bump chromium to 146.0.7680.208 (41-x-y) (#51089)

* chore: bump chromium in DEPS to 146.0.7680.201

* chore: update patches

* chore: bump chromium in DEPS to 146.0.7680.208

* chore: update patches

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
This commit is contained in:
electron-roller[bot]
2026-04-28 12:38:31 -04:00
committed by GitHub
parent 9867bbbde8
commit f596e35554
18 changed files with 8 additions and 1969 deletions

2
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'146.0.7680.188',
'146.0.7680.208',
'node_version':
'v24.15.0',
'nan_version':

View File

@@ -157,19 +157,14 @@ fix_initialize_com_on_desktopmedialistcapturethread_on_windows.patch
fix_use_fresh_lazynow_for_onendworkitemimpl_after_didruntask.patch
cherry-pick-4073d491fb55.patch
cherry-pick-8c1ead5a699f.patch
cherry-pick-8b08fb7c9dce.patch
cherry-pick-be87466afecb.patch
cherry-pick-c215f8e6f049.patch
cherry-pick-a6357144e7bf.patch
cherry-pick-41bfbc009df8.patch
cherry-pick-4002a66778d2.patch
cherry-pick-23865499a86a.patch
cherry-pick-c81f01b469c4.patch
cherry-pick-1b69067db7d2.patch
cherry-pick-d513cd2fe668.patch
cherry-pick-bb8d4c29dfdb.patch
cherry-pick-847b11ad2fa3.patch
cherry-pick-eeb3e031eb89.patch
cherry-pick-fccaeb9e0967.patch
cherry-pick-d141d62357df.patch
cherry-pick-c75f63de7188.patch

View File

@@ -1,224 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Fergal Daly <fergal@chromium.org>
Date: Sun, 12 Apr 2026 20:37:39 -0700
Subject: [M146] Fix UAF in FileSystemAccessChangeSource.
Original change's description:
> Fix UAF in FileSystemAccessChangeSource.
>
> `DidInitialize` calls any outstanding initialization callbacks but a
> callback can delete this. The code guards against this in its access
> of `initialization_callbacks_` but not `initialization_result_`.
>
> This fix keeps a copy of the result on the stack.
>
> This also adds a test which fails with ASAN before the fix is applied
> and passes after.
>
> The basic test code was written by Gemini.
>
> Fixed: 497880137
> Change-Id: I046831db23cb4b8e41964910e2aede9b1be0db7f
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7728464
> Auto-Submit: Fergal Daly <fergal@chromium.org>
> Reviewed-by: Ming-Ying Chung <mych@chromium.org>
> Commit-Queue: Ming-Ying Chung <mych@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1610499}
(cherry picked from commit c0390bcd64ba1fd6594fbc9f6246a1649662d683)
Bug: 500247135,497880137
Change-Id: I046831db23cb4b8e41964910e2aede9b1be0db7f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7754020
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Auto-Submit: Chrome Cherry Picker <chrome-cherry-picker@chops-service-accounts.iam.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/7680@{#3929}
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
diff --git a/content/browser/file_system_access/file_system_access_change_source.cc b/content/browser/file_system_access/file_system_access_change_source.cc
index 566dc1ea40b43a54b33d70e82a20ff5695b57b5e..48bd867a9d3d140eaf515ea7bc1613231f7e79e9 100644
--- a/content/browser/file_system_access/file_system_access_change_source.cc
+++ b/content/browser/file_system_access/file_system_access_change_source.cc
@@ -71,13 +71,14 @@ void FileSystemAccessChangeSource::DidInitialize(
CHECK(!initialization_result_.has_value());
CHECK(!initialization_callbacks_.empty());
- initialization_result_ = std::move(result);
+ // The callbacks may cause |this| to be deleted, so we should only use
+ // stack-based objects below.
+ initialization_result_ = result->Clone();
- // Move the callbacks to the stack since they may cause |this| to be deleted.
auto initialization_callbacks = std::move(initialization_callbacks_);
initialization_callbacks_.clear();
for (auto& callback : initialization_callbacks) {
- std::move(callback).Run(initialization_result_->Clone());
+ std::move(callback).Run(result->Clone());
}
}
diff --git a/content/browser/file_system_access/file_system_access_change_source_unittest.cc b/content/browser/file_system_access/file_system_access_change_source_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b0f15909bebda29fc2ec689a6d3b15d797dcc722
--- /dev/null
+++ b/content/browser/file_system_access/file_system_access_change_source_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/file_system_access/file_system_access_change_source.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/functional/bind.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "content/browser/file_system_access/file_system_access_watch_scope.h"
+#include "storage/browser/file_system/file_system_context.h"
+#include "storage/browser/file_system/file_system_url.h"
+#include "storage/browser/quota/quota_manager_proxy.h"
+#include "storage/browser/test/test_file_system_context.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom.h"
+
+namespace content {
+
+namespace {
+
+class MockRawChangeObserver
+ : public FileSystemAccessChangeSource::RawChangeObserver {
+ public:
+ MOCK_METHOD(void,
+ OnRawChange,
+ (const storage::FileSystemURL& changed_url,
+ bool error,
+ const FileSystemAccessChangeSource::ChangeInfo& change_info,
+ const FileSystemAccessWatchScope& scope),
+ (override));
+ MOCK_METHOD(void,
+ OnUsageChange,
+ (size_t old_usage,
+ size_t new_usage,
+ const FileSystemAccessWatchScope& scope),
+ (override));
+ MOCK_METHOD(void,
+ OnSourceBeingDestroyed,
+ (FileSystemAccessChangeSource * source),
+ (override));
+};
+
+class FakeChangeSource : public FileSystemAccessChangeSource {
+ public:
+ FakeChangeSource(
+ FileSystemAccessWatchScope scope,
+ scoped_refptr<storage::FileSystemContext> file_system_context)
+ : FileSystemAccessChangeSource(std::move(scope),
+ std::move(file_system_context)) {}
+ ~FakeChangeSource() override = default;
+
+ // FileSystemAccessChangeSource:
+ void Initialize(
+ base::OnceCallback<void(blink::mojom::FileSystemAccessErrorPtr)>
+ on_source_initialized) override {
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(on_source_initialized),
+ blink::mojom::FileSystemAccessError::New(
+ blink::mojom::FileSystemAccessStatus::kOk,
+ base::File::FILE_OK, "")));
+ }
+
+ void Signal(const storage::FileSystemURL& changed_url,
+ bool error = false,
+ ChangeInfo change_info = ChangeInfo()) {
+ NotifyOfChange(changed_url, error, change_info);
+ }
+};
+
+} // namespace
+
+class FileSystemAccessChangeSourceTest : public testing::Test {
+ public:
+ FileSystemAccessChangeSourceTest()
+ : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}
+
+ void SetUp() override {
+ ASSERT_TRUE(dir_.CreateUniqueTempDir());
+ file_system_context_ = storage::CreateFileSystemContextForTesting(
+ /*quota_manager_proxy=*/nullptr, dir_.GetPath());
+ }
+
+ protected:
+ base::test::TaskEnvironment task_environment_;
+ base::ScopedTempDir dir_;
+ scoped_refptr<storage::FileSystemContext> file_system_context_;
+};
+
+TEST_F(FileSystemAccessChangeSourceTest, CreateAndInitialize) {
+ auto file_path = dir_.GetPath().AppendASCII("file");
+ auto file_url = file_system_context_->CreateCrackedFileSystemURL(
+ blink::StorageKey(), storage::kFileSystemTypeLocal, file_path);
+
+ auto scope = FileSystemAccessWatchScope::GetScopeForFileWatch(file_url);
+ FakeChangeSource source(scope, file_system_context_);
+
+ base::test::TestFuture<blink::mojom::FileSystemAccessErrorPtr> future;
+ source.EnsureInitialized(future.GetCallback());
+ EXPECT_EQ(future.Get()->status, blink::mojom::FileSystemAccessStatus::kOk);
+}
+
+TEST_F(FileSystemAccessChangeSourceTest, NotifyOfChange) {
+ auto file_path = dir_.GetPath().AppendASCII("file");
+ auto file_url = file_system_context_->CreateCrackedFileSystemURL(
+ blink::StorageKey(), storage::kFileSystemTypeLocal, file_path);
+
+ auto scope = FileSystemAccessWatchScope::GetScopeForFileWatch(file_url);
+ FakeChangeSource source(scope, file_system_context_);
+
+ MockRawChangeObserver observer;
+ source.AddObserver(&observer);
+
+ EXPECT_CALL(observer, OnRawChange(testing::Eq(file_url), testing::IsFalse(),
+ testing::_, testing::Eq(scope)));
+ source.Signal(file_url);
+
+ source.RemoveObserver(&observer);
+}
+
+// A callback passed to `EnsureInitialized` may result in `this` being
+// destroyed. This tests that `DidInitialize` (which calls the callbacks) is
+// robust to that situation. See https://crbug.com/497880137.
+TEST_F(FileSystemAccessChangeSourceTest, TestDestroyFromInitializeCallback) {
+ auto file_path = dir_.GetPath().AppendASCII("file");
+ auto file_url = file_system_context_->CreateCrackedFileSystemURL(
+ blink::StorageKey(), storage::kFileSystemTypeLocal, file_path);
+
+ auto scope = FileSystemAccessWatchScope::GetScopeForFileWatch(file_url);
+ FakeChangeSource* source = new FakeChangeSource(scope, file_system_context_);
+
+ source->EnsureInitialized(base::BindOnce(
+ [](FakeChangeSource* source, blink::mojom::FileSystemAccessErrorPtr) {
+ delete source;
+ },
+ base::Unretained(source)));
+ base::test::TestFuture<blink::mojom::FileSystemAccessErrorPtr> future;
+ source->EnsureInitialized(future.GetCallback());
+ EXPECT_EQ(future.Get()->status, blink::mojom::FileSystemAccessStatus::kOk);
+}
+
+} // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 07cbf495717714d71d977a8820e08050c3062526..f5d72a89c7229bf8e897c90660feca482ac82594 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2656,6 +2656,7 @@ test("content_unittests") {
"../browser/fenced_frame/redacted_fenced_frame_config_mojom_traits_unittest.cc",
"../browser/file_system/browser_file_system_helper_unittest.cc",
"../browser/file_system/file_system_operation_runner_unittest.cc",
+ "../browser/file_system_access/file_system_access_change_source_unittest.cc",
"../browser/file_system_access/file_system_access_directory_handle_impl_unittest.cc",
"../browser/file_system_access/file_system_access_file_handle_impl_unittest.cc",
"../browser/file_system_access/file_system_access_file_modification_host_impl_unittest.cc",

View File

@@ -1,67 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: p0-tato <smartphonewithbear@gmail.com>
Date: Tue, 14 Apr 2026 13:14:30 -0700
Subject: [M146] Fix dangling pointers in OpenXrSpatialFrameworkManager
Original change's description:
> Fix dangling pointers in OpenXrSpatialFrameworkManager
>
> Pointers to vector elements were collected during emplace_back,
> which invalidates them on reallocation. Split into two loops
> and reserve the correct capacity.
>
> Bug: 497724498
> Change-Id: I204534bc1bd1522fe03db86f03c2c3e0d285631c
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7735242
> Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
> Reviewed-by: Brian Sheedy <bsheedy@chromium.org>
> Reviewed-by: Brandon Jones <bajones@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1613990}
(cherry picked from commit b173791bf4026a6bb43124f7c5f46cfa4539c014)
Bug: 502440265,497724498
Change-Id: I204534bc1bd1522fe03db86f03c2c3e0d285631c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7759844
Auto-Submit: chrome-cherry-picker@chops-service-accounts.iam.gserviceaccount.com <chrome-cherry-picker@chops-service-accounts.iam.gserviceaccount.com>
Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/7680@{#3944}
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
diff --git a/AUTHORS b/AUTHORS
index 7cc777b399ab46f88b6b1809bf6fd0cb22170694..505480b09c1d41b1facf4e2b165bad86b1815127 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -729,6 +729,7 @@ Jihoon Chung <jihoon@gmail.com>
Jihun Brent Kim <devgrapher@gmail.com>
Jihwan Marc Kim <bluewhale.marc@gmail.com>
Jihye Hyun <jijinny26@gmail.com>
+Jihyeon Jeong <smartphonewithbear@gmail.com>
Jihyeon Lee <wlgus7464@gmail.com>
Jim Wu <lofoz.tw@gmail.com>
Jin Yang <jin.a.yang@intel.com>
diff --git a/device/vr/openxr/openxr_spatial_framework_manager.cc b/device/vr/openxr/openxr_spatial_framework_manager.cc
index 520f25230c427bf775333910530d1ad841f3ad71..5c93d694aa5a2259c683f1d521611046293195a2 100644
--- a/device/vr/openxr/openxr_spatial_framework_manager.cc
+++ b/device/vr/openxr/openxr_spatial_framework_manager.cc
@@ -71,12 +71,15 @@ OpenXrSpatialFrameworkManager::OpenXrSpatialFrameworkManager(
// to help abstract some of the details of creating the child structs, even
// though at present we only have a configuration base.
std::vector<OpenXrSpatialCapabilityConfigurationBase> capability_configs;
- std::vector<XrSpatialCapabilityConfigurationBaseHeaderEXT*>
- capability_config_ptrs;
+ capability_configs.reserve(capability_configuration.size());
for (auto& [capability, components] : capability_configuration) {
capability_configs.emplace_back(capability, components);
- capability_config_ptrs.push_back(
- capability_configs.back().GetAsBaseHeader());
+ }
+
+ std::vector<XrSpatialCapabilityConfigurationBaseHeaderEXT*>
+ capability_config_ptrs;
+ for (auto& config : capability_configs) {
+ capability_config_ptrs.push_back(config.GetAsBaseHeader());
}
XrSpatialContextCreateInfoEXT create_info = {

View File

@@ -1,94 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sunny Sachanandani <sunnyps@chromium.org>
Date: Fri, 10 Apr 2026 23:37:43 -0700
Subject: [M146] [gpu] Fix OOB write due to unvalidated get_offset
Original change's description:
> [gpu] Fix OOB write due to unvalidated get_offset
>
> A compromised GPU process can provide an invalid get_offset to the
> CommandBufferHelper (e.g., via shared memory). This offset is used to
> calculate available space and could lead to out-of-bounds writes in the
> Browser process if not validated.
>
> This change adds a bounds check in
> CommandBufferHelper::UpdateCachedState to ensure that the cached
> get_offset is within the valid range [0, total_entry_count_]. If an
> invalid offset is detected, it forces a context loss, frees the ring
> buffer, and marks the helper as unusable, preventing further operations.
>
> Bug: 498782145
> Test: CommandBufferHelperTest.*
> Change-Id: I8c64e546ecdc90a5a22d15e57ff762a86a6a6964
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7739951
> Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
> Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
> Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1611853}
(cherry picked from commit dc5e20c4c055d6952854a566d520211c6d505f74)
Bug: 498782145
Fixed: 500956607
Change-Id: Ia726612e0a930ee79460fbd7d795afa4d94e2a7b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7745786
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Cr-Commit-Position: refs/branch-heads/7680@{#3919}
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.cc b/gpu/command_buffer/client/cmd_buffer_helper.cc
index ccda45b133c6a9f2ee60ccc8900bd4a4ce328394..5aea0c81b29b3507099f399c374f3cb372a3100e 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper.cc
@@ -158,6 +158,17 @@ void CommandBufferHelper::UpdateCachedState(const CommandBuffer::State& state) {
service_on_old_buffer_ =
(state.set_get_buffer_count != set_get_buffer_count_);
cached_get_offset_ = service_on_old_buffer_ ? 0 : state.get_offset;
+
+ if (!service_on_old_buffer_ &&
+ (cached_get_offset_ < 0 || cached_get_offset_ > total_entry_count_)) {
+ command_buffer_->ForceLostContext(error::kGuilty);
+ FreeRingBuffer();
+ usable_ = false;
+ context_lost_ = true;
+ cached_get_offset_ = 0; // Safe fallback
+ return;
+ }
+
cached_last_token_read_ = state.token;
// Don't transition from a lost context to a working context.
context_lost_ |= error::IsError(state.error);
diff --git a/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
index 1b9254d318ae770ca980d2fed1399a69438afa10..009a87e8bf7a3475f63cd51206868dec187f5e06 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper_test.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
@@ -67,6 +67,8 @@ class CommandBufferHelperTest : public testing::Test {
return helper_->immediate_entry_count_;
}
+ int32_t TotalEntryCount() const { return helper_->total_entry_count_; }
+
// Adds a command to the buffer through the helper, while adding it as an
// expected call on the API mock.
void AddCommandWithExpect(error::Error _return,
@@ -655,6 +657,17 @@ TEST_F(CommandBufferHelperTest, IsContextLost) {
EXPECT_TRUE(helper_->IsContextLost());
}
+TEST_F(CommandBufferHelperTest, TestInvalidGetOffset) {
+ EXPECT_FALSE(helper_->IsContextLost());
+ EXPECT_TRUE(helper_->usable());
+
+ command_buffer_->SetGetOffsetForTest(TotalEntryCount() + 1);
+ helper_->RefreshCachedToken(); // calls UpdateCachedState internally.
+
+ EXPECT_TRUE(helper_->IsContextLost());
+ EXPECT_FALSE(helper_->usable());
+}
+
// Checks helper's 'flush generation' updates.
TEST_F(CommandBufferHelperTest, TestFlushGeneration) {
// Explicit flushing only.

View File

@@ -1,224 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonathan Ross <jonross@chromium.org>
Date: Wed, 8 Apr 2026 17:15:45 -0700
Subject: gl: Make DCOMPSurfaceRegistry thread-safe
DCOMPSurfaceRegistry is accessed from both the GPU IO thread (via
GpuServiceImpl) and the GPU main scheduler thread (via DCOMPTexture).
The underlying base::flat_map is not thread-safe, leading to potential
container corruption and crashes (UAF, BOf) during concurrent access.
This CL adds a base::Lock to protect all accesses to the map and
includes a new multi-threaded stress test to verify the fix.
Bug: 493315759
Change-Id: Ibb7ef5e602f222410fde06a61fb3f5e571e7a70f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7737061
Reviewed-by: Sunny Sachanandani <sunnyps@chromium.org>
Commit-Queue: Jonathan Ross <jonross@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1611867}
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 3584b693370b5199456608a26ceb763f6e9c3446..1cb66199a0b8adf2035a05fecc411c67180f7e80 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -552,6 +552,7 @@ test("gl_unittests") {
if (is_win) {
sources += [
"dcomp_presenter_unittest.cc",
+ "dcomp_surface_registry_unittest.cc",
"delegated_ink_point_renderer_gpu_unittest.cc",
"gl_fence_win_unittest.cc",
"hdr_metadata_helper_win_unittest.cc",
diff --git a/ui/gl/dcomp_surface_registry.cc b/ui/gl/dcomp_surface_registry.cc
index 352cc298b9ea97361ae2a7d668b7d7e9eb455cd5..410f76f8980438abae32b6c89e7083ae48cf1699 100644
--- a/ui/gl/dcomp_surface_registry.cc
+++ b/ui/gl/dcomp_surface_registry.cc
@@ -3,8 +3,11 @@
// found in the LICENSE file.
#include "ui/gl/dcomp_surface_registry.h"
+
+#include "base/check.h"
#include "base/logging.h"
#include "base/no_destructor.h"
+#include "base/synchronization/lock.h"
namespace gl {
@@ -20,8 +23,11 @@ base::UnguessableToken DCOMPSurfaceRegistry::RegisterDCOMPSurfaceHandle(
base::win::ScopedHandle surface) {
DVLOG(1) << __func__;
base::UnguessableToken token = base::UnguessableToken::Create();
- DCHECK(surface_handle_map_.find(token) == surface_handle_map_.end());
- surface_handle_map_[token] = std::move(surface);
+ {
+ base::AutoLock lock(lock_);
+ DCHECK(surface_handle_map_.find(token) == surface_handle_map_.end());
+ surface_handle_map_[token] = std::move(surface);
+ }
DVLOG(1) << __func__ << ": Surface handle registered with token " << token;
return token;
}
@@ -29,12 +35,14 @@ base::UnguessableToken DCOMPSurfaceRegistry::RegisterDCOMPSurfaceHandle(
void DCOMPSurfaceRegistry::UnregisterDCOMPSurfaceHandle(
const base::UnguessableToken& token) {
DVLOG(1) << __func__;
+ base::AutoLock lock(lock_);
surface_handle_map_.erase(token);
}
base::win::ScopedHandle DCOMPSurfaceRegistry::TakeDCOMPSurfaceHandle(
const base::UnguessableToken& token) {
DVLOG(1) << __func__;
+ base::AutoLock lock(lock_);
auto surface_iter = surface_handle_map_.find(token);
if (surface_iter != surface_handle_map_.end()) {
// Take ownership.
diff --git a/ui/gl/dcomp_surface_registry.h b/ui/gl/dcomp_surface_registry.h
index 803a3cc6398f0777504063118920998869086d7f..7cd9fdbfe8669bc97d4b664fdb29573ec2ea26de 100644
--- a/ui/gl/dcomp_surface_registry.h
+++ b/ui/gl/dcomp_surface_registry.h
@@ -7,6 +7,7 @@
#include "base/containers/flat_map.h"
#include "base/no_destructor.h"
+#include "base/synchronization/lock.h"
#include "base/unguessable_token.h"
#include "base/win/scoped_handle.h"
#include "ui/gl/gl_export.h"
@@ -44,7 +45,9 @@ class GL_EXPORT DCOMPSurfaceRegistry {
~DCOMPSurfaceRegistry();
base::flat_map<base::UnguessableToken, base::win::ScopedHandle>
- surface_handle_map_;
+ surface_handle_map_ GUARDED_BY(lock_);
+
+ base::Lock lock_;
};
} // namespace gl
diff --git a/ui/gl/dcomp_surface_registry_unittest.cc b/ui/gl/dcomp_surface_registry_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..595e2388e9f50df33214359ecef0c135d94610b8
--- /dev/null
+++ b/ui/gl/dcomp_surface_registry_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gl/dcomp_surface_registry.h"
+
+#include <windows.h>
+
+#include <atomic>
+#include <thread>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/unguessable_token.h"
+#include "base/win/scoped_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gl {
+
+namespace {
+
+class DCOMPSurfaceRegistryTest : public testing::Test {
+ public:
+ void SetUp() override { registry_ = DCOMPSurfaceRegistry::GetInstance(); }
+
+ protected:
+ raw_ptr<DCOMPSurfaceRegistry> registry_;
+};
+
+} // namespace
+
+// Stress test for concurrent access to DCOMPSurfaceRegistry using the
+// barrier pattern to ensure TSAN consistently catches data races.
+//
+// Without proper synchronization (e.g., base::Lock), this test would likely
+// fail in the following ways:
+// 1. Memory Corruption (UAF/HeapBOf): base::flat_map uses a contiguous
+// std::vector. If one thread triggers a reallocation during an insertion
+// while another thread is searching or erasing, the latter will hold an
+// invalidated iterator or pointer.
+// 2. Container Inconsistency: Concurrent insertions and erasures can leave
+// the map in an unsorted or corrupted state, leading to failed lookups
+// for valid tokens.
+// 3. Sanitizer Triggers: ASan would detect container-overflow or
+// heap-use-after-free, and TSan would flag a data race.
+TEST_F(DCOMPSurfaceRegistryTest, ConcurrentRegisterAndTake) {
+ const int kOpsPerThread = 100;
+
+ std::vector<base::UnguessableToken> tokens;
+ base::Lock tokens_lock;
+
+ std::atomic<bool> start_flag{false};
+ std::atomic<int> threads_ready{0};
+
+ auto register_worker = [&]() {
+ threads_ready++;
+ while (!start_flag.load(std::memory_order_acquire)) {
+ std::this_thread::yield();
+ }
+
+ for (int i = 0; i < kOpsPerThread; ++i) {
+ base::win::ScopedHandle handle(
+ ::CreateEvent(nullptr, FALSE, FALSE, nullptr));
+ base::UnguessableToken token =
+ registry_->RegisterDCOMPSurfaceHandle(std::move(handle));
+ {
+ base::AutoLock lock(tokens_lock);
+ tokens.push_back(token);
+ }
+ }
+ };
+
+ auto take_worker = [&]() {
+ threads_ready++;
+ while (!start_flag.load(std::memory_order_acquire)) {
+ std::this_thread::yield();
+ }
+
+ int taken = 0;
+ while (taken < kOpsPerThread) {
+ base::UnguessableToken token;
+ {
+ base::AutoLock lock(tokens_lock);
+ if (!tokens.empty()) {
+ token = tokens.back();
+ tokens.pop_back();
+ }
+ }
+ if (!token.is_empty()) {
+ base::win::ScopedHandle handle =
+ registry_->TakeDCOMPSurfaceHandle(token);
+ taken++;
+ } else {
+ std::this_thread::yield();
+ }
+ }
+ };
+
+ // With the barrier pattern, two threads are sufficient to trigger
+ // the race condition for TSAN.
+ std::thread t1(register_worker);
+ std::thread t2(take_worker);
+
+ // Wait until both threads are ready at the starting line.
+ while (threads_ready.load(std::memory_order_relaxed) < 2) {
+ std::this_thread::yield();
+ }
+
+ // Signal the staring flag to allow both threads to race from the initialized
+ // state.
+ start_flag.store(true, std::memory_order_release);
+
+ t1.join();
+ t2.join();
+}
+
+} // namespace gl

View File

@@ -21,10 +21,10 @@ Cr-Commit-Position: refs/branch-heads/7680@{#3951}
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index e56cd25e65b0ba5db4ab39ba9ab0314ee13696d8..6918504a510d5a2a0aba3539156f72d53331d622 100644
index 6918504a510d5a2a0aba3539156f72d53331d622..52aabbf3d5d52717e4c14e47c38cc3959194c973 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -971,6 +971,11 @@ void GpuChannel::CreateCommandBuffer(
@@ -976,6 +976,11 @@ void GpuChannel::CreateCommandBuffer(
return;
}
@@ -36,9 +36,9 @@ index e56cd25e65b0ba5db4ab39ba9ab0314ee13696d8..6918504a510d5a2a0aba3539156f72d5
int32_t stream_id = init_params->stream_id;
CommandBufferId command_buffer_id =
CommandBufferIdFromChannelAndRoute(client_id_, route_id);
@@ -1032,6 +1037,10 @@ void GpuChannel::DestroyCommandBuffer(int32_t route_id) {
TRACE_EVENT1("gpu", "GpuChannel::OnDestroyCommandBuffer", "route_id",
route_id);
@@ -1041,6 +1046,10 @@ void GpuChannel::DestroyCommandBuffer(int32_t route_id) {
return;
}
+ if (route_id <= static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue)) {
+ return;

View File

@@ -1,373 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Eugene Zemtsov <eugene@chromium.org>
Date: Mon, 13 Apr 2026 22:52:33 -0700
Subject: [M146] media: Zero-copy VP9 alpha decoding in VpxVideoDecoder
Original change's description:
> media: Zero-copy VP9 alpha decoding in VpxVideoDecoder
>
> Configures the VP9 alpha decoder to use `memory_pool_` for external
> frame buffers, eliminating the need for `libyuv::CopyPlane`.
>
> The `VideoFrame` now wraps the alpha data directly from the pool using
> a second destruction observer. `AllocateAlphaPlaneForFrameBuffer` and
> `alpha_data` tracking are removed from `FrameBufferPool`.
>
> Bug: 500066234
> Change-Id: I6e7cf13bcc8a5a1759acfd51961859c4c57fcbf2
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7737984
> Reviewed-by: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
> Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
> Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1611919}
(cherry picked from commit fc79e8cc2dfcc8f7ec8ee9cf0acf0993f32aec27)
Bug: 501314839,500066234
Change-Id: I6e7cf13bcc8a5a1759acfd51961859c4c57fcbf2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7757063
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Cr-Commit-Position: refs/branch-heads/7680@{#3937}
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
diff --git a/media/base/frame_buffer_pool.cc b/media/base/frame_buffer_pool.cc
index e90f07036baab4056398c93a03f8751bbfaa5d69..e2aa3a9243e3ce45b5087853eb2bd7d7dae7acfe 100644
--- a/media/base/frame_buffer_pool.cc
+++ b/media/base/frame_buffer_pool.cc
@@ -56,7 +56,6 @@ struct FrameBufferPool::FrameBuffer {
// Not using std::vector<uint8_t> as resize() calls take a really long time
// for large buffers.
BytesArray data;
- BytesArray alpha_data;
bool held_by_library = false;
// Needs to be a counter since a frame buffer might be used multiple times.
int held_by_frame = 0;
@@ -148,31 +147,6 @@ void FrameBufferPool::ReleaseFrameBuffer(void* fb_priv) {
}
}
-base::span<uint8_t> FrameBufferPool::AllocateAlphaPlaneForFrameBuffer(
- size_t min_size,
- void* fb_priv) {
- base::AutoLock lock(lock_);
- DCHECK(fb_priv);
-
- auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
- DCHECK(IsUsedLocked(frame_buffer));
- if (frame_buffer->alpha_data.size() < min_size) {
- // Free the existing |alpha_data| first so that the memory can be reused,
- // if possible. Note that the new array is purposely not initialized.
- frame_buffer->alpha_data = {};
- uint8_t* data = nullptr;
- if (force_allocation_error_ ||
- !base::UncheckedMalloc(min_size, reinterpret_cast<void**>(&data)) ||
- !data) {
- return {};
- }
- // SAFETY: We have just allocated `min_size` of memory for `data`.
- frame_buffer->alpha_data =
- UNSAFE_BUFFERS(BytesArray::FromOwningPointer(data, min_size));
- }
- return frame_buffer->alpha_data;
-}
-
base::OnceClosure FrameBufferPool::CreateFrameCallback(void* fb_priv) {
base::AutoLock lock(lock_);
@@ -210,10 +184,9 @@ bool FrameBufferPool::OnMemoryDump(
size_t bytes_reserved = 0;
for (const auto& frame_buffer : frame_buffers_) {
if (IsUsedLocked(frame_buffer.get())) {
- bytes_used += frame_buffer->data.size() + frame_buffer->alpha_data.size();
+ bytes_used += frame_buffer->data.size();
}
- bytes_reserved +=
- frame_buffer->data.size() + frame_buffer->alpha_data.size();
+ bytes_reserved += frame_buffer->data.size();
}
memory_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
diff --git a/media/base/frame_buffer_pool.h b/media/base/frame_buffer_pool.h
index ac839b8e8bfa00d2fea203be5248a56f04cecc71..2ccb01676b0e8e1e3ca1b3cb60f2883538f2f13c 100644
--- a/media/base/frame_buffer_pool.h
+++ b/media/base/frame_buffer_pool.h
@@ -48,11 +48,6 @@ class MEDIA_EXPORT FrameBufferPool
// Called when a frame buffer allocation is no longer needed.
void ReleaseFrameBuffer(void* fb_priv);
- // Allocates (or reuses) room for an alpha plane on a given frame buffer.
- // |fb_priv| must be a value previously returned by GetFrameBuffer().
- base::span<uint8_t> AllocateAlphaPlaneForFrameBuffer(size_t min_size,
- void* fb_priv);
-
// Generates a "no_longer_needed" closure that holds a reference to this pool;
// |fb_priv| must be a value previously returned by GetFrameBuffer(). The
// callback may be called on any thread.
diff --git a/media/base/frame_buffer_pool_unittest.cc b/media/base/frame_buffer_pool_unittest.cc
index a5b7bff2b8af3d2f9a531e894ec28e31e7823ac0..4cfdb1520cc18548fd91b2cca8b03a0124de944f 100644
--- a/media/base/frame_buffer_pool_unittest.cc
+++ b/media/base/frame_buffer_pool_unittest.cc
@@ -32,12 +32,6 @@ TEST(FrameBufferPool, BasicFunctionality) {
EXPECT_NE(buf1.data(), buf2.data());
std::ranges::fill(buf2, 0);
- auto alpha = pool->AllocateAlphaPlaneForFrameBuffer(kBufferSize, priv1);
- ASSERT_FALSE(alpha.empty());
- EXPECT_NE(alpha.data(), buf1.data());
- EXPECT_NE(alpha.data(), buf2.data());
- std::ranges::fill(alpha, 0);
-
EXPECT_EQ(2u, pool->get_pool_size_for_testing());
// Frames are not released immediately, so this should still show two frames.
@@ -52,7 +46,6 @@ TEST(FrameBufferPool, BasicFunctionality) {
EXPECT_EQ(1u, pool->get_pool_size_for_testing());
std::ranges::fill(buf1, 0);
- std::ranges::fill(alpha, 0);
// This will release all memory since we're in the shutdown state.
std::move(frame_release_cb).Run();
diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc
index 0be38f7ee110a0084854c571784e9dd3c8144f51..32cd3c423f4f01aa4cbe21ae71bf149f26a1deee 100644
--- a/media/filters/vpx_video_decoder.cc
+++ b/media/filters/vpx_video_decoder.cc
@@ -269,7 +269,21 @@ bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
DCHECK(!vpx_codec_alpha_);
vpx_codec_alpha_ = InitializeVpxContext(config);
- return !!vpx_codec_alpha_;
+ if (!vpx_codec_alpha_) {
+ return false;
+ }
+
+ if (config.codec() == VideoCodec::kVP9) {
+ if (vpx_codec_set_frame_buffer_functions(
+ vpx_codec_alpha_.get(), &GetVP9FrameBuffer, &ReleaseVP9FrameBuffer,
+ memory_pool_.get())) {
+ DLOG(ERROR) << "Failed to configure external buffers for alpha. "
+ << vpx_codec_error(vpx_codec_alpha_.get());
+ return false;
+ }
+ }
+
+ return true;
}
void VpxVideoDecoder::CloseDecoder() {
@@ -576,20 +590,13 @@ bool VpxVideoDecoder::CopyVpxImageToVideoFrame(
if (memory_pool_) {
DCHECK_EQ(VideoCodec::kVP9, config_.codec());
if (vpx_image_alpha) {
+ CHECK_GT(vpx_image_alpha->stride[VPX_PLANE_Y], 0);
size_t alpha_plane_size =
vpx_image_alpha->stride[VPX_PLANE_Y] * vpx_image_alpha->d_h;
- auto alpha_plane = memory_pool_->AllocateAlphaPlaneForFrameBuffer(
- alpha_plane_size, vpx_image->fb_priv);
- if (alpha_plane.empty()) {
- error_status_ = DecoderStatus::Codes::kOutOfMemory;
- // In case of OOM, abort copy.
- return false;
- }
- libyuv::CopyPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
- vpx_image_alpha->stride[VPX_PLANE_Y],
- alpha_plane.data(),
- vpx_image_alpha->stride[VPX_PLANE_Y],
- vpx_image_alpha->d_w, vpx_image_alpha->d_h);
+ // SAFETY: libvpx guarantees that the Y plane has at least `stride * d_h`
+ // bytes available.
+ auto alpha_plane = UNSAFE_BUFFERS(base::span<uint8_t>(
+ vpx_image_alpha->planes[VPX_PLANE_Y], alpha_plane_size));
*video_frame = VideoFrame::WrapExternalYuvaData(
codec_format, coded_size, gfx::Rect(visible_size), natural_size,
vpx_image->stride[VPX_PLANE_Y], vpx_image->stride[VPX_PLANE_U],
@@ -605,8 +612,14 @@ bool VpxVideoDecoder::CopyVpxImageToVideoFrame(
if (!(*video_frame))
return false;
- video_frame->get()->AddDestructionObserver(
- memory_pool_->CreateFrameCallback(vpx_image->fb_priv));
+ (*video_frame)
+ ->AddDestructionObserver(
+ memory_pool_->CreateFrameCallback(vpx_image->fb_priv));
+ if (vpx_image_alpha) {
+ (*video_frame)
+ ->AddDestructionObserver(
+ memory_pool_->CreateFrameCallback(vpx_image_alpha->fb_priv));
+ }
return true;
}
diff --git a/media/filters/vpx_video_decoder.h b/media/filters/vpx_video_decoder.h
index 7bcba319954ed43175e42c2dc1b991c5b6129138..2ab3767680ee408215bf2debb6f85c033f45af68 100644
--- a/media/filters/vpx_video_decoder.h
+++ b/media/filters/vpx_video_decoder.h
@@ -104,8 +104,8 @@ class MEDIA_EXPORT VpxVideoDecoder : public OffloadableVideoDecoder {
std::unique_ptr<vpx_codec_ctx> vpx_codec_;
std::unique_ptr<vpx_codec_ctx> vpx_codec_alpha_;
- // |memory_pool_| is a single-threaded memory pool used for VP9 decoding
- // with no alpha. |frame_pool_| is used for all other cases.
+ // |memory_pool_| is a thread-safe memory pool used for zero-copy VP9 decoding
+ // (both with and without alpha). |frame_pool_| is used for VP8.
scoped_refptr<FrameBufferPool> memory_pool_;
VideoFramePool frame_pool_;
diff --git a/media/filters/vpx_video_decoder_unittest.cc b/media/filters/vpx_video_decoder_unittest.cc
index c7f6d13bd825425230b63d87c13466e49f3c3c59..5203645bc8ec89dd93827fc0cbebb92e803faac1 100644
--- a/media/filters/vpx_video_decoder_unittest.cc
+++ b/media/filters/vpx_video_decoder_unittest.cc
@@ -176,6 +176,28 @@ class VpxVideoDecoderTest : public testing::Test {
output_frames_.push_back(std::move(frame));
}
+ // Extracts the compressed video data from the AVPacket and also checks for
+ // side data containing an alpha channel. If found, it copies the alpha data
+ // into the DecoderBuffer's side data. This is necessary because FFmpeg
+ // demuxes alpha channel data as side data associated with the video packet.
+ static scoped_refptr<DecoderBuffer> CreateBufferWithAlphaFromPacket(
+ const AVPacket* packet) {
+ auto buffer = DecoderBuffer::CopyFrom(AVPacketData(*packet));
+ size_t side_data_size = 0;
+ uint8_t* side_data_ptr = av_packet_get_side_data(
+ packet, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, &side_data_size);
+ if (side_data_size > 8) {
+ // SAFETY: The best we can do here is trust the size reported by ffmpeg.
+ auto side_data =
+ UNSAFE_BUFFERS(base::span(side_data_ptr, side_data_size));
+ if (base::U64FromBigEndian(side_data.first<8u>()) == 1) {
+ buffer->WritableSideData().alpha_data =
+ base::HeapArray<uint8_t>::CopiedFrom(side_data.subspan(8u));
+ }
+ }
+ return buffer;
+ }
+
MOCK_METHOD1(DecodeDone, void(DecoderStatus));
base::test::TaskEnvironment task_env_;
@@ -293,6 +315,68 @@ TEST_F(VpxVideoDecoderTest, SimpleFrameReuse) {
EXPECT_EQ(old_y_data, output_frames_.back()->data(VideoFrame::Plane::kY));
}
+TEST_F(VpxVideoDecoderTest, SimpleAlphaFrameReuse) {
+ VideoDecoderConfig config = TestVideoConfig::Normal(VideoCodec::kVP9);
+ config.Initialize(
+ config.codec(), config.profile(),
+ VideoDecoderConfig::AlphaMode::kHasAlpha, config.color_space_info(),
+ config.video_transformation(), config.coded_size(), config.visible_rect(),
+ config.natural_size(), config.extra_data(), config.encryption_scheme());
+ InitializeWithConfig(config);
+ scoped_refptr<DecoderBuffer> alpha_frame = ReadTestDataFile("bear-vp9a.webm");
+
+ // Read frames from the webm file.
+ InMemoryUrlProtocol protocol(*alpha_frame, false);
+ FFmpegGlue glue(&protocol);
+ ASSERT_TRUE(glue.OpenContext());
+
+ auto packet = ScopedAVPacket::Allocate();
+
+ // Decode first frame
+ ASSERT_GE(av_read_frame(glue.format_context(), packet.get()), 0);
+ auto buffer = CreateBufferWithAlphaFromPacket(packet.get());
+ Decode(buffer);
+ av_packet_unref(packet.get());
+
+ ASSERT_EQ(1u, output_frames_.size());
+ scoped_refptr<VideoFrame> frame = std::move(output_frames_.front());
+ EXPECT_EQ(PIXEL_FORMAT_I420A, frame->format());
+ const uint8_t* old_y_data = frame->data(VideoFrame::Plane::kY);
+ const uint8_t* old_a_data = frame->data(VideoFrame::Plane::kA);
+ output_frames_.pop_back();
+
+ // Clear frame reference to return the frame to the pool.
+ frame = nullptr;
+
+ // Decode second frame.
+ Decode(buffer);
+ const uint8_t* mid_y_data =
+ output_frames_.front()->data(VideoFrame::Plane::kY);
+ const uint8_t* mid_a_data =
+ output_frames_.front()->data(VideoFrame::Plane::kA);
+ output_frames_.clear();
+
+ // Issuing another decode should reuse buffers from the pool.
+ Decode(buffer);
+
+ ASSERT_EQ(1u, output_frames_.size());
+ const uint8_t* new_y_data =
+ output_frames_.back()->data(VideoFrame::Plane::kY);
+ const uint8_t* new_a_data =
+ output_frames_.back()->data(VideoFrame::Plane::kA);
+
+ // The pool is shared, so buffers might be reused in a different order (e.g. Y
+ // might get the buffer previously used for A). Because libvpx allocates the
+ // new frame before releasing the old reference frame, we need to check across
+ // all previously allocated buffers.
+ bool reused_y = new_y_data == old_y_data || new_y_data == old_a_data ||
+ new_y_data == mid_y_data || new_y_data == mid_a_data;
+ bool reused_a = new_a_data == old_y_data || new_a_data == old_a_data ||
+ new_a_data == mid_y_data || new_a_data == mid_a_data;
+ EXPECT_TRUE(reused_y);
+ EXPECT_TRUE(reused_a);
+}
+
TEST_F(VpxVideoDecoderTest, SimpleFormatChange) {
scoped_refptr<DecoderBuffer> large_frame =
ReadTestDataFile("vp9-I-frame-1280x720");
@@ -312,9 +396,41 @@ TEST_F(VpxVideoDecoderTest, FrameValidAfterPoolDestruction) {
// Write to the Y plane. The memory tools should detect a
// use-after-free if the storage was actually removed by pool destruction.
- memset(output_frames_.front()->writable_data(VideoFrame::Plane::kY), 0xff,
- output_frames_.front()->rows(VideoFrame::Plane::kY) *
- output_frames_.front()->stride(VideoFrame::Plane::kY));
+ std::ranges::fill(
+ output_frames_.front()->writable_span(VideoFrame::Plane::kY), 0xff);
+}
+
+TEST_F(VpxVideoDecoderTest, AlphaFrameValidAfterPoolDestruction) {
+ VideoDecoderConfig config = TestVideoConfig::Normal(VideoCodec::kVP9);
+ config.Initialize(
+ config.codec(), config.profile(),
+ VideoDecoderConfig::AlphaMode::kHasAlpha, config.color_space_info(),
+ config.video_transformation(), config.coded_size(), config.visible_rect(),
+ config.natural_size(), config.extra_data(), config.encryption_scheme());
+ InitializeWithConfig(config);
+ scoped_refptr<DecoderBuffer> alpha_frame = ReadTestDataFile("bear-vp9a.webm");
+
+ InMemoryUrlProtocol protocol(*alpha_frame, false);
+ FFmpegGlue glue(&protocol);
+ ASSERT_TRUE(glue.OpenContext());
+
+ auto packet = ScopedAVPacket::Allocate();
+ ASSERT_GE(av_read_frame(glue.format_context(), packet.get()), 0);
+ auto buffer = CreateBufferWithAlphaFromPacket(packet.get());
+ Decode(std::move(buffer));
+ av_packet_unref(packet.get());
+
+ ASSERT_EQ(1u, output_frames_.size());
+ EXPECT_EQ(PIXEL_FORMAT_I420A, output_frames_.front()->format());
+
+ Destroy();
+
+ // Write to the Y and A planes. The memory tools should detect a
+ // use-after-free if the storage was actually removed by pool destruction.
+ std::ranges::fill(
+ output_frames_.front()->writable_span(VideoFrame::Plane::kY), 0xff);
+ std::ranges::fill(
+ output_frames_.front()->writable_span(VideoFrame::Plane::kA), 0xff);
}
// The test stream uses profile 2, which needs high bit depth support in libvpx.
@@ -362,8 +478,7 @@ TEST_F(VpxVideoDecoderTest, MemoryPoolAllowsMultipleDisplay) {
Destroy();
// ASAN will be very unhappy with this line if the above is incorrect.
- memset(last_frame->writable_data(VideoFrame::Plane::kY), 0,
- last_frame->row_bytes(VideoFrame::Plane::kY));
+ std::ranges::fill(last_frame->writable_span(VideoFrame::Plane::kY), 0);
}
#endif // !defined(LIBVPX_NO_HIGH_BIT_DEPTH) && !defined(ARCH_CPU_ARM_FAMILY)

View File

@@ -1209,7 +1209,7 @@ index a1068589ad844518038ee7bc15a3de9bc5cba525..1ff781c49f086ec8015c7d3c44567dbe
} // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index d368b2481156bb79c6e74c8b09a828eb2fa2d44c..07cbf495717714d71d977a8820e08050c3062526 100644
index fa04ab07ac1a5a0b0ff2dec4dba6cb2d1a0ab2d0..f5d72a89c7229bf8e897c90660feca482ac82594 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -700,6 +700,7 @@ static_library("test_support") {
@@ -1237,7 +1237,7 @@ index d368b2481156bb79c6e74c8b09a828eb2fa2d44c..07cbf495717714d71d977a8820e08050
]
if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) {
@@ -3412,6 +3416,7 @@ test("content_unittests") {
@@ -3413,6 +3417,7 @@ test("content_unittests") {
"//ui/shell_dialogs",
"//ui/webui:test_support",
"//url",

View File

@@ -15,6 +15,5 @@
{ "patch_dir": "src/electron/patches/sqlite", "repo": "src/third_party/sqlite/src" },
{ "patch_dir": "src/electron/patches/angle", "repo": "src/third_party/angle" },
{ "patch_dir": "src/electron/patches/skia", "repo": "src/third_party/skia" },
{ "patch_dir": "src/electron/patches/pdfium", "repo": "src/third_party/pdfium" },
{ "patch_dir": "src/electron/patches/libaom", "repo": "src/third_party/libaom/source/libaom" }
]

View File

@@ -1,4 +1,2 @@
cherry-pick-4369bd1258dc.patch
cherry-pick-a047955845e5.patch
cherry-pick-c61e9586156f.patch
cherry-pick-395efd18d8ef.patch

View File

@@ -1,24 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: James Zern <jzern@google.com>
Date: Fri, 27 Mar 2026 10:56:13 -0700
Subject: av1_nonrd_pick_inter_mode_sb: add missing ref_frame_flags check
Before calling `set_block_source_sad()` ensure `LAST_FRAME` is
available. Fixes a crash that may present as a use after free (UAF).
Bug: 495477995, 495996858
Change-Id: I61452ce412fb9071c3370b4350ed8878013a8355
(cherry picked from commit 4369bd1258dc99fa759916d9aba6509cdda9d877)
diff --git a/av1/encoder/nonrd_pickmode.c b/av1/encoder/nonrd_pickmode.c
index f2010062323b0ff4a1236ef63516d9b2d8f3007a..0f2a1c780a56a51f69bba8893fea9d9ad98b85a3 100644
--- a/av1/encoder/nonrd_pickmode.c
+++ b/av1/encoder/nonrd_pickmode.c
@@ -3440,6 +3440,7 @@ void av1_nonrd_pick_inter_mode_sb(AV1_COMP *cpi, TileDataEnc *tile_data,
!x->force_zeromv_skip_for_blk &&
x->content_state_sb.source_sad_nonrd != kZeroSad &&
x->source_variance == 0 && bsize < cm->seq_params->sb_size &&
+ (cpi->ref_frame_flags & AOM_LAST_FLAG) &&
search_state.yv12_mb[LAST_FRAME][0].width == cm->width &&
search_state.yv12_mb[LAST_FRAME][0].height == cm->height) {
set_block_source_sad(cpi, x, bsize, &search_state.yv12_mb[LAST_FRAME][0]);

View File

@@ -1,187 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Marco Paniconi <marpan@google.com>
Date: Sun, 29 Mar 2026 20:27:20 -0700
Subject: Set force_mv_inter_layer earlier in skip_inter_mode
For nonrd_pickmode: move the setting of
force_mv_inter_layer earlier in the
skip_inter_mode_nonrd(), to make sure it always
get set (in case of false return in that function).
Thie prevents the usage of a scaled_ref in pickmode
(combined_motion search) when it has actually not been
set/scaled in av1_scale_references (before encoding).
Fixes a crash for use after free (UAF), reported
in the issues below.
Added svc unittest to generate the issue. Also added
assert check for scaled_ref in combined_motion_search.
Bug: 495477995, 495996858
Change-Id: I578d19156d97a50546edc9422bc3581566f1236e
(cherry picked from commit a047955845e50e43786d51cdefcfc9e87804ed61)
diff --git a/av1/encoder/nonrd_pickmode.c b/av1/encoder/nonrd_pickmode.c
index 0f2a1c780a56a51f69bba8893fea9d9ad98b85a3..942b8ab23a2d448877c8801940fee4d0baae9aef 100644
--- a/av1/encoder/nonrd_pickmode.c
+++ b/av1/encoder/nonrd_pickmode.c
@@ -192,7 +192,7 @@ static int combined_motion_search(AV1_COMP *cpi, MACROBLOCK *x,
int *rate_mv, int64_t best_rd_sofar,
int use_base_mv) {
MACROBLOCKD *xd = &x->e_mbd;
- const AV1_COMMON *cm = &cpi->common;
+ AV1_COMMON *cm = &cpi->common;
const SPEED_FEATURES *sf = &cpi->sf;
MB_MODE_INFO *mi = xd->mi[0];
int step_param = (sf->rt_sf.fullpel_search_step_param)
@@ -207,6 +207,14 @@ static int combined_motion_search(AV1_COMP *cpi, MACROBLOCK *x,
int cost_list[5];
int search_subpel = 1;
+ if (av1_is_scaled(get_ref_scale_factors(cm, ref))) {
+ const YV12_BUFFER_CONFIG *scaled_ref = av1_get_scaled_ref_frame(cpi, ref);
+ (void)scaled_ref;
+ assert(scaled_ref != NULL);
+ assert(scaled_ref->y_crop_width == cm->width &&
+ scaled_ref->y_crop_height == cm->height);
+ }
+
start_mv = get_fullmv_from_mv(&ref_mv);
if (!use_base_mv)
@@ -2490,6 +2498,23 @@ static AOM_FORCE_INLINE bool skip_inter_mode_nonrd(
(*this_mode != GLOBALMV || *ref_frame != LAST_FRAME))
return true;
+ *force_mv_inter_layer = 0;
+ if (cpi->ppi->use_svc && svc->spatial_layer_id > 0 &&
+ ((*ref_frame == LAST_FRAME && svc->skip_mvsearch_last) ||
+ (*ref_frame == GOLDEN_FRAME && svc->skip_mvsearch_gf) ||
+ (*ref_frame == ALTREF_FRAME && svc->skip_mvsearch_altref))) {
+ // Only test mode if NEARESTMV/NEARMV is (svc_mv.mv.col, svc_mv.mv.row),
+ // otherwise set NEWMV to (svc_mv.mv.col, svc_mv.mv.row).
+ // Skip newmv and filter search.
+ *force_mv_inter_layer = 1;
+ if (*this_mode == NEWMV) {
+ search_state->frame_mv[*this_mode][*ref_frame] = svc_mv;
+ } else if (search_state->frame_mv[*this_mode][*ref_frame].as_int !=
+ svc_mv.as_int) {
+ return true;
+ }
+ }
+
// If the segment reference frame feature is enabled then do nothing if the
// current ref frame is not allowed.
if (segfeature_active(seg, segment_id, SEG_LVL_REF_FRAME)) {
@@ -2565,23 +2590,6 @@ static AOM_FORCE_INLINE bool skip_inter_mode_nonrd(
return true;
}
- *force_mv_inter_layer = 0;
- if (cpi->ppi->use_svc && svc->spatial_layer_id > 0 &&
- ((*ref_frame == LAST_FRAME && svc->skip_mvsearch_last) ||
- (*ref_frame == GOLDEN_FRAME && svc->skip_mvsearch_gf) ||
- (*ref_frame == ALTREF_FRAME && svc->skip_mvsearch_altref))) {
- // Only test mode if NEARESTMV/NEARMV is (svc_mv.mv.col, svc_mv.mv.row),
- // otherwise set NEWMV to (svc_mv.mv.col, svc_mv.mv.row).
- // Skip newmv and filter search.
- *force_mv_inter_layer = 1;
- if (*this_mode == NEWMV) {
- search_state->frame_mv[*this_mode][*ref_frame] = svc_mv;
- } else if (search_state->frame_mv[*this_mode][*ref_frame].as_int !=
- svc_mv.as_int) {
- return true;
- }
- }
-
// For screen content: skip mode testing based on source_sad.
if (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN &&
!x->force_zeromv_skip_for_blk) {
diff --git a/test/svc_datarate_test.cc b/test/svc_datarate_test.cc
index 0df678212acb0519aa4420ae57186840e12c682c..2f68ba7a214932b284a6eacbe1a9b5b474b6c659 100644
--- a/test/svc_datarate_test.cc
+++ b/test/svc_datarate_test.cc
@@ -247,6 +247,7 @@ class DatarateTestSVC
external_resize_pattern_ = 0;
dynamic_tl_ = false;
dynamic_scale_factors_ = false;
+ disable_last_ref_ = false;
}
void PreEncodeFrameHook(::libaom_test::VideoSource *video,
@@ -302,7 +303,7 @@ class DatarateTestSVC
spatial_layer_id, multi_ref_, comp_pred_,
(video->frame() % cfg_.kf_max_dist) == 0, dynamic_enable_disable_mode_,
rps_mode_, rps_recovery_frame_, simulcast_mode_, use_last_as_scaled_,
- use_last_as_scaled_single_ref_);
+ use_last_as_scaled_single_ref_, disable_last_ref_);
if (intra_only_ == 1 && frame_sync_ > 0) {
// Set an Intra-only frame on SL0 at frame_sync_.
// In order to allow decoding to start on SL0 in mid-sequence we need to
@@ -964,7 +965,7 @@ class DatarateTestSVC
int multi_ref, int comp_pred, int is_key_frame,
int dynamic_enable_disable_mode, int rps_mode, int rps_recovery_frame,
int simulcast_mode, bool use_last_as_scaled,
- bool use_last_as_scaled_single_ref) {
+ bool use_last_as_scaled_single_ref, bool disable_last_ref) {
int lag_index = 0;
int base_count = frame_cnt >> 2;
layer_id->spatial_layer_id = spatial_layer;
@@ -1164,6 +1165,11 @@ class DatarateTestSVC
if (dynamic_enable_disable_mode == 1 &&
layer_id->spatial_layer_id == number_spatial_layers_ - 1)
ref_frame_config->reference[0] = 0;
+ // Always disable LAST reference under this flag. use GOLDEN reference.
+ if (disable_last_ref) {
+ ref_frame_config->reference[0] = 0;
+ ref_frame_config->reference[3] = 1;
+ }
return layer_flags;
}
@@ -1508,6 +1514,23 @@ class DatarateTestSVC
CheckDatarate(0.80, 1.60);
}
+ virtual void BasicRateTargetingSVC1TL2SLDisableLASTTest() {
+ SetUpCbr();
+ cfg_.g_error_resilient = 0;
+
+ ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
+ 288, 30, 1, 0, 300);
+ const int bitrate_array[2] = { 300, 600 };
+ cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)];
+ ResetModel();
+ disable_last_ref_ = true;
+ screen_mode_ = true;
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+#if CONFIG_AV1_DECODER
+ EXPECT_EQ((int)GetMismatchFrames(), 0);
+#endif
+ }
+
virtual void BasicRateTargetingSVC3TL3SLIntraStartDecodeBaseMidSeq() {
SetUpCbr();
cfg_.rc_max_quantizer = 56;
@@ -2380,6 +2403,7 @@ class DatarateTestSVC
int external_resize_pattern_;
bool dynamic_tl_;
bool dynamic_scale_factors_;
+ bool disable_last_ref_;
};
// Check basic rate targeting for CBR, for 3 temporal layers, 1 spatial.
@@ -2458,6 +2482,12 @@ TEST_P(DatarateTestSVC, BasicRateTargetingSVC1TL2SL) {
BasicRateTargetingSVC1TL2SLTest();
}
+// Check basic rate targeting for CBR, for 2 spatial layers, 1 temporal.
+// Disable the usage of LAST referenc frame.
+TEST_P(DatarateTestSVC, BasicRateTargetingSVC1TL2SLDisableLAST) {
+ BasicRateTargetingSVC1TL2SLDisableLASTTest();
+}
+
// Check basic rate targeting for CBR, for 3 spatial layers, 3 temporal,
// with Intra-only frame inserted in the stream. Verify that we can start
// decoding the SL0 stream at the intra_only frame in mid-sequence.

View File

@@ -1,2 +0,0 @@
cherry-pick-ca8a943c247c.patch
cherry-pick-bce2e6728279.patch

View File

@@ -1,36 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tom Sepez <tsepez@google.com>
Date: Tue, 7 Apr 2026 15:50:30 -0700
Subject: Use safe arithmetic in CFX_PSRenderer::DrawDIBits()
Hardening suggestion from the AI bot.
Bug: 500036290
Change-Id: Ie521629d06ba944f610b941a8c9e9505fa29aea7
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/145731
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp
index b38f1a2b7c3271769e609763be2e183f2890ebb3..b8710e50ed01233b2aefbf1760e26e05964b315e 100644
--- a/core/fxge/win32/cfx_psrenderer.cpp
+++ b/core/fxge/win32/cfx_psrenderer.cpp
@@ -620,8 +620,16 @@ bool CFX_PSRenderer::DrawDIBits(RetainPtr<const CFX_DIBBase> bitmap,
encoder_iface_->pJpegEncodeFunc(bitmap, &output_buf, &output_size)) {
filter = "/DCTDecode filter ";
} else {
- int src_pitch = width * bytes_per_pixel;
- output_size = height * src_pitch;
+ FX_SAFE_UINT32 safe_pitch = bytes_per_pixel;
+ safe_pitch *= width;
+ FX_SAFE_UINT32 safe_output_size = safe_pitch;
+ safe_output_size *= height;
+ if (!safe_output_size.IsValid()) {
+ WriteString("\nQ\n");
+ return false;
+ }
+ uint32_t src_pitch = safe_pitch.ValueOrDie();
+ output_size = safe_output_size.ValueOrDie();
output_buf = FX_Alloc(uint8_t, output_size);
for (int row = 0; row < height; row++) {
const uint8_t* src_scan = bitmap->GetScanline(row).data();

View File

@@ -1,70 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig@chromium.org>
Date: Fri, 27 Mar 2026 14:52:16 -0700
Subject: Patch an overflow in libtiff
Apply fix [1] from upstream, which is not in the most recent versioned
release.
[1] https://gitlab.com/libtiff/libtiff/-/commit/0f726d9
Bug: 496907110
Change-Id: Ic8665879ebdd4445f473e9a1e156cfc42c294d51
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/145550
Reviewed-by: Andy Phan <andyphan@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/third_party/libtiff/0034-tiff-jpeg-overflow.patch b/third_party/libtiff/0034-tiff-jpeg-overflow.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ba6086a38adfa0bd7726affda0f11381e04501e5
--- /dev/null
+++ b/third_party/libtiff/0034-tiff-jpeg-overflow.patch
@@ -0,0 +1,25 @@
+commit 0f726d9477a11e15eb67ca349c03907f6cfb82a9
+Author: Mikhail Khachaiants <mkhachaiants@gmail.com>
+Date: Mon Dec 1 22:26:34 2025 +0200
+
+ tif_jpeg: reject mismatched JPEG data precision to avoid write overflow
+
+ Ensure TIFF BitsPerSample matches both BITS_IN_JSAMPLE and the JPEG
+ header data_precision for JPEG-compressed images. This prevents
+ under-sized scanline buffers that can lead to write buffer overflows
+ in jdcolor.c/null_convert when decoding malformed inputs.
+
+diff --git a/libtiff/tif_jpeg.c b/libtiff/tif_jpeg.c
+index aba5f99b..4d6370b5 100644
+--- a/libtiff/tif_jpeg.c
++++ b/libtiff/tif_jpeg.c
+@@ -1282,7 +1282,8 @@ int TIFFJPEGIsFullStripRequired(TIFF *tif)
+ sp->cinfo.d.data_precision = td->td_bitspersample;
+ sp->cinfo.d.bits_in_jsample = td->td_bitspersample;
+ #else
+- if (sp->cinfo.d.data_precision != td->td_bitspersample)
++ if (td->td_bitspersample != BITS_IN_JSAMPLE ||
++ sp->cinfo.d.data_precision != td->td_bitspersample)
+ {
+ TIFFErrorExtR(tif, module, "Improper JPEG data precision");
+ return (0);
diff --git a/third_party/libtiff/README.pdfium b/third_party/libtiff/README.pdfium
index 9953e767853bcd30683cc24d0d1839c916659185..e3f352d747007641b5d0bd2256a5dbc8af7c20af 100644
--- a/third_party/libtiff/README.pdfium
+++ b/third_party/libtiff/README.pdfium
@@ -19,3 +19,4 @@ Local Modifications:
0028-nstrips-OOM.patch: return error for excess number of tiles/strips.
0031-safe_size_ingtStripContig.patch: return error if the size to read overflow from int32.
0033-avail-out-overflow.patch: signed comparison in PixarLogDecode().
+0034-tiff-jpeg-overflow.patch: reject mismatched JPEG data precision.
diff --git a/third_party/libtiff/tif_jpeg.c b/third_party/libtiff/tif_jpeg.c
index 5281457d936a0dfa5f877c6a7efff6a65066f520..a9764f073db04d6e593e421105d0f59efbfbbeb2 100644
--- a/third_party/libtiff/tif_jpeg.c
+++ b/third_party/libtiff/tif_jpeg.c
@@ -1287,7 +1287,8 @@ int TIFFJPEGIsFullStripRequired(TIFF *tif)
sp->cinfo.d.data_precision = td->td_bitspersample;
sp->cinfo.d.bits_in_jsample = td->td_bitspersample;
#else
- if (sp->cinfo.d.data_precision != td->td_bitspersample)
+ if (td->td_bitspersample != BITS_IN_JSAMPLE ||
+ sp->cinfo.d.data_precision != td->td_bitspersample)
{
TIFFErrorExtR(tif, module, "Improper JPEG data precision");
return (0);

View File

@@ -1,3 +1,2 @@
cherry-pick-0566b2f5f0d1.patch
cherry-pick-3f9969421ad5.patch
cherry-pick-8c705ac86366.patch

View File

@@ -1,651 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Ludwig <michaelludwig@google.com>
Date: Wed, 1 Apr 2026 09:48:48 -0400
Subject: Use 16-bit size for ResourceKeys
Internally, ResourceKey required the size to fit into a uint16_t so this
makes that explicit in the public API. It also changes how the size is
stored to instead record the num32DataCount directly and then convert to
bytes as needed, whereas previously it was requiring that the actual
byte count fit into a uint16_t. This gives a bit more head room.
Call sites to the ResourceKey builders are updated to now have the
responsibility of checking that their size can fit into a uint16_t. For
the most part, these were fixed or trivially small variable key sizes.
The two exceptions were Ganesh's style key (with dashes) and its
inherited key system for shapes with applied styles and path effects.
They now have reasonable limits to prevent the keys from growing bigger
than about 1kb.
Bug: b/495700484
Change-Id: I6ac4f17628b9a2e1a777c473b74e6d1f5c68b27d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1199497
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/ResourceKey.h b/src/gpu/ResourceKey.h
index f8dee7983036a95d2f5fd7404553916b5c616e83..19851a67653669058361570c615d9be45dc5153a 100644
--- a/src/gpu/ResourceKey.h
+++ b/src/gpu/ResourceKey.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <cstring>
+#include <limits>
#include <new>
#include <utility>
@@ -77,14 +78,10 @@ public:
}
protected:
- Builder(ResourceKey* key, uint32_t domain, int data32Count) : fKey(key) {
- size_t count = SkToSizeT(data32Count);
+ Builder(ResourceKey* key, uint16_t domain, uint16_t data32Count) : fKey(key) {
SkASSERT(domain != kInvalidDomain);
- key->fKey.reset(kMetaDataCnt + count);
- size_t size = (count + kMetaDataCnt) * sizeof(uint32_t);
- SkASSERT(SkToU16(size) == size);
- SkASSERT(SkToU16(domain) == domain);
- key->fKey[kDomainAndSize_MetaDataIdx] = SkToU32(domain | (size << 16));
+ key->fKey.reset(kMetaDataCnt + data32Count);
+ key->fKey[kDomainAndSize_MetaDataIdx] = domain | (data32Count << 16);
}
private:
@@ -92,7 +89,7 @@ public:
};
protected:
- static const uint32_t kInvalidDomain = 0;
+ static const uint16_t kInvalidDomain = 0;
ResourceKey() { this->reset(); }
@@ -118,10 +115,10 @@ protected:
return *this;
}
- uint32_t domain() const { return fKey[kDomainAndSize_MetaDataIdx] & 0xffff; }
+ uint16_t domain() const { return fKey[kDomainAndSize_MetaDataIdx] & 0xffff; }
/** size of the key data, excluding meta-data (hash, domain, etc). */
- size_t dataSize() const { return this->size() - 4 * kMetaDataCnt; }
+ size_t dataSize() const { return (fKey[kDomainAndSize_MetaDataIdx] >> 16) * sizeof(uint32_t); }
/** ptr to the key data, excluding meta-data (hash, domain, etc). */
const uint32_t* data() const {
@@ -149,14 +146,17 @@ protected:
private:
enum MetaDataIdx {
kHash_MetaDataIdx,
- // The key domain and size are packed into a single uint32_t.
+ // The key domain and size are packed into a single uint32_t. The stored size is in units
+ // of uint32_t and does not include the metadata, i.e. it stores the data32Count provided
+ // to the original key builder.
kDomainAndSize_MetaDataIdx,
kLastMetaDataIdx = kDomainAndSize_MetaDataIdx
};
static const uint32_t kMetaDataCnt = kLastMetaDataIdx + 1;
- size_t internalSize() const { return fKey[kDomainAndSize_MetaDataIdx] >> 16; }
+ // Total size in bytes, including metadata
+ size_t internalSize() const { return this->dataSize() + sizeof(uint32_t) * kMetaDataCnt; }
void validate() const {
SkASSERT(this->isValid());
@@ -197,7 +197,7 @@ private:
class ScratchKey : public ResourceKey {
public:
/** Uniquely identifies the type of resource that is cached as scratch. */
- typedef uint32_t ResourceType;
+ typedef uint16_t ResourceType;
/** Generate a unique ResourceType. */
static ResourceType GenerateResourceType();
@@ -219,7 +219,7 @@ public:
class Builder : public ResourceKey::Builder {
public:
- Builder(ScratchKey* key, ResourceType type, int data32Count)
+ Builder(ScratchKey* key, ResourceType type, uint16_t data32Count)
: ResourceKey::Builder(key, type, data32Count) {}
};
};
@@ -240,7 +240,7 @@ public:
*/
class UniqueKey : public ResourceKey {
public:
- typedef uint32_t Domain;
+ typedef uint16_t Domain;
/** Generate a Domain for unique keys. */
static Domain GenerateDomain();
@@ -279,17 +279,17 @@ public:
class Builder : public ResourceKey::Builder {
public:
- Builder(UniqueKey* key, Domain type, int data32Count, const char* tag = nullptr)
+ Builder(UniqueKey* key, Domain type, uint16_t data32Count, const char* tag = nullptr)
: ResourceKey::Builder(key, type, data32Count) {
key->fTag = tag;
}
/** Used to build a key that wraps another key and adds additional data. */
- Builder(UniqueKey* key, const UniqueKey& innerKey, Domain domain, int extraData32Cnt,
+ Builder(UniqueKey* key, const UniqueKey& innerKey, Domain domain, uint16_t extraData32Cnt,
const char* tag = nullptr)
: ResourceKey::Builder(key,
domain,
- Data32CntForInnerKey(innerKey) + extraData32Cnt) {
+ Data32CntForInnerKey(innerKey, extraData32Cnt)) {
SkASSERT(&innerKey != key);
// add the inner key to the end of the key so that op[] can be indexed normally.
uint32_t* innerKeyData = &this->operator[](extraData32Cnt);
@@ -300,9 +300,15 @@ public:
}
private:
- static int Data32CntForInnerKey(const UniqueKey& innerKey) {
- // key data + domain
- return SkToInt((innerKey.dataSize() >> 2) + 1);
+ static uint16_t Data32CntForInnerKey(const UniqueKey& innerKey, uint16_t extraData32Cnt) {
+ // key data + domain + extraData32Cnt needs to fit into a uint16_t. This key builder is
+ // only used in Ganesh for wrapping textures
+ uint16_t innerData32Cnt = innerKey.dataSize() >> 2;
+ // The Builder API doesn't have a way to return a failure, so if this is somehow
+ // exceeded, then we have no way to recover.
+ SkASSERT_RELEASE((uint32_t) extraData32Cnt + (uint32_t) innerData32Cnt + 1 <=
+ (uint32_t) std::numeric_limits<uint16_t>::max());
+ return innerData32Cnt + extraData32Cnt + 1;
}
};
diff --git a/src/gpu/ganesh/GrStyle.cpp b/src/gpu/ganesh/GrStyle.cpp
index 5d7bc9c1d971bbcf3df0fa720f660f23dfdcbab5..d1bdcf5a117b61f3a8ac3010d6d3cc3702f82ced 100644
--- a/src/gpu/ganesh/GrStyle.cpp
+++ b/src/gpu/ganesh/GrStyle.cpp
@@ -18,8 +18,18 @@
int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) {
static_assert(sizeof(uint32_t) == sizeof(SkScalar));
+
+ // We embed the dash interval pattern into the key, and the key size must fit within 16-bits.
+ // However, we put a more conservative upper limit on the dashes because we don't want to keep
+ // key memory locked up in caches during pathological cases.
+ static constexpr int kDashIntervalKeyLimit = 512;
+
int size = 0;
if (style.isDashed()) {
+ if (style.dashIntervalCnt() > kDashIntervalKeyLimit) {
+ return -1; // Disable caching for pathologically large dash patterns
+ }
+
// One scalar for scale, one for dash phase, and one for each dash value.
size += 2 + style.dashIntervalCnt();
} else if (style.pathEffect()) {
diff --git a/src/gpu/ganesh/GrStyle.h b/src/gpu/ganesh/GrStyle.h
index 41b0ce9db13e57db63cd1949dc87b9c20023fcc6..252b975e1e66dd654326449f3c9cbc317b8a2fda 100644
--- a/src/gpu/ganesh/GrStyle.h
+++ b/src/gpu/ganesh/GrStyle.h
@@ -74,6 +74,8 @@ public:
* into a key. This occurs when there is a path effect that is not a dash. The key can
* either reflect just the path effect (if one) or the path effect and the strokerec. Note
* that a simple fill has a zero sized key.
+ *
+ * If a positive value is returned, it will fit in a uint16_t.
*/
static int KeySize(const GrStyle&, Apply, uint32_t flags = 0);
diff --git a/src/gpu/ganesh/geometry/GrStyledShape.cpp b/src/gpu/ganesh/geometry/GrStyledShape.cpp
index 3c2b942aa6c614a6312e6695309cb9fc8dd6f5d5..6aa4daa3f8d76ab0dfe062dfb2f4b1503a8a5e1f 100644
--- a/src/gpu/ganesh/geometry/GrStyledShape.cpp
+++ b/src/gpu/ganesh/geometry/GrStyledShape.cpp
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cstring>
+#include <limits>
#include <utility>
@@ -141,12 +142,12 @@ static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
SkASSERT(key - origKey == path_key_from_data_size(path));
}
-int GrStyledShape::unstyledKeySize() const {
+uint16_t GrStyledShape::unstyledKeySize() const {
if (fInheritedKey.count()) {
- return fInheritedKey.count();
+ return SkTo<uint16_t>(fInheritedKey.count());
}
- int count = 1; // Every key has the state flags from the GrShape
+ uint16_t count = 1; // Every key has the state flags from the GrShape
switch(fShape.type()) {
case GrShape::Type::kPoint:
static_assert(0 == sizeof(SkPoint) % sizeof(uint32_t));
@@ -170,11 +171,13 @@ int GrStyledShape::unstyledKeySize() const {
break;
case GrShape::Type::kPath: {
if (0 == fGenID) {
- return -1; // volatile, so won't be keyed
+ return 0; // volatile, so won't be keyed
}
+ // When >= 0, `dataKeySize` is a reasonably small number bounded by
+ // kMaxKeyFromDataVerbCnt since point count is derived from verb count.
int dataKeySize = path_key_from_data_size(fShape.path());
if (dataKeySize >= 0) {
- count += dataKeySize;
+ count += SkTo<uint16_t>(dataKeySize);
} else {
count++; // Just adds the gen ID.
}
@@ -251,6 +254,7 @@ void GrStyledShape::writeUnstyledKey(uint32_t* key) const {
void GrStyledShape::setInheritedKey(const GrStyledShape &parent, GrStyle::Apply apply,
SkScalar scale) {
+ static constexpr int kInheritedKeyLimit = 1024;
SkASSERT(!fInheritedKey.count());
// If the output shape turns out to be simple, then we will just use its geometric key
if (fShape.isPath()) {
@@ -264,7 +268,7 @@ void GrStyledShape::setInheritedKey(const GrStyledShape &parent, GrStyle::Apply
bool useParentGeoKey = !parentCnt;
if (useParentGeoKey) {
parentCnt = parent.unstyledKeySize();
- if (parentCnt < 0) {
+ if (!parentCnt) {
// The parent's geometry has no key so we will have no key.
fGenID = 0;
return;
@@ -283,7 +287,12 @@ void GrStyledShape::setInheritedKey(const GrStyledShape &parent, GrStyle::Apply
// we try to get a key for the shape.
fGenID = 0;
return;
+ } else if (parentCnt + styleCnt > kInheritedKeyLimit) {
+ // Prevent chained path effects and styles from growing the key too large
+ fGenID = 0;
+ return;
}
+
fInheritedKey.reset(parentCnt + styleCnt);
if (useParentGeoKey) {
// This will be the geo key.
diff --git a/src/gpu/ganesh/geometry/GrStyledShape.h b/src/gpu/ganesh/geometry/GrStyledShape.h
index 97db583a5cf8aa8c7fe8c0e054ef622ca6945744..ed4345355478121dfedb1a894e159b80c4fca304 100644
--- a/src/gpu/ganesh/geometry/GrStyledShape.h
+++ b/src/gpu/ganesh/geometry/GrStyledShape.h
@@ -252,11 +252,11 @@ public:
/**
* Gets the size of the key for the shape represented by this GrStyledShape (ignoring its
- * styling). A negative value is returned if the shape has no key (shouldn't be cached).
+ * styling). A zero value is returned if the shape has no key (shouldn't be cached).
*/
- int unstyledKeySize() const;
+ uint16_t unstyledKeySize() const;
- bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
+ bool hasUnstyledKey() const { return this->unstyledKeySize() > 0; }
/**
* Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
diff --git a/src/gpu/ganesh/image/GrImageUtils.cpp b/src/gpu/ganesh/image/GrImageUtils.cpp
index a88ff15c0a59f1a5f417aa37f243385503a3dfd9..8fd9ea6a55e4a77c793c7af2361aab7e06c63f08 100644
--- a/src/gpu/ganesh/image/GrImageUtils.cpp
+++ b/src/gpu/ganesh/image/GrImageUtils.cpp
@@ -683,8 +683,7 @@ GrSurfaceProxyView FindOrMakeCachedMipmappedView(GrRecordingContext* rContext,
SkASSERT(baseKey.isValid());
skgpu::UniqueKey mipmappedKey;
static const skgpu::UniqueKey::Domain kMipmappedDomain = skgpu::UniqueKey::GenerateDomain();
- { // No extra values beyond the domain are required. Must name the var to please
- // clang-tidy.
+ { // No extra values beyond the domain are required. Must name the var to please clang-tidy.
skgpu::UniqueKey::Builder b(&mipmappedKey, baseKey, kMipmappedDomain, 0);
}
SkASSERT(mipmappedKey.isValid());
diff --git a/src/gpu/ganesh/ops/TriangulatingPathRenderer.cpp b/src/gpu/ganesh/ops/TriangulatingPathRenderer.cpp
index 124e51842eafbf7d3b010ca3232731d549515a9b..1bf3038847223c8167fa60c852c23216d186c5cc 100644
--- a/src/gpu/ganesh/ops/TriangulatingPathRenderer.cpp
+++ b/src/gpu/ganesh/ops/TriangulatingPathRenderer.cpp
@@ -282,8 +282,7 @@ private:
bool inverseFill = shape.inverseFilled();
static constexpr int kClipBoundsCnt = sizeof(devClipBounds) / sizeof(uint32_t);
- int shapeKeyDataCnt = shape.unstyledKeySize();
- SkASSERT(shapeKeyDataCnt >= 0);
+ uint16_t shapeKeyDataCnt = shape.unstyledKeySize();
skgpu::UniqueKey::Builder builder(key, kDomain, shapeKeyDataCnt + kClipBoundsCnt, "Path");
shape.writeUnstyledKey(&builder[0]);
// For inverse fills, the tessellation is dependent on clip bounds.
diff --git a/src/gpu/graphite/GraphiteResourceKey.h b/src/gpu/graphite/GraphiteResourceKey.h
index 12e72d1b24f45ac5885a125cb3d52cb648a55402..d52f0099a722aaf2a4b655abf6bf8c0ea26dcf57 100644
--- a/src/gpu/graphite/GraphiteResourceKey.h
+++ b/src/gpu/graphite/GraphiteResourceKey.h
@@ -46,7 +46,7 @@ public:
class Builder : public ResourceKey::Builder {
public:
- Builder(GraphiteResourceKey* key, ResourceType type, int data32Count)
+ Builder(GraphiteResourceKey* key, ResourceType type, uint16_t data32Count)
: ResourceKey::Builder(key, type, data32Count) {}
};
};
diff --git a/src/gpu/graphite/RasterPathUtils.cpp b/src/gpu/graphite/RasterPathUtils.cpp
index 1d8b5563e41bf8e3f515438c666760b228c3947c..8557d35bc4803cf71224457d103c3a9ce874012c 100644
--- a/src/gpu/graphite/RasterPathUtils.cpp
+++ b/src/gpu/graphite/RasterPathUtils.cpp
@@ -140,7 +140,7 @@ skgpu::UniqueKey GeneratePathMaskKey(const Shape& shape,
skgpu::UniqueKey maskKey;
{
static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
- int styleKeySize = 7;
+ uint16_t styleKeySize = 7;
if (!strokeRec.isHairlineStyle() && !strokeRec.isFillStyle()) {
// Add space for width and miter if needed
styleKeySize += 2;
@@ -185,66 +185,56 @@ skgpu::UniqueKey GenerateClipMaskKey(uint32_t stackRecordID,
skgpu::UniqueKey maskKey;
// if the element list is too large we just use the stackRecordID
if (elementsForMask->size() <= kMaxShapeCountForKey) {
- constexpr int kXformKeySize = 5;
- int keySize = 0;
- bool canCreateKey = true;
- // Iterate through to get key size and see if we can create a key at all
+ static constexpr int kXformKeySize = 5;
+ uint16_t keySize = includeBounds ? 2 : 0;
+ // Iterate through to get key size; given kMaxShapeCountForKey and Shape's own key size
+ // limitations, this should always fit safely within a 16-bit number
for (int i = 0; i < elementsForMask->size(); ++i) {
- int shapeKeySize = (*elementsForMask)[i]->fShape.keySize();
- if (shapeKeySize < 0) {
- canCreateKey = false;
- break;
- }
- keySize += kXformKeySize + shapeKeySize;
+ keySize += kXformKeySize + (*elementsForMask)[i]->fShape.keySize();
}
- if (canCreateKey) {
- if (includeBounds) {
- keySize += 2;
- }
- skgpu::UniqueKey::Builder builder(&maskKey, kDomain, keySize,
- "Clip Path Mask");
- int elementKeyIndex = 0;
- Rect unclippedBounds = Rect::InfiniteInverted();
- for (int i = 0; i < elementsForMask->size(); ++i) {
- const ClipStack::Element* element = (*elementsForMask)[i];
-
- // Add transform key and get packed fractional translation bits
- uint32_t fracBits = add_transform_key(&builder,
- elementKeyIndex,
- element->fLocalToDevice);
- uint32_t opBits = static_cast<uint32_t>(element->fOp);
- builder[elementKeyIndex + 4] = fracBits | (opBits << 16);
-
- const Shape& shape = element->fShape;
- shape.writeKey(&builder[elementKeyIndex + kXformKeySize],
- /*includeInverted=*/true);
-
- elementKeyIndex += kXformKeySize + shape.keySize();
-
- Rect transformedBounds = element->fLocalToDevice.mapRect(element->fShape.bounds());
- unclippedBounds.join(transformedBounds);
- }
-
- // The keyBounds are the maskDeviceBounds relative to the full transformed mask. We use
- // this to ensure we capture the situation where the maskDeviceBounds are equal in two
- // cases but actually enclose different regions of the full mask due to an integer
- // translation (which is not captured in the key) in the element transforms.
- *keyBounds = maskDeviceBounds.makeOffset(-unclippedBounds.left(),
- -unclippedBounds.top());
-
- if (includeBounds) {
- SkASSERT(SkTFitsIn<int16_t>(keyBounds->left()));
- SkASSERT(SkTFitsIn<int16_t>(keyBounds->top()));
- SkASSERT(SkTFitsIn<int16_t>(keyBounds->right()));
- SkASSERT(SkTFitsIn<int16_t>(keyBounds->bottom()));
-
- builder[elementKeyIndex] = keyBounds->left() | (keyBounds->top() << 16);
- builder[elementKeyIndex+1] = keyBounds->right() | (keyBounds->bottom() << 16);
- }
-
- *usesPathKey = true;
- return maskKey;
+
+ skgpu::UniqueKey::Builder builder(&maskKey, kDomain, keySize, "Clip Path Mask");
+ int elementKeyIndex = 0;
+ Rect unclippedBounds = Rect::InfiniteInverted();
+ for (int i = 0; i < elementsForMask->size(); ++i) {
+ const ClipStack::Element* element = (*elementsForMask)[i];
+
+ // Add transform key and get packed fractional translation bits
+ uint32_t fracBits = add_transform_key(&builder,
+ elementKeyIndex,
+ element->fLocalToDevice);
+ uint32_t opBits = static_cast<uint32_t>(element->fOp);
+ builder[elementKeyIndex + 4] = fracBits | (opBits << 16);
+
+ const Shape& shape = element->fShape;
+ shape.writeKey(&builder[elementKeyIndex + kXformKeySize],
+ /*includeInverted=*/true);
+
+ elementKeyIndex += kXformKeySize + shape.keySize();
+
+ Rect transformedBounds = element->fLocalToDevice.mapRect(element->fShape.bounds());
+ unclippedBounds.join(transformedBounds);
+ }
+
+ // The keyBounds are the maskDeviceBounds relative to the full transformed mask. We use
+ // this to ensure we capture the situation where the maskDeviceBounds are equal in two
+ // cases but actually enclose different regions of the full mask due to an integer
+ // translation (which is not captured in the key) in the element transforms.
+ *keyBounds = maskDeviceBounds.makeOffset(-unclippedBounds.left(),
+ -unclippedBounds.top());
+
+ if (includeBounds) {
+ SkASSERT(SkTFitsIn<int16_t>(keyBounds->left()));
+ SkASSERT(SkTFitsIn<int16_t>(keyBounds->top()));
+ SkASSERT(SkTFitsIn<int16_t>(keyBounds->right()));
+ SkASSERT(SkTFitsIn<int16_t>(keyBounds->bottom()));
+
+ builder[elementKeyIndex] = keyBounds->left() | (keyBounds->top() << 16);
+ builder[elementKeyIndex+1] = keyBounds->right() | (keyBounds->bottom() << 16);
}
+
+ *usesPathKey = true;
+ return maskKey;
}
// Either we have too many elements or at least one shape can't create a key
diff --git a/src/gpu/graphite/ResourceProvider.cpp b/src/gpu/graphite/ResourceProvider.cpp
index cd67a8af6fe60c2239dc6cfc7adaa8604ef3743a..80ce839d942b96f6ba7eb0456881a986accfe145 100644
--- a/src/gpu/graphite/ResourceProvider.cpp
+++ b/src/gpu/graphite/ResourceProvider.cpp
@@ -177,7 +177,7 @@ sk_sp<Sampler> ResourceProvider::findOrCreateCompatibleSampler(const SamplerDesc
// immutable sampler details into the SamplerDesc, so there is no need to delegate to Caps
// to create a specific key.
const SkSpan<const uint32_t>& samplerData = samplerDesc.asSpan();
- GraphiteResourceKey::Builder builder(&key, kType, samplerData.size());
+ GraphiteResourceKey::Builder builder(&key, kType, SkTo<uint16_t>(samplerData.size()));
for (size_t i = 0; i < samplerData.size(); i++) {
builder[i] = samplerData[i];
@@ -231,8 +231,8 @@ sk_sp<Buffer> ResourceProvider::findOrCreateBuffer(
// For the key we need ((sizeof(size_t) + (sizeof(uint32_t) - 1)) / (sizeof(uint32_t))
// uint32_t's for the size and one uint32_t for the rest.
static_assert(sizeof(uint32_t) == 4);
- static const int kSizeKeyNum32DataCnt = (sizeof(size_t) + 3) / 4;
- static const int kKeyNum32DataCnt = kSizeKeyNum32DataCnt + 1;
+ static const uint16_t kSizeKeyNum32DataCnt = (sizeof(size_t) + 3) / 4;
+ static const uint16_t kKeyNum32DataCnt = kSizeKeyNum32DataCnt + 1;
SkASSERT(static_cast<uint32_t>(type) < (1u << 4));
SkASSERT(static_cast<uint32_t>(accessPattern) < (1u << 2));
diff --git a/src/gpu/graphite/dawn/DawnCaps.cpp b/src/gpu/graphite/dawn/DawnCaps.cpp
index 3717790e1413401c0fbf12ff4cebd31132153ffd..1c281e3e7fd0299fb14d7fa8c763690bf68bae6b 100644
--- a/src/gpu/graphite/dawn/DawnCaps.cpp
+++ b/src/gpu/graphite/dawn/DawnCaps.cpp
@@ -1016,7 +1016,7 @@ uint32_t DawnCaps::getRenderPassDescKeyForPipeline(const RenderPassDesc& renderP
loadResolveAttachmentKey;
}
-static constexpr int kDawnGraphicsPipelineKeyData32Count = 4;
+static constexpr uint16_t kDawnGraphicsPipelineKeyData32Count = 4;
UniqueKey DawnCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc) const {
@@ -1234,7 +1234,7 @@ void DawnCaps::buildKeyForTexture(SkISize dimensions,
SkASSERT(static_cast<uint32_t>(dawnInfo.fUsage) < (1u << 28)); // usage is remaining 28 bits
// We need two uint32_ts for dimensions, 1 for format, and 1 for the rest of the key;
- int num32DataCnt = 2 + 1 + 1;
+ uint16_t num32DataCnt = 2 + 1 + 1;
bool hasYcbcrInfo = false;
#if !defined(__EMSCRIPTEN__)
// If we are using ycbcr texture/sampling, more key information is needed.
diff --git a/src/gpu/graphite/geom/AnalyticBlurMask.cpp b/src/gpu/graphite/geom/AnalyticBlurMask.cpp
index 97f38ba054f66249d70c739cea0318f4d3e30203..5a118bf8b1792be4400a5a43db2d936366157fd0 100644
--- a/src/gpu/graphite/geom/AnalyticBlurMask.cpp
+++ b/src/gpu/graphite/geom/AnalyticBlurMask.cpp
@@ -375,7 +375,7 @@ std::optional<AnalyticBlurMask> AnalyticBlurMask::MakeRRect(Recorder* recorder,
static const UniqueKey::Domain kRRectBlurDomain = UniqueKey::GenerateDomain();
UniqueKey key;
{
- static constexpr int kKeySize = sizeof(DerivedParams) / sizeof(uint32_t);
+ static constexpr uint16_t kKeySize = sizeof(DerivedParams) / sizeof(uint32_t);
static_assert(SkIsAlign4(sizeof(DerivedParams)));
// TODO: We should discretize the sigma to perceptibly meaningful changes to the table,
// as well as the underlying the round rect geometry.
diff --git a/src/gpu/graphite/geom/Shape.cpp b/src/gpu/graphite/geom/Shape.cpp
index 29898fb00507aa64317ffd78354f36a18ca0a7b0..2465dcfb9fc92b678e67520e7b0e104d8514c147 100644
--- a/src/gpu/graphite/geom/Shape.cpp
+++ b/src/gpu/graphite/geom/Shape.cpp
@@ -183,8 +183,8 @@ void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
}
} // anonymous namespace
-int Shape::keySize() const {
- int count = 1; // Every key has the state flags from the Shape
+uint16_t Shape::keySize() const {
+ uint16_t count = 1; // Every key has the state flags from the Shape
switch(this->type()) {
case Type::kLine:
static_assert(0 == sizeof(skvx::float4) % sizeof(uint32_t));
@@ -207,7 +207,7 @@ int Shape::keySize() const {
if (!this->path().isEmpty()) {
int dataKeySize = path_key_from_data_size(this->path());
if (dataKeySize >= 0) {
- count += dataKeySize;
+ count += SkTo<uint16_t>(dataKeySize);
} else {
count++; // Just adds the gen ID.
}
diff --git a/src/gpu/graphite/geom/Shape.h b/src/gpu/graphite/geom/Shape.h
index 8c02945b7090779385a82aa4a8d257dad758e331..30dd8fffb797aee0c8a85093f5d5cdb97ef1a0fa 100644
--- a/src/gpu/graphite/geom/Shape.h
+++ b/src/gpu/graphite/geom/Shape.h
@@ -184,7 +184,7 @@ public:
/**
* Gets the size of the key for the shape represented by this Shape.
*/
- int keySize() const;
+ uint16_t keySize() const;
/**
* Writes keySize() bytes into the provided pointer. Assumes that there is enough
diff --git a/src/gpu/graphite/mtl/MtlCaps.mm b/src/gpu/graphite/mtl/MtlCaps.mm
index 7816ca699def160c1aa56afb28463d3c9d77926f..e5939af8fef877c297e98195e111860c88b0e876 100644
--- a/src/gpu/graphite/mtl/MtlCaps.mm
+++ b/src/gpu/graphite/mtl/MtlCaps.mm
@@ -949,7 +949,7 @@ MTLPixelFormat format_from_compression(SkTextureCompressionType compression) {
return {formatInfo.fColorTypeInfos.get(), formatInfo.fColorTypeInfoCount};
}
-static constexpr int kMtlGraphicsPipelineKeyData32Count = 4;
+static constexpr uint16_t kMtlGraphicsPipelineKeyData32Count = 4;
UniqueKey MtlCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc) const {
@@ -1193,7 +1193,7 @@ MTLPixelFormat format_from_compression(SkTextureCompressionType compression) {
SkASSERT(static_cast<uint32_t>(isFBOnly) < (1u << 1));
// We need two uint32_ts for dimensions, 2 for format, and 1 for the rest of the key;
- static int kNum32DataCnt = 2 + 2 + 1;
+ static uint16_t kNum32DataCnt = 2 + 2 + 1;
GraphiteResourceKey::Builder builder(key, type, kNum32DataCnt);
diff --git a/src/gpu/graphite/vk/VulkanCaps.cpp b/src/gpu/graphite/vk/VulkanCaps.cpp
index 799d90b03c54cee89b24281e25c46813adef5046..f5ae0b882af66050b3e90c121675ab1b3a4ec870 100644
--- a/src/gpu/graphite/vk/VulkanCaps.cpp
+++ b/src/gpu/graphite/vk/VulkanCaps.cpp
@@ -2087,7 +2087,7 @@ bool VulkanCaps::msaaTextureRenderToSingleSampledSupport(const TextureInfo& info
// 4 uint32s for the render step id, paint id, compatible render pass description, and write
// swizzle.
-static constexpr int kPipelineKeyData32Count = 4;
+static constexpr uint16_t kPipelineKeyData32Count = 4;
static constexpr int kPipelineKeyRenderStepIDIndex = 0;
static constexpr int kPipelineKeyPaintParamsIDIndex = 1;
@@ -2173,15 +2173,15 @@ void VulkanCaps::buildKeyForTexture(SkISize dimensions,
SkASSERT(vkInfo.fAspectMask < (1u << 11)); // aspectMask is bits 8 - 19
// We need two uint32_ts for dimensions and 3 for miscellaneous information.
- static constexpr int kNum32DimensionDataCnt = 2;
- static constexpr int kNum32MiscDataCnt = 3;
+ static constexpr uint16_t kNum32DimensionDataCnt = 2;
+ static constexpr uint16_t kNum32MiscDataCnt = 3;
// Non-YCbCr formats need 1 int for format.
// YCbCr conversion needs 1 int for non-format flags, and a 64-bit format (external or regular).
- static constexpr int kNum32FormatDataCntNoYcbcr = 1;
- static constexpr int kNum32FormatDataCntYcbcr = 3;
+ static constexpr uint16_t kNum32FormatDataCntNoYcbcr = 1;
+ static constexpr uint16_t kNum32FormatDataCntYcbcr = 3;
const VulkanYcbcrConversionInfo& ycbcrInfo = vkInfo.fYcbcrConversionInfo;
- const int num32DataCnt =
+ const uint16_t num32DataCnt =
kNum32DimensionDataCnt + kNum32MiscDataCnt +
(ycbcrInfo.isValid() ? kNum32FormatDataCntYcbcr : kNum32FormatDataCntNoYcbcr);
diff --git a/src/gpu/graphite/vk/VulkanResourceProvider.cpp b/src/gpu/graphite/vk/VulkanResourceProvider.cpp
index bb2f400250c569b73120f857133cafcca51c1c77..f66c6d0d2cf8c8bb4b34c7479445185c3a3c7cf4 100644
--- a/src/gpu/graphite/vk/VulkanResourceProvider.cpp
+++ b/src/gpu/graphite/vk/VulkanResourceProvider.cpp
@@ -218,7 +218,7 @@ GraphiteResourceKey build_desc_set_key(const SkSpan<DescriptorData>& requestedDe
}
GraphiteResourceKey key;
- GraphiteResourceKey::Builder builder(&key, kType, keyData.size());
+ GraphiteResourceKey::Builder builder(&key, kType, SkTo<uint16_t>(keyData.size()));
for (int i = 0; i < keyData.size(); i++) {
builder[i] = keyData[i];
@@ -548,7 +548,7 @@ sk_sp<VulkanYcbcrConversion> VulkanResourceProvider::findOrCreateCompatibleYcbcr
GraphiteResourceKey key;
{
static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
- static constexpr int kKeySize = 3;
+ static constexpr uint16_t kKeySize = 3;
GraphiteResourceKey::Builder builder(&key, kType, kKeySize);
ImmutableSamplerInfo packedInfo = VulkanYcbcrConversion::ToImmutableSamplerInfo(ycbcrInfo);
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index 68da13ca9b048cd0d1dc9b62090c17793a11b3a1..9c3ef544ffda6ba72c2972f020ac2a06232484b6 100644
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -358,7 +358,10 @@ public:
const SkMatrix& viewMatrix() const { return *fViewMatrix; }
#if defined(SK_GANESH)
/** Negative means the vertices should not be cached for this path. */
- int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
+ int keyBytes() const {
+ return fShapeForKey.hasUnstyledKey() ? fShapeForKey.unstyledKeySize() * sizeof(uint32_t)
+ : -1;
+ }
void writeKey(void* key) const {
fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
}