mirror of
https://github.com/electron/electron.git
synced 2026-05-02 03:00:22 -04:00
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:
committed by
GitHub
parent
9867bbbde8
commit
f596e35554
2
DEPS
2
DEPS
@@ -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':
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
@@ -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 = {
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
]
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
cherry-pick-4369bd1258dc.patch
|
||||
cherry-pick-a047955845e5.patch
|
||||
cherry-pick-c61e9586156f.patch
|
||||
cherry-pick-395efd18d8ef.patch
|
||||
|
||||
@@ -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]);
|
||||
@@ -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.
|
||||
@@ -1,2 +0,0 @@
|
||||
cherry-pick-ca8a943c247c.patch
|
||||
cherry-pick-bce2e6728279.patch
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
@@ -1,3 +1,2 @@
|
||||
cherry-pick-0566b2f5f0d1.patch
|
||||
cherry-pick-3f9969421ad5.patch
|
||||
cherry-pick-8c705ac86366.patch
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user