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