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