• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 "host/libs/graphics_detector/graphics_detector_vk_precision_qualifiers_on_yuv_samplers.h"
18 
19 #include <vector>
20 
21 #include <android-base/logging.h>
22 
23 #include "host/libs/graphics_detector/img.h"
24 #include "host/libs/graphics_detector/subprocess.h"
25 #include "host/libs/graphics_detector/vk.h"
26 
27 namespace cuttlefish {
28 namespace {
29 
30 // kBlitTextureVert
31 #include "host/libs/graphics_detector/shaders/blit_texture.vert.inl"
32 // kBlitTextureFrag
33 #include "host/libs/graphics_detector/shaders/blit_texture.frag.inl"
34 // kBlitTextureLowpFrag
35 #include "host/libs/graphics_detector/shaders/blit_texture_lowp.frag.inl"
36 // kBlitTextureMediumpFrag
37 #include "host/libs/graphics_detector/shaders/blit_texture_mediump.frag.inl"
38 // kBlitTextureHighpFrag
39 #include "host/libs/graphics_detector/shaders/blit_texture_highp.frag.inl"
40 
CanHandlePrecisionQualifierWithYuvSampler(const std::vector<uint8_t> & blit_vert_shader_spirv,const std::vector<uint8_t> & blit_frag_shader_spirv,bool * out_passed_test)41 vk::Result CanHandlePrecisionQualifierWithYuvSampler(
42     const std::vector<uint8_t>& blit_vert_shader_spirv,
43     const std::vector<uint8_t>& blit_frag_shader_spirv, bool* out_passed_test) {
44   std::optional<Vk> vk = Vk::Load(
45       /*instance_extensions=*/{},
46       /*instance_layers=*/{},
47       /*device_extensions=*/
48       {
49           VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
50       });
51   if (!vk) {
52     LOG(FATAL) << "Failed to load vk";
53   }
54 
55   uint32_t texture_width = 32;
56   uint32_t texture_height = 32;
57   std::vector<uint8_t> texture_data_rgba8888;
58   FillWithColor(texture_width, texture_height,
59                 /*red=*/0xFF,
60                 /*green=*/0x00,
61                 /*blue=*/0x00,
62                 /*alpha=*/0xFF, &texture_data_rgba8888);
63 
64   std::vector<uint8_t> texture_data_yuv420_y;
65   std::vector<uint8_t> texture_data_yuv420_u;
66   std::vector<uint8_t> texture_data_yuv420_v;
67   ConvertRGBA8888ToYUV420(texture_width, texture_height, texture_data_rgba8888,
68                           &texture_data_yuv420_y, &texture_data_yuv420_u,
69                           &texture_data_yuv420_v);
70 
71 #if 0
72     // Debugging can be easier with a larger image with more details.
73     texture_data_yuv420_y.clear();
74     texture_data_yuv420_u.clear();
75     texture_data_yuv420_v.clear();
76     LoadYUV420FromBitmapFile("custom.bmp",
77                              &texture_width,
78                              &texture_height,
79                              &texture_data_yuv420_y,
80                              &texture_data_yuv420_u,
81                              &texture_data_yuv420_v);
82 #endif
83 
84   Vk::YuvImageWithMemory sampled_image = VK_EXPECT_RESULT(vk->CreateYuvImage(
85       texture_width, texture_height,
86       vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst |
87           vk::ImageUsageFlagBits::eTransferSrc,
88       vk::MemoryPropertyFlagBits::eDeviceLocal,
89       vk::ImageLayout::eTransferDstOptimal));
90 
91   VK_ASSERT(vk->LoadYuvImage(
92       sampled_image.image, texture_width, texture_height, texture_data_yuv420_y,
93       texture_data_yuv420_u, texture_data_yuv420_v,
94       /*current_layout=*/vk::ImageLayout::eTransferDstOptimal,
95       /*returned_layout=*/vk::ImageLayout::eShaderReadOnlyOptimal));
96 
97   Vk::FramebufferWithAttachments framebuffer =
98       VK_EXPECT_RESULT(vk->CreateFramebuffer(
99           texture_width, texture_height,
100           /*color_attachment_format=*/vk::Format::eR8G8B8A8Unorm));
101 
102   const vk::Sampler descriptor_set_0_binding_0_sampler =
103       *sampled_image.image_sampler;
104   const std::vector<vk::DescriptorSetLayoutBinding> descriptor_set_0_bindings =
105       {
106           vk::DescriptorSetLayoutBinding{
107               .binding = 0,
108               .descriptorType = vk::DescriptorType::eCombinedImageSampler,
109               .descriptorCount = 1,
110               .stageFlags = vk::ShaderStageFlagBits::eFragment,
111               .pImmutableSamplers = &descriptor_set_0_binding_0_sampler,
112           },
113       };
114   const vk::DescriptorSetLayoutCreateInfo descriptor_set_0_create_info = {
115       .bindingCount = static_cast<uint32_t>(descriptor_set_0_bindings.size()),
116       .pBindings = descriptor_set_0_bindings.data(),
117   };
118   auto descriptor_set_0_layout =
119       VK_EXPECT_RESULT(vk::raii::DescriptorSetLayout::create(
120           vk->vk_device, descriptor_set_0_create_info));
121 
122   const std::vector<vk::DescriptorPoolSize> descriptor_pool_sizes = {
123       vk::DescriptorPoolSize{
124           .type = vk::DescriptorType::eCombinedImageSampler,
125           .descriptorCount = 1,
126       },
127   };
128   const vk::DescriptorPoolCreateInfo descriptor_pool_create_info = {
129       .flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
130       .maxSets = 1,
131       .poolSizeCount = static_cast<uint32_t>(descriptor_pool_sizes.size()),
132       .pPoolSizes = descriptor_pool_sizes.data(),
133   };
134   auto descriptor_set_0_pool =
135       VK_EXPECT_RESULT(vk::raii::DescriptorPool::create(
136           vk->vk_device, descriptor_pool_create_info));
137 
138   const vk::DescriptorSetLayout descriptor_set_0_layout_handle =
139       *descriptor_set_0_layout;
140   const vk::DescriptorSetAllocateInfo descriptor_set_allocate_info = {
141       .descriptorPool = *descriptor_set_0_pool,
142       .descriptorSetCount = 1,
143       .pSetLayouts = &descriptor_set_0_layout_handle,
144   };
145   auto descriptor_sets = VK_EXPECT_RESULT(vk::raii::DescriptorSets::create(
146       vk->vk_device, descriptor_set_allocate_info));
147   auto descriptor_set_0(std::move(descriptor_sets[0]));
148 
149   const vk::DescriptorImageInfo descriptor_set_0_binding_0_image_info = {
150       .sampler = VK_NULL_HANDLE,
151       .imageView = *sampled_image.image_view,
152       .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
153   };
154   const std::vector<vk::WriteDescriptorSet> descriptor_set_0_writes = {
155       vk::WriteDescriptorSet{
156           .dstSet = *descriptor_set_0,
157           .dstBinding = 0,
158           .dstArrayElement = 0,
159           .descriptorCount = 1,
160           .descriptorType = vk::DescriptorType::eCombinedImageSampler,
161           .pImageInfo = &descriptor_set_0_binding_0_image_info,
162           .pBufferInfo = nullptr,
163           .pTexelBufferView = nullptr,
164       },
165   };
166   vk->vk_device.updateDescriptorSets(descriptor_set_0_writes, {});
167 
168   const std::vector<vk::DescriptorSetLayout>
169       pipeline_layout_descriptor_set_layouts = {
170           *descriptor_set_0_layout,
171       };
172   const vk::PipelineLayoutCreateInfo pipeline_layout_create_info = {
173       .setLayoutCount =
174           static_cast<uint32_t>(pipeline_layout_descriptor_set_layouts.size()),
175       .pSetLayouts = pipeline_layout_descriptor_set_layouts.data(),
176   };
177   auto pipeline_layout = VK_EXPECT_RESULT(vk::raii::PipelineLayout::create(
178       vk->vk_device, pipeline_layout_create_info));
179 
180   const vk::ShaderModuleCreateInfo vert_shader_create_info = {
181       .codeSize = static_cast<uint32_t>(blit_vert_shader_spirv.size()),
182       .pCode = reinterpret_cast<const uint32_t*>(blit_vert_shader_spirv.data()),
183   };
184   auto vert_shader_module = VK_EXPECT_RESULT(
185       vk::raii::ShaderModule::create(vk->vk_device, vert_shader_create_info));
186 
187   const vk::ShaderModuleCreateInfo frag_shader_create_info = {
188       .codeSize = static_cast<uint32_t>(blit_frag_shader_spirv.size()),
189       .pCode = reinterpret_cast<const uint32_t*>(blit_frag_shader_spirv.data()),
190   };
191   auto frag_shader_module = VK_EXPECT_RESULT(
192       vk::raii::ShaderModule::create(vk->vk_device, frag_shader_create_info));
193 
194   const std::vector<vk::PipelineShaderStageCreateInfo> pipeline_stages = {
195       vk::PipelineShaderStageCreateInfo{
196           .stage = vk::ShaderStageFlagBits::eVertex,
197           .module = *vert_shader_module,
198           .pName = "main",
199       },
200       vk::PipelineShaderStageCreateInfo{
201           .stage = vk::ShaderStageFlagBits::eFragment,
202           .module = *frag_shader_module,
203           .pName = "main",
204       },
205   };
206   const vk::PipelineVertexInputStateCreateInfo
207       pipeline_vertex_input_state_create_info = {};
208   const vk::PipelineInputAssemblyStateCreateInfo
209       pipeline_input_assembly_state_create_info = {
210           .topology = vk::PrimitiveTopology::eTriangleStrip,
211       };
212   const vk::PipelineViewportStateCreateInfo
213       pipeline_viewport_state_create_info = {
214           .viewportCount = 1,
215           .pViewports = nullptr,
216           .scissorCount = 1,
217           .pScissors = nullptr,
218       };
219   const vk::PipelineRasterizationStateCreateInfo
220       pipeline_rasterization_state_create_info = {
221           .depthClampEnable = VK_FALSE,
222           .rasterizerDiscardEnable = VK_FALSE,
223           .polygonMode = vk::PolygonMode::eFill,
224           .cullMode = {},
225           .frontFace = vk::FrontFace::eCounterClockwise,
226           .depthBiasEnable = VK_FALSE,
227           .depthBiasConstantFactor = 0.0f,
228           .depthBiasClamp = 0.0f,
229           .depthBiasSlopeFactor = 0.0f,
230           .lineWidth = 1.0f,
231       };
232   const vk::SampleMask pipeline_sample_mask = 65535;
233   const vk::PipelineMultisampleStateCreateInfo
234       pipeline_multisample_state_create_info = {
235           .rasterizationSamples = vk::SampleCountFlagBits::e1,
236           .sampleShadingEnable = VK_FALSE,
237           .minSampleShading = 1.0f,
238           .pSampleMask = &pipeline_sample_mask,
239           .alphaToCoverageEnable = VK_FALSE,
240           .alphaToOneEnable = VK_FALSE,
241       };
242   const vk::PipelineDepthStencilStateCreateInfo
243       pipeline_depth_stencil_state_create_info = {
244           .depthTestEnable = VK_FALSE,
245           .depthWriteEnable = VK_FALSE,
246           .depthCompareOp = vk::CompareOp::eLess,
247           .depthBoundsTestEnable = VK_FALSE,
248           .stencilTestEnable = VK_FALSE,
249           .front =
250               {
251                   .failOp = vk::StencilOp::eKeep,
252                   .passOp = vk::StencilOp::eKeep,
253                   .depthFailOp = vk::StencilOp::eKeep,
254                   .compareOp = vk::CompareOp::eAlways,
255                   .compareMask = 0,
256                   .writeMask = 0,
257                   .reference = 0,
258               },
259           .back =
260               {
261                   .failOp = vk::StencilOp::eKeep,
262                   .passOp = vk::StencilOp::eKeep,
263                   .depthFailOp = vk::StencilOp::eKeep,
264                   .compareOp = vk::CompareOp::eAlways,
265                   .compareMask = 0,
266                   .writeMask = 0,
267                   .reference = 0,
268               },
269           .minDepthBounds = 0.0f,
270           .maxDepthBounds = 0.0f,
271       };
272   const std::vector<vk::PipelineColorBlendAttachmentState>
273       pipeline_color_blend_attachments = {
274           vk::PipelineColorBlendAttachmentState{
275               .blendEnable = VK_FALSE,
276               .srcColorBlendFactor = vk::BlendFactor::eOne,
277               .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
278               .colorBlendOp = vk::BlendOp::eAdd,
279               .srcAlphaBlendFactor = vk::BlendFactor::eOne,
280               .dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
281               .alphaBlendOp = vk::BlendOp::eAdd,
282               .colorWriteMask = vk::ColorComponentFlagBits::eR |
283                                 vk::ColorComponentFlagBits::eG |
284                                 vk::ColorComponentFlagBits::eB |
285                                 vk::ColorComponentFlagBits::eA,
286           },
287       };
288   const vk::PipelineColorBlendStateCreateInfo
289       pipeline_color_blend_state_create_info = {
290           .logicOpEnable = VK_FALSE,
291           .logicOp = vk::LogicOp::eCopy,
292           .attachmentCount =
293               static_cast<uint32_t>(pipeline_color_blend_attachments.size()),
294           .pAttachments = pipeline_color_blend_attachments.data(),
295           .blendConstants = {{
296               0.0f,
297               0.0f,
298               0.0f,
299               0.0f,
300           }},
301       };
302   const std::vector<vk::DynamicState> pipeline_dynamic_states = {
303       vk::DynamicState::eViewport,
304       vk::DynamicState::eScissor,
305   };
306   const vk::PipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info =
307       {
308           .dynamicStateCount =
309               static_cast<uint32_t>(pipeline_dynamic_states.size()),
310           .pDynamicStates = pipeline_dynamic_states.data(),
311       };
312   const vk::GraphicsPipelineCreateInfo pipeline_create_info = {
313       .stageCount = static_cast<uint32_t>(pipeline_stages.size()),
314       .pStages = pipeline_stages.data(),
315       .pVertexInputState = &pipeline_vertex_input_state_create_info,
316       .pInputAssemblyState = &pipeline_input_assembly_state_create_info,
317       .pTessellationState = nullptr,
318       .pViewportState = &pipeline_viewport_state_create_info,
319       .pRasterizationState = &pipeline_rasterization_state_create_info,
320       .pMultisampleState = &pipeline_multisample_state_create_info,
321       .pDepthStencilState = &pipeline_depth_stencil_state_create_info,
322       .pColorBlendState = &pipeline_color_blend_state_create_info,
323       .pDynamicState = &pipeline_dynamic_state_create_info,
324       .layout = *pipeline_layout,
325       .renderPass = *framebuffer.renderpass,
326       .subpass = 0,
327       .basePipelineHandle = VK_NULL_HANDLE,
328       .basePipelineIndex = 0,
329   };
330   auto pipeline = VK_EXPECT_RESULT(
331       vk::raii::Pipeline::create(vk->vk_device, nullptr, pipeline_create_info));
332 
333   VK_RETURN_IF_NOT_SUCCESS(
334       vk->DoCommandsImmediate([&](vk::raii::CommandBuffer& command_buffer) {
335         const std::vector<vk::ClearValue> render_pass_begin_clear_values = {
336             vk::ClearValue{
337                 .color =
338                     {
339                         .float32 = {{
340                             1.0f,
341                             0.0f,
342                             0.0f,
343                             1.0f,
344                         }},
345                     },
346             },
347         };
348         const vk::RenderPassBeginInfo render_pass_begin_info = {
349             .renderPass = *framebuffer.renderpass,
350             .framebuffer = *framebuffer.framebuffer,
351             .renderArea =
352                 {
353                     .offset =
354                         {
355                             .x = 0,
356                             .y = 0,
357                         },
358                     .extent =
359                         {
360                             .width = texture_width,
361                             .height = texture_height,
362                         },
363                 },
364             .clearValueCount =
365                 static_cast<uint32_t>(render_pass_begin_clear_values.size()),
366             .pClearValues = render_pass_begin_clear_values.data(),
367         };
368         command_buffer.beginRenderPass(render_pass_begin_info,
369                                        vk::SubpassContents::eInline);
370 
371         command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics,
372                                     *pipeline);
373 
374         command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
375                                           *pipeline_layout,
376                                           /*firstSet=*/0, {*descriptor_set_0},
377                                           /*dynamicOffsets=*/{});
378         const vk::Viewport viewport = {
379             .x = 0.0f,
380             .y = 0.0f,
381             .width = static_cast<float>(texture_width),
382             .height = static_cast<float>(texture_height),
383             .minDepth = 0.0f,
384             .maxDepth = 1.0f,
385         };
386         command_buffer.setViewport(0, {viewport});
387 
388         const vk::Rect2D scissor = {
389             .offset =
390                 {
391                     .x = 0,
392                     .y = 0,
393                 },
394             .extent =
395                 {
396                     .width = texture_width,
397                     .height = texture_height,
398                 },
399         };
400         command_buffer.setScissor(0, {scissor});
401 
402         command_buffer.draw(4, 1, 0, 0);
403 
404         command_buffer.endRenderPass();
405         return vk::Result::eSuccess;
406       }));
407 
408   std::vector<uint8_t> rendered_pixels;
409   VK_RETURN_IF_NOT_SUCCESS(vk->DownloadImage(
410       texture_width, texture_height, framebuffer.color_attachment->image,
411       vk::ImageLayout::eColorAttachmentOptimal,
412       vk::ImageLayout::eColorAttachmentOptimal, &rendered_pixels));
413 #if 0
414     SaveRGBAToBitmapFile(texture_width,
415                          texture_height,
416                          rendered_pixels.data(),
417                          "rendered.bmp");
418 #endif
419 
420   *out_passed_test = ImagesAreSimilar(texture_width, texture_height,
421                                       texture_data_rgba8888, rendered_pixels);
422   return vk::Result::eSuccess;
423 }
424 
PopulateVulkanPrecisionQualifiersOnYuvSamplersQuirkImpl(GraphicsAvailability * availability)425 void PopulateVulkanPrecisionQualifiersOnYuvSamplersQuirkImpl(
426     GraphicsAvailability* availability) {
427   struct ShaderCombo {
428     std::string name;
429     const std::vector<uint8_t>& vert;
430     const std::vector<uint8_t>& frag;
431   };
432   const std::vector<ShaderCombo> combos = {
433       ShaderCombo{
434           .name = "sampler2D has no precision qualifier",
435           .vert = kBlitTextureVert,
436           .frag = kBlitTextureFrag,
437       },
438       ShaderCombo{
439           .name = "sampler2D has a 'lowp' precision qualifier",
440           .vert = kBlitTextureVert,
441           .frag = kBlitTextureLowpFrag,
442       },
443       ShaderCombo{
444           .name = "sampler2D has a 'mediump' precision qualifier",
445           .vert = kBlitTextureVert,
446           .frag = kBlitTextureMediumpFrag,
447       },
448       ShaderCombo{
449           .name = "sampler2D has a 'highp' precision qualifier",
450           .vert = kBlitTextureVert,
451           .frag = kBlitTextureHighpFrag,
452       },
453   };
454   for (const auto& combo : combos) {
455     bool passed_test = false;
456     auto result = CanHandlePrecisionQualifierWithYuvSampler(
457         combo.vert, combo.frag, &passed_test);
458     if (result != vk::Result::eSuccess) {
459       LOG(ERROR) << "Failed to fully check if driver has issue when "
460                  << combo.name;
461       availability->vulkan_has_issue_with_precision_qualifiers_on_yuv_samplers =
462           true;
463       return;
464     }
465     if (!passed_test) {
466       LOG(ERROR) << "Driver has issue when " << combo.name;
467       availability->vulkan_has_issue_with_precision_qualifiers_on_yuv_samplers =
468           true;
469       return;
470     }
471   }
472 }
473 
474 }  // namespace
475 
PopulateVulkanPrecisionQualifiersOnYuvSamplersQuirk(GraphicsAvailability * availability)476 void PopulateVulkanPrecisionQualifiersOnYuvSamplersQuirk(
477     GraphicsAvailability* availability) {
478   auto result = DoWithSubprocessCheck(
479       "PopulateVulkanPrecisionQualifiersOnYuvSamplersQuirk", [&]() {
480         PopulateVulkanPrecisionQualifiersOnYuvSamplersQuirkImpl(availability);
481       });
482   if (result == SubprocessResult::kFailure) {
483     availability->vulkan_has_issue_with_precision_qualifiers_on_yuv_samplers =
484         true;
485   }
486 }
487 
488 }  // namespace cuttlefish