1 // Copyright 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expresso or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "ColorBuffer.h"
16 
17 #if GFXSTREAM_ENABLE_HOST_GLES
18 #include "gl/EmulationGl.h"
19 #endif
20 
21 #include "host-common/GfxstreamFatalError.h"
22 #include "host-common/logging.h"
23 #include "vulkan/ColorBufferVk.h"
24 #include "vulkan/VkCommonOperations.h"
25 
26 using android::base::ManagedDescriptor;
27 using emugl::ABORT_REASON_OTHER;
28 using emugl::FatalError;
29 
30 namespace gfxstream {
31 namespace {
32 
33 // ColorBufferVk natively supports YUV images. However, ColorBufferGl
34 // needs to emulate YUV support by having an underlying RGBA texture
35 // and adding in additional YUV<->RGBA conversions when needed. The
36 // memory should not be shared between the VK YUV image and the GL RGBA
37 // texture.
shouldAttemptExternalMemorySharing(FrameworkFormat format)38 bool shouldAttemptExternalMemorySharing(FrameworkFormat format) {
39     return format == FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE;
40 }
41 
42 }  // namespace
43 
ColorBuffer(HandleType handle,uint32_t width,uint32_t height,GLenum format,FrameworkFormat frameworkFormat)44 ColorBuffer::ColorBuffer(HandleType handle, uint32_t width, uint32_t height, GLenum format,
45                          FrameworkFormat frameworkFormat)
46     : mHandle(handle),
47       mWidth(width),
48       mHeight(height),
49       mFormat(format),
50       mFrameworkFormat(frameworkFormat) {}
51 
52 /*static*/
create(gl::EmulationGl * emulationGl,vk::VkEmulation * emulationVk,uint32_t width,uint32_t height,GLenum format,FrameworkFormat frameworkFormat,HandleType handle,android::base::Stream * stream,bool linear)53 std::shared_ptr<ColorBuffer> ColorBuffer::create(gl::EmulationGl* emulationGl,
54                                                  vk::VkEmulation* emulationVk, uint32_t width,
55                                                  uint32_t height, GLenum format,
56                                                  FrameworkFormat frameworkFormat, HandleType handle,
57                                                  android::base::Stream* stream, bool linear) {
58     std::shared_ptr<ColorBuffer> colorBuffer(
59         new ColorBuffer(handle, width, height, format, frameworkFormat));
60 
61     if (stream) {
62         // When vk snapshot enabled, mNeedRestore will be touched and set to false immediately.
63         colorBuffer->mNeedRestore = true;
64     }
65 #if GFXSTREAM_ENABLE_HOST_GLES
66     if (emulationGl) {
67         if (stream) {
68             colorBuffer->mColorBufferGl = emulationGl->loadColorBuffer(stream);
69             assert(width == colorBuffer->mColorBufferGl->getWidth());
70             assert(height == colorBuffer->mColorBufferGl->getHeight());
71             assert(frameworkFormat == colorBuffer->mColorBufferGl->getFrameworkFormat());
72         } else {
73             colorBuffer->mColorBufferGl =
74                 emulationGl->createColorBuffer(width, height, format, frameworkFormat, handle);
75         }
76         if (!colorBuffer->mColorBufferGl) {
77             ERR("Failed to initialize ColorBufferGl.");
78             return nullptr;
79         }
80     }
81 #endif
82 
83     if (emulationVk && emulationVk->live) {
84         const bool vulkanOnly = colorBuffer->mColorBufferGl == nullptr;
85         uint32_t memoryProperty = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
86         if (vulkanOnly && linear) {
87             memoryProperty |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
88         }
89         colorBuffer->mColorBufferVk = vk::ColorBufferVk::create(
90             handle, width, height, format, frameworkFormat, vulkanOnly, memoryProperty, stream);
91         if (!colorBuffer->mColorBufferVk) {
92             if (emulationGl) {
93                 // Historically, ColorBufferVk setup was deferred until the first actual Vulkan
94                 // usage. This allowed ColorBufferVk setup failures to be unintentionally avoided.
95             } else {
96                 ERR("Failed to initialize ColorBufferVk.");
97                 return nullptr;
98             }
99         }
100     }
101 
102 #if GFXSTREAM_ENABLE_HOST_GLES
103     bool b271028352Workaround = emulationGl && strstr(emulationGl->getGlesRenderer().c_str(), "Intel");
104     bool vkSnapshotEnabled = emulationVk && emulationVk->features.VulkanSnapshots.enabled;
105 
106     if ((!stream || vkSnapshotEnabled) && colorBuffer->mColorBufferGl && colorBuffer->mColorBufferVk &&
107         !b271028352Workaround && shouldAttemptExternalMemorySharing(frameworkFormat)) {
108         colorBuffer->touch();
109         auto memoryExport = vk::exportColorBufferMemory(handle);
110         if (memoryExport) {
111             if (colorBuffer->mColorBufferGl->importMemory(
112                     std::move(memoryExport->descriptor), memoryExport->size,
113                     memoryExport->dedicatedAllocation, memoryExport->linearTiling)) {
114                 colorBuffer->mGlAndVkAreSharingExternalMemory = true;
115             } else {
116                 ERR("Failed to import memory to ColorBufferGl:%d", handle);
117             }
118         }
119     }
120 #endif
121 
122     return colorBuffer;
123 }
124 
125 /*static*/
onLoad(gl::EmulationGl * emulationGl,vk::VkEmulation * emulationVk,android::base::Stream * stream)126 std::shared_ptr<ColorBuffer> ColorBuffer::onLoad(gl::EmulationGl* emulationGl,
127                                                  vk::VkEmulation* emulationVk,
128                                                  android::base::Stream* stream) {
129     const auto handle = static_cast<HandleType>(stream->getBe32());
130     const auto width = static_cast<uint32_t>(stream->getBe32());
131     const auto height = static_cast<uint32_t>(stream->getBe32());
132     const auto format = static_cast<GLenum>(stream->getBe32());
133     const auto frameworkFormat = static_cast<FrameworkFormat>(stream->getBe32());
134 
135     std::shared_ptr<ColorBuffer> colorBuffer = ColorBuffer::create(
136         emulationGl, emulationVk, width, height, format, frameworkFormat, handle, stream);
137 
138     return colorBuffer;
139 }
140 
onSave(android::base::Stream * stream)141 void ColorBuffer::onSave(android::base::Stream* stream) {
142     stream->putBe32(getHndl());
143     stream->putBe32(mWidth);
144     stream->putBe32(mHeight);
145     stream->putBe32(static_cast<uint32_t>(mFormat));
146     stream->putBe32(static_cast<uint32_t>(mFrameworkFormat));
147 
148 #if GFXSTREAM_ENABLE_HOST_GLES
149     if (mColorBufferGl) {
150         mColorBufferGl->onSave(stream);
151     }
152 #endif
153     if (mColorBufferVk) {
154         mColorBufferVk->onSave(stream);
155     }
156 }
157 
restore()158 void ColorBuffer::restore() {
159 #if GFXSTREAM_ENABLE_HOST_GLES
160     if (mColorBufferGl) {
161         mColorBufferGl->restore();
162     }
163 #endif
164 }
165 
readToBytes(int x,int y,int width,int height,GLenum pixelsFormat,GLenum pixelsType,void * outPixels)166 void ColorBuffer::readToBytes(int x, int y, int width, int height, GLenum pixelsFormat,
167                               GLenum pixelsType, void* outPixels) {
168     touch();
169 
170 #if GFXSTREAM_ENABLE_HOST_GLES
171     if (mColorBufferGl) {
172         mColorBufferGl->readPixels(x, y, width, height, pixelsFormat, pixelsType, outPixels);
173         return;
174     }
175 #endif
176 
177     if (mColorBufferVk) {
178         mColorBufferVk->readToBytes(x, y, width, height, outPixels);
179         return;
180     }
181 
182     GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No ColorBuffer impl?";
183 }
184 
readToBytesScaled(int pixelsWidth,int pixelsHeight,GLenum pixelsFormat,GLenum pixelsType,int pixelsRotation,Rect rect,void * outPixels)185 void ColorBuffer::readToBytesScaled(int pixelsWidth, int pixelsHeight, GLenum pixelsFormat,
186                                     GLenum pixelsType, int pixelsRotation, Rect rect,
187                                     void* outPixels) {
188     touch();
189 
190 #if GFXSTREAM_ENABLE_HOST_GLES
191     if (mColorBufferGl) {
192         mColorBufferGl->readPixelsScaled(pixelsWidth, pixelsHeight, pixelsFormat, pixelsType,
193                                          pixelsRotation, rect, outPixels);
194         return;
195     }
196 #endif
197 
198     GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Unimplemented.";
199 }
200 
readYuvToBytes(int x,int y,int width,int height,void * outPixels,uint32_t pixelsSize)201 void ColorBuffer::readYuvToBytes(int x, int y, int width, int height, void* outPixels,
202                                  uint32_t pixelsSize) {
203     touch();
204 
205 #if GFXSTREAM_ENABLE_HOST_GLES
206     if (mColorBufferGl) {
207         mColorBufferGl->readPixelsYUVCached(x, y, width, height, outPixels, pixelsSize);
208         return;
209     }
210 #endif
211 
212     if (mColorBufferVk) {
213         mColorBufferVk->readToBytes(x, y, width, height, outPixels);
214         return;
215     }
216 
217     GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No ColorBuffer impl?";
218 }
219 
updateFromBytes(int x,int y,int width,int height,FrameworkFormat frameworkFormat,GLenum pixelsFormat,GLenum pixelsType,const void * pixels,void * metadata)220 bool ColorBuffer::updateFromBytes(int x, int y, int width, int height,
221                                   FrameworkFormat frameworkFormat, GLenum pixelsFormat,
222                                   GLenum pixelsType, const void* pixels, void* metadata) {
223     touch();
224 
225 #if GFXSTREAM_ENABLE_HOST_GLES
226     if (mColorBufferGl) {
227         mColorBufferGl->subUpdateFromFrameworkFormat(x, y, width, height, frameworkFormat,
228                                                      pixelsFormat, pixelsType, pixels, metadata);
229         return true;
230     }
231 #endif
232 
233     if (mColorBufferVk) {
234         return mColorBufferVk->updateFromBytes(x, y, width, height, pixels);
235     }
236 
237     GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No ColorBuffer impl?";
238     return false;
239 }
240 
updateFromBytes(int x,int y,int width,int height,GLenum pixelsFormat,GLenum pixelsType,const void * pixels)241 bool ColorBuffer::updateFromBytes(int x, int y, int width, int height, GLenum pixelsFormat,
242                                   GLenum pixelsType, const void* pixels) {
243     touch();
244 
245 #if GFXSTREAM_ENABLE_HOST_GLES
246     if (mColorBufferGl) {
247         return mColorBufferGl->subUpdate(x, y, width, height, pixelsFormat, pixelsType, pixels);
248     }
249 #endif
250 
251     if (mColorBufferVk) {
252         return mColorBufferVk->updateFromBytes(x, y, width, height, pixels);
253     }
254 
255     GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No ColorBuffer impl?";
256     return false;
257 }
258 
updateGlFromBytes(const void * bytes,std::size_t bytesSize)259 bool ColorBuffer::updateGlFromBytes(const void* bytes, std::size_t bytesSize) {
260 #if GFXSTREAM_ENABLE_HOST_GLES
261     if (mColorBufferGl) {
262         touch();
263 
264         return mColorBufferGl->replaceContents(bytes, bytesSize);
265     }
266 #endif
267 
268     return true;
269 }
270 
borrowForComposition(UsedApi api,bool isTarget)271 std::unique_ptr<BorrowedImageInfo> ColorBuffer::borrowForComposition(UsedApi api, bool isTarget) {
272     switch (api) {
273         case UsedApi::kGl: {
274 #if GFXSTREAM_ENABLE_HOST_GLES
275             if (!mColorBufferGl) {
276                 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
277             }
278             return mColorBufferGl->getBorrowedImageInfo();
279 #endif
280         }
281         case UsedApi::kVk: {
282             if (!mColorBufferVk) {
283                 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
284             }
285             return vk::borrowColorBufferForComposition(getHndl(), isTarget);
286         }
287     }
288     GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Unimplemented";
289     return nullptr;
290 }
291 
borrowForDisplay(UsedApi api)292 std::unique_ptr<BorrowedImageInfo> ColorBuffer::borrowForDisplay(UsedApi api) {
293     switch (api) {
294         case UsedApi::kGl: {
295 #if GFXSTREAM_ENABLE_HOST_GLES
296             if (!mColorBufferGl) {
297                 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
298             }
299             return mColorBufferGl->getBorrowedImageInfo();
300 #endif
301         }
302         case UsedApi::kVk: {
303             if (!mColorBufferVk) {
304                 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
305             }
306             return vk::borrowColorBufferForDisplay(getHndl());
307         }
308     }
309     GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Unimplemented";
310     return nullptr;
311 }
312 
flushFromGl()313 bool ColorBuffer::flushFromGl() {
314     if (!(mColorBufferGl && mColorBufferVk)) {
315         return true;
316     }
317 
318     if (mGlAndVkAreSharingExternalMemory) {
319         return true;
320     }
321 
322     // ColorBufferGl is currently considered the "main" backing. If this changes,
323     // the "main"  should be updated from the current contents of the GL backing.
324     return true;
325 }
326 
flushFromVk()327 bool ColorBuffer::flushFromVk() {
328     if (!(mColorBufferGl && mColorBufferVk)) {
329         return true;
330     }
331 
332     if (mGlAndVkAreSharingExternalMemory) {
333         return true;
334     }
335     std::vector<uint8_t> contents;
336     if (!vk::readColorBufferToBytes(mHandle, &contents)) {
337         ERR("Failed to get VK contents for ColorBuffer:%d", mHandle);
338         return false;
339     }
340 
341     if (contents.empty()) {
342         return false;
343     }
344 
345 #if GFXSTREAM_ENABLE_HOST_GLES
346     if (!mColorBufferGl->replaceContents(contents.data(), contents.size())) {
347         ERR("Failed to set GL contents for ColorBuffer:%d", mHandle);
348         return false;
349     }
350 #endif
351 
352     return true;
353 }
354 
flushFromVkBytes(const void * bytes,size_t bytesSize)355 bool ColorBuffer::flushFromVkBytes(const void* bytes, size_t bytesSize) {
356     if (!(mColorBufferGl && mColorBufferVk)) {
357         return true;
358     }
359 
360     if (mGlAndVkAreSharingExternalMemory) {
361         return true;
362     }
363 
364 #if GFXSTREAM_ENABLE_HOST_GLES
365     if (mColorBufferGl) {
366         if (!mColorBufferGl->replaceContents(bytes, bytesSize)) {
367             ERR("Failed to update ColorBuffer:%d GL backing from VK bytes.", mHandle);
368             return false;
369         }
370     }
371 #endif
372 
373     return true;
374 }
375 
invalidateForGl()376 bool ColorBuffer::invalidateForGl() {
377     if (!(mColorBufferGl && mColorBufferVk)) {
378         return true;
379     }
380 
381     if (mGlAndVkAreSharingExternalMemory) {
382         return true;
383     }
384 
385     // ColorBufferGl is currently considered the "main" backing. If this changes,
386     // the GL backing should be updated from the "main" backing.
387     return true;
388 }
389 
invalidateForVk()390 bool ColorBuffer::invalidateForVk() {
391     if (!(mColorBufferGl && mColorBufferVk)) {
392         return true;
393     }
394 
395     if (mGlAndVkAreSharingExternalMemory) {
396         return true;
397     }
398 
399 #if GFXSTREAM_ENABLE_HOST_GLES
400     std::size_t contentsSize = 0;
401     if (!mColorBufferGl->readContents(&contentsSize, nullptr)) {
402         ERR("Failed to get GL contents size for ColorBuffer:%d", mHandle);
403         return false;
404     }
405 
406     std::vector<uint8_t> contents(contentsSize, 0);
407 
408     if (!mColorBufferGl->readContents(&contentsSize, contents.data())) {
409         ERR("Failed to get GL contents for ColorBuffer:%d", mHandle);
410         return false;
411     }
412 
413     if (!mColorBufferVk->updateFromBytes(contents)) {
414         ERR("Failed to set VK contents for ColorBuffer:%d", mHandle);
415         return false;
416     }
417 #endif
418 
419     return true;
420 }
421 
importNativeResource(void * nativeResource,uint32_t type,bool preserveContent)422 bool ColorBuffer::importNativeResource(void* nativeResource, uint32_t type, bool preserveContent) {
423     switch (type) {
424         case RESOURCE_TYPE_VK_EXT_MEMORY_HANDLE: {
425             if (mColorBufferGl) {
426                 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
427                     << "Native resource import type %s is invalid when GL emulation is active. "
428                     << "Use RESOURCE_TYPE_EGL_NATIVE_PIXMAP of RESOURCE_TYPE_EGL_IMAGE imports "
429                        "instead.";
430                 return false;
431             } else if (!mColorBufferVk) {
432                 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
433                     << "Vulkan emulation must be available for RESOURCE_TYPE_VK_EXT_MEMORY_HANDLE "
434                        "import.";
435                 return false;
436             }
437             return mColorBufferVk->importExtMemoryHandle(nativeResource, type, preserveContent);
438         }
439         default:
440             GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
441                 << "Unrecognized type for ColorBuffer::importNativeResource.";
442             return false;
443     }
444 }
445 
446 #if GFXSTREAM_ENABLE_HOST_GLES
glOpBlitFromCurrentReadBuffer()447 bool ColorBuffer::glOpBlitFromCurrentReadBuffer() {
448     if (!mColorBufferGl) {
449         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
450     }
451 
452     touch();
453 
454     return mColorBufferGl->blitFromCurrentReadBuffer();
455 }
456 
glOpBindToTexture()457 bool ColorBuffer::glOpBindToTexture() {
458     if (!mColorBufferGl) {
459         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
460     }
461 
462     touch();
463 
464     return mColorBufferGl->bindToTexture();
465 }
466 
glOpBindToTexture2()467 bool ColorBuffer::glOpBindToTexture2() {
468     if (!mColorBufferGl) {
469         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
470     }
471 
472     return mColorBufferGl->bindToTexture2();
473 }
474 
glOpBindToRenderbuffer()475 bool ColorBuffer::glOpBindToRenderbuffer() {
476     if (!mColorBufferGl) {
477         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
478     }
479 
480     touch();
481 
482     return mColorBufferGl->bindToRenderbuffer();
483 }
484 
glOpGetTexture()485 GLuint ColorBuffer::glOpGetTexture() {
486     if (!mColorBufferGl) {
487         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
488     }
489 
490     touch();
491 
492     return mColorBufferGl->getTexture();
493 }
494 
glOpReadback(unsigned char * img,bool readbackBgra)495 void ColorBuffer::glOpReadback(unsigned char* img, bool readbackBgra) {
496     if (!mColorBufferGl) {
497         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
498     }
499 
500     touch();
501 
502     return mColorBufferGl->readback(img, readbackBgra);
503 }
504 
glOpReadbackAsync(GLuint buffer,bool readbackBgra)505 void ColorBuffer::glOpReadbackAsync(GLuint buffer, bool readbackBgra) {
506     if (!mColorBufferGl) {
507         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
508     }
509 
510     touch();
511 
512     mColorBufferGl->readbackAsync(buffer, readbackBgra);
513 }
514 
glOpImportEglImage(void * image,bool preserveContent)515 bool ColorBuffer::glOpImportEglImage(void* image, bool preserveContent) {
516     if (!mColorBufferGl) {
517         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
518     }
519 
520     return mColorBufferGl->importEglImage(image, preserveContent);
521 }
522 
glOpImportEglNativePixmap(void * pixmap,bool preserveContent)523 bool ColorBuffer::glOpImportEglNativePixmap(void* pixmap, bool preserveContent) {
524     if (!mColorBufferGl) {
525         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
526     }
527 
528     return mColorBufferGl->importEglNativePixmap(pixmap, preserveContent);
529 }
530 
glOpSwapYuvTexturesAndUpdate(GLenum format,GLenum type,FrameworkFormat frameworkFormat,GLuint * textures)531 void ColorBuffer::glOpSwapYuvTexturesAndUpdate(GLenum format, GLenum type,
532                                                FrameworkFormat frameworkFormat, GLuint* textures) {
533     if (!mColorBufferGl) {
534         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
535     }
536 
537     mColorBufferGl->swapYUVTextures(frameworkFormat, textures);
538 
539     // This makes ColorBufferGl regenerate the RGBA texture using
540     // YUVConverter::drawConvert() with the updated YUV textures.
541     mColorBufferGl->subUpdate(0, 0, mWidth, mHeight, format, type, nullptr);
542 
543     flushFromGl();
544 }
545 
glOpReadContents(size_t * outNumBytes,void * outContents)546 bool ColorBuffer::glOpReadContents(size_t* outNumBytes, void* outContents) {
547     if (!mColorBufferGl) {
548         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
549     }
550 
551     return mColorBufferGl->readContents(outNumBytes, outContents);
552 }
553 
glOpIsFastBlitSupported() const554 bool ColorBuffer::glOpIsFastBlitSupported() const {
555     if (!mColorBufferGl) {
556         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
557     }
558 
559     return mColorBufferGl->isFastBlitSupported();
560 }
561 
glOpPostLayer(const ComposeLayer & l,int frameWidth,int frameHeight)562 void ColorBuffer::glOpPostLayer(const ComposeLayer& l, int frameWidth, int frameHeight) {
563     if (!mColorBufferGl) {
564         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
565     }
566 
567     mColorBufferGl->postLayer(l, frameWidth, frameHeight);
568 }
569 
glOpPostViewportScaledWithOverlay(float rotation,float dx,float dy)570 void ColorBuffer::glOpPostViewportScaledWithOverlay(float rotation, float dx, float dy) {
571     if (!mColorBufferGl) {
572         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
573     }
574 
575     mColorBufferGl->postViewportScaledWithOverlay(rotation, dx, dy);
576 }
577 #endif
578 
579 }  // namespace gfxstream
580