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 <android-base/logging.h>
29 #include "host/frontend/vnc_server/vnc_utils.h"
30 #include "host/libs/screen_connector/screen_connector.h"
31 
32 using cuttlefish::vnc::FrameBufferWatcher;
33 
FrameBufferWatcher(BlackBoard * bb)34 FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb)
35     : bb_{bb}, hwcomposer{bb_} {
36   for (auto& stripes_vec : stripes_) {
37     std::generate_n(std::back_inserter(stripes_vec),
38                     SimulatedHWComposer::NumberOfStripes(),
39                     std::make_shared<Stripe>);
40   }
41   bb_->set_frame_buffer_watcher(this);
42   auto num_workers = std::max(std::thread::hardware_concurrency(), 1u);
__anon00141b7e0102null43   std::generate_n(std::back_inserter(workers_), num_workers, [this] {
44     return std::thread{&FrameBufferWatcher::Worker, this};
45   });
46 }
47 
~FrameBufferWatcher()48 FrameBufferWatcher::~FrameBufferWatcher() {
49   {
50     std::lock_guard<std::mutex> guard(m_);
51     closed_ = true;
52   }
53   for (auto& tid : workers_) {
54     tid.join();
55   }
56 }
57 
closed() const58 bool FrameBufferWatcher::closed() const {
59   std::lock_guard<std::mutex> guard(m_);
60   return closed_;
61 }
62 
Rotated(Stripe stripe)63 cuttlefish::vnc::Stripe FrameBufferWatcher::Rotated(Stripe stripe) {
64   if (stripe.orientation == ScreenOrientation::Landscape) {
65     LOG(FATAL) << "Rotating a landscape stripe, this is a mistake";
66   }
67   auto w = stripe.width;
68   auto s = stripe.stride;
69   auto h = stripe.height;
70   const auto& raw = stripe.raw_data;
71   Message rotated(raw.size(), 0xAA);
72   for (std::uint16_t i = 0; i < w; ++i) {
73     for (std::uint16_t j = 0; j < h; ++j) {
74       size_t to = (i * h + j) * ScreenConnector::BytesPerPixel();
75       size_t from = (w - (i + 1)) * ScreenConnector::BytesPerPixel() + s * j;
76       CHECK(from < raw.size());
77       CHECK(to < rotated.size());
78       std::memcpy(&rotated[to], &raw[from], ScreenConnector::BytesPerPixel());
79     }
80   }
81   std::swap(stripe.x, stripe.y);
82   std::swap(stripe.width, stripe.height);
83   // The new stride after rotating is the height, as it is not aligned again.
84   stripe.stride = stripe.width * ScreenConnector::BytesPerPixel();
85   stripe.raw_data = std::move(rotated);
86   stripe.orientation = ScreenOrientation::Landscape;
87   return stripe;
88 }
89 
StripeIsDifferentFromPrevious(const Stripe & stripe) const90 bool FrameBufferWatcher::StripeIsDifferentFromPrevious(
91     const Stripe& stripe) const {
92   return Stripes(stripe.orientation)[stripe.index]->raw_data != stripe.raw_data;
93 }
94 
StripesNewerThan(ScreenOrientation orientation,const SeqNumberVec & seq_numbers) const95 cuttlefish::vnc::StripePtrVec FrameBufferWatcher::StripesNewerThan(
96     ScreenOrientation orientation, const SeqNumberVec& seq_numbers) const {
97   std::lock_guard<std::mutex> guard(stripes_lock_);
98   const auto& stripes = Stripes(orientation);
99   CHECK(seq_numbers.size() == stripes.size());
100   StripePtrVec new_stripes;
101   auto seq_number_it = seq_numbers.begin();
102   std::copy_if(stripes.begin(), stripes.end(), std::back_inserter(new_stripes),
103                [seq_number_it](const StripePtrVec::value_type& s) mutable {
104                  return *(seq_number_it++) < s->seq_number;
105                });
106   return new_stripes;
107 }
108 
Stripes(ScreenOrientation orientation)109 cuttlefish::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
110     ScreenOrientation orientation) {
111   return stripes_[static_cast<int>(orientation)];
112 }
113 
Stripes(ScreenOrientation orientation) const114 const cuttlefish::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
115     ScreenOrientation orientation) const {
116   return stripes_[static_cast<int>(orientation)];
117 }
118 
UpdateMostRecentSeqNumIfStripeIsNew(const Stripe & stripe)119 bool FrameBufferWatcher::UpdateMostRecentSeqNumIfStripeIsNew(
120     const Stripe& stripe) {
121   if (most_recent_identical_stripe_seq_nums_[stripe.index] <=
122       stripe.seq_number) {
123     most_recent_identical_stripe_seq_nums_[stripe.index] = stripe.seq_number;
124     return true;
125   }
126   return false;
127 }
128 
UpdateStripeIfStripeIsNew(const std::shared_ptr<const Stripe> & stripe)129 bool FrameBufferWatcher::UpdateStripeIfStripeIsNew(
130     const std::shared_ptr<const Stripe>& stripe) {
131   std::lock_guard<std::mutex> guard(stripes_lock_);
132   if (UpdateMostRecentSeqNumIfStripeIsNew(*stripe)) {
133     Stripes(stripe->orientation)[stripe->index] = stripe;
134     return true;
135   }
136   return false;
137 }
138 
CompressStripe(JpegCompressor * jpeg_compressor,Stripe * stripe)139 void FrameBufferWatcher::CompressStripe(JpegCompressor* jpeg_compressor,
140                                         Stripe* stripe) {
141   stripe->jpeg_data = jpeg_compressor->Compress(
142       stripe->raw_data, bb_->jpeg_quality_level(), 0, 0, stripe->width,
143       stripe->height, stripe->stride);
144 }
145 
Worker()146 void FrameBufferWatcher::Worker() {
147   JpegCompressor jpeg_compressor;
148 #ifdef FUZZ_TEST_VNC
149   std::default_random_engine e{std::random_device{}()};
150   std::uniform_int_distribution<int> random{0, 2};
151 #endif
152   while (!closed()) {
153     auto portrait_stripe = hwcomposer.GetNewStripe();
154     if (closed()) {
155       break;
156     }
157     {
158       // TODO(haining) use if (with init) and else for c++17 instead of extra
159       // scope and continue
160       // if (std::lock_guard guard(stripes_lock_); /*condition*/) { }
161       std::lock_guard<std::mutex> guard(stripes_lock_);
162       if (!StripeIsDifferentFromPrevious(portrait_stripe)) {
163         UpdateMostRecentSeqNumIfStripeIsNew(portrait_stripe);
164         continue;
165       }
166     }
167     auto seq_num = portrait_stripe.seq_number;
168     auto index = portrait_stripe.index;
169     auto landscape_stripe = Rotated(portrait_stripe);
170     auto stripes = {std::make_shared<Stripe>(std::move(portrait_stripe)),
171                     std::make_shared<Stripe>(std::move(landscape_stripe))};
172     for (auto& stripe : stripes) {
173 #ifdef FUZZ_TEST_VNC
174       if (random(e)) {
175         usleep(10000);
176       }
177 #endif
178       CompressStripe(&jpeg_compressor, stripe.get());
179     }
180     bool any_new_stripes = false;
181     for (auto& stripe : stripes) {
182       any_new_stripes = UpdateStripeIfStripeIsNew(stripe) || any_new_stripes;
183     }
184     if (any_new_stripes) {
185       bb_->NewStripeReady(index, seq_num);
186     }
187   }
188 }
189 
StripesPerFrame()190 int FrameBufferWatcher::StripesPerFrame() {
191   return SimulatedHWComposer::NumberOfStripes();
192 }
193