blob: 55fdacde44d996bf7784d644ae5a2a0f93d3f883 [file] [log] [blame]
Lloyd Pique32cbe282018-10-19 13:09:22 -07001/*
2 * Copyright 2019 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 <android-base/stringprintf.h>
18#include <compositionengine/CompositionEngine.h>
Lloyd Pique3d0c02e2018-10-19 18:38:12 -070019#include <compositionengine/DisplayColorProfile.h>
Lloyd Pique688abd42019-02-15 15:42:24 -080020#include <compositionengine/Layer.h>
Lloyd Piquecc01a452018-12-04 17:24:00 -080021#include <compositionengine/LayerFE.h>
Lloyd Pique31cb2942018-10-19 17:23:03 -070022#include <compositionengine/RenderSurface.h>
Lloyd Pique688abd42019-02-15 15:42:24 -080023#include <compositionengine/impl/LayerCompositionState.h>
Lloyd Pique32cbe282018-10-19 13:09:22 -070024#include <compositionengine/impl/Output.h>
Lloyd Piquecc01a452018-12-04 17:24:00 -080025#include <compositionengine/impl/OutputLayer.h>
Lloyd Pique688abd42019-02-15 15:42:24 -080026#include <renderengine/DisplaySettings.h>
27#include <renderengine/RenderEngine.h>
Lloyd Pique32cbe282018-10-19 13:09:22 -070028#include <ui/DebugUtils.h>
Lloyd Pique688abd42019-02-15 15:42:24 -080029#include <ui/HdrCapabilities.h>
Lloyd Pique66d68602019-02-13 14:23:31 -080030#include <utils/Trace.h>
Lloyd Pique32cbe282018-10-19 13:09:22 -070031
Lloyd Pique688abd42019-02-15 15:42:24 -080032#include "TracedOrdinal.h"
33
Lloyd Piquefeb73d72018-12-04 17:23:44 -080034namespace android::compositionengine {
35
36Output::~Output() = default;
37
38namespace impl {
Lloyd Pique32cbe282018-10-19 13:09:22 -070039
40Output::Output(const CompositionEngine& compositionEngine)
41 : mCompositionEngine(compositionEngine) {}
42
43Output::~Output() = default;
44
45const CompositionEngine& Output::getCompositionEngine() const {
46 return mCompositionEngine;
47}
48
49bool Output::isValid() const {
Lloyd Pique3d0c02e2018-10-19 18:38:12 -070050 return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface &&
51 mRenderSurface->isValid();
Lloyd Pique32cbe282018-10-19 13:09:22 -070052}
53
54const std::string& Output::getName() const {
55 return mName;
56}
57
58void Output::setName(const std::string& name) {
59 mName = name;
60}
61
62void Output::setCompositionEnabled(bool enabled) {
63 if (mState.isEnabled == enabled) {
64 return;
65 }
66
67 mState.isEnabled = enabled;
68 dirtyEntireOutput();
69}
70
71void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
72 const Rect& viewport, const Rect& scissor, bool needsFiltering) {
73 mState.transform = transform;
74 mState.orientation = orientation;
75 mState.scissor = scissor;
76 mState.frame = frame;
77 mState.viewport = viewport;
78 mState.needsFiltering = needsFiltering;
79
80 dirtyEntireOutput();
81}
82
Lloyd Pique688abd42019-02-15 15:42:24 -080083// TODO(b/121291683): Rename setSize() once more is moved.
Lloyd Pique31cb2942018-10-19 17:23:03 -070084void Output::setBounds(const ui::Size& size) {
85 mRenderSurface->setDisplaySize(size);
Lloyd Pique688abd42019-02-15 15:42:24 -080086 // TODO(b/121291683): Rename mState.size once more is moved.
Lloyd Pique31cb2942018-10-19 17:23:03 -070087 mState.bounds = Rect(mRenderSurface->getSize());
Lloyd Pique32cbe282018-10-19 13:09:22 -070088
89 dirtyEntireOutput();
90}
91
Lloyd Piqueef36b002019-01-23 17:52:04 -080092void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
93 mState.layerStackId = layerStackId;
94 mState.layerStackInternal = isInternal;
Lloyd Pique32cbe282018-10-19 13:09:22 -070095
96 dirtyEntireOutput();
97}
98
99void Output::setColorTransform(const mat4& transform) {
Lloyd Pique77f79a22019-04-29 15:55:40 -0700100 if (mState.colorTransformMat == transform) {
101 return;
102 }
103
Lloyd Pique32cbe282018-10-19 13:09:22 -0700104 const bool isIdentity = (transform == mat4());
Lloyd Piqueef958122019-02-05 18:00:12 -0800105 const auto newColorTransform =
Lloyd Pique32cbe282018-10-19 13:09:22 -0700106 isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
Lloyd Piqueef958122019-02-05 18:00:12 -0800107
Lloyd Piqueef958122019-02-05 18:00:12 -0800108 mState.colorTransform = newColorTransform;
Alec Mouric7e8ce82019-04-02 12:28:05 -0700109 mState.colorTransformMat = transform;
Lloyd Piqueef958122019-02-05 18:00:12 -0800110
111 dirtyEntireOutput();
Lloyd Pique32cbe282018-10-19 13:09:22 -0700112}
113
114void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
Lloyd Piquef5275482019-01-29 18:42:42 -0800115 ui::RenderIntent renderIntent,
116 ui::Dataspace colorSpaceAgnosticDataspace) {
117 ui::Dataspace targetDataspace =
118 getDisplayColorProfile()->getTargetDataspace(mode, dataspace,
119 colorSpaceAgnosticDataspace);
120
Lloyd Piqueef958122019-02-05 18:00:12 -0800121 if (mState.colorMode == mode && mState.dataspace == dataspace &&
Lloyd Piquef5275482019-01-29 18:42:42 -0800122 mState.renderIntent == renderIntent && mState.targetDataspace == targetDataspace) {
Lloyd Piqueef958122019-02-05 18:00:12 -0800123 return;
124 }
125
Lloyd Pique32cbe282018-10-19 13:09:22 -0700126 mState.colorMode = mode;
127 mState.dataspace = dataspace;
128 mState.renderIntent = renderIntent;
Lloyd Piquef5275482019-01-29 18:42:42 -0800129 mState.targetDataspace = targetDataspace;
Lloyd Pique32cbe282018-10-19 13:09:22 -0700130
Lloyd Pique31cb2942018-10-19 17:23:03 -0700131 mRenderSurface->setBufferDataspace(dataspace);
132
Lloyd Pique32cbe282018-10-19 13:09:22 -0700133 ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
134 decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
135 renderIntent);
Lloyd Piqueef958122019-02-05 18:00:12 -0800136
137 dirtyEntireOutput();
Lloyd Pique32cbe282018-10-19 13:09:22 -0700138}
139
140void Output::dump(std::string& out) const {
141 using android::base::StringAppendF;
142
143 StringAppendF(&out, " Composition Output State: [\"%s\"]", mName.c_str());
144
145 out.append("\n ");
146
147 dumpBase(out);
148}
149
150void Output::dumpBase(std::string& out) const {
151 mState.dump(out);
Lloyd Pique31cb2942018-10-19 17:23:03 -0700152
Lloyd Pique3d0c02e2018-10-19 18:38:12 -0700153 if (mDisplayColorProfile) {
154 mDisplayColorProfile->dump(out);
155 } else {
156 out.append(" No display color profile!\n");
157 }
158
Lloyd Pique31cb2942018-10-19 17:23:03 -0700159 if (mRenderSurface) {
160 mRenderSurface->dump(out);
161 } else {
162 out.append(" No render surface!\n");
163 }
Lloyd Pique37c2c9b2018-12-04 17:25:10 -0800164
Lloyd Pique207def92019-02-28 16:09:52 -0800165 android::base::StringAppendF(&out, "\n %zu Layers\b", mOutputLayersOrderedByZ.size());
Lloyd Pique37c2c9b2018-12-04 17:25:10 -0800166 for (const auto& outputLayer : mOutputLayersOrderedByZ) {
167 if (!outputLayer) {
168 continue;
169 }
170 outputLayer->dump(out);
171 }
Lloyd Pique31cb2942018-10-19 17:23:03 -0700172}
173
Lloyd Pique3d0c02e2018-10-19 18:38:12 -0700174compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
175 return mDisplayColorProfile.get();
176}
177
178void Output::setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
179 mDisplayColorProfile = std::move(mode);
180}
181
182void Output::setDisplayColorProfileForTest(
183 std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
184 mDisplayColorProfile = std::move(mode);
185}
186
Lloyd Pique31cb2942018-10-19 17:23:03 -0700187compositionengine::RenderSurface* Output::getRenderSurface() const {
188 return mRenderSurface.get();
189}
190
191void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
192 mRenderSurface = std::move(surface);
193 mState.bounds = Rect(mRenderSurface->getSize());
194
195 dirtyEntireOutput();
196}
197
198void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface> surface) {
199 mRenderSurface = std::move(surface);
Lloyd Pique32cbe282018-10-19 13:09:22 -0700200}
201
202const OutputCompositionState& Output::getState() const {
203 return mState;
204}
205
206OutputCompositionState& Output::editState() {
207 return mState;
208}
209
Alec Mourie7d1d4a2019-02-05 01:13:46 +0000210Region Output::getDirtyRegion(bool repaintEverything) const {
211 Region dirty(mState.viewport);
212 if (!repaintEverything) {
213 dirty.andSelf(mState.dirtyRegion);
Lloyd Pique32cbe282018-10-19 13:09:22 -0700214 }
215 return dirty;
216}
217
Lloyd Piqueef36b002019-01-23 17:52:04 -0800218bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
219 // The layerStackId's must match, and also the layer must not be internal
220 // only when not on an internal output.
221 return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal);
Lloyd Pique32cbe282018-10-19 13:09:22 -0700222}
223
Lloyd Piquecc01a452018-12-04 17:24:00 -0800224compositionengine::OutputLayer* Output::getOutputLayerForLayer(
225 compositionengine::Layer* layer) const {
226 for (const auto& outputLayer : mOutputLayersOrderedByZ) {
227 if (outputLayer && &outputLayer->getLayer() == layer) {
228 return outputLayer.get();
229 }
230 }
231 return nullptr;
232}
233
234std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
Lloyd Pique07e33212018-12-18 16:33:37 -0800235 std::optional<DisplayId> displayId, std::shared_ptr<compositionengine::Layer> layer,
236 sp<compositionengine::LayerFE> layerFE) {
Lloyd Piquecc01a452018-12-04 17:24:00 -0800237 for (auto& outputLayer : mOutputLayersOrderedByZ) {
238 if (outputLayer && &outputLayer->getLayer() == layer.get()) {
239 return std::move(outputLayer);
240 }
241 }
Lloyd Pique07e33212018-12-18 16:33:37 -0800242 return createOutputLayer(mCompositionEngine, displayId, *this, layer, layerFE);
Lloyd Piquecc01a452018-12-04 17:24:00 -0800243}
244
245void Output::setOutputLayersOrderedByZ(OutputLayers&& layers) {
246 mOutputLayersOrderedByZ = std::move(layers);
247}
248
249const Output::OutputLayers& Output::getOutputLayersOrderedByZ() const {
250 return mOutputLayersOrderedByZ;
251}
252
Lloyd Piquec7ef21b2019-01-29 18:43:00 -0800253void Output::setReleasedLayers(Output::ReleasedLayers&& layers) {
254 mReleasedLayers = std::move(layers);
255}
256
257Output::ReleasedLayers Output::takeReleasedLayers() {
258 return std::move(mReleasedLayers);
259}
260
Lloyd Piqued0a92a02019-02-19 17:47:26 -0800261void Output::beginFrame() {
262 const bool dirty = !getDirtyRegion(false).isEmpty();
263 const bool empty = mOutputLayersOrderedByZ.empty();
264 const bool wasEmpty = !mState.lastCompositionHadVisibleLayers;
265
266 // If nothing has changed (!dirty), don't recompose.
267 // If something changed, but we don't currently have any visible layers,
268 // and didn't when we last did a composition, then skip it this time.
269 // The second rule does two things:
270 // - When all layers are removed from a display, we'll emit one black
271 // frame, then nothing more until we get new layers.
272 // - When a display is created with a private layer stack, we won't
273 // emit any black frames until a layer is added to the layer stack.
274 const bool mustRecompose = dirty && !(empty && wasEmpty);
275
276 const char flagPrefix[] = {'-', '+'};
277 static_cast<void>(flagPrefix);
278 ALOGV_IF("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __FUNCTION__,
279 mustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
280 flagPrefix[empty], flagPrefix[wasEmpty]);
281
282 mRenderSurface->beginFrame(mustRecompose);
283
284 if (mustRecompose) {
285 mState.lastCompositionHadVisibleLayers = !empty;
286 }
287}
288
Lloyd Pique66d68602019-02-13 14:23:31 -0800289void Output::prepareFrame() {
290 ATRACE_CALL();
291 ALOGV(__FUNCTION__);
292
293 if (!mState.isEnabled) {
294 return;
295 }
296
297 chooseCompositionStrategy();
298
299 mRenderSurface->prepareFrame(mState.usesClientComposition, mState.usesDeviceComposition);
300}
301
Lloyd Pique688abd42019-02-15 15:42:24 -0800302bool Output::composeSurfaces(const Region& debugRegion, base::unique_fd* readyFence) {
303 ATRACE_CALL();
304 ALOGV(__FUNCTION__);
305
306 const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
307 mState.usesClientComposition};
308 if (!hasClientComposition) {
309 return true;
310 }
311
312 ALOGV("hasClientComposition");
313
314 auto& renderEngine = mCompositionEngine.getRenderEngine();
315 const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
316
317 renderengine::DisplaySettings clientCompositionDisplay;
Lloyd Pique810b0302019-08-14 16:09:32 -0700318 clientCompositionDisplay.physicalDisplay = mState.scissor;
Lloyd Pique688abd42019-02-15 15:42:24 -0800319 clientCompositionDisplay.clip = mState.scissor;
320 clientCompositionDisplay.globalTransform = mState.transform.asMatrix4();
321 clientCompositionDisplay.orientation = mState.orientation;
322 clientCompositionDisplay.outputDataspace =
323 mDisplayColorProfile->hasWideColorGamut() ? mState.dataspace : ui::Dataspace::UNKNOWN;
324 clientCompositionDisplay.maxLuminance =
325 mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
326
327 // Compute the global color transform matrix.
328 if (!mState.usesDeviceComposition && !getSkipColorTransform()) {
329 clientCompositionDisplay.colorTransform = mState.colorTransformMat;
330 }
331
332 // Note: Updated by generateClientCompositionRequests
333 clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
334
335 // Generate the client composition requests for the layers on this output.
336 std::vector<renderengine::LayerSettings> clientCompositionLayers =
337 generateClientCompositionRequests(supportsProtectedContent,
338 clientCompositionDisplay.clearRegion);
339 appendRegionFlashRequests(debugRegion, clientCompositionLayers);
340
341 // If we the display is secure, protected content support is enabled, and at
342 // least one layer has protected content, we need to use a secure back
343 // buffer.
344 if (mState.isSecure && supportsProtectedContent) {
345 bool needsProtected =
346 std::any_of(mOutputLayersOrderedByZ.begin(), mOutputLayersOrderedByZ.end(),
347 [](auto& layer) {
348 return layer->getLayer().getState().frontEnd.hasProtectedContent;
349 });
350 if (needsProtected != renderEngine.isProtected()) {
351 renderEngine.useProtectedContext(needsProtected);
352 }
353 if (needsProtected != mRenderSurface->isProtected() &&
354 needsProtected == renderEngine.isProtected()) {
355 mRenderSurface->setProtected(needsProtected);
356 }
357 }
358
359 base::unique_fd fd;
360 sp<GraphicBuffer> buf = mRenderSurface->dequeueBuffer(&fd);
361 if (buf == nullptr) {
362 ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
363 "client composition for this frame",
364 mName.c_str());
365 return false;
366 }
367
368 // We boost GPU frequency here because there will be color spaces conversion
369 // and it's expensive. We boost the GPU frequency so that GPU composition can
370 // finish in time. We must reset GPU frequency afterwards, because high frequency
371 // consumes extra battery.
372 const bool expensiveRenderingExpected =
373 clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3;
374 if (expensiveRenderingExpected) {
375 setExpensiveRenderingExpected(true);
376 }
377
378 renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
379 buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
380 readyFence);
381
382 if (expensiveRenderingExpected) {
383 setExpensiveRenderingExpected(false);
384 }
385
386 return true;
387}
388
389std::vector<renderengine::LayerSettings> Output::generateClientCompositionRequests(
390 bool supportsProtectedContent, Region& clearRegion) {
391 std::vector<renderengine::LayerSettings> clientCompositionLayers;
392 ALOGV("Rendering client layers");
393
394 const Region viewportRegion(mState.viewport);
395 const bool useIdentityTransform = false;
396 bool firstLayer = true;
397 // Used when a layer clears part of the buffer.
398 Region dummyRegion;
399
400 for (auto& layer : mOutputLayersOrderedByZ) {
401 const auto& layerState = layer->getState();
402 const auto& layerFEState = layer->getLayer().getState().frontEnd;
403 auto& layerFE = layer->getLayerFE();
404
405 const Region clip(viewportRegion.intersect(layer->getState().visibleRegion));
406 ALOGV("Layer: %s", layerFE.getDebugName());
407 if (clip.isEmpty()) {
408 ALOGV(" Skipping for empty clip");
409 firstLayer = false;
410 continue;
411 }
412
413 bool clientComposition = layer->requiresClientComposition();
414
415 // We clear the client target for non-client composed layers if
416 // requested by the HWC. We skip this if the layer is not an opaque
417 // rectangle, as by definition the layer must blend with whatever is
418 // underneath. We also skip the first layer as the buffer target is
419 // guaranteed to start out cleared.
420 bool clearClientComposition =
421 layerState.clearClientTarget && layerFEState.isOpaque && !firstLayer;
422
423 ALOGV(" Composition type: client %d clear %d", clientComposition, clearClientComposition);
424
425 if (clientComposition || clearClientComposition) {
426 compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
427 clip,
428 useIdentityTransform,
429 layer->needsFiltering() || mState.needsFiltering,
430 mState.isSecure,
431 supportsProtectedContent,
432 clientComposition ? clearRegion : dummyRegion,
433 };
434 if (auto result = layerFE.prepareClientComposition(targetSettings)) {
435 if (clearClientComposition) {
436 auto& layerSettings = *result;
437 layerSettings.source.buffer.buffer = nullptr;
438 layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
439 layerSettings.alpha = half(0.0);
440 layerSettings.disableBlending = true;
441 }
442
443 clientCompositionLayers.push_back(*result);
444 }
445 }
446
447 firstLayer = false;
448 }
449
450 return clientCompositionLayers;
451}
452
453void Output::appendRegionFlashRequests(
454 const Region& flashRegion,
455 std::vector<renderengine::LayerSettings>& clientCompositionLayers) {
456 if (flashRegion.isEmpty()) {
457 return;
458 }
459
460 renderengine::LayerSettings layerSettings;
461 layerSettings.source.buffer.buffer = nullptr;
462 layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
463 layerSettings.alpha = half(1.0);
464
465 for (const auto& rect : flashRegion) {
466 layerSettings.geometry.boundaries = rect.toFloatRect();
467 clientCompositionLayers.push_back(layerSettings);
468 }
469}
470
471void Output::setExpensiveRenderingExpected(bool) {
472 // The base class does nothing with this call.
473}
474
Lloyd Pique35fca9d2019-02-13 14:24:11 -0800475void Output::postFramebuffer() {
476 ATRACE_CALL();
477 ALOGV(__FUNCTION__);
478
479 if (!getState().isEnabled) {
480 return;
481 }
482
Lloyd Pique35fca9d2019-02-13 14:24:11 -0800483 auto frame = presentAndGetFrameFences();
484
Lloyd Pique7d90ba52019-08-08 11:57:53 -0700485 mRenderSurface->onPresentDisplayCompleted();
486
Lloyd Pique35fca9d2019-02-13 14:24:11 -0800487 for (auto& layer : getOutputLayersOrderedByZ()) {
488 // The layer buffer from the previous frame (if any) is released
489 // by HWC only when the release fence from this frame (if any) is
490 // signaled. Always get the release fence from HWC first.
491 sp<Fence> releaseFence = Fence::NO_FENCE;
492
493 if (auto hwcLayer = layer->getHwcLayer()) {
494 if (auto f = frame.layerFences.find(hwcLayer); f != frame.layerFences.end()) {
495 releaseFence = f->second;
496 }
497 }
498
499 // If the layer was client composited in the previous frame, we
500 // need to merge with the previous client target acquire fence.
501 // Since we do not track that, always merge with the current
502 // client target acquire fence when it is available, even though
503 // this is suboptimal.
504 // TODO(b/121291683): Track previous frame client target acquire fence.
505 if (mState.usesClientComposition) {
506 releaseFence =
507 Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
508 }
509
510 layer->getLayerFE().onLayerDisplayed(releaseFence);
511 }
512
513 // We've got a list of layers needing fences, that are disjoint with
514 // getOutputLayersOrderedByZ. The best we can do is to
515 // supply them with the present fence.
516 for (auto& weakLayer : mReleasedLayers) {
517 if (auto layer = weakLayer.promote(); layer != nullptr) {
518 layer->onLayerDisplayed(frame.presentFence);
519 }
520 }
521
522 // Clear out the released layers now that we're done with them.
523 mReleasedLayers.clear();
524}
525
Lloyd Pique32cbe282018-10-19 13:09:22 -0700526void Output::dirtyEntireOutput() {
527 mState.dirtyRegion.set(mState.bounds);
528}
529
Lloyd Pique66d68602019-02-13 14:23:31 -0800530void Output::chooseCompositionStrategy() {
531 // The base output implementation can only do client composition
532 mState.usesClientComposition = true;
533 mState.usesDeviceComposition = false;
534}
535
Lloyd Pique688abd42019-02-15 15:42:24 -0800536bool Output::getSkipColorTransform() const {
537 return true;
538}
539
Lloyd Pique35fca9d2019-02-13 14:24:11 -0800540compositionengine::Output::FrameFences Output::presentAndGetFrameFences() {
541 compositionengine::Output::FrameFences result;
542 if (mState.usesClientComposition) {
543 result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence();
544 }
545 return result;
546}
547
Lloyd Piquefeb73d72018-12-04 17:23:44 -0800548} // namespace impl
549} // namespace android::compositionengine