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