| /* |
| * 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 <gflags/gflags.h> |
| |
| #include "host/frontend/vnc_server/vnc_utils.h" |
| #include "host/libs/config/cuttlefish_config.h" |
| |
| DEFINE_int32(frame_server_fd, -1, ""); |
| |
| using cuttlefish::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); |
| screen_connector_->SetCallback(std::move(GetScreenConnectorCallback())); |
| } |
| |
| SimulatedHWComposer::~SimulatedHWComposer() { |
| close(); |
| stripe_maker_.join(); |
| } |
| |
| cuttlefish::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<std::mutex> guard(m_); |
| return closed_; |
| } |
| |
| void SimulatedHWComposer::close() { |
| std::lock_guard<std::mutex> 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<Stripe>::QueueImpl* q) { |
| q->erase(q->begin(), std::next(q->begin(), kMaxQueueElements / 2)); |
| } |
| |
| SimulatedHWComposer::GenerateProcessedFrameCallback |
| SimulatedHWComposer::GetScreenConnectorCallback() { |
| return [](std::uint32_t display_number, std::uint8_t* frame_pixels, |
| cuttlefish::vnc::VncScProcessedFrame& processed_frame) { |
| // TODO(171305898): handle multiple displays. |
| if (display_number != 0) { |
| processed_frame.is_success_ = false; |
| return; |
| } |
| const std::uint32_t display_w = |
| SimulatedHWComposer::ScreenConnector::ScreenWidth(display_number); |
| const std::uint32_t display_h = |
| SimulatedHWComposer::ScreenConnector::ScreenHeight(display_number); |
| const std::uint32_t display_stride_bytes = |
| SimulatedHWComposer::ScreenConnector::ScreenStrideBytes(display_number); |
| const std::uint32_t display_bpp = |
| SimulatedHWComposer::ScreenConnector::BytesPerPixel(); |
| const std::uint32_t display_size_bytes = |
| SimulatedHWComposer::ScreenConnector::ScreenSizeInBytes(display_number); |
| |
| auto& raw_screen = processed_frame.raw_screen_; |
| raw_screen.assign(frame_pixels, frame_pixels + display_size_bytes); |
| |
| static std::uint32_t next_frame_number = 0; |
| |
| const auto num_stripes = SimulatedHWComposer::kNumStripes; |
| for (int i = 0; i < num_stripes; ++i) { |
| std::uint16_t y = (display_h / num_stripes) * 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 = |
| display_h / num_stripes + |
| (i + 1 == num_stripes ? display_h % num_stripes : 0); |
| const auto* raw_start = &raw_screen[y * display_w * display_bpp]; |
| const auto* raw_end = raw_start + (height * display_w * display_bpp); |
| // 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.x = 0; |
| s.y = y; |
| s.width = display_w; |
| s.stride = display_stride_bytes; |
| s.height = height; |
| s.frame_id = next_frame_number++; |
| s.raw_data.assign(raw_start, raw_end); |
| s.orientation = ScreenOrientation::Portrait; |
| processed_frame.stripes_.push_back(std::move(s)); |
| } |
| |
| processed_frame.display_number_ = display_number; |
| processed_frame.is_success_ = true; |
| }; |
| } |
| |
| void SimulatedHWComposer::MakeStripes() { |
| std::uint64_t stripe_seq_num = 1; |
| /* |
| * callback should be set before the first WaitForAtLeastOneClientConnection() |
| * (b/178504150) and the first OnFrameAfter(). |
| */ |
| if (!screen_connector_->IsCallbackSet()) { |
| LOG(FATAL) << "ScreenConnector callback hasn't been set before MakeStripes"; |
| } |
| while (!closed()) { |
| bb_->WaitForAtLeastOneClientConnection(); |
| auto sim_hw_processed_frame = screen_connector_->OnNextFrame(); |
| // sim_hw_processed_frame has display number from the guest |
| if (!sim_hw_processed_frame.is_success_) { |
| continue; |
| } |
| while (!sim_hw_processed_frame.stripes_.empty()) { |
| /* |
| * ScreenConnector that supplies the frames into the queue |
| * cannot be aware of stripe_seq_num. The callback was set at the |
| * ScreenConnector creation time. ScreenConnector calls the callback |
| * function autonomously to make the processed frames to supply the |
| * queue with. |
| * |
| * Besides, ScreenConnector is not VNC specific. Thus, stripe_seq_num, |
| * a VNC specific information, is maintained here. |
| * |
| * OnFrameAfter returns a sim_hw_processed_frame, that contains N consecutive stripes. |
| * each stripe s has an invalid seq_number, default-initialzed |
| * We set the field properly, and push to the stripes_ |
| */ |
| auto& s = sim_hw_processed_frame.stripes_.front(); |
| stripe_seq_num++; |
| s.seq_number = StripeSeqNumber{stripe_seq_num}; |
| stripes_.Push(std::move(s)); |
| sim_hw_processed_frame.stripes_.pop_front(); |
| } |
| } |
| } |
| |
| int SimulatedHWComposer::NumberOfStripes() { return kNumStripes; } |
| |
| void SimulatedHWComposer::ReportClientsConnected() { |
| screen_connector_->ReportClientsConnected(true); |
| } |