• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2018 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 express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <gtest/gtest.h>
16 
17 #include "FrameBuffer.h"
18 #include "VkCommonOperations.h"
19 #include "VulkanDispatch.h"
20 #include "host-common/feature_control.h"
21 
22 #include "base/ArraySize.h"
23 #include "base/GLObjectCounter.h"
24 #include "base/PathUtils.h"
25 #include "base/System.h"
26 #include "base/testing/TestSystem.h"
27 #include "host-common/AndroidAgentFactory.h"
28 
29 #include "Standalone.h"
30 
31 #include <sstream>
32 #include <string>
33 #include <vulkan/vulkan.h>
34 
35 #ifdef _WIN32
36 #include <windows.h>
37 #include "base/Win32UnicodeString.h"
38 using android::base::Win32UnicodeString;
39 #else
40 #include <dlfcn.h>
41 #endif
42 
43 using android::base::arraySize;
44 using android::base::pj;
45 using android::base::TestSystem;
46 
47 namespace emugl {
48 
49 #ifdef _WIN32
50 #define SKIP_TEST_IF_WIN32() GTEST_SKIP()
51 #else
52 #define SKIP_TEST_IF_WIN32()
53 #endif
54 
dlOpenFuncForTesting()55 static void* dlOpenFuncForTesting() {
56 #ifdef _WIN32
57     const Win32UnicodeString name("vulkan-1.dll");
58     return LoadLibraryW(name.c_str());
59 #else
60 
61 #ifdef __APPLE__
62     constexpr char suffix[] = ".dylib";
63 #else
64     constexpr char suffix[] = ".so";
65 #endif
66 
67     std::string libName =
68         std::string("libvulkan") + suffix;
69 
70     auto res = dlopen(libName.c_str(), RTLD_NOW);
71     if (!res) {
72         libName = std::string("libvulkan") + suffix + ".1";
73     }
74     res = dlopen(libName.c_str(), RTLD_NOW);
75     return res;
76 #endif
77 }
78 
dlSymFuncForTesting(void * lib,const char * sym)79 static void* dlSymFuncForTesting(void* lib, const char* sym) {
80 #ifdef _WIN32
81     return (void*)GetProcAddress((HMODULE)lib, sym);
82 #else
83     return dlsym(lib, sym);
84 #endif
85 }
86 
deviceTypeToString(VkPhysicalDeviceType type)87 static std::string deviceTypeToString(VkPhysicalDeviceType type) {
88 #define DO_ENUM_RETURN_STRING(e) \
89     case e: \
90         return #e; \
91 
92     switch (type) {
93     DO_ENUM_RETURN_STRING(VK_PHYSICAL_DEVICE_TYPE_OTHER)
94     DO_ENUM_RETURN_STRING(VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
95     DO_ENUM_RETURN_STRING(VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
96     DO_ENUM_RETURN_STRING(VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)
97     DO_ENUM_RETURN_STRING(VK_PHYSICAL_DEVICE_TYPE_CPU)
98         default:
99             return "Unknown";
100     }
101 }
102 
queueFlagsToString(VkQueueFlags queueFlags)103 static std::string queueFlagsToString(VkQueueFlags queueFlags) {
104     std::stringstream ss;
105 
106     if (queueFlags & VK_QUEUE_GRAPHICS_BIT) {
107         ss << "VK_QUEUE_GRAPHICS_BIT | ";
108     }
109     if (queueFlags & VK_QUEUE_COMPUTE_BIT) {
110         ss << "VK_QUEUE_COMPUTE_BIT | ";
111     }
112     if (queueFlags & VK_QUEUE_TRANSFER_BIT) {
113         ss << "VK_QUEUE_TRANSFER_BIT | ";
114     }
115     if (queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) {
116         ss << "VK_QUEUE_SPARSE_BINDING_BIT | ";
117     }
118     if (queueFlags & VK_QUEUE_PROTECTED_BIT) {
119         ss << "VK_QUEUE_PROTECTED_BIT";
120     }
121 
122     return ss.str();
123 }
124 
getPhysicalDevicePropertiesString(const goldfish_vk::VulkanDispatch * vk,VkPhysicalDevice physicalDevice,const VkPhysicalDeviceProperties & props)125 static std::string getPhysicalDevicePropertiesString(
126     const goldfish_vk::VulkanDispatch* vk,
127     VkPhysicalDevice physicalDevice,
128     const VkPhysicalDeviceProperties& props) {
129 
130     std::stringstream ss;
131 
132     uint16_t apiMaj = (uint16_t)(props.apiVersion >> 22);
133     uint16_t apiMin = (uint16_t)(0x000003ff & (props.apiVersion >> 12));
134     uint16_t apiPatch = (uint16_t)(0x000007ff & (props.apiVersion));
135 
136     ss << "API version: " << apiMaj << "." << apiMin << "." << apiPatch << "\n";
137     ss << "Driver version: " << std::hex << props.driverVersion << "\n";
138     ss << "Vendor ID: " << std::hex << props.vendorID << "\n";
139     ss << "Device ID: " << std::hex << props.deviceID << "\n";
140     ss << "Device type: " << deviceTypeToString(props.deviceType) << "\n";
141     ss << "Device name: " << props.deviceName << "\n";
142 
143     uint32_t deviceExtensionCount;
144     std::vector<VkExtensionProperties> deviceExtensionProperties;
145     vk->vkEnumerateDeviceExtensionProperties(
146         physicalDevice,
147         nullptr,
148         &deviceExtensionCount,
149         nullptr);
150 
151     deviceExtensionProperties.resize(deviceExtensionCount);
152     vk->vkEnumerateDeviceExtensionProperties(
153         physicalDevice,
154         nullptr,
155         &deviceExtensionCount,
156         deviceExtensionProperties.data());
157 
158     for (uint32_t i = 0; i < deviceExtensionCount; ++i) {
159         ss << "Device extension: " <<
160             deviceExtensionProperties[i].extensionName << "\n";
161     }
162 
163     return ss.str();
164 }
165 
testInstanceCreation(const VulkanDispatch * vk,VkInstance * instance_out)166 static void testInstanceCreation(const VulkanDispatch* vk,
167                                  VkInstance* instance_out) {
168 
169     EXPECT_TRUE(vk->vkEnumerateInstanceExtensionProperties);
170     EXPECT_TRUE(vk->vkCreateInstance);
171 
172     uint32_t count;
173     vk->vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
174 
175     fprintf(stderr, "%s: exts: %u\n", __func__, count);
176 
177     std::vector<VkExtensionProperties> props(count);
178     vk->vkEnumerateInstanceExtensionProperties(nullptr, &count, props.data());
179 
180     for (uint32_t i = 0; i < count; i++) {
181         fprintf(stderr, "%s: ext: %s\n", __func__, props[i].extensionName);
182     }
183 
184     VkInstanceCreateInfo instanceCreateInfo = {
185         VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 0, 0,
186         nullptr,
187         0, nullptr,
188         0, nullptr,
189     };
190 
191     VkInstance instance;
192 
193     EXPECT_EQ(VK_SUCCESS,
194         vk->vkCreateInstance(
195             &instanceCreateInfo, nullptr, &instance));
196 
197     *instance_out = instance;
198 }
199 
testDeviceCreation(const VulkanDispatch * vk,VkInstance instance,VkPhysicalDevice * physDevice_out,VkDevice * device_out)200 static void testDeviceCreation(const VulkanDispatch* vk,
201                                VkInstance instance,
202                                VkPhysicalDevice* physDevice_out,
203                                VkDevice* device_out) {
204 
205     fprintf(stderr, "%s: call\n", __func__);
206 
207     EXPECT_TRUE(vk->vkEnumeratePhysicalDevices);
208     EXPECT_TRUE(vk->vkGetPhysicalDeviceProperties);
209     EXPECT_TRUE(vk->vkGetPhysicalDeviceQueueFamilyProperties);
210 
211     uint32_t physicalDeviceCount;
212     std::vector<VkPhysicalDevice> physicalDevices;
213 
214     EXPECT_EQ(VK_SUCCESS,
215         vk->vkEnumeratePhysicalDevices(
216             instance, &physicalDeviceCount, nullptr));
217 
218     physicalDevices.resize(physicalDeviceCount);
219 
220     EXPECT_EQ(VK_SUCCESS,
221         vk->vkEnumeratePhysicalDevices(
222             instance, &physicalDeviceCount, physicalDevices.data()));
223 
224     std::vector<VkPhysicalDeviceProperties> physicalDeviceProps(physicalDeviceCount);
225 
226     // at the end of the day, we need to pick a physical device.
227     // Pick one that has graphics + compute if possible, otherwise settle for a device
228     // that has at least one queue family capable of graphics.
229     // TODO: Pick the device that has present capability for that queue if
230     // we are not running in no-window mode.
231 
232     bool bestPhysicalDeviceFound = false;
233     uint32_t bestPhysicalDeviceIndex = 0;
234 
235     std::vector<uint32_t> physDevsWithBothGraphicsAndCompute;
236     std::vector<uint32_t> physDevsWithGraphicsOnly;
237 
238     for (uint32_t i = 0; i < physicalDeviceCount; i++) {
239         uint32_t deviceExtensionCount;
240         std::vector<VkExtensionProperties> deviceExtensionProperties;
241         vk->vkEnumerateDeviceExtensionProperties(
242             physicalDevices[i],
243             nullptr,
244             &deviceExtensionCount,
245             nullptr);
246 
247         deviceExtensionProperties.resize(deviceExtensionCount);
248         vk->vkEnumerateDeviceExtensionProperties(
249             physicalDevices[i],
250             nullptr,
251             &deviceExtensionCount,
252             deviceExtensionProperties.data());
253 
254         bool hasSwapchainExtension = false;
255 
256         fprintf(stderr, "%s: check swapchain ext\n", __func__);
257         for (uint32_t j = 0; j < deviceExtensionCount; j++) {
258             std::string ext = deviceExtensionProperties[j].extensionName;
259             if (ext == "VK_KHR_swapchain") {
260                 hasSwapchainExtension = true;
261             }
262         }
263 
264         if (!hasSwapchainExtension) continue;
265 
266         vk->vkGetPhysicalDeviceProperties(
267             physicalDevices[i],
268             physicalDeviceProps.data() + i);
269 
270         auto str = getPhysicalDevicePropertiesString(vk, physicalDevices[i], physicalDeviceProps[i]);
271         fprintf(stderr, "device %u: %s\n", i, str.c_str());
272 
273         uint32_t queueFamilyCount;
274         vk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevices[i], &queueFamilyCount, nullptr);
275 
276         std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
277         vk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevices[i], &queueFamilyCount, queueFamilies.data());
278 
279         bool hasGraphicsQueue = false;
280         bool hasComputeQueue = false;
281 
282         for (uint32_t j = 0; j < queueFamilyCount; j++) {
283             if (queueFamilies[j].queueCount > 0) {
284 
285                 auto flags = queueFamilies[j].queueFlags;
286                 auto flagsAsString =
287                     queueFlagsToString(flags);
288 
289                 fprintf(stderr, "%s: found %u @ family %u with caps: %s\n",
290                         __func__,
291                         queueFamilies[j].queueCount, j,
292                         flagsAsString.c_str());
293 
294                 if ((flags & VK_QUEUE_GRAPHICS_BIT) &&
295                     (flags & VK_QUEUE_COMPUTE_BIT)) {
296                     hasGraphicsQueue = true;
297                     hasComputeQueue = true;
298                     bestPhysicalDeviceFound = true;
299                     break;
300                 }
301 
302                 if (flags & VK_QUEUE_GRAPHICS_BIT) {
303                     hasGraphicsQueue = true;
304                     bestPhysicalDeviceFound = true;
305                 }
306 
307                 if (flags & VK_QUEUE_COMPUTE_BIT) {
308                     hasComputeQueue = true;
309                     bestPhysicalDeviceFound = true;
310                 }
311             }
312         }
313 
314         if (hasGraphicsQueue && hasComputeQueue) {
315             physDevsWithBothGraphicsAndCompute.push_back(i);
316             break;
317         }
318 
319         if (hasGraphicsQueue) {
320             physDevsWithGraphicsOnly.push_back(i);
321         }
322     }
323 
324     EXPECT_TRUE(bestPhysicalDeviceFound);
325 
326     if (physDevsWithBothGraphicsAndCompute.size() > 0) {
327         bestPhysicalDeviceIndex = physDevsWithBothGraphicsAndCompute[0];
328     } else if (physDevsWithGraphicsOnly.size() > 0) {
329         bestPhysicalDeviceIndex = physDevsWithGraphicsOnly[0];
330     } else {
331         EXPECT_TRUE(false);
332         return;
333     }
334 
335     // Now we got our device; select it
336     VkPhysicalDevice bestPhysicalDevice = physicalDevices[bestPhysicalDeviceIndex];
337 
338     uint32_t queueFamilyCount;
339     vk->vkGetPhysicalDeviceQueueFamilyProperties(
340         bestPhysicalDevice, &queueFamilyCount, nullptr);
341 
342     std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
343     vk->vkGetPhysicalDeviceQueueFamilyProperties(
344         bestPhysicalDevice, &queueFamilyCount, queueFamilies.data());
345 
346     std::vector<uint32_t> wantedQueueFamilies;
347     std::vector<uint32_t> wantedQueueFamilyCounts;
348 
349     for (uint32_t i = 0; i < queueFamilyCount; i++) {
350         if (queueFamilies[i].queueCount > 0) {
351             auto flags = queueFamilies[i].queueFlags;
352             if ((flags & VK_QUEUE_GRAPHICS_BIT) &&
353                     (flags & VK_QUEUE_COMPUTE_BIT)) {
354                 wantedQueueFamilies.push_back(i);
355                 wantedQueueFamilyCounts.push_back(queueFamilies[i].queueCount);
356                 break;
357             }
358 
359             if ((flags & VK_QUEUE_GRAPHICS_BIT) ||
360                     (flags & VK_QUEUE_COMPUTE_BIT)) {
361                 wantedQueueFamilies.push_back(i);
362                 wantedQueueFamilyCounts.push_back(queueFamilies[i].queueCount);
363             }
364         }
365     }
366 
367     std::vector<VkDeviceQueueCreateInfo> queueCis;
368 
369     for (uint32_t i = 0; i < wantedQueueFamilies.size(); ++i) {
370         auto familyIndex = wantedQueueFamilies[i];
371         auto queueCount = wantedQueueFamilyCounts[i];
372 
373         std::vector<float> priorities;
374 
375         for (uint32_t j = 0; j < queueCount; ++j) {
376             priorities.push_back(1.0f);
377         }
378 
379         VkDeviceQueueCreateInfo dqci = {
380             VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 0, 0,
381             familyIndex,
382             queueCount,
383             priorities.data(),
384         };
385 
386         queueCis.push_back(dqci);
387     }
388 
389     const char* exts[] = {
390         "VK_KHR_swapchain",
391     };
392 
393     VkDeviceCreateInfo ci = {
394         VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 0, 0,
395         (uint32_t)queueCis.size(),
396         queueCis.data(),
397         0, nullptr,
398         arraySize(exts), exts,
399         nullptr,
400     };
401 
402     VkDevice device;
403     EXPECT_EQ(VK_SUCCESS,
404         vk->vkCreateDevice(bestPhysicalDevice, &ci, nullptr, &device));
405 
406     *physDevice_out = bestPhysicalDevice;
407     *device_out = device;
408 }
409 
teardownVulkanTest(const VulkanDispatch * vk,VkDevice dev,VkInstance instance)410 static void teardownVulkanTest(const VulkanDispatch* vk,
411                                VkDevice dev,
412                                VkInstance instance) {
413     vk->vkDestroyDevice(dev, nullptr);
414     vk->vkDestroyInstance(instance, nullptr);
415 }
416 
417 class VulkanTest : public ::testing::Test {
418 protected:
SetUp()419     void SetUp() override {
420         goldfish_vk::init_vulkan_dispatch_from_system_loader(
421                 dlOpenFuncForTesting,
422                 dlSymFuncForTesting,
423                 &mVk);
424 
425         testInstanceCreation(&mVk, &mInstance);
426         testDeviceCreation(
427             &mVk, mInstance, &mPhysicalDevice, &mDevice);
428     }
429 
TearDown()430     void TearDown() override {
431         teardownVulkanTest(&mVk, mDevice, mInstance);
432     }
433 
434     VulkanDispatch mVk;
435     VkInstance mInstance;
436     VkPhysicalDevice mPhysicalDevice;
437     VkDevice mDevice;
438 };
439 
440 // Basic Vulkan instance/device setup.
TEST_F(VulkanTest,Basic)441 TEST_F(VulkanTest, Basic) { }
442 
443 // Checks that staging memory query is successful.
TEST_F(VulkanTest,StagingMemoryQuery)444 TEST_F(VulkanTest, StagingMemoryQuery) {
445     VkPhysicalDeviceMemoryProperties memProps;
446     uint32_t typeIndex;
447 
448     mVk.vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProps);
449 
450     EXPECT_TRUE(goldfish_vk::getStagingMemoryTypeIndex(
451         &mVk, mDevice, &memProps, &typeIndex));
452 }
453 
454 #ifndef _WIN32 // TODO: Get this working w/ Swiftshader vk on Windows
455 class VulkanFrameBufferTest : public VulkanTest {
456 protected:
SetUp()457     void SetUp() override {
458         // SwiftShader Vulkan doesn't work on Windows, so we skip all
459         // the rendering tests on Windows for now.
460         SKIP_TEST_IF_WIN32();
461 
462         feature_set_enabled_override(kFeature_GLESDynamicVersion, true);
463         feature_set_enabled_override(kFeature_PlayStoreImage, false);
464         feature_set_enabled_override(kFeature_Vulkan, true);
465         feature_set_enabled_override(kFeature_VulkanIgnoredHandles, true);
466 
467         VulkanTest::SetUp();
468 
469         emugl::setGLObjectCounter(android::base::GLObjectCounter::get());
470         emugl::set_emugl_window_operations(*getConsoleAgents()->emu);
471         emugl::set_emugl_multi_display_operations(*getConsoleAgents()->multi_display);
472         const EGLDispatch* egl = LazyLoadedEGLDispatch::get();
473         ASSERT_NE(nullptr, egl);
474         ASSERT_NE(nullptr, LazyLoadedGLESv2Dispatch::get());
475 
476         bool useHostGpu = false;
477         EXPECT_TRUE(FrameBuffer::initialize(mWidth, mHeight, false,
478                                             !useHostGpu /* egl2egl */));
479         mFb = FrameBuffer::getFB();
480         ASSERT_NE(nullptr, mFb);
481         mRenderThreadInfo = std::make_unique<RenderThreadInfo>();
482     }
483 
TearDown()484     void TearDown() override {
485         VulkanTest::TearDown();
486         if (mFb) { delete mFb; mFb = nullptr; }
487         if (mRenderThreadInfo) mRenderThreadInfo.reset();
488     }
489 
490     FrameBuffer* mFb = nullptr;
491     std::unique_ptr<RenderThreadInfo> mRenderThreadInfo;
492 
493     constexpr static uint32_t mWidth = 640u;
494     constexpr static uint32_t mHeight = 480u;
495 };
496 
TEST_F(VulkanFrameBufferTest,VkColorBufferWithoutMemoryProperties)497 TEST_F(VulkanFrameBufferTest, VkColorBufferWithoutMemoryProperties) {
498     // Create a color buffer without any memory properties restriction.
499     HandleType colorBuffer = mFb->createColorBuffer(
500             mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE);
501     ASSERT_NE(colorBuffer, 0u);
502     EXPECT_TRUE(goldfish_vk::setupVkColorBuffer(colorBuffer,
503                                                 true, /* vulkanOnly */
504                                                 0     /* memoryProperty */
505                                                 ));
506     EXPECT_TRUE(goldfish_vk::teardownVkColorBuffer(colorBuffer));
507     mFb->closeColorBuffer(colorBuffer);
508 }
509 
TEST_F(VulkanFrameBufferTest,VkColorBufferWithMemoryPropertyFlags)510 TEST_F(VulkanFrameBufferTest, VkColorBufferWithMemoryPropertyFlags) {
511     auto* vkEmulation = goldfish_vk::getGlobalVkEmulation();
512     VkMemoryPropertyFlags kTargetMemoryPropertyFlags =
513             VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
514 
515     // Create a Vulkan image with the same dimension and usage as
516     // the color buffer, to get a possible memory type index.
517     VkImageCreateInfo testImageCi = {
518             VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
519             nullptr,
520             0,
521             VK_IMAGE_TYPE_2D,
522             VK_FORMAT_R8G8B8A8_UNORM,
523             {
524                     mWidth,
525                     mHeight,
526                     1,
527             },
528             1,
529             1,
530             VK_SAMPLE_COUNT_1_BIT,
531             VK_IMAGE_TILING_OPTIMAL,
532             VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
533             VK_SHARING_MODE_EXCLUSIVE,
534             0,
535             nullptr /* shared queue families */,
536             VK_IMAGE_LAYOUT_UNDEFINED,
537     };
538     VkImage image;
539     mVk.vkCreateImage(mDevice, &testImageCi, nullptr, &image);
540 
541     VkMemoryRequirements memReq;
542     mVk.vkGetImageMemoryRequirements(mDevice, image, &memReq);
543 
544     mVk.vkDestroyImage(mDevice, image, nullptr);
545 
546     if (!memReq.memoryTypeBits) {
547         GTEST_SKIP();
548     }
549 
550     int32_t memoryTypeIndex = 31;
551     do {
552         if (((1 << memoryTypeIndex) & memReq.memoryTypeBits) &&
553             (vkEmulation->deviceInfo.memProps.memoryTypes[memoryTypeIndex]
554                      .propertyFlags &
555              kTargetMemoryPropertyFlags)) {
556             break;
557         }
558     } while (--memoryTypeIndex >= 0);
559 
560     if (memoryTypeIndex < 0) {
561         fprintf(stderr,
562                 "No memory type supported for HOST_VISBILE memory "
563                 "properties. Test skipped.\n");
564         GTEST_SKIP();
565     }
566 
567     // Create a color buffer with the target memory property flags.
568     uint32_t allocatedTypeIndex = 0u;
569     HandleType colorBuffer = mFb->createColorBuffer(
570             mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE);
571     ASSERT_NE(colorBuffer, 0u);
572     EXPECT_TRUE(goldfish_vk::setupVkColorBuffer(
573             colorBuffer, true, /* vulkanOnly */
574             static_cast<uint32_t>(kTargetMemoryPropertyFlags), nullptr, nullptr,
575             &allocatedTypeIndex));
576     EXPECT_TRUE(vkEmulation->deviceInfo.memProps.memoryTypes[allocatedTypeIndex]
577                         .propertyFlags &
578                 kTargetMemoryPropertyFlags);
579     EXPECT_TRUE(goldfish_vk::teardownVkColorBuffer(colorBuffer));
580     mFb->closeColorBuffer(colorBuffer);
581 }
582 #endif // !_WIN32
583 } // namespace emugl
584