123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef UI_GL_DELEGATED_INK_POINT_RENDERER_GPU_H_
- #define UI_GL_DELEGATED_INK_POINT_RENDERER_GPU_H_
- #include <dcomp.h>
- #include <wrl/client.h>
- #include <algorithm>
- #include <iterator>
- #include <memory>
- #include <utility>
- #include <vector>
- #include "base/containers/flat_map.h"
- #include "base/memory/raw_ptr.h"
- #include "base/time/time.h"
- #include "base/trace_event/trace_event.h"
- #include "mojo/public/cpp/bindings/receiver.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #include "ui/gfx/delegated_ink_metadata.h"
- #include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h"
- // Required for SFINAE to check if these Win10 types exist.
- // TODO(1171374): Remove this when the types are available in the Win10 SDK.
- struct IDCompositionInkTrailDevice;
- struct IDCompositionDelegatedInkTrail;
- struct DCompositionInkTrailPoint;
- namespace gl {
- namespace {
- // Maximum number of points that can be drawn. This is used to limit the total
- // number of ink trail tokens that we will store, and the total number of points
- // that we will store to provide to the Direct Composition APIs. It should match
- // the exact number of points that the OS Compositor will store to draw as part
- // of a trail.
- constexpr int kMaximumNumberOfPoints = 128;
- // Maximum number of pointer ids that will be considered for drawing. Number is
- // chosen arbitrarily, as it seems unlikely that the total number of input
- // sources using delegated ink trails would exceed 10, but it can be raised if
- // the need arises.
- constexpr uint64_t kMaximumNumberOfPointerIds = 10;
- struct DelegatedInkPointCompare {
- bool operator()(const gfx::DelegatedInkPoint& lhs,
- const gfx::DelegatedInkPoint& rhs) const {
- return lhs.timestamp() < rhs.timestamp();
- }
- };
- using DelegatedInkPointTokenMap = base::flat_map<gfx::DelegatedInkPoint,
- absl::optional<unsigned int>,
- DelegatedInkPointCompare>;
- } // namespace
- // TODO(1171374): Remove this class and remove templates when the types are
- // available in the Win10 SDK.
- template <typename InkTrailDevice,
- typename DelegatedInkTrail,
- typename InkTrailPoint,
- typename = void,
- typename = void,
- typename = void>
- class DelegatedInkPointRendererGpu {
- public:
- DelegatedInkPointRendererGpu() = default;
- bool Initialize(
- const Microsoft::WRL::ComPtr<IDCompositionDevice2>& dcomp_device,
- const Microsoft::WRL::ComPtr<IDXGISwapChain1>& root_swap_chain) {
- NOTREACHED();
- return false;
- }
- bool HasBeenInitialized() const { return false; }
- IDCompositionVisual* GetInkVisual() const {
- NOTREACHED();
- return nullptr;
- }
- bool DelegatedInkIsSupported(
- const Microsoft::WRL::ComPtr<IDCompositionDevice2>& dcomp_device) const {
- return false;
- }
- void SetDelegatedInkTrailStartPoint(
- std::unique_ptr<gfx::DelegatedInkMetadata> metadata) {
- NOTREACHED();
- }
- void InitMessagePipeline(
- mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer> receiver) {
- NOTREACHED();
- }
- void StoreDelegatedInkPoint(const gfx::DelegatedInkPoint& point) {
- NOTREACHED();
- }
- gfx::DelegatedInkMetadata* MetadataForTesting() const {
- NOTREACHED();
- return nullptr;
- }
- uint64_t DelegatedInkPointPointerIdCountForTesting() const {
- NOTREACHED();
- return 0u;
- }
- DelegatedInkPointTokenMap DelegatedInkPointsForTesting(
- int32_t pointer_id) const {
- NOTREACHED();
- return DelegatedInkPointTokenMap();
- }
- uint64_t InkTrailTokenCountForTesting() const {
- NOTREACHED();
- return 0u;
- }
- bool WaitForNewTrailToDrawForTesting() const {
- NOTREACHED();
- return false;
- }
- bool CheckForPointerIdForTesting(int32_t pointer_id) const {
- NOTREACHED();
- return false;
- }
- uint64_t GetMaximumNumberOfPointerIdsForTesting() const {
- NOTREACHED();
- return kMaximumNumberOfPointerIds;
- }
- void SetNeedsDcompPropertiesUpdate() const { NOTREACHED(); }
- };
- // This class will call the OS delegated ink trail APIs when they become
- // available. Currently using SFINAE to land changes ahead of the SDK, so that
- // the OS APIs can be used immediately.
- // TODO(1171374): Remove the template SFINAE.
- //
- // On construction, this class will create a new visual for the visual tree with
- // an IDCompositionDelegatedInk object as the contents. This will be added as
- // a child of the root surface visual in the tree, and the trail will be drawn
- // to it. It is a child of the root surface visual because this visual contains
- // the swapchain, and there will be no transforms applied to the delegated ink
- // visual this way.
- // For more information about the design of this class and using the OS APIs,
- // view the design doc here: https://aka.ms/GPUBackedDesignDoc
- template <typename InkTrailDevice,
- typename DelegatedInkTrail,
- typename InkTrailPoint>
- class DelegatedInkPointRendererGpu<InkTrailDevice,
- DelegatedInkTrail,
- InkTrailPoint,
- decltype(typeid(InkTrailDevice), void()),
- decltype(typeid(DelegatedInkTrail), void()),
- decltype(typeid(InkTrailPoint), void())>
- : public gfx::mojom::DelegatedInkPointRenderer {
- public:
- DelegatedInkPointRendererGpu() = default;
- void InitMessagePipeline(
- mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer>
- pending_receiver) {
- // The remote end of this pipeline exists on a per-tab basis, so if tab A
- // is using the feature and then tab B starts trying to use it, a new
- // PendingReceiver will arrive here while |receiver_| is still bound to the
- // remote in tab A. Because of this possibility, we unconditionally reset
- // |receiver_| before trying to bind anything here. This is fine to do
- // because we only ever need to actively receive points from one tab as only
- // one tab can be in the foreground and actively inking at a time.
- receiver_.reset();
- receiver_.Bind(std::move(pending_receiver));
- }
- bool Initialize(
- const Microsoft::WRL::ComPtr<IDCompositionDevice2>& dcomp_device2,
- const Microsoft::WRL::ComPtr<IDXGISwapChain1>& root_swap_chain) {
- if (dcomp_device_ == dcomp_device2.Get() &&
- swap_chain_ == root_swap_chain.Get() && HasBeenInitialized()) {
- return true;
- }
- dcomp_device_ = dcomp_device2.Get();
- swap_chain_ = root_swap_chain.Get();
- Microsoft::WRL::ComPtr<InkTrailDevice> ink_trail_device;
- TraceEventOnFailure(dcomp_device2.As(&ink_trail_device),
- "DelegatedInkPointRendererGpu::Initialize - "
- "DCompDevice2 as InkTrailDevice failed");
- if (!ink_trail_device)
- return false;
- TraceEventOnFailure(
- ink_trail_device->CreateDelegatedInkTrailForSwapChain(
- root_swap_chain.Get(), &delegated_ink_trail_),
- "DelegatedInkPointRendererGpu::Initialize - Failed to create "
- "delegated ink trail.");
- if (!delegated_ink_trail_)
- return false;
- Microsoft::WRL::ComPtr<IDCompositionDevice> dcomp_device;
- TraceEventOnFailure(dcomp_device2.As(&dcomp_device),
- "DelegatedInkPointRendererGpu::Initialize - "
- "DCompDevice2 as DCompDevice failed");
- if (!dcomp_device)
- return false;
- TraceEventOnFailure(dcomp_device->CreateVisual(&ink_visual_),
- "DelegatedInkPointRendererGpu::Initialize - "
- "Failed to create ink visual.");
- if (!ink_visual_)
- return false;
- if (TraceEventOnFailure(
- ink_visual_->SetContent(delegated_ink_trail_.Get()),
- "DelegatedInkPointRendererGpu::Initialize - SetContent failed")) {
- // Initialization has failed because SetContent failed. However, we must
- // reset the members so that HasBeenInitialized() does not return true
- // when queried.
- ink_visual_.Reset();
- delegated_ink_trail_.Reset();
- return false;
- }
- return true;
- }
- bool HasBeenInitialized() const {
- return ink_visual_ && delegated_ink_trail_;
- }
- IDCompositionVisual* GetInkVisual() const { return ink_visual_.Get(); }
- bool DelegatedInkIsSupported(
- const Microsoft::WRL::ComPtr<IDCompositionDevice2>& dcomp_device) const {
- Microsoft::WRL::ComPtr<InkTrailDevice> ink_trail_device;
- HRESULT hr = dcomp_device.As(&ink_trail_device);
- return hr == S_OK;
- }
- void SetDelegatedInkTrailStartPoint(
- std::unique_ptr<gfx::DelegatedInkMetadata> metadata) {
- TRACE_EVENT_WITH_FLOW1(
- "delegated_ink_trails",
- "DelegatedInkPointRendererGpu::SetDelegatedInkTrailStartPoint",
- TRACE_ID_GLOBAL(metadata_->trace_id()), TRACE_EVENT_FLAG_FLOW_IN,
- "metadata", metadata_->ToString());
- DCHECK(ink_visual_);
- DCHECK(delegated_ink_trail_);
- // If certain conditions are met, we can simply erase the trail up to the
- // point matching |metadata|, instead of starting a new trail. These
- // conditions include:
- // 1. A trail has already been started, meaning that |metadata_| and
- // |pointer_id_| exist.
- // 2. The current |metadata_| and |metadata| both have the same color, so
- // the color of the trail does not need to change.
- // 3. |needs_dcomp_properties_update_| isn't forcing an update of the DCOMP
- // resources: |ink_visual_|, |delegated_ink_trail_|. This happens after
- // a swap chain recreation.
- // (continued below)
- if (!needs_dcomp_properties_update_ && metadata_ &&
- metadata->color() == metadata_->color() && pointer_id_) {
- DelegatedInkPointTokenMap& token_map =
- delegated_ink_points_[pointer_id_.value()];
- auto point_matching_metadata_it = token_map.find(
- gfx::DelegatedInkPoint(metadata->point(), metadata->timestamp()));
- // (continued from above)
- // 4. We have a DelegatedInkPoint with a timestamp equal to or greater
- // than |metadata|'s timestamp in the token map associated with
- // |pointer_id_|.
- // (continued below)
- if (point_matching_metadata_it != token_map.end()) {
- gfx::DelegatedInkPoint point_matching_metadata =
- point_matching_metadata_it->first;
- absl::optional<unsigned int> token = point_matching_metadata_it->second;
- // (continued from above)
- // 5. The DelegatedInkPoint retrieved above matches |metadata| - this
- // means that the timestamp is an exact match and the point is
- // acceptably close.
- // 6. The token associated with this DelegatedInkPoint is valid,
- // meaning that the point has previously been drawn as part of a
- // trail.
- //
- // If all of these conditions are met, then we can attempt to just
- // remove points from the trail instead of starting a new trail
- // altogether.
- if (point_matching_metadata.MatchesDelegatedInkMetadata(
- metadata.get()) &&
- token) {
- bool remove_trail_points_failed = TraceEventOnFailure(
- delegated_ink_trail_->RemoveTrailPoints(token.value()),
- "DelegatedInkPointRendererGpu::SetDelegatedInkTrailStartPoint - "
- "Failed to remove trail points.");
- if (!remove_trail_points_failed &&
- UpdateVisualClip(metadata->presentation_area(), false)) {
- // Remove all points up to and including the point that matches
- // |metadata|. No need to hold on to the point that matches metadata
- // because we've already added it to AddTrailPoints previously, and
- // the next valid |metadata| is guaranteed to be after it.
- token_map.erase(token_map.begin(),
- std::next(point_matching_metadata_it));
- metadata_ = std::move(metadata);
- return;
- }
- }
- }
- }
- if (!UpdateVisualClip(metadata->presentation_area(),
- needs_dcomp_properties_update_))
- return;
- D2D1_COLOR_F d2d1_color;
- const float kMaxRGBAValue = 255.f;
- d2d1_color.a = SkColorGetA(metadata->color()) / kMaxRGBAValue;
- d2d1_color.r = SkColorGetR(metadata->color()) / kMaxRGBAValue;
- d2d1_color.g = SkColorGetG(metadata->color()) / kMaxRGBAValue;
- d2d1_color.b = SkColorGetB(metadata->color()) / kMaxRGBAValue;
- if (TraceEventOnFailure(
- delegated_ink_trail_->StartNewTrail(d2d1_color),
- "DelegatedInkPointRendererGpu::SetDelegatedInkTrailStartPoint - "
- "Failed to start a new trail.")) {
- return;
- }
- wait_for_new_trail_to_draw_ = false;
- metadata_ = std::move(metadata);
- DrawSavedTrailPoints();
- needs_dcomp_properties_update_ = false;
- }
- void StoreDelegatedInkPoint(const gfx::DelegatedInkPoint& point) override {
- TRACE_EVENT_WITH_FLOW1(
- "delegated_ink_trails",
- "DelegatedInkPointRendererGpu::StoreDelegatedInkPoint",
- TRACE_ID_GLOBAL(point.trace_id()),
- TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "point",
- point.ToString());
- const int32_t pointer_id = point.pointer_id();
- // TODO(1238497): Understand why we are being sent points from browser
- // process that break this assertion so frequently and prevent it from
- // happening.
- // DCHECK(delegated_ink_points_.find(pointer_id) ==
- // delegated_ink_points_.end() ||
- // point.timestamp() >
- // delegated_ink_points_[pointer_id].rbegin()->
- // first.timestamp());
- if (metadata_ && point.timestamp() < metadata_->timestamp())
- return;
- DelegatedInkPointTokenMap& token_map = delegated_ink_points_[pointer_id];
- // Always save the point so that it can be drawn later. This allows points
- // that arrive before the first metadata makes it here to be drawn after
- // StartNewTrail is called. If we get to the maximum number of points that
- // we will store, start erasing the oldest ones first. This matches what the
- // OS compositor does internally when it hits the max number of points.
- if (token_map.size() == kMaximumNumberOfPoints)
- token_map.erase(token_map.begin());
- token_map.insert({point, absl::nullopt});
- EraseExcessPointerIds();
- if (pointer_id_ && pointer_id_.value() == pointer_id) {
- DrawDelegatedInkPoint(point);
- } else if (!pointer_id_ && metadata_) {
- DrawSavedTrailPoints();
- }
- }
- void ResetPrediction() override {
- // Don't reset |metadata_| here so that RemoveTrailPoints() can continue
- // to be called as the final metadata(s) arrive.
- // TODO(1052145): Start predicting points and reset it here.
- wait_for_new_trail_to_draw_ = true;
- }
- gfx::DelegatedInkMetadata* MetadataForTesting() const {
- return metadata_.get();
- }
- uint64_t InkTrailTokenCountForTesting() const {
- DCHECK_EQ(delegated_ink_points_.size(), 1u);
- uint64_t valid_tokens = 0u;
- for (const auto& it : delegated_ink_points_.begin()->second) {
- if (it.second)
- valid_tokens++;
- }
- return valid_tokens;
- }
- uint64_t DelegatedInkPointPointerIdCountForTesting() const {
- return delegated_ink_points_.size();
- }
- bool CheckForPointerIdForTesting(int32_t pointer_id) const {
- return delegated_ink_points_.find(pointer_id) !=
- delegated_ink_points_.end();
- }
- const DelegatedInkPointTokenMap& DelegatedInkPointsForTesting(
- int32_t pointer_id) {
- DCHECK(delegated_ink_points_.find(pointer_id) !=
- delegated_ink_points_.end());
- return delegated_ink_points_[pointer_id];
- }
- bool WaitForNewTrailToDrawForTesting() const {
- return wait_for_new_trail_to_draw_;
- }
- uint64_t GetMaximumNumberOfPointerIdsForTesting() const {
- return kMaximumNumberOfPointerIds;
- }
- void SetNeedsDcompPropertiesUpdate() {
- // This should be set from an external event that invalidates our DCOMP
- // resources: |ink_visual_|, |delegated_ink_trail_|. This will be checked in
- // the next call to |SetDelegatedInkTrailStartPoint| - the entry point for
- // using these resources to render a new trail. That code optimizes based on
- // the consideration that properties persist after being set.
- needs_dcomp_properties_update_ = true;
- }
- private:
- // Note that this returns true if the HRESULT is anything other than S_OK,
- // meaning that it returns true when an event is traced (because of a
- // failure).
- static bool TraceEventOnFailure(HRESULT hr, const char* name) {
- if (SUCCEEDED(hr))
- return false;
- TRACE_EVENT_INSTANT1("delegated_ink_trails", name, TRACE_EVENT_SCOPE_THREAD,
- "hr", hr);
- return true;
- }
- bool UpdateVisualClip(const gfx::RectF& new_presentation_area,
- bool force_update) {
- if (!force_update && metadata_ &&
- metadata_->presentation_area() == new_presentation_area)
- return true;
- // If (0,0) of a visual is clipped out, it can result in delegated ink not
- // being drawn at all. This is more common when DComp Surfaces are enabled,
- // but doesn't negatively impact things when the swapchain is used, so just
- // offset the visual instead of clipping the top left corner in all cases.
- ink_visual_->SetOffsetX(new_presentation_area.x());
- ink_visual_->SetOffsetY(new_presentation_area.y());
- D2D_RECT_F clip_rect;
- clip_rect.bottom = new_presentation_area.bottom();
- clip_rect.right = new_presentation_area.right();
- // If setting the clip or committing failed, we want to bail early so that a
- // trail can't incorrectly appear over things on the user's screen.
- return SUCCEEDED(ink_visual_->SetClip(clip_rect)) &&
- SUCCEEDED(dcomp_device_->Commit());
- }
- void EraseExcessPointerIds() {
- auto token_map_it = delegated_ink_points_.begin();
- while (token_map_it != delegated_ink_points_.end()) {
- const DelegatedInkPointTokenMap& token_map = token_map_it->second;
- if (token_map.empty())
- token_map_it = delegated_ink_points_.erase(token_map_it);
- else
- token_map_it++;
- }
- // In order to get to the maximum pointer id limit, sometimes one token map
- // will need to be removed even if it has a valid DelegatedInkPoint. In this
- // case, we'll remove the token map that has gone the longest without
- // receiving a new point - similar to a least recently used eviction policy.
- // This would also mean that the token map that would result in the smallest
- // latency improvement if it were drawn is being removed. The only exception
- // to this is if the above heuristic would result in the token map that has
- // a pointer id matching |pointer_id_| being removed. Since |pointer_id_|
- // matches the trail that is currently being drawn to the screen, we prefer
- // to save it so we can try to continue that trail. In this case, just
- // remove the token map with the oldest new point that is not currently
- // being drawn.
- if (delegated_ink_points_.size() > kMaximumNumberOfPointerIds) {
- std::vector<std::pair<base::TimeTicks, int32_t>>
- earliest_time_and_pointer_id;
- for (const auto& it : delegated_ink_points_) {
- earliest_time_and_pointer_id.emplace_back(
- it.second.rbegin()->first.timestamp(), it.first);
- }
- std::sort(earliest_time_and_pointer_id.begin(),
- earliest_time_and_pointer_id.end());
- uint64_t pointer_id_to_remove = 0;
- while (pointer_id_to_remove < earliest_time_and_pointer_id.size() &&
- pointer_id_ &&
- earliest_time_and_pointer_id[pointer_id_to_remove].second ==
- pointer_id_.value()) {
- pointer_id_to_remove++;
- }
- CHECK_LT(pointer_id_to_remove, earliest_time_and_pointer_id.size());
- delegated_ink_points_.erase(
- earliest_time_and_pointer_id[pointer_id_to_remove].second);
- }
- DCHECK_LE(delegated_ink_points_.size(), kMaximumNumberOfPointerIds);
- }
- absl::optional<int32_t> GetPointerIdForMetadata() {
- if (pointer_id_ && delegated_ink_points_.find(pointer_id_.value()) !=
- delegated_ink_points_.end()) {
- // Since we remove all DelegatedInkPoints with timestamp before
- // |metadata_|'s before calling GetPointerIdForMetadata(), we know we can
- // just grab the first DelegatedInkPoint matching |pointer_id_|.
- const gfx::DelegatedInkPoint& point_matching_metadata =
- delegated_ink_points_[pointer_id_.value()].begin()->first;
- if (point_matching_metadata.MatchesDelegatedInkMetadata(
- metadata_.get())) {
- return pointer_id_;
- }
- }
- pointer_id_ = absl::nullopt;
- for (auto token_map_it = delegated_ink_points_.begin();
- token_map_it != delegated_ink_points_.end() && !pointer_id_;
- ++token_map_it) {
- int32_t potential_pointer_id = token_map_it->first;
- const DelegatedInkPointTokenMap& token_map = token_map_it->second;
- DCHECK(!token_map.empty());
- if (token_map.begin()->first.MatchesDelegatedInkMetadata(
- metadata_.get())) {
- DCHECK(!pointer_id_);
- pointer_id_ = potential_pointer_id;
- }
- }
- return pointer_id_;
- }
- void DrawSavedTrailPoints() {
- DCHECK(metadata_);
- TRACE_EVENT0("delegated_ink_trails", "DrawSavedTrailPoints");
- // Remove all points that have a timestamp earlier than |metadata_|'s, since
- // we know that we won't need to draw them. This is subtly different than
- // the erasing done in SetDelegatedInkTrailStartPoint() - there the point
- // matching the metadata is removed, here it is not. The reason for this
- // difference is because there we know that it matches |metadata_| and
- // therefore it cannot possibly be drawn again, so it is safe to remove.
- // Here however, we are erasing all points up to, but not including, the
- // first point with a timestamp equal to or greater than |metadata_|'s so
- // that we can then check that point to confirm that it matches |metadata_|
- // before deciding to draw an ink trail. The point could then be erased
- // after it is successfully checked against |metadata_| and drawn, it just
- // isn't for simplicity's sake.
- for (auto& it : delegated_ink_points_) {
- DelegatedInkPointTokenMap& token_map = it.second;
- token_map.erase(token_map.begin(),
- token_map.lower_bound(gfx::DelegatedInkPoint(
- metadata_->point(), metadata_->timestamp())));
- }
- EraseExcessPointerIds();
- absl::optional<unsigned int> pointer_id = GetPointerIdForMetadata();
- // Now, the very first point must match |metadata_|, and as long as it does
- // we can continue to draw everything else. If at any point something can't
- // or fails to draw though, don't attempt to draw anything after it so that
- // the trail can match the user's actual stroke.
- if (pointer_id && delegated_ink_points_.find(pointer_id.value()) !=
- delegated_ink_points_.end()) {
- DelegatedInkPointTokenMap& token_map =
- delegated_ink_points_[pointer_id.value()];
- if (!token_map.empty() &&
- token_map.begin()->first.MatchesDelegatedInkMetadata(
- metadata_.get())) {
- for (const auto& it : token_map) {
- if (!DrawDelegatedInkPoint(it.first))
- break;
- }
- }
- } else {
- TRACE_EVENT_INSTANT0("delegated_ink_trails",
- "DrawSavedTrailPoints failed - no pointer id",
- TRACE_EVENT_SCOPE_THREAD);
- }
- }
- bool DrawDelegatedInkPoint(const gfx::DelegatedInkPoint& point) {
- // Always wait for a new trail to be started before attempting to draw
- // anything, even if |metadata_| exists.
- if (wait_for_new_trail_to_draw_)
- return false;
- DCHECK(metadata_);
- if (!metadata_->presentation_area().Contains(point.point()))
- return false;
- DCHECK(delegated_ink_trail_);
- InkTrailPoint ink_point;
- ink_point.radius = metadata_->diameter() / 2.f;
- // In order to account for the visual offset, the point must be offset in
- // the opposite direction.
- ink_point.x = point.point().x() - metadata_->presentation_area().x();
- ink_point.y = point.point().y() - metadata_->presentation_area().y();
- unsigned int token;
- // AddTrailPoints() can accept and draw more than one InkTrailPoint per
- // call. However, all the points get lumped together in the one token then,
- // which means that they can only be removed all together. This may be fine
- // in some scenarios, but in the vast majority of cases we will need to
- // remove one point at a time, so we choose to only add one InkTrailPoint at
- // a time.
- if (TraceEventOnFailure(
- delegated_ink_trail_->AddTrailPoints(&ink_point,
- /*inkPointsCount*/ 1, &token),
- "DelegatedInkPointRendererGpu::DrawDelegatedInkPoint - Failed to "
- "add trail point")) {
- // TODO(1052145): Start predicting points.
- return false;
- }
- TRACE_EVENT_WITH_FLOW1(
- "delegated_ink_trails",
- "DelegatedInkPointRendererGpu::DrawDelegatedInkPoint "
- "- Point added to trail",
- TRACE_ID_GLOBAL(point.trace_id()), TRACE_EVENT_FLAG_FLOW_IN, "point",
- point.ToString());
- delegated_ink_points_[point.pointer_id()][point] = token;
- return true;
- }
- // The visual within the tree that will contain the delegated ink trail. It
- // should be a child of the root surface visual.
- Microsoft::WRL::ComPtr<IDCompositionVisual> ink_visual_;
- // The delegated ink trail object that the ink trail is drawn on. This is the
- // content of the ink visual.
- Microsoft::WRL::ComPtr<DelegatedInkTrail> delegated_ink_trail_;
- // Remember the dcomp device and swap chain used to create
- // |delegated_ink_trail_| and |ink_visual_| so that we can avoid recreating
- // them when it isn't necessary.
- raw_ptr<IDCompositionDevice2> dcomp_device_ = nullptr;
- raw_ptr<IDXGISwapChain1> swap_chain_ = nullptr;
- // The most recent metadata received. The metadata marks the last point of
- // the app rendered stroke, which corresponds to the first point of the
- // delegated ink trail that will be drawn.
- std::unique_ptr<gfx::DelegatedInkMetadata> metadata_;
- // A base::flat_map of all the points that have arrived in
- // StoreDelegatedInkPoint() with a timestamp greater than or equal to that of
- // |metadata_|, where the key is the DelegatedInkPoint that was received, and
- // the value is an optional token. The value will be null until the
- // DelegatedInkPoint is added to the trail, at which point the value is the
- // token that was returned by AddTrailPoints. The elements of the flat_map are
- // sorted by the timestamp of the DelegatedInkPoint. All points are stored in
- // here until we receive a |metadata_|, then any DelegatedInkPoints that have
- // a timestamp earlier than |metadata_|'s are removed, and any new
- // DelegatedInkPoints that may arrive with an earlier timestamp are ignored.
- // Then as each new |metadata| arrives in SetDelegatedInkTrailStartPoint(),
- // we remove any old elements with earlier timestamps, up to and including the
- // element that matches the DelegatedInkMetadata.
- base::flat_map<int32_t, DelegatedInkPointTokenMap> delegated_ink_points_;
- // Cached pointer id of the most recently drawn trail.
- absl::optional<int32_t> pointer_id_;
- // Flag to know if new DelegatedInkPoints that arrive should be drawn
- // immediately or if they should wait for a new trail to be started. Set to
- // true when ResetPrediction is called, as this tells us that something about
- // the pointerevents in the browser process changed. When that happens, we
- // should continue to remove points for any metadata that arrive with
- // timestamps that match DelegatedInkPoints that arrived before the
- // ResetPrediction call. However, once a metadata arrives with a timestamp
- // after the ResetPrediction, then we know that inking should continue, so a
- // new trail is started and we try to draw everything in
- // |delegated_ink_points_|.
- bool wait_for_new_trail_to_draw_ = true;
- // When the visual tree was updated, all properties we've set on DCOMP are
- // outdated, and need to be re-set (i.e. from |ink_visual_| and
- // |delegated_ink_trail_|). This is done at |SetDelegatedInkTrailStartPoint|.
- bool needs_dcomp_properties_update_ = false;
- mojo::Receiver<gfx::mojom::DelegatedInkPointRenderer> receiver_{this};
- };
- } // namespace gl
- #endif // UI_GL_DELEGATED_INK_POINT_RENDERER_GPU_H_
|