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 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); 42 std::generate_n(std::back_inserter(workers_), num_workers, [this] { 43 return std::thread{&FrameBufferWatcher::Worker, this}; 44 }); 45 } 46 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 57 bool FrameBufferWatcher::closed() const { 58 std::lock_guard<std::mutex> guard(m_); 59 return closed_; 60 } 61 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 h = stripe.height; 68 const auto& raw = stripe.raw_data; 69 Message rotated(raw.size(), 0xAA); 70 for (std::uint16_t i = 0; i < w; ++i) { 71 for (std::uint16_t j = 0; j < h; ++j) { 72 size_t to = (i * h + j) * BytesPerPixel(); 73 size_t from = (w - (i + 1) + w * j) * BytesPerPixel(); 74 CHECK(from < raw.size()); 75 CHECK(to < rotated.size()); 76 std::memcpy(&rotated[to], &raw[from], BytesPerPixel()); 77 } 78 } 79 std::swap(stripe.x, stripe.y); 80 std::swap(stripe.width, stripe.height); 81 stripe.raw_data = std::move(rotated); 82 stripe.orientation = ScreenOrientation::Landscape; 83 return stripe; 84 } 85 86 bool FrameBufferWatcher::StripeIsDifferentFromPrevious( 87 const Stripe& stripe) const { 88 return Stripes(stripe.orientation)[stripe.index]->raw_data != stripe.raw_data; 89 } 90 91 cvd::vnc::StripePtrVec FrameBufferWatcher::StripesNewerThan( 92 ScreenOrientation orientation, const SeqNumberVec& seq_numbers) const { 93 std::lock_guard<std::mutex> guard(stripes_lock_); 94 const auto& stripes = Stripes(orientation); 95 CHECK(seq_numbers.size() == stripes.size()); 96 StripePtrVec new_stripes; 97 auto seq_number_it = seq_numbers.begin(); 98 std::copy_if(stripes.begin(), stripes.end(), std::back_inserter(new_stripes), 99 [seq_number_it](const StripePtrVec::value_type& s) mutable { 100 return *(seq_number_it++) < s->seq_number; 101 }); 102 return new_stripes; 103 } 104 105 cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes( 106 ScreenOrientation orientation) { 107 return stripes_[static_cast<int>(orientation)]; 108 } 109 110 const cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes( 111 ScreenOrientation orientation) const { 112 return stripes_[static_cast<int>(orientation)]; 113 } 114 115 bool FrameBufferWatcher::UpdateMostRecentSeqNumIfStripeIsNew( 116 const Stripe& stripe) { 117 if (most_recent_identical_stripe_seq_nums_[stripe.index] <= 118 stripe.seq_number) { 119 most_recent_identical_stripe_seq_nums_[stripe.index] = stripe.seq_number; 120 return true; 121 } 122 return false; 123 } 124 125 bool FrameBufferWatcher::UpdateStripeIfStripeIsNew( 126 const std::shared_ptr<const Stripe>& stripe) { 127 std::lock_guard<std::mutex> guard(stripes_lock_); 128 if (UpdateMostRecentSeqNumIfStripeIsNew(*stripe)) { 129 Stripes(stripe->orientation)[stripe->index] = stripe; 130 return true; 131 } 132 return false; 133 } 134 135 void FrameBufferWatcher::CompressStripe(JpegCompressor* jpeg_compressor, 136 Stripe* stripe) { 137 stripe->jpeg_data = jpeg_compressor->Compress( 138 stripe->raw_data, bb_->jpeg_quality_level(), 0, 0, stripe->width, 139 stripe->height, stripe->width); 140 } 141 142 void FrameBufferWatcher::Worker() { 143 JpegCompressor jpeg_compressor; 144 #ifdef FUZZ_TEST_VNC 145 std::default_random_engine e{std::random_device{}()}; 146 std::uniform_int_distribution<int> random{0, 2}; 147 #endif 148 while (!closed()) { 149 auto portrait_stripe = hwcomposer.GetNewStripe(); 150 if (closed()) { 151 break; 152 } 153 { 154 // TODO(haining) use if (with init) and else for c++17 instead of extra 155 // scope and continue 156 // if (std::lock_guard guard(stripes_lock_); /*condition*/) { } 157 std::lock_guard<std::mutex> guard(stripes_lock_); 158 if (!StripeIsDifferentFromPrevious(portrait_stripe)) { 159 UpdateMostRecentSeqNumIfStripeIsNew(portrait_stripe); 160 continue; 161 } 162 } 163 auto seq_num = portrait_stripe.seq_number; 164 auto index = portrait_stripe.index; 165 auto landscape_stripe = Rotated(portrait_stripe); 166 auto stripes = {std::make_shared<Stripe>(std::move(portrait_stripe)), 167 std::make_shared<Stripe>(std::move(landscape_stripe))}; 168 for (auto& stripe : stripes) { 169 #ifdef FUZZ_TEST_VNC 170 if (random(e)) { 171 usleep(10000); 172 } 173 #endif 174 CompressStripe(&jpeg_compressor, stripe.get()); 175 } 176 bool any_new_stripes = false; 177 for (auto& stripe : stripes) { 178 any_new_stripes = UpdateStripeIfStripeIsNew(stripe) || any_new_stripes; 179 } 180 if (any_new_stripes) { 181 bb_->NewStripeReady(index, seq_num); 182 } 183 } 184 } 185 186 int FrameBufferWatcher::StripesPerFrame() { 187 return SimulatedHWComposer::NumberOfStripes(); 188 } 189