blob: 592b88e02a91801fbd7750162e080028a6a1709d [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "host/frontend/vnc_server/frame_buffer_watcher.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <glog/logging.h>
#include "host/frontend/vnc_server/vnc_utils.h"
using cvd::vnc::FrameBufferWatcher;
FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb)
: bb_{bb}, hwcomposer{bb_} {
for (auto& stripes_vec : stripes_) {
std::generate_n(std::back_inserter(stripes_vec),
SimulatedHWComposer::NumberOfStripes(),
std::make_shared<Stripe>);
}
bb_->set_frame_buffer_watcher(this);
auto num_workers = std::max(std::thread::hardware_concurrency(), 1u);
std::generate_n(std::back_inserter(workers_), num_workers, [this] {
return std::thread{&FrameBufferWatcher::Worker, this};
});
}
FrameBufferWatcher::~FrameBufferWatcher() {
{
std::lock_guard<std::mutex> guard(m_);
closed_ = true;
}
for (auto& tid : workers_) {
tid.join();
}
}
bool FrameBufferWatcher::closed() const {
std::lock_guard<std::mutex> guard(m_);
return closed_;
}
cvd::vnc::Stripe FrameBufferWatcher::Rotated(Stripe stripe) {
if (stripe.orientation == ScreenOrientation::Landscape) {
LOG(FATAL) << "Rotating a landscape stripe, this is a mistake";
}
auto w = stripe.width;
auto s = stripe.stride;
auto h = stripe.height;
const auto& raw = stripe.raw_data;
Message rotated(raw.size(), 0xAA);
for (std::uint16_t i = 0; i < w; ++i) {
for (std::uint16_t j = 0; j < h; ++j) {
size_t to = (i * h + j) * BytesPerPixel();
size_t from = (w - (i + 1)) * BytesPerPixel() + s * j;
CHECK(from < raw.size());
CHECK(to < rotated.size());
std::memcpy(&rotated[to], &raw[from], BytesPerPixel());
}
}
std::swap(stripe.x, stripe.y);
std::swap(stripe.width, stripe.height);
// The new stride after rotating is the height, as it is not aligned again.
stripe.stride = stripe.width * BytesPerPixel();
stripe.raw_data = std::move(rotated);
stripe.orientation = ScreenOrientation::Landscape;
return stripe;
}
bool FrameBufferWatcher::StripeIsDifferentFromPrevious(
const Stripe& stripe) const {
return Stripes(stripe.orientation)[stripe.index]->raw_data != stripe.raw_data;
}
cvd::vnc::StripePtrVec FrameBufferWatcher::StripesNewerThan(
ScreenOrientation orientation, const SeqNumberVec& seq_numbers) const {
std::lock_guard<std::mutex> guard(stripes_lock_);
const auto& stripes = Stripes(orientation);
CHECK(seq_numbers.size() == stripes.size());
StripePtrVec new_stripes;
auto seq_number_it = seq_numbers.begin();
std::copy_if(stripes.begin(), stripes.end(), std::back_inserter(new_stripes),
[seq_number_it](const StripePtrVec::value_type& s) mutable {
return *(seq_number_it++) < s->seq_number;
});
return new_stripes;
}
cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
ScreenOrientation orientation) {
return stripes_[static_cast<int>(orientation)];
}
const cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
ScreenOrientation orientation) const {
return stripes_[static_cast<int>(orientation)];
}
bool FrameBufferWatcher::UpdateMostRecentSeqNumIfStripeIsNew(
const Stripe& stripe) {
if (most_recent_identical_stripe_seq_nums_[stripe.index] <=
stripe.seq_number) {
most_recent_identical_stripe_seq_nums_[stripe.index] = stripe.seq_number;
return true;
}
return false;
}
bool FrameBufferWatcher::UpdateStripeIfStripeIsNew(
const std::shared_ptr<const Stripe>& stripe) {
std::lock_guard<std::mutex> guard(stripes_lock_);
if (UpdateMostRecentSeqNumIfStripeIsNew(*stripe)) {
Stripes(stripe->orientation)[stripe->index] = stripe;
return true;
}
return false;
}
void FrameBufferWatcher::CompressStripe(JpegCompressor* jpeg_compressor,
Stripe* stripe) {
stripe->jpeg_data = jpeg_compressor->Compress(
stripe->raw_data, bb_->jpeg_quality_level(), 0, 0, stripe->width,
stripe->height, stripe->stride);
}
void FrameBufferWatcher::Worker() {
JpegCompressor jpeg_compressor;
#ifdef FUZZ_TEST_VNC
std::default_random_engine e{std::random_device{}()};
std::uniform_int_distribution<int> random{0, 2};
#endif
while (!closed()) {
auto portrait_stripe = hwcomposer.GetNewStripe();
if (closed()) {
break;
}
{
// TODO(haining) use if (with init) and else for c++17 instead of extra
// scope and continue
// if (std::lock_guard guard(stripes_lock_); /*condition*/) { }
std::lock_guard<std::mutex> guard(stripes_lock_);
if (!StripeIsDifferentFromPrevious(portrait_stripe)) {
UpdateMostRecentSeqNumIfStripeIsNew(portrait_stripe);
continue;
}
}
auto seq_num = portrait_stripe.seq_number;
auto index = portrait_stripe.index;
auto landscape_stripe = Rotated(portrait_stripe);
auto stripes = {std::make_shared<Stripe>(std::move(portrait_stripe)),
std::make_shared<Stripe>(std::move(landscape_stripe))};
for (auto& stripe : stripes) {
#ifdef FUZZ_TEST_VNC
if (random(e)) {
usleep(10000);
}
#endif
CompressStripe(&jpeg_compressor, stripe.get());
}
bool any_new_stripes = false;
for (auto& stripe : stripes) {
any_new_stripes = UpdateStripeIfStripeIsNew(stripe) || any_new_stripes;
}
if (any_new_stripes) {
bb_->NewStripeReady(index, seq_num);
}
}
}
int FrameBufferWatcher::StripesPerFrame() {
return SimulatedHWComposer::NumberOfStripes();
}