• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "host/frontend/vnc_server/frame_buffer_watcher.h"
18 
19 #include <algorithm>
20 #include <cstdint>
21 #include <cstring>
22 #include <iterator>
23 #include <memory>
24 #include <mutex>
25 #include <thread>
26 #include <utility>
27 
28 #include <glog/logging.h>
29 #include "host/frontend/vnc_server/vnc_utils.h"
30 
31 using cvd::vnc::FrameBufferWatcher;
32 
FrameBufferWatcher(BlackBoard * bb)33 FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb)
34     : bb_{bb}, hwcomposer{bb_} {
35   for (auto& stripes_vec : stripes_) {
36     std::generate_n(std::back_inserter(stripes_vec),
37                     SimulatedHWComposer::NumberOfStripes(),
38                     std::make_shared<Stripe>);
39   }
40   bb_->set_frame_buffer_watcher(this);
41   auto num_workers = std::max(std::thread::hardware_concurrency(), 1u);
__anone9ea39cf0102null42   std::generate_n(std::back_inserter(workers_), num_workers, [this] {
43     return std::thread{&FrameBufferWatcher::Worker, this};
44   });
45 }
46 
~FrameBufferWatcher()47 FrameBufferWatcher::~FrameBufferWatcher() {
48   {
49     std::lock_guard<std::mutex> guard(m_);
50     closed_ = true;
51   }
52   for (auto& tid : workers_) {
53     tid.join();
54   }
55 }
56 
closed() const57 bool FrameBufferWatcher::closed() const {
58   std::lock_guard<std::mutex> guard(m_);
59   return closed_;
60 }
61 
Rotated(Stripe stripe)62 cvd::vnc::Stripe FrameBufferWatcher::Rotated(Stripe stripe) {
63   if (stripe.orientation == ScreenOrientation::Landscape) {
64     LOG(FATAL) << "Rotating a landscape stripe, this is a mistake";
65   }
66   auto w = stripe.width;
67   auto s = stripe.stride;
68   auto h = stripe.height;
69   const auto& raw = stripe.raw_data;
70   Message rotated(raw.size(), 0xAA);
71   for (std::uint16_t i = 0; i < w; ++i) {
72     for (std::uint16_t j = 0; j < h; ++j) {
73       size_t to = (i * h + j) * BytesPerPixel();
74       size_t from = (w - (i + 1)) * BytesPerPixel() + s * j;
75       CHECK(from < raw.size());
76       CHECK(to < rotated.size());
77       std::memcpy(&rotated[to], &raw[from], BytesPerPixel());
78     }
79   }
80   std::swap(stripe.x, stripe.y);
81   std::swap(stripe.width, stripe.height);
82   // The new stride after rotating is the height, as it is not aligned again.
83   stripe.stride = stripe.width * BytesPerPixel();
84   stripe.raw_data = std::move(rotated);
85   stripe.orientation = ScreenOrientation::Landscape;
86   return stripe;
87 }
88 
StripeIsDifferentFromPrevious(const Stripe & stripe) const89 bool FrameBufferWatcher::StripeIsDifferentFromPrevious(
90     const Stripe& stripe) const {
91   return Stripes(stripe.orientation)[stripe.index]->raw_data != stripe.raw_data;
92 }
93 
StripesNewerThan(ScreenOrientation orientation,const SeqNumberVec & seq_numbers) const94 cvd::vnc::StripePtrVec FrameBufferWatcher::StripesNewerThan(
95     ScreenOrientation orientation, const SeqNumberVec& seq_numbers) const {
96   std::lock_guard<std::mutex> guard(stripes_lock_);
97   const auto& stripes = Stripes(orientation);
98   CHECK(seq_numbers.size() == stripes.size());
99   StripePtrVec new_stripes;
100   auto seq_number_it = seq_numbers.begin();
101   std::copy_if(stripes.begin(), stripes.end(), std::back_inserter(new_stripes),
102                [seq_number_it](const StripePtrVec::value_type& s) mutable {
103                  return *(seq_number_it++) < s->seq_number;
104                });
105   return new_stripes;
106 }
107 
Stripes(ScreenOrientation orientation)108 cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
109     ScreenOrientation orientation) {
110   return stripes_[static_cast<int>(orientation)];
111 }
112 
Stripes(ScreenOrientation orientation) const113 const cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
114     ScreenOrientation orientation) const {
115   return stripes_[static_cast<int>(orientation)];
116 }
117 
UpdateMostRecentSeqNumIfStripeIsNew(const Stripe & stripe)118 bool FrameBufferWatcher::UpdateMostRecentSeqNumIfStripeIsNew(
119     const Stripe& stripe) {
120   if (most_recent_identical_stripe_seq_nums_[stripe.index] <=
121       stripe.seq_number) {
122     most_recent_identical_stripe_seq_nums_[stripe.index] = stripe.seq_number;
123     return true;
124   }
125   return false;
126 }
127 
UpdateStripeIfStripeIsNew(const std::shared_ptr<const Stripe> & stripe)128 bool FrameBufferWatcher::UpdateStripeIfStripeIsNew(
129     const std::shared_ptr<const Stripe>& stripe) {
130   std::lock_guard<std::mutex> guard(stripes_lock_);
131   if (UpdateMostRecentSeqNumIfStripeIsNew(*stripe)) {
132     Stripes(stripe->orientation)[stripe->index] = stripe;
133     return true;
134   }
135   return false;
136 }
137 
CompressStripe(JpegCompressor * jpeg_compressor,Stripe * stripe)138 void FrameBufferWatcher::CompressStripe(JpegCompressor* jpeg_compressor,
139                                         Stripe* stripe) {
140   stripe->jpeg_data = jpeg_compressor->Compress(
141       stripe->raw_data, bb_->jpeg_quality_level(), 0, 0, stripe->width,
142       stripe->height, stripe->stride);
143 }
144 
Worker()145 void FrameBufferWatcher::Worker() {
146   JpegCompressor jpeg_compressor;
147 #ifdef FUZZ_TEST_VNC
148   std::default_random_engine e{std::random_device{}()};
149   std::uniform_int_distribution<int> random{0, 2};
150 #endif
151   while (!closed()) {
152     auto portrait_stripe = hwcomposer.GetNewStripe();
153     if (closed()) {
154       break;
155     }
156     {
157       // TODO(haining) use if (with init) and else for c++17 instead of extra
158       // scope and continue
159       // if (std::lock_guard guard(stripes_lock_); /*condition*/) { }
160       std::lock_guard<std::mutex> guard(stripes_lock_);
161       if (!StripeIsDifferentFromPrevious(portrait_stripe)) {
162         UpdateMostRecentSeqNumIfStripeIsNew(portrait_stripe);
163         continue;
164       }
165     }
166     auto seq_num = portrait_stripe.seq_number;
167     auto index = portrait_stripe.index;
168     auto landscape_stripe = Rotated(portrait_stripe);
169     auto stripes = {std::make_shared<Stripe>(std::move(portrait_stripe)),
170                     std::make_shared<Stripe>(std::move(landscape_stripe))};
171     for (auto& stripe : stripes) {
172 #ifdef FUZZ_TEST_VNC
173       if (random(e)) {
174         usleep(10000);
175       }
176 #endif
177       CompressStripe(&jpeg_compressor, stripe.get());
178     }
179     bool any_new_stripes = false;
180     for (auto& stripe : stripes) {
181       any_new_stripes = UpdateStripeIfStripeIsNew(stripe) || any_new_stripes;
182     }
183     if (any_new_stripes) {
184       bb_->NewStripeReady(index, seq_num);
185     }
186   }
187 }
188 
StripesPerFrame()189 int FrameBufferWatcher::StripesPerFrame() {
190   return SimulatedHWComposer::NumberOfStripes();
191 }
192