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 #include "PostWorker.h"
17
18 #include <string.h>
19
20 #include <chrono>
21
22 #include "ColorBuffer.h"
23 #include "FrameBuffer.h"
24 #include "RenderThreadInfo.h"
25 #include "aemu/base/Tracing.h"
26 #include "gl/DisplayGl.h"
27 #include "host-common/GfxstreamFatalError.h"
28 #include "host-common/logging.h"
29 #include "host-common/misc.h"
30 #include "vulkan/DisplayVk.h"
31 #include "vulkan/VkCommonOperations.h"
32
33 #define POST_DEBUG 0
34 #if POST_DEBUG >= 1
35 #define DD(fmt, ...) \
36 fprintf(stderr, "%s:%d| " fmt, __func__, __LINE__, ##__VA_ARGS__)
37 #else
38 #define DD(fmt, ...) (void)0
39 #endif
40
41 #define POST_ERROR(fmt, ...) \
42 do { \
43 fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, \
44 ##__VA_ARGS__); \
45 fflush(stderr); \
46 } while (0)
47
sDefaultRunOnUiThread(UiUpdateFunc f,void * data,bool wait)48 static void sDefaultRunOnUiThread(UiUpdateFunc f, void* data, bool wait) {
49 (void)f;
50 (void)data;
51 (void)wait;
52 }
53
54 namespace gfxstream {
55 namespace {
56
57 using emugl::ABORT_REASON_OTHER;
58 using emugl::FatalError;
59 using gl::DisplayGl;
60 using vk::DisplayVk;
61
getTransformFromRotation(int rotation)62 hwc_transform_t getTransformFromRotation(int rotation) {
63 switch (static_cast<int>(rotation / 90)) {
64 case 1:
65 return HWC_TRANSFORM_ROT_270;
66 case 2:
67 return HWC_TRANSFORM_ROT_180;
68 case 3:
69 return HWC_TRANSFORM_ROT_90;
70 default:
71 return HWC_TRANSFORM_NONE;
72 }
73 }
74
75 } // namespace
76
PostWorker(bool mainThreadPostingOnly,Compositor * compositor,DisplayGl * displayGl,DisplayVk * displayVk)77 PostWorker::PostWorker(bool mainThreadPostingOnly, Compositor* compositor,
78 DisplayGl* displayGl, DisplayVk* displayVk)
79 : mFb(FrameBuffer::getFB()),
80 m_mainThreadPostingOnly(mainThreadPostingOnly),
81 m_runOnUiThread(m_mainThreadPostingOnly ? emugl::get_emugl_window_operations().runOnUiThread
82 : sDefaultRunOnUiThread),
83 m_compositor(compositor),
84 m_displayGl(displayGl),
85 m_displayVk(displayVk) {}
86
postWithOverlay(ColorBuffer * cb)87 DisplayGl::PostLayer PostWorker::postWithOverlay(ColorBuffer* cb) {
88 float dpr = mFb->getDpr();
89 int windowWidth = mFb->windowWidth();
90 int windowHeight = mFb->windowHeight();
91 float px = mFb->getPx();
92 float py = mFb->getPy();
93 int zRot = mFb->getZrot();
94 hwc_transform_t rotation = (hwc_transform_t)0;
95
96 // Find the x and y values at the origin when "fully scrolled."
97 // Multiply by 2 because the texture goes from -1 to 1, not 0 to 1.
98 // Multiply the windowing coordinates by DPR because they ignore
99 // DPR, but the viewport includes DPR.
100 float fx = 2.f * (m_viewportWidth - windowWidth * dpr) / (float)m_viewportWidth;
101 float fy = 2.f * (m_viewportHeight - windowHeight * dpr) / (float)m_viewportHeight;
102
103 // finally, compute translation values
104 float dx = px * fx;
105 float dy = py * fy;
106
107 return DisplayGl::PostLayer{
108 .colorBuffer = cb,
109 .overlayOptions =
110 DisplayGl::PostLayer::OverlayOptions{
111 .rotation = static_cast<float>(zRot),
112 .dx = dx,
113 .dy = dy,
114 },
115 };
116 }
117
postImpl(ColorBuffer * cb)118 std::shared_future<void> PostWorker::postImpl(ColorBuffer* cb) {
119 std::shared_future<void> completedFuture =
120 std::async(std::launch::deferred, [] {}).share();
121 completedFuture.wait();
122
123 if (m_displayVk) {
124 constexpr const int kMaxPostRetries = 2;
125 for (int i = 0; i < kMaxPostRetries; i++) {
126 const auto imageInfo = mFb->borrowColorBufferForDisplay(cb->getHndl());
127 auto result = m_displayVk->post(imageInfo.get());
128 if (result.success) {
129 return result.postCompletedWaitable;
130 }
131 }
132
133 ERR("Failed to post ColorBuffer after %d retries.", kMaxPostRetries);
134 return completedFuture;
135 }
136
137 if (!m_displayGl) {
138 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
139 << "PostWorker missing DisplayGl.";
140 }
141
142 DisplayGl::Post post = {};
143
144 ComposeLayer postLayerOptions = {
145 .composeMode = HWC2_COMPOSITION_DEVICE,
146 .blendMode = HWC2_BLEND_MODE_NONE,
147 .alpha = 1.0f,
148 .transform = HWC_TRANSFORM_NONE,
149 };
150
151 const auto& multiDisplay = emugl::get_emugl_multi_display_operations();
152 if (multiDisplay.isMultiDisplayEnabled()) {
153 if (multiDisplay.isMultiDisplayWindow()) {
154 int32_t previousDisplayId = -1;
155 uint32_t currentDisplayId;
156 uint32_t currentDisplayColorBufferHandle;
157 while (multiDisplay.getNextMultiDisplay(previousDisplayId, ¤tDisplayId,
158 /*x=*/nullptr,
159 /*y=*/nullptr,
160 /*w=*/nullptr,
161 /*h=*/nullptr,
162 /*dpi=*/nullptr,
163 /*flags=*/nullptr,
164 ¤tDisplayColorBufferHandle)) {
165 previousDisplayId = currentDisplayId;
166
167 if (currentDisplayColorBufferHandle == 0) {
168 continue;
169 }
170 emugl::get_emugl_window_operations().paintMultiDisplayWindow(
171 currentDisplayId, currentDisplayColorBufferHandle);
172 }
173 post.layers.push_back(postWithOverlay(cb));
174 } else {
175 uint32_t combinedDisplayW = 0;
176 uint32_t combinedDisplayH = 0;
177 multiDisplay.getCombinedDisplaySize(&combinedDisplayW, &combinedDisplayH);
178
179 post.frameWidth = combinedDisplayW;
180 post.frameHeight = combinedDisplayH;
181
182 int32_t previousDisplayId = -1;
183 uint32_t currentDisplayId;
184 int32_t currentDisplayOffsetX;
185 int32_t currentDisplayOffsetY;
186 uint32_t currentDisplayW;
187 uint32_t currentDisplayH;
188 uint32_t currentDisplayColorBufferHandle;
189 while (multiDisplay.getNextMultiDisplay(previousDisplayId,
190 ¤tDisplayId,
191 ¤tDisplayOffsetX,
192 ¤tDisplayOffsetY,
193 ¤tDisplayW,
194 ¤tDisplayH,
195 /*dpi=*/nullptr,
196 /*flags=*/nullptr,
197 ¤tDisplayColorBufferHandle)) {
198 previousDisplayId = currentDisplayId;
199
200 if (currentDisplayW == 0 || currentDisplayH == 0 ||
201 (currentDisplayId != 0 && currentDisplayColorBufferHandle == 0)) {
202 continue;
203 }
204
205 ColorBuffer* currentCb =
206 currentDisplayId == 0
207 ? cb
208 : mFb->findColorBuffer(currentDisplayColorBufferHandle).get();
209 if (!currentCb) {
210 continue;
211 }
212
213 postLayerOptions.displayFrame = {
214 .left = static_cast<int>(currentDisplayOffsetX),
215 .top = static_cast<int>(currentDisplayOffsetY),
216 .right = static_cast<int>(currentDisplayOffsetX + currentDisplayW),
217 .bottom = static_cast<int>(currentDisplayOffsetY + currentDisplayH),
218 };
219 postLayerOptions.crop = {
220 .left = 0.0f,
221 .top = static_cast<float>(currentCb->getHeight()),
222 .right = static_cast<float>(currentCb->getWidth()),
223 .bottom = 0.0f,
224 };
225
226 post.layers.push_back(DisplayGl::PostLayer{
227 .colorBuffer = currentCb,
228 .layerOptions = postLayerOptions,
229 });
230 }
231 }
232 } else if (emugl::get_emugl_window_operations().isFolded()) {
233 const float dpr = mFb->getDpr();
234
235 post.frameWidth = m_viewportWidth / dpr;
236 post.frameHeight = m_viewportHeight / dpr;
237
238 int displayOffsetX;
239 int displayOffsetY;
240 int displayW;
241 int displayH;
242 emugl::get_emugl_window_operations().getFoldedArea(&displayOffsetX,
243 &displayOffsetY,
244 &displayW,
245 &displayH);
246
247 postLayerOptions.displayFrame = {
248 .left = 0,
249 .top = 0,
250 .right = mFb->windowWidth(),
251 .bottom = mFb->windowHeight(),
252 };
253 postLayerOptions.crop = {
254 .left = static_cast<float>(displayOffsetX),
255 .top = static_cast<float>(displayOffsetY + displayH),
256 .right = static_cast<float>(displayOffsetX + displayW),
257 .bottom = static_cast<float>(displayOffsetY),
258 };
259 postLayerOptions.transform = getTransformFromRotation(mFb->getZrot());
260
261 post.layers.push_back(DisplayGl::PostLayer{
262 .colorBuffer = cb,
263 .layerOptions = postLayerOptions,
264 });
265 } else {
266 post.layers.push_back(postWithOverlay(cb));
267 }
268
269 return m_displayGl->post(post);
270 }
271
272 // Called whenever the subwindow needs a refresh (FrameBuffer::setupSubWindow).
273 // This rebinds the subwindow context (to account for
274 // when the refresh is a display change, for instance)
275 // and resets the posting viewport.
viewportImpl(int width,int height)276 void PostWorker::viewportImpl(int width, int height) {
277 if (m_displayVk) {
278 return;
279 }
280
281 const float dpr = mFb->getDpr();
282 m_viewportWidth = width * dpr;
283 m_viewportHeight = height * dpr;
284
285 if (!m_displayGl) {
286 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
287 << "PostWorker missing DisplayGl.";
288 }
289 m_displayGl->viewport(m_viewportWidth, m_viewportHeight);
290 }
291
292 // Called when the subwindow refreshes, but there is no
293 // last posted color buffer to show to the user. Instead of
294 // displaying whatever happens to be in the back buffer,
295 // clear() is useful for outputting consistent colors.
clearImpl()296 void PostWorker::clearImpl() {
297 if (m_displayVk) {
298 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
299 << "PostWorker with Vulkan doesn't support clear";
300 }
301
302 if (!m_displayGl) {
303 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
304 << "PostWorker missing DisplayGl.";
305 }
306 m_displayGl->clear();
307 }
308
composeImpl(const FlatComposeRequest & composeRequest)309 std::shared_future<void> PostWorker::composeImpl(const FlatComposeRequest& composeRequest) {
310 std::shared_future<void> completedFuture =
311 std::async(std::launch::deferred, [] {}).share();
312 completedFuture.wait();
313
314 if (!isComposeTargetReady(composeRequest.targetHandle)) {
315 ERR("The last composition on the target buffer hasn't completed.");
316 }
317
318 Compositor::CompositionRequest compositorRequest = {};
319 compositorRequest.target = mFb->borrowColorBufferForComposition(composeRequest.targetHandle,
320 /*colorBufferIsTarget=*/true);
321 if (!compositorRequest.target) {
322 ERR("Compose target is null (cb=0x%x).", composeRequest.targetHandle);
323 return completedFuture;
324 }
325
326 for (const ComposeLayer& guestLayer : composeRequest.layers) {
327 // Skip the ColorBuffer whose id is 0.
328 if (!guestLayer.cbHandle) {
329 continue;
330 }
331 auto source = mFb->borrowColorBufferForComposition(guestLayer.cbHandle,
332 /*colorBufferIsTarget=*/false);
333 if (!source) {
334 continue;
335 }
336
337 auto& compositorLayer = compositorRequest.layers.emplace_back();
338 compositorLayer.props = guestLayer;
339 compositorLayer.source = std::move(source);
340 }
341
342 return m_compositor->compose(compositorRequest);
343 }
344
screenshot(ColorBuffer * cb,int width,int height,GLenum format,GLenum type,int rotation,void * pixels,Rect rect)345 void PostWorker::screenshot(ColorBuffer* cb, int width, int height, GLenum format, GLenum type,
346 int rotation, void* pixels, Rect rect) {
347 if (m_displayVk) {
348 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
349 "Screenshot not supported with native Vulkan swapchain enabled.";
350 }
351 cb->readToBytesScaled(width, height, format, type, rotation, rect, pixels);
352 }
353
block(std::promise<void> scheduledSignal,std::future<void> continueSignal)354 void PostWorker::block(std::promise<void> scheduledSignal, std::future<void> continueSignal) {
355 // Do not block mainthread.
356 if (m_mainThreadPostingOnly) {
357 return;
358 }
359 // MSVC STL doesn't support not copyable std::packaged_task. As a workaround, we use the
360 // copyable std::shared_ptr here.
361 auto block = std::make_shared<Post::Block>(Post::Block{
362 .scheduledSignal = std::move(scheduledSignal),
363 .continueSignal = std::move(continueSignal),
364 });
365 runTask(std::packaged_task<void()>([block] {
366 block->scheduledSignal.set_value();
367 block->continueSignal.wait();
368 }));
369 }
370
~PostWorker()371 PostWorker::~PostWorker() {}
372
post(ColorBuffer * cb,std::unique_ptr<Post::CompletionCallback> postCallback)373 void PostWorker::post(ColorBuffer* cb, std::unique_ptr<Post::CompletionCallback> postCallback) {
374 auto packagedPostCallback = std::shared_ptr<Post::CompletionCallback>(std::move(postCallback));
375 runTask(
376 std::packaged_task<void()>([cb, packagedPostCallback, this] {
377 auto completedFuture = postImpl(cb);
378 (*packagedPostCallback)(completedFuture);
379 }));
380 }
381
viewport(int width,int height)382 void PostWorker::viewport(int width, int height) {
383 runTask(std::packaged_task<void()>(
384 [width, height, this] { viewportImpl(width, height); }));
385 }
386
compose(std::unique_ptr<FlatComposeRequest> composeRequest,std::unique_ptr<Post::CompletionCallback> composeCallback)387 void PostWorker::compose(std::unique_ptr<FlatComposeRequest> composeRequest,
388 std::unique_ptr<Post::CompletionCallback> composeCallback) {
389 // std::shared_ptr(std::move(...)) is WA for MSFT STL implementation bug:
390 // https://developercommunity.visualstudio.com/t/unable-to-move-stdpackaged-task-into-any-stl-conta/108672
391 auto packagedComposeCallback =
392 std::shared_ptr<Post::CompletionCallback>(std::move(composeCallback));
393 auto packagedComposeRequest = std::shared_ptr<FlatComposeRequest>(std::move(composeRequest));
394 runTask(
395 std::packaged_task<void()>([packagedComposeCallback, packagedComposeRequest, this] {
396 auto completedFuture = composeImpl(*packagedComposeRequest);
397 m_composeTargetToComposeFuture.emplace(packagedComposeRequest->targetHandle,
398 completedFuture);
399 (*packagedComposeCallback)(completedFuture);
400 }));
401 }
402
clear()403 void PostWorker::clear() {
404 runTask(std::packaged_task<void()>([this] { clearImpl(); }));
405 }
406
runTask(std::packaged_task<void ()> task)407 void PostWorker::runTask(std::packaged_task<void()> task) {
408 using Task = std::packaged_task<void()>;
409 auto taskPtr = std::make_unique<Task>(std::move(task));
410 if (m_mainThreadPostingOnly) {
411 m_runOnUiThread(
412 [](void* data) {
413 std::unique_ptr<Task> taskPtr(reinterpret_cast<Task*>(data));
414 (*taskPtr)();
415 },
416 taskPtr.release(), false);
417 } else {
418 (*taskPtr)();
419 }
420 }
421
isComposeTargetReady(uint32_t targetHandle)422 bool PostWorker::isComposeTargetReady(uint32_t targetHandle) {
423 // Even if the target ColorBuffer has already been destroyed, the compose future should have
424 // been waited and set to the ready state.
425 for (auto i = m_composeTargetToComposeFuture.begin();
426 i != m_composeTargetToComposeFuture.end();) {
427 auto& composeFuture = i->second;
428 if (composeFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
429 i = m_composeTargetToComposeFuture.erase(i);
430 } else {
431 i++;
432 }
433 }
434 if (m_composeTargetToComposeFuture.find(targetHandle) == m_composeTargetToComposeFuture.end()) {
435 return true;
436 }
437 return false;
438 }
439
440 } // namespace gfxstream
441