/* * 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/simulated_hw_composer.h" #include #include "host/frontend/vnc_server/vnc_utils.h" #include "host/libs/config/cuttlefish_config.h" DEFINE_int32(frame_server_fd, -1, ""); using cvd::vnc::SimulatedHWComposer; SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb) : #ifdef FUZZ_TEST_VNC engine_{std::random_device{}()}, #endif bb_{bb}, stripes_(kMaxQueueElements, &SimulatedHWComposer::EraseHalfOfElements), screen_connector_(ScreenConnector::Get(FLAGS_frame_server_fd)) { stripe_maker_ = std::thread(&SimulatedHWComposer::MakeStripes, this); } SimulatedHWComposer::~SimulatedHWComposer() { close(); stripe_maker_.join(); } cvd::vnc::Stripe SimulatedHWComposer::GetNewStripe() { auto s = stripes_.Pop(); #ifdef FUZZ_TEST_VNC if (random_(engine_)) { usleep(7000); stripes_.Push(std::move(s)); s = stripes_.Pop(); } #endif return s; } bool SimulatedHWComposer::closed() { std::lock_guard guard(m_); return closed_; } void SimulatedHWComposer::close() { std::lock_guard guard(m_); closed_ = true; } // Assuming the number of stripes is less than half the size of the queue // this will be safe as the newest stripes won't be lost. In the real // hwcomposer, where stripes are coming in a different order, the full // queue case would probably need a different approach to be safe. void SimulatedHWComposer::EraseHalfOfElements( ThreadSafeQueue::QueueImpl* q) { q->erase(q->begin(), std::next(q->begin(), kMaxQueueElements / 2)); } void SimulatedHWComposer::MakeStripes() { std::uint32_t previous_frame_number = 0; auto screen_height = ScreenConnector::ScreenHeight(); Message raw_screen; std::uint64_t stripe_seq_num = 1; const FrameCallback frame_callback = [&](uint32_t frame_number, uint8_t* frame_pixels) { raw_screen.assign(frame_pixels, frame_pixels + ScreenConnector::ScreenSizeInBytes()); for (int i = 0; i < kNumStripes; ++i) { ++stripe_seq_num; std::uint16_t y = (screen_height / kNumStripes) * i; // Last frames on the right and/or bottom handle extra pixels // when a screen dimension is not evenly divisible by Frame::kNumSlots. std::uint16_t height = screen_height / kNumStripes + (i + 1 == kNumStripes ? screen_height % kNumStripes : 0); const auto* raw_start = &raw_screen[y * ScreenConnector::ScreenWidth() * ScreenConnector::BytesPerPixel()]; const auto* raw_end = raw_start + (height * ScreenConnector::ScreenWidth() * ScreenConnector::BytesPerPixel()); // creating a named object and setting individual data members in order // to make klp happy // TODO (haining) construct this inside the call when not compiling // on klp Stripe s{}; s.index = i; s.frame_id = frame_number; s.x = 0; s.y = y; s.width = ScreenConnector::ScreenWidth(); s.stride = ScreenConnector::ScreenStride(); s.height = height; s.raw_data.assign(raw_start, raw_end); s.seq_number = StripeSeqNumber{stripe_seq_num}; s.orientation = ScreenOrientation::Portrait; stripes_.Push(std::move(s)); } previous_frame_number = frame_number; }; while (!closed()) { bb_->WaitForAtLeastOneClientConnection(); screen_connector_->OnFrameAfter(previous_frame_number, frame_callback); } } int SimulatedHWComposer::NumberOfStripes() { return kNumStripes; }