• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/simulated_hw_composer.h"
18 
19 #include "host/frontend/vnc_server/vnc_utils.h"
20 #include "host/libs/config/cuttlefish_config.h"
21 
22 using cuttlefish::vnc::SimulatedHWComposer;
23 using ScreenConnector = cuttlefish::vnc::ScreenConnector;
24 
SimulatedHWComposer(BlackBoard * bb,ScreenConnector & screen_connector)25 SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb,
26                                          ScreenConnector& screen_connector)
27     :
28 #ifdef FUZZ_TEST_VNC
29       engine_{std::random_device{}()},
30 #endif
31       bb_{bb},
32       stripes_(kMaxQueueElements, &SimulatedHWComposer::EraseHalfOfElements),
33       screen_connector_(screen_connector) {
34   stripe_maker_ = std::thread(&SimulatedHWComposer::MakeStripes, this);
35   screen_connector_.SetCallback(std::move(GetScreenConnectorCallback()));
36 }
37 
~SimulatedHWComposer()38 SimulatedHWComposer::~SimulatedHWComposer() {
39   close();
40   stripe_maker_.join();
41 }
42 
GetNewStripe()43 cuttlefish::vnc::Stripe SimulatedHWComposer::GetNewStripe() {
44   auto s = stripes_.Pop();
45 #ifdef FUZZ_TEST_VNC
46   if (random_(engine_)) {
47     usleep(7000);
48     stripes_.Push(std::move(s));
49     s = stripes_.Pop();
50   }
51 #endif
52   return s;
53 }
54 
closed()55 bool SimulatedHWComposer::closed() {
56   std::lock_guard<std::mutex> guard(m_);
57   return closed_;
58 }
59 
close()60 void SimulatedHWComposer::close() {
61   std::lock_guard<std::mutex> guard(m_);
62   closed_ = true;
63 }
64 
65 // Assuming the number of stripes is less than half the size of the queue
66 // this will be safe as the newest stripes won't be lost. In the real
67 // hwcomposer, where stripes are coming in a different order, the full
68 // queue case would probably need a different approach to be safe.
EraseHalfOfElements(ThreadSafeQueue<Stripe>::QueueImpl * q)69 void SimulatedHWComposer::EraseHalfOfElements(
70     ThreadSafeQueue<Stripe>::QueueImpl* q) {
71   q->erase(q->begin(), std::next(q->begin(), kMaxQueueElements / 2));
72 }
73 
74 SimulatedHWComposer::GenerateProcessedFrameCallback
GetScreenConnectorCallback()75 SimulatedHWComposer::GetScreenConnectorCallback() {
76   return [](std::uint32_t display_number, std::uint8_t* frame_pixels,
77             cuttlefish::vnc::VncScProcessedFrame& processed_frame) {
78     processed_frame.display_number_ = display_number;
79     // TODO(171305898): handle multiple displays.
80     if (display_number != 0) {
81       // BUG 186580833: display_number comes from surface_id in crosvm
82       // create_surface from virtio_gpu.rs set_scanout.  We cannot use it as
83       // the display number. Either crosvm virtio-gpu is incorrectly ignoring
84       // scanout id and instead using a monotonically increasing surface id
85       // number as the scanout resource is replaced over time, or frontend code
86       // here is incorrectly assuming  surface id == display id.
87       display_number = 0;
88     }
89     const std::uint32_t display_w =
90         ScreenConnector::ScreenWidth(display_number);
91     const std::uint32_t display_h =
92         ScreenConnector::ScreenHeight(display_number);
93     const std::uint32_t display_stride_bytes =
94         ScreenConnector::ScreenStrideBytes(display_number);
95     const std::uint32_t display_bpp = ScreenConnector::BytesPerPixel();
96     const std::uint32_t display_size_bytes =
97         ScreenConnector::ScreenSizeInBytes(display_number);
98 
99     auto& raw_screen = processed_frame.raw_screen_;
100     raw_screen.assign(frame_pixels, frame_pixels + display_size_bytes);
101 
102     static std::uint32_t next_frame_number = 0;
103 
104     const auto num_stripes = SimulatedHWComposer::kNumStripes;
105     for (int i = 0; i < num_stripes; ++i) {
106       std::uint16_t y = (display_h / num_stripes) * i;
107 
108       // Last frames on the right and/or bottom handle extra pixels
109       // when a screen dimension is not evenly divisible by Frame::kNumSlots.
110       std::uint16_t height =
111           display_h / num_stripes +
112           (i + 1 == num_stripes ? display_h % num_stripes : 0);
113       const auto* raw_start = &raw_screen[y * display_w * display_bpp];
114       const auto* raw_end = raw_start + (height * display_w * display_bpp);
115       // creating a named object and setting individual data members in order
116       // to make klp happy
117       // TODO (haining) construct this inside the call when not compiling
118       // on klp
119       Stripe s{};
120       s.index = i;
121       s.x = 0;
122       s.y = y;
123       s.width = display_w;
124       s.stride = display_stride_bytes;
125       s.height = height;
126       s.frame_id = next_frame_number++;
127       s.raw_data.assign(raw_start, raw_end);
128       s.orientation = ScreenOrientation::Portrait;
129       processed_frame.stripes_.push_back(std::move(s));
130     }
131 
132     processed_frame.display_number_ = display_number;
133     processed_frame.is_success_ = true;
134   };
135 }
136 
MakeStripes()137 void SimulatedHWComposer::MakeStripes() {
138   std::uint64_t stripe_seq_num = 1;
139   /*
140    * callback should be set before the first WaitForAtLeastOneClientConnection()
141    * (b/178504150) and the first OnFrameAfter().
142    */
143   if (!screen_connector_.IsCallbackSet()) {
144     LOG(FATAL) << "ScreenConnector callback hasn't been set before MakeStripes";
145   }
146   while (!closed()) {
147     bb_->WaitForAtLeastOneClientConnection();
148     auto sim_hw_processed_frame = screen_connector_.OnNextFrame();
149     // sim_hw_processed_frame has display number from the guest
150     if (!sim_hw_processed_frame.is_success_) {
151       continue;
152     }
153     while (!sim_hw_processed_frame.stripes_.empty()) {
154       /*
155        * ScreenConnector that supplies the frames into the queue
156        * cannot be aware of stripe_seq_num. The callback was set at the
157        * ScreenConnector creation time. ScreenConnector calls the callback
158        * function autonomously to make the processed frames to supply the
159        * queue with.
160        *
161        * Besides, ScreenConnector is not VNC specific. Thus, stripe_seq_num,
162        * a VNC specific information, is maintained here.
163        *
164        * OnFrameAfter returns a sim_hw_processed_frame, that contains N consecutive stripes.
165        * each stripe s has an invalid seq_number, default-initialzed
166        * We set the field properly, and push to the stripes_
167        */
168       auto& s = sim_hw_processed_frame.stripes_.front();
169       stripe_seq_num++;
170       s.seq_number = StripeSeqNumber{stripe_seq_num};
171       stripes_.Push(std::move(s));
172       sim_hw_processed_frame.stripes_.pop_front();
173     }
174   }
175 }
176 
NumberOfStripes()177 int SimulatedHWComposer::NumberOfStripes() { return kNumStripes; }
178 
ReportClientsConnected()179 void SimulatedHWComposer::ReportClientsConnected() {
180   screen_connector_.ReportClientsConnected(true);
181 }
182