1 /*
2  * Copyright (C) 2021 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 "Vulkan.h"
18 
19 #include <iostream>
20 #include <string>
21 #include <unordered_set>
22 #include <vector>
23 
24 VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
25 
26 namespace gfxstream {
27 namespace {
28 
29 constexpr const bool kEnableValidationLayers = false;
30 
VulkanDebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,VkDebugUtilsMessageTypeFlagsEXT,const VkDebugUtilsMessengerCallbackDataEXT * pCallbackData,void *)31 static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugCallback(
32         VkDebugUtilsMessageSeverityFlagBitsEXT severity,
33         VkDebugUtilsMessageTypeFlagsEXT,
34         const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
35         void*) {
36     if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
37         std::cout << pCallbackData->pMessage << std::endl;
38     } else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
39         std::cout << pCallbackData->pMessage << std::endl;
40     } else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
41         std::cout << pCallbackData->pMessage << std::endl;
42     } else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
43         std::cout << pCallbackData->pMessage << std::endl;
44     }
45     return VK_FALSE;
46 }
47 
GetMemoryType(const vkhpp::PhysicalDevice & physical_device,uint32_t memory_type_mask,vkhpp::MemoryPropertyFlags memoryProperties)48 uint32_t GetMemoryType(const vkhpp::PhysicalDevice& physical_device,
49                        uint32_t memory_type_mask,
50                        vkhpp::MemoryPropertyFlags memoryProperties) {
51     const auto props = physical_device.getMemoryProperties();
52     for (uint32_t i = 0; i < props.memoryTypeCount; i++) {
53         if (!(memory_type_mask & (1 << i))) {
54             continue;
55         }
56         if ((props.memoryTypes[i].propertyFlags & memoryProperties) != memoryProperties) {
57             continue;
58         }
59         return i;
60     }
61     return -1;
62 }
63 
64 }  // namespace
65 
DoCreateBuffer(const vkhpp::PhysicalDevice & physical_device,const vkhpp::UniqueDevice & device,vkhpp::DeviceSize buffer_size,vkhpp::BufferUsageFlags buffer_usages,vkhpp::MemoryPropertyFlags bufferMemoryProperties)66 gfxstream::expected<Vk::BufferWithMemory, vkhpp::Result> DoCreateBuffer(
67         const vkhpp::PhysicalDevice& physical_device,
68         const vkhpp::UniqueDevice& device, vkhpp::DeviceSize buffer_size,
69         vkhpp::BufferUsageFlags buffer_usages,
70         vkhpp::MemoryPropertyFlags bufferMemoryProperties) {
71     const vkhpp::BufferCreateInfo bufferCreateInfo = {
72         .size = static_cast<VkDeviceSize>(buffer_size),
73         .usage = buffer_usages,
74         .sharingMode = vkhpp::SharingMode::eExclusive,
75     };
76     auto buffer = VK_EXPECT_RV(device->createBufferUnique(bufferCreateInfo));
77 
78     vkhpp::MemoryRequirements bufferMemoryRequirements{};
79     device->getBufferMemoryRequirements(*buffer, &bufferMemoryRequirements);
80 
81     const auto bufferMemoryType =
82         GetMemoryType(physical_device,
83                     bufferMemoryRequirements.memoryTypeBits,
84                     bufferMemoryProperties);
85 
86     const vkhpp::MemoryAllocateInfo bufferMemoryAllocateInfo = {
87         .allocationSize = bufferMemoryRequirements.size,
88         .memoryTypeIndex = bufferMemoryType,
89     };
90     auto bufferMemory = VK_EXPECT_RV(device->allocateMemoryUnique(bufferMemoryAllocateInfo));
91 
92     VK_EXPECT_RESULT(device->bindBufferMemory(*buffer, *bufferMemory, 0));
93 
94     return Vk::BufferWithMemory{
95         .buffer = std::move(buffer),
96         .bufferMemory = std::move(bufferMemory),
97     };
98 }
99 
100 /*static*/
Load(const std::vector<std::string> & requestedInstanceExtensions,const std::vector<std::string> & requestedInstanceLayers,const std::vector<std::string> & requestedDeviceExtensions)101 gfxstream::expected<Vk, vkhpp::Result> Vk::Load(
102         const std::vector<std::string>& requestedInstanceExtensions,
103         const std::vector<std::string>& requestedInstanceLayers,
104         const std::vector<std::string>& requestedDeviceExtensions) {
105     vkhpp::DynamicLoader loader;
106 
107     VULKAN_HPP_DEFAULT_DISPATCHER.init(
108         loader.getProcAddress<PFN_vkGetInstanceProcAddr>(
109             "vkGetInstanceProcAddr"));
110 
111     std::vector<const char*> requestedInstanceExtensionsChars;
112     requestedInstanceExtensionsChars.reserve(requestedInstanceExtensions.size());
113     for (const auto& e : requestedInstanceExtensions) {
114         requestedInstanceExtensionsChars.push_back(e.c_str());
115     }
116     if (kEnableValidationLayers) {
117         requestedInstanceExtensionsChars.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
118     }
119 
120     std::vector<const char*> requestedInstanceLayersChars;
121     requestedInstanceLayersChars.reserve(requestedInstanceLayers.size());
122     for (const auto& l : requestedInstanceLayers) {
123         requestedInstanceLayersChars.push_back(l.c_str());
124     }
125 
126     const vkhpp::ApplicationInfo applicationInfo = {
127         .pApplicationName = "Cuttlefish Graphics Detector",
128         .applicationVersion = 1,
129         .pEngineName = "Cuttlefish Graphics Detector",
130         .engineVersion = 1,
131         .apiVersion = VK_API_VERSION_1_2,
132     };
133     const vkhpp::InstanceCreateInfo instanceCreateInfo = {
134         .pApplicationInfo = &applicationInfo,
135         .enabledLayerCount = static_cast<uint32_t>(requestedInstanceLayersChars.size()),
136         .ppEnabledLayerNames = requestedInstanceLayersChars.data(),
137         .enabledExtensionCount = static_cast<uint32_t>(requestedInstanceExtensionsChars.size()),
138         .ppEnabledExtensionNames = requestedInstanceExtensionsChars.data(),
139     };
140 
141     auto instance = VK_EXPECT_RV(vkhpp::createInstanceUnique(instanceCreateInfo));
142 
143     VULKAN_HPP_DEFAULT_DISPATCHER.init(*instance);
144 
145     std::optional<vkhpp::UniqueDebugUtilsMessengerEXT> debugMessenger;
146     if (kEnableValidationLayers) {
147         const vkhpp::DebugUtilsMessengerCreateInfoEXT debugCreateInfo = {
148             .messageSeverity = vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
149                             vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
150                             vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eError,
151             .messageType = vkhpp::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
152                         vkhpp::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
153                         vkhpp::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
154             .pfnUserCallback = VulkanDebugCallback,
155             .pUserData = nullptr,
156         };
157         debugMessenger = VK_EXPECT_RV(instance->createDebugUtilsMessengerEXTUnique(debugCreateInfo));
158     }
159 
160     const auto physicalDevices = VK_EXPECT_RV(instance->enumeratePhysicalDevices());
161     vkhpp::PhysicalDevice physicalDevice = std::move(physicalDevices[0]);
162 
163 
164     std::unordered_set<std::string> availableDeviceExtensions;
165     {
166         const auto exts = VK_EXPECT_RV(physicalDevice.enumerateDeviceExtensionProperties());
167         for (const auto& ext : exts) {
168             availableDeviceExtensions.emplace(ext.extensionName);
169         }
170     }
171 
172     const auto features2 =
173         physicalDevice
174             .getFeatures2<vkhpp::PhysicalDeviceFeatures2,  //
175                             vkhpp::PhysicalDeviceSamplerYcbcrConversionFeatures>();
176 
177     bool ycbcr_conversion_needed = false;
178 
179     std::vector<const char*> requestedDeviceExtensionsChars;
180     requestedDeviceExtensionsChars.reserve(requestedDeviceExtensions.size());
181     for (const auto& e : requestedDeviceExtensions) {
182         if (e == std::string(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME)) {
183             // The interface of VK_KHR_sampler_ycbcr_conversion was promoted to core
184             // in Vulkan 1.1 but the feature/functionality is still optional. Check
185             // here:
186             const auto& sampler_features =
187                 features2.get<vkhpp::PhysicalDeviceSamplerYcbcrConversionFeatures>();
188 
189             if (sampler_features.samplerYcbcrConversion == VK_FALSE) {
190                 return gfxstream::unexpected(vkhpp::Result::eErrorExtensionNotPresent);
191             }
192             ycbcr_conversion_needed = true;
193         } else {
194             if (availableDeviceExtensions.find(e) == availableDeviceExtensions.end()) {
195                 return gfxstream::unexpected(vkhpp::Result::eErrorExtensionNotPresent);
196             }
197             requestedDeviceExtensionsChars.push_back(e.c_str());
198         }
199     }
200 
201     uint32_t queueFamilyIndex = -1;
202     {
203         const auto props = physicalDevice.getQueueFamilyProperties();
204         for (uint32_t i = 0; i < props.size(); i++) {
205             const auto& prop = props[i];
206             if (prop.queueFlags & vkhpp::QueueFlagBits::eGraphics) {
207                 queueFamilyIndex = i;
208                 break;
209             }
210         }
211     }
212 
213     const float queue_priority = 1.0f;
214     const vkhpp::DeviceQueueCreateInfo device_queue_create_info = {
215         .queueFamilyIndex = queueFamilyIndex,
216         .queueCount = 1,
217         .pQueuePriorities = &queue_priority,
218     };
219     const vkhpp::PhysicalDeviceVulkan11Features device_enable_features = {
220         .samplerYcbcrConversion = ycbcr_conversion_needed,
221     };
222     const vkhpp::DeviceCreateInfo deviceCreateInfo = {
223         .pNext = &device_enable_features,
224         .queueCreateInfoCount = 1,
225         .pQueueCreateInfos = &device_queue_create_info,
226         .enabledLayerCount = static_cast<uint32_t>(requestedInstanceLayersChars.size()),
227         .ppEnabledLayerNames = requestedInstanceLayersChars.data(),
228         .enabledExtensionCount = static_cast<uint32_t>(requestedDeviceExtensionsChars.size()),
229         .ppEnabledExtensionNames = requestedDeviceExtensionsChars.data(),
230     };
231     auto device = VK_EXPECT_RV(physicalDevice.createDeviceUnique(deviceCreateInfo));
232     auto queue = device->getQueue(queueFamilyIndex, 0);
233 
234     const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
235         .queueFamilyIndex = queueFamilyIndex,
236     };
237     auto commandPool = VK_EXPECT_RV(device->createCommandPoolUnique(commandPoolCreateInfo));
238 
239     auto stagingBuffer =
240         VK_EXPECT(DoCreateBuffer(physicalDevice, device, kStagingBufferSize,
241                                  vkhpp::BufferUsageFlagBits::eTransferDst |
242                                     vkhpp::BufferUsageFlagBits::eTransferSrc,
243                                  vkhpp::MemoryPropertyFlagBits::eHostVisible |
244                                     vkhpp::MemoryPropertyFlagBits::eHostCoherent));
245 
246     return Vk(std::move(loader),
247               std::move(instance),
248               std::move(debugMessenger),
249               std::move(physicalDevice),
250               std::move(device),
251               std::move(queue),
252               queueFamilyIndex,
253               std::move(commandPool),
254               std::move(stagingBuffer.buffer),
255               std::move(stagingBuffer.bufferMemory));
256 }
257 
CreateBuffer(vkhpp::DeviceSize bufferSize,vkhpp::BufferUsageFlags bufferUsages,vkhpp::MemoryPropertyFlags bufferMemoryProperties)258 gfxstream::expected<Vk::BufferWithMemory, vkhpp::Result> Vk::CreateBuffer(
259     vkhpp::DeviceSize bufferSize,
260     vkhpp::BufferUsageFlags bufferUsages,
261     vkhpp::MemoryPropertyFlags bufferMemoryProperties) {
262   return DoCreateBuffer(mPhysicalDevice,
263                         mDevice,
264                         bufferSize,
265                         bufferUsages,
266                         bufferMemoryProperties);
267 }
268 
CreateBufferWithData(vkhpp::DeviceSize bufferSize,vkhpp::BufferUsageFlags bufferUsages,vkhpp::MemoryPropertyFlags bufferMemoryProperties,const uint8_t * buffer_data)269 gfxstream::expected<Vk::BufferWithMemory, vkhpp::Result> Vk::CreateBufferWithData(
270         vkhpp::DeviceSize bufferSize,
271         vkhpp::BufferUsageFlags bufferUsages,
272         vkhpp::MemoryPropertyFlags bufferMemoryProperties,
273         const uint8_t* buffer_data) {
274     auto buffer = VK_EXPECT(CreateBuffer(
275         bufferSize,
276         bufferUsages | vkhpp::BufferUsageFlagBits::eTransferDst,
277         bufferMemoryProperties));
278 
279     void* mapped = VK_EXPECT_RV(mDevice->mapMemory(*mStagingBufferMemory, 0, kStagingBufferSize));
280 
281     std::memcpy(mapped, buffer_data, bufferSize);
282 
283     mDevice->unmapMemory(*mStagingBufferMemory);
284 
285     DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
286         const std::vector<vkhpp::BufferCopy> regions = {
287             vkhpp::BufferCopy{
288                 .srcOffset = 0,
289                 .dstOffset = 0,
290                 .size = bufferSize,
291             },
292         };
293         cmd->copyBuffer(*mStagingBuffer, *buffer.buffer, regions);
294         return vkhpp::Result::eSuccess;
295     });
296 
297     return std::move(buffer);
298 }
299 
CreateImage(uint32_t width,uint32_t height,vkhpp::Format format,vkhpp::ImageUsageFlags usages,vkhpp::MemoryPropertyFlags memoryProperties,vkhpp::ImageLayout returnedLayout)300 gfxstream::expected<Vk::ImageWithMemory, vkhpp::Result> Vk::CreateImage(
301         uint32_t width,
302         uint32_t height,
303         vkhpp::Format format,
304         vkhpp::ImageUsageFlags usages,
305         vkhpp::MemoryPropertyFlags memoryProperties,
306         vkhpp::ImageLayout returnedLayout) {
307     const vkhpp::ImageCreateInfo imageCreateInfo = {
308         .imageType = vkhpp::ImageType::e2D,
309         .format = format,
310         .extent = {
311             .width = width,
312             .height = height,
313             .depth = 1,
314         },
315         .mipLevels = 1,
316         .arrayLayers = 1,
317         .samples = vkhpp::SampleCountFlagBits::e1,
318         .tiling = vkhpp::ImageTiling::eOptimal,
319         .usage = usages,
320         .sharingMode = vkhpp::SharingMode::eExclusive,
321         .initialLayout = vkhpp::ImageLayout::eUndefined,
322     };
323     auto image = VK_EXPECT_RV(mDevice->createImageUnique(imageCreateInfo));
324 
325     const auto memoryRequirements = mDevice->getImageMemoryRequirements(*image);
326     const uint32_t memoryIndex =
327         GetMemoryType(mPhysicalDevice,
328                       memoryRequirements.memoryTypeBits,
329                       memoryProperties);
330 
331     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
332         .allocationSize = memoryRequirements.size,
333         .memoryTypeIndex = memoryIndex,
334     };
335     auto imageMemory = VK_EXPECT_RV(mDevice->allocateMemoryUnique(imageMemoryAllocateInfo));
336 
337     mDevice->bindImageMemory(*image, *imageMemory, 0);
338 
339     const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
340         .image = *image,
341         .viewType = vkhpp::ImageViewType::e2D,
342         .format = format,
343         .components = {
344             .r = vkhpp::ComponentSwizzle::eIdentity,
345             .g = vkhpp::ComponentSwizzle::eIdentity,
346             .b = vkhpp::ComponentSwizzle::eIdentity,
347             .a = vkhpp::ComponentSwizzle::eIdentity,
348         },
349         .subresourceRange = {
350             .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
351             .baseMipLevel = 0,
352             .levelCount = 1,
353             .baseArrayLayer = 0,
354             .layerCount = 1,
355         },
356     };
357     auto imageView = VK_EXPECT_RV(mDevice->createImageViewUnique(imageViewCreateInfo));
358 
359     VK_EXPECT_RESULT(DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
360         const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
361             vkhpp::ImageMemoryBarrier{
362                 .srcAccessMask = {},
363                 .dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
364                 .oldLayout = vkhpp::ImageLayout::eUndefined,
365                 .newLayout = returnedLayout,
366                 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
367                 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
368                 .image = *image,
369                 .subresourceRange = {
370                     .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
371                     .baseMipLevel = 0,
372                     .levelCount = 1,
373                     .baseArrayLayer = 0,
374                     .layerCount = 1,
375                 },
376             },
377         };
378         cmd->pipelineBarrier(
379             /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
380             /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
381             /*dependencyFlags=*/{},
382             /*memoryBarriers=*/{},
383             /*bufferMemoryBarriers=*/{},
384             /*imageMemoryBarriers=*/imageMemoryBarriers);
385 
386         return vkhpp::Result::eSuccess;
387     }));
388 
389     return ImageWithMemory{
390         .image = std::move(image),
391         .imageMemory = std::move(imageMemory),
392         .imageView = std::move(imageView),
393     };
394 }
395 
DownloadImage(uint32_t width,uint32_t height,const vkhpp::UniqueImage & image,vkhpp::ImageLayout currentLayout,vkhpp::ImageLayout returnedLayout)396 gfxstream::expected<std::vector<uint8_t>, vkhpp::Result> Vk::DownloadImage(
397         uint32_t width,
398         uint32_t height,
399         const vkhpp::UniqueImage& image,
400         vkhpp::ImageLayout currentLayout,
401         vkhpp::ImageLayout returnedLayout) {
402     VK_EXPECT_RESULT(
403         DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
404             if (currentLayout != vkhpp::ImageLayout::eTransferSrcOptimal) {
405             const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
406                 vkhpp::ImageMemoryBarrier{
407                     .srcAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
408                                      vkhpp::AccessFlagBits::eMemoryWrite,
409                     .dstAccessMask = vkhpp::AccessFlagBits::eTransferRead,
410                     .oldLayout = currentLayout,
411                     .newLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
412                     .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
413                     .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
414                     .image = *image,
415                     .subresourceRange = {
416                         .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
417                         .baseMipLevel = 0,
418                         .levelCount = 1,
419                         .baseArrayLayer = 0,
420                         .layerCount = 1,
421                     },
422                 },
423             };
424             cmd->pipelineBarrier(
425                 /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
426                 /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
427                 /*dependencyFlags=*/{},
428                 /*memoryBarriers=*/{},
429                 /*bufferMemoryBarriers=*/{},
430                 /*imageMemoryBarriers=*/imageMemoryBarriers);
431             }
432 
433             const std::vector<vkhpp::BufferImageCopy> regions = {
434                 vkhpp::BufferImageCopy{
435                     .bufferOffset = 0,
436                     .bufferRowLength = 0,
437                     .bufferImageHeight = 0,
438                     .imageSubresource =
439                         {
440                             .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
441                             .mipLevel = 0,
442                             .baseArrayLayer = 0,
443                             .layerCount = 1,
444                         },
445                     .imageOffset =
446                         {
447                             .x = 0,
448                             .y = 0,
449                             .z = 0,
450                         },
451                     .imageExtent =
452                         {
453                             .width = width,
454                             .height = height,
455                             .depth = 1,
456                         },
457                 },
458             };
459             cmd->copyImageToBuffer(*image,
460                                    vkhpp::ImageLayout::eTransferSrcOptimal,
461                                    *mStagingBuffer, regions);
462 
463             if (returnedLayout != vkhpp::ImageLayout::eTransferSrcOptimal) {
464                 const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
465                     vkhpp::ImageMemoryBarrier{
466                         .srcAccessMask = vkhpp::AccessFlagBits::eTransferRead,
467                         .dstAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
468                                          vkhpp::AccessFlagBits::eMemoryWrite,
469                         .oldLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
470                         .newLayout = returnedLayout,
471                         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
472                         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
473                         .image = *image,
474                         .subresourceRange = {
475                                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
476                                 .baseMipLevel = 0,
477                                 .levelCount = 1,
478                                 .baseArrayLayer = 0,
479                                 .layerCount = 1,
480                             },
481                     },
482                 };
483                 cmd->pipelineBarrier(
484                     /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
485                     /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
486                     /*dependencyFlags=*/{},
487                     /*memoryBarriers=*/{},
488                     /*bufferMemoryBarriers=*/{},
489                     /*imageMemoryBarriers=*/imageMemoryBarriers);
490             }
491 
492             return vkhpp::Result::eSuccess;
493         }));
494 
495     auto* mapped = reinterpret_cast<uint8_t*>(
496         VK_EXPECT_RV(mDevice->mapMemory(*mStagingBufferMemory, 0, kStagingBufferSize)));
497 
498     std::vector<uint8_t> outPixels;
499     outPixels.resize(width * height * 4);
500 
501     std::memcpy(outPixels.data(), mapped, outPixels.size());
502 
503     mDevice->unmapMemory(*mStagingBufferMemory);
504 
505     return outPixels;
506 }
507 
CreateYuvImage(uint32_t width,uint32_t height,vkhpp::ImageUsageFlags usages,vkhpp::MemoryPropertyFlags memoryProperties,vkhpp::ImageLayout layout)508 gfxstream::expected<Vk::YuvImageWithMemory, vkhpp::Result> Vk::CreateYuvImage(
509         uint32_t width,
510         uint32_t height,
511         vkhpp::ImageUsageFlags usages,
512         vkhpp::MemoryPropertyFlags memoryProperties,
513         vkhpp::ImageLayout layout) {
514     const vkhpp::SamplerYcbcrConversionCreateInfo conversionCreateInfo = {
515         .format = vkhpp::Format::eG8B8R83Plane420Unorm,
516         .ycbcrModel = vkhpp::SamplerYcbcrModelConversion::eYcbcr601,
517         .ycbcrRange = vkhpp::SamplerYcbcrRange::eItuNarrow,
518         .components = {
519             .r = vkhpp::ComponentSwizzle::eIdentity,
520             .g = vkhpp::ComponentSwizzle::eIdentity,
521             .b = vkhpp::ComponentSwizzle::eIdentity,
522             .a = vkhpp::ComponentSwizzle::eIdentity,
523         },
524         .xChromaOffset = vkhpp::ChromaLocation::eMidpoint,
525         .yChromaOffset = vkhpp::ChromaLocation::eMidpoint,
526         .chromaFilter = vkhpp::Filter::eLinear,
527         .forceExplicitReconstruction = VK_FALSE,
528     };
529     auto imageSamplerConversion = VK_EXPECT_RV(mDevice->createSamplerYcbcrConversionUnique(conversionCreateInfo));
530 
531     const vkhpp::SamplerYcbcrConversionInfo samplerConversionInfo = {
532         .conversion = *imageSamplerConversion,
533     };
534     const vkhpp::SamplerCreateInfo samplerCreateInfo = {
535         .pNext = &samplerConversionInfo,
536         .magFilter = vkhpp::Filter::eLinear,
537         .minFilter = vkhpp::Filter::eLinear,
538         .mipmapMode = vkhpp::SamplerMipmapMode::eNearest,
539         .addressModeU = vkhpp::SamplerAddressMode::eClampToEdge,
540         .addressModeV = vkhpp::SamplerAddressMode::eClampToEdge,
541         .addressModeW = vkhpp::SamplerAddressMode::eClampToEdge,
542         .mipLodBias = 0.0f,
543         .anisotropyEnable = VK_FALSE,
544         .maxAnisotropy = 1.0f,
545         .compareEnable = VK_FALSE,
546         .compareOp = vkhpp::CompareOp::eLessOrEqual,
547         .minLod = 0.0f,
548         .maxLod = 0.25f,
549         .borderColor = vkhpp::BorderColor::eIntTransparentBlack,
550         .unnormalizedCoordinates = VK_FALSE,
551     };
552     auto imageSampler = VK_EXPECT_RV(mDevice->createSamplerUnique(samplerCreateInfo));
553 
554     const vkhpp::ImageCreateInfo imageCreateInfo = {
555         .imageType = vkhpp::ImageType::e2D,
556         .format = vkhpp::Format::eG8B8R83Plane420Unorm,
557         .extent = {
558             .width = width,
559             .height = height,
560             .depth = 1,
561         },
562         .mipLevels = 1,
563         .arrayLayers = 1,
564         .samples = vkhpp::SampleCountFlagBits::e1,
565         .tiling = vkhpp::ImageTiling::eOptimal,
566         .usage = usages,
567         .sharingMode = vkhpp::SharingMode::eExclusive,
568         .initialLayout = vkhpp::ImageLayout::eUndefined,
569     };
570     auto image = VK_EXPECT_RV(mDevice->createImageUnique(imageCreateInfo));
571 
572     const auto memoryRequirements = mDevice->getImageMemoryRequirements(*image);
573 
574     const uint32_t memoryIndex =
575         GetMemoryType(mPhysicalDevice,
576                       memoryRequirements.memoryTypeBits,
577                       memoryProperties);
578 
579     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
580         .allocationSize = memoryRequirements.size,
581         .memoryTypeIndex = memoryIndex,
582     };
583     auto imageMemory = VK_EXPECT_RV(mDevice->allocateMemoryUnique(imageMemoryAllocateInfo));
584 
585     mDevice->bindImageMemory(*image, *imageMemory, 0);
586 
587     const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
588         .pNext = &samplerConversionInfo,
589         .image = *image,
590         .viewType = vkhpp::ImageViewType::e2D,
591         .format = vkhpp::Format::eG8B8R83Plane420Unorm,
592         .components = {
593             .r = vkhpp::ComponentSwizzle::eIdentity,
594             .g = vkhpp::ComponentSwizzle::eIdentity,
595             .b = vkhpp::ComponentSwizzle::eIdentity,
596             .a = vkhpp::ComponentSwizzle::eIdentity,
597         },
598         .subresourceRange = {
599             .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
600             .baseMipLevel = 0,
601             .levelCount = 1,
602             .baseArrayLayer = 0,
603             .layerCount = 1,
604         },
605     };
606     auto imageView = VK_EXPECT_RV(mDevice->createImageViewUnique(imageViewCreateInfo));
607 
608     VK_EXPECT_RESULT(DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
609         const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
610             vkhpp::ImageMemoryBarrier{
611                 .srcAccessMask = {},
612                 .dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
613                 .oldLayout = vkhpp::ImageLayout::eUndefined,
614                 .newLayout = layout,
615                 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
616                 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
617                 .image = *image,
618                 .subresourceRange = {
619                     .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
620                     .baseMipLevel = 0,
621                     .levelCount = 1,
622                     .baseArrayLayer = 0,
623                     .layerCount = 1,
624                 },
625 
626             },
627         };
628         cmd->pipelineBarrier(
629             /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
630             /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
631             /*dependencyFlags=*/{},
632             /*memoryBarriers=*/{},
633             /*bufferMemoryBarriers=*/{},
634             /*imageMemoryBarriers=*/imageMemoryBarriers);
635         return vkhpp::Result::eSuccess;
636     }));
637 
638     return YuvImageWithMemory{
639         .imageSamplerConversion = std::move(imageSamplerConversion),
640         .imageSampler = std::move(imageSampler),
641         .imageMemory = std::move(imageMemory),
642         .image = std::move(image),
643         .imageView = std::move(imageView),
644     };
645 }
646 
LoadYuvImage(const vkhpp::UniqueImage & image,uint32_t width,uint32_t height,const std::vector<uint8_t> & imageDataY,const std::vector<uint8_t> & imageDataU,const std::vector<uint8_t> & imageDataV,vkhpp::ImageLayout currentLayout,vkhpp::ImageLayout returnedLayout)647 vkhpp::Result Vk::LoadYuvImage(
648         const vkhpp::UniqueImage& image,
649         uint32_t width,
650         uint32_t height,
651         const std::vector<uint8_t>& imageDataY,
652         const std::vector<uint8_t>& imageDataU,
653         const std::vector<uint8_t>& imageDataV,
654         vkhpp::ImageLayout currentLayout,
655         vkhpp::ImageLayout returnedLayout) {
656     auto* mapped = reinterpret_cast<uint8_t*>(VK_TRY_RV(mDevice->mapMemory(*mStagingBufferMemory, 0, kStagingBufferSize)));
657 
658     const VkDeviceSize yOffset = 0;
659     const VkDeviceSize uOffset = imageDataY.size();
660     const VkDeviceSize vOffset = imageDataY.size() + imageDataU.size();
661     std::memcpy(mapped + yOffset, imageDataY.data(), imageDataY.size());
662     std::memcpy(mapped + uOffset, imageDataU.data(), imageDataU.size());
663     std::memcpy(mapped + vOffset, imageDataV.data(), imageDataV.size());
664     mDevice->unmapMemory(*mStagingBufferMemory);
665 
666     return DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
667         if (currentLayout != vkhpp::ImageLayout::eTransferDstOptimal) {
668         const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
669             vkhpp::ImageMemoryBarrier{
670                 .srcAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
671                                 vkhpp::AccessFlagBits::eMemoryWrite,
672                 .dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
673                 .oldLayout = currentLayout,
674                 .newLayout = vkhpp::ImageLayout::eTransferDstOptimal,
675                 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
676                 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
677                 .image = *image,
678                 .subresourceRange = {
679                     .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
680                     .baseMipLevel = 0,
681                     .levelCount = 1,
682                     .baseArrayLayer = 0,
683                     .layerCount = 1,
684                 },
685 
686             },
687         };
688         cmd->pipelineBarrier(
689             /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
690             /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
691             /*dependencyFlags=*/{},
692             /*memoryBarriers=*/{},
693             /*bufferMemoryBarriers=*/{},
694             /*imageMemoryBarriers=*/imageMemoryBarriers);
695         }
696 
697         const std::vector<vkhpp::BufferImageCopy> imageCopyRegions = {
698             vkhpp::BufferImageCopy{
699                 .bufferOffset = yOffset,
700                 .bufferRowLength = 0,
701                 .bufferImageHeight = 0,
702                 .imageSubresource = {
703                     .aspectMask = vkhpp::ImageAspectFlagBits::ePlane0,
704                     .mipLevel = 0,
705                     .baseArrayLayer = 0,
706                     .layerCount = 1,
707                 },
708                 .imageOffset = {
709                     .x = 0,
710                     .y = 0,
711                     .z = 0,
712                 },
713                 .imageExtent = {
714                     .width = width,
715                     .height = height,
716                     .depth = 1,
717                 },
718             },
719             vkhpp::BufferImageCopy{
720                 .bufferOffset = uOffset,
721                 .bufferRowLength = 0,
722                 .bufferImageHeight = 0,
723                 .imageSubresource = {
724                     .aspectMask = vkhpp::ImageAspectFlagBits::ePlane1,
725                     .mipLevel = 0,
726                     .baseArrayLayer = 0,
727                     .layerCount = 1,
728                 },
729                 .imageOffset = {
730                     .x = 0,
731                     .y = 0,
732                     .z = 0,
733                 },
734                 .imageExtent = {
735                     .width = width / 2,
736                     .height = height / 2,
737                     .depth = 1,
738                 },
739             },
740             vkhpp::BufferImageCopy{
741                 .bufferOffset = vOffset,
742                 .bufferRowLength = 0,
743                 .bufferImageHeight = 0,
744                 .imageSubresource = {
745                     .aspectMask = vkhpp::ImageAspectFlagBits::ePlane2,
746                     .mipLevel = 0,
747                     .baseArrayLayer = 0,
748                     .layerCount = 1,
749                 },
750                 .imageOffset = {
751                     .x = 0,
752                     .y = 0,
753                     .z = 0,
754                 },
755                 .imageExtent = {
756                     .width = width / 2,
757                     .height = height / 2,
758                     .depth = 1,
759                 },
760             },
761         };
762         cmd->copyBufferToImage(*mStagingBuffer,
763                                *image,
764                                vkhpp::ImageLayout::eTransferDstOptimal,
765                                imageCopyRegions);
766 
767         if (returnedLayout != vkhpp::ImageLayout::eTransferDstOptimal) {
768             const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
769                 vkhpp::ImageMemoryBarrier{
770                     .srcAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
771                     .dstAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
772                                     vkhpp::AccessFlagBits::eMemoryWrite,
773                     .oldLayout = vkhpp::ImageLayout::eTransferDstOptimal,
774                     .newLayout = returnedLayout,
775                     .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
776                     .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
777                     .image = *image,
778                     .subresourceRange = {
779                         .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
780                         .baseMipLevel = 0,
781                         .levelCount = 1,
782                         .baseArrayLayer = 0,
783                         .layerCount = 1,
784                     },
785                 },
786             };
787             cmd->pipelineBarrier(
788                 /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
789                 /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
790                 /*dependencyFlags=*/{},
791                 /*memoryBarriers=*/{},
792                 /*bufferMemoryBarriers=*/{},
793                 /*imageMemoryBarriers=*/imageMemoryBarriers);
794         }
795         return vkhpp::Result::eSuccess;
796     });
797 }
798 
799 gfxstream::expected<Vk::FramebufferWithAttachments, vkhpp::Result>
CreateFramebuffer(uint32_t width,uint32_t height,vkhpp::Format color_format,vkhpp::Format depth_format)800 Vk::CreateFramebuffer(
801         uint32_t width,
802         uint32_t height,
803         vkhpp::Format color_format,
804         vkhpp::Format depth_format) {
805     std::optional<Vk::ImageWithMemory> colorAttachment;
806     if (color_format != vkhpp::Format::eUndefined) {
807         colorAttachment =
808             GFXSTREAM_EXPECT(CreateImage(width, height, color_format,
809                                 vkhpp::ImageUsageFlagBits::eColorAttachment |
810                                     vkhpp::ImageUsageFlagBits::eTransferSrc,
811                                 vkhpp::MemoryPropertyFlagBits::eDeviceLocal,
812                                 vkhpp::ImageLayout::eColorAttachmentOptimal));
813     }
814 
815     std::optional<Vk::ImageWithMemory> depthAttachment;
816     if (depth_format != vkhpp::Format::eUndefined) {
817         depthAttachment =
818             GFXSTREAM_EXPECT(CreateImage(width, height, depth_format,
819                                 vkhpp::ImageUsageFlagBits::eDepthStencilAttachment |
820                                     vkhpp::ImageUsageFlagBits::eTransferSrc,
821                                 vkhpp::MemoryPropertyFlagBits::eDeviceLocal,
822                                 vkhpp::ImageLayout::eDepthStencilAttachmentOptimal));
823     }
824 
825     std::vector<vkhpp::AttachmentDescription> attachments;
826 
827     std::optional<vkhpp::AttachmentReference> colorAttachment_reference;
828     if (color_format != vkhpp::Format::eUndefined) {
829         attachments.push_back(vkhpp::AttachmentDescription{
830             .format = color_format,
831             .samples = vkhpp::SampleCountFlagBits::e1,
832             .loadOp = vkhpp::AttachmentLoadOp::eClear,
833             .storeOp = vkhpp::AttachmentStoreOp::eStore,
834             .stencilLoadOp = vkhpp::AttachmentLoadOp::eClear,
835             .stencilStoreOp = vkhpp::AttachmentStoreOp::eStore,
836             .initialLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
837             .finalLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
838         });
839 
840         colorAttachment_reference = vkhpp::AttachmentReference{
841             .attachment = static_cast<uint32_t>(attachments.size() - 1),
842             .layout = vkhpp::ImageLayout::eColorAttachmentOptimal,
843         };
844     }
845 
846     std::optional<vkhpp::AttachmentReference> depthAttachment_reference;
847     if (depth_format != vkhpp::Format::eUndefined) {
848         attachments.push_back(vkhpp::AttachmentDescription{
849             .format = depth_format,
850             .samples = vkhpp::SampleCountFlagBits::e1,
851             .loadOp = vkhpp::AttachmentLoadOp::eClear,
852             .storeOp = vkhpp::AttachmentStoreOp::eStore,
853             .stencilLoadOp = vkhpp::AttachmentLoadOp::eClear,
854             .stencilStoreOp = vkhpp::AttachmentStoreOp::eStore,
855             .initialLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
856             .finalLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
857         });
858 
859         depthAttachment_reference = vkhpp::AttachmentReference{
860             .attachment = static_cast<uint32_t>(attachments.size() - 1),
861             .layout = vkhpp::ImageLayout::eDepthStencilAttachmentOptimal,
862         };
863     }
864 
865     vkhpp::SubpassDependency dependency = {
866         .srcSubpass = 0,
867         .dstSubpass = 0,
868         .srcStageMask = {},
869         .dstStageMask = vkhpp::PipelineStageFlagBits::eFragmentShader,
870         .srcAccessMask = {},
871         .dstAccessMask = vkhpp::AccessFlagBits::eInputAttachmentRead,
872         .dependencyFlags = vkhpp::DependencyFlagBits::eByRegion,
873     };
874     if (color_format != vkhpp::Format::eUndefined) {
875         dependency.srcStageMask |=
876             vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
877         dependency.dstStageMask |=
878             vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
879         dependency.srcAccessMask |= vkhpp::AccessFlagBits::eColorAttachmentWrite;
880     }
881     if (depth_format != vkhpp::Format::eUndefined) {
882         dependency.srcStageMask |=
883             vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
884         dependency.dstStageMask |=
885             vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
886         dependency.srcAccessMask |= vkhpp::AccessFlagBits::eColorAttachmentWrite;
887     }
888 
889     vkhpp::SubpassDescription subpass = {
890         .pipelineBindPoint = vkhpp::PipelineBindPoint::eGraphics,
891         .inputAttachmentCount = 0,
892         .pInputAttachments = nullptr,
893         .colorAttachmentCount = 0,
894         .pColorAttachments = nullptr,
895         .pResolveAttachments = nullptr,
896         .pDepthStencilAttachment = nullptr,
897         .pPreserveAttachments = nullptr,
898     };
899     if (color_format != vkhpp::Format::eUndefined) {
900         subpass.colorAttachmentCount = 1;
901         subpass.pColorAttachments = &*colorAttachment_reference;
902     }
903     if (depth_format != vkhpp::Format::eUndefined) {
904         subpass.pDepthStencilAttachment = &*depthAttachment_reference;
905     }
906 
907     const vkhpp::RenderPassCreateInfo renderpassCreateInfo = {
908         .attachmentCount = static_cast<uint32_t>(attachments.size()),
909         .pAttachments = attachments.data(),
910         .subpassCount = 1,
911         .pSubpasses = &subpass,
912         .dependencyCount = 1,
913         .pDependencies = &dependency,
914     };
915     auto renderpass = VK_EXPECT_RV(mDevice->createRenderPassUnique(renderpassCreateInfo));
916 
917     std::vector<vkhpp::ImageView> framebufferAttachments;
918     if (colorAttachment) {
919         framebufferAttachments.push_back(*colorAttachment->imageView);
920     }
921     if (depthAttachment) {
922         framebufferAttachments.push_back(*depthAttachment->imageView);
923     }
924     const vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
925         .renderPass = *renderpass,
926         .attachmentCount = static_cast<uint32_t>(framebufferAttachments.size()),
927         .pAttachments = framebufferAttachments.data(),
928         .width = width,
929         .height = height,
930         .layers = 1,
931     };
932     auto framebuffer = VK_EXPECT_RV(mDevice->createFramebufferUnique(framebufferCreateInfo));
933 
934     return Vk::FramebufferWithAttachments{
935         .colorAttachment = std::move(colorAttachment),
936         .depthAttachment = std::move(depthAttachment),
937         .renderpass = std::move(renderpass),
938         .framebuffer = std::move(framebuffer),
939     };
940 }
941 
DoCommandsImmediate(const std::function<vkhpp::Result (vkhpp::UniqueCommandBuffer &)> & func,const std::vector<vkhpp::UniqueSemaphore> & semaphores_wait,const std::vector<vkhpp::UniqueSemaphore> & semaphores_signal)942 vkhpp::Result Vk::DoCommandsImmediate(
943         const std::function<vkhpp::Result(vkhpp::UniqueCommandBuffer&)>& func,
944         const std::vector<vkhpp::UniqueSemaphore>& semaphores_wait,
945         const std::vector<vkhpp::UniqueSemaphore>& semaphores_signal) {
946     const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
947         .commandPool = *mCommandPool,
948         .level = vkhpp::CommandBufferLevel::ePrimary,
949         .commandBufferCount = 1,
950     };
951     auto commandBuffers = VK_TRY_RV(mDevice->allocateCommandBuffersUnique(commandBufferAllocateInfo));
952     auto commandBuffer = std::move(commandBuffers[0]);
953 
954     const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
955         .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
956     };
957     commandBuffer->begin(commandBufferBeginInfo);
958     VK_TRY(func(commandBuffer));
959     commandBuffer->end();
960 
961     std::vector<vkhpp::CommandBuffer> commandBufferHandles;
962     commandBufferHandles.push_back(*commandBuffer);
963 
964     std::vector<vkhpp::Semaphore> semaphoreHandlesWait;
965     semaphoreHandlesWait.reserve(semaphores_wait.size());
966     for (const auto& s : semaphores_wait) {
967         semaphoreHandlesWait.emplace_back(*s);
968     }
969 
970     std::vector<vkhpp::Semaphore> semaphoreHandlesSignal;
971     semaphoreHandlesSignal.reserve(semaphores_signal.size());
972     for (const auto& s : semaphores_signal) {
973         semaphoreHandlesSignal.emplace_back(*s);
974     }
975 
976     vkhpp::SubmitInfo submitInfo = {
977         .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
978         .pCommandBuffers = commandBufferHandles.data(),
979     };
980     if (!semaphoreHandlesWait.empty()) {
981         submitInfo.waitSemaphoreCount = static_cast<uint32_t>(semaphoreHandlesWait.size());
982         submitInfo.pWaitSemaphores = semaphoreHandlesWait.data();
983     }
984     if (!semaphoreHandlesSignal.empty()) {
985         submitInfo.signalSemaphoreCount = static_cast<uint32_t>(semaphoreHandlesSignal.size());
986         submitInfo.pSignalSemaphores = semaphoreHandlesSignal.data();
987     }
988     mQueue.submit(submitInfo);
989     mQueue.waitIdle();
990 
991     return vkhpp::Result::eSuccess;
992 }
993 
994 }  // namespace gfxstream
995