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/commands/assemble_cvd/graphics_flags.h"
18 
19 #include <ostream>
20 
21 #include <GraphicsDetector.pb.h>
22 #include <android-base/file.h>
23 #include <android-base/strings.h>
24 #include <fmt/format.h>
25 #include <google/protobuf/text_format.h>
26 
27 #include "common/libs/utils/contains.h"
28 #include "common/libs/utils/files.h"
29 #include "common/libs/utils/subprocess.h"
30 #include "host/libs/config/cuttlefish_config.h"
31 
32 #ifdef __APPLE__
33 #define CF_UNUSED_ON_MACOS [[maybe_unused]]
34 #else
35 #define CF_UNUSED_ON_MACOS
36 #endif
37 
38 namespace cuttlefish {
39 namespace {
40 
41 enum class RenderingMode {
42   kNone,
43   kCustom,
44   kGuestSwiftShader,
45   kGfxstream,
46   kGfxstreamGuestAngle,
47   kGfxstreamGuestAngleHostSwiftshader,
48   kVirglRenderer,
49 };
50 
51 CF_UNUSED_ON_MACOS
GetRenderingMode(const std::string & mode)52 Result<RenderingMode> GetRenderingMode(const std::string& mode) {
53   if (mode == std::string(kGpuModeDrmVirgl)) {
54     return RenderingMode::kVirglRenderer;
55   }
56   if (mode == std::string(kGpuModeGfxstream)) {
57     return RenderingMode::kGfxstream;
58   }
59   if (mode == std::string(kGpuModeGfxstreamGuestAngle)) {
60     return RenderingMode::kGfxstreamGuestAngle;
61   }
62   if (mode == std::string(kGpuModeGfxstreamGuestAngleHostSwiftShader)) {
63     return RenderingMode::kGfxstreamGuestAngleHostSwiftshader;
64   }
65   if (mode == std::string(kGpuModeGuestSwiftshader)) {
66     return RenderingMode::kGuestSwiftShader;
67   }
68   if (mode == std::string(kGpuModeCustom)) {
69     return RenderingMode::kCustom;
70   }
71   if (mode == std::string(kGpuModeNone)) {
72     return RenderingMode::kNone;
73   }
74   return CF_ERR("Unsupported rendering mode: " << mode);
75 }
76 
77 struct AngleFeatures {
78   // Prefer linear filtering for YUV AHBs to pass
79   // android.media.decoder.cts.DecodeAccuracyTest on older branches.
80   // Generally not needed after b/315387961.
81   bool prefer_linear_filtering_for_yuv = false;
82 
83   // Map unspecified color spaces to PASS_THROUGH to pass
84   // android.media.codec.cts.DecodeEditEncodeTest and
85   // android.media.codec.cts.EncodeDecodeTest.
86   bool map_unspecified_color_space_to_pass_through = true;
87 
88   // b/264575911: Nvidia seems to have issues with YUV samplers with
89   // 'lowp' and 'mediump' precision qualifiers.
90   bool ignore_precision_qualifiers = false;
91 
92   // ANGLE has a feature to expose 3.2 early even if the device does
93   // not fully support all of the 3.2 features. This should be
94   // disabled for Cuttlefish as SwiftShader does not have geometry
95   // shader nor tesselation shader support.
96   bool disable_expose_opengles_3_2_for_testing = false;
97 };
98 
operator <<(std::ostream & stream,const AngleFeatures & features)99 std::ostream& operator<<(std::ostream& stream, const AngleFeatures& features) {
100   fmt::print(stream, "ANGLE features: \n");
101   fmt::print(stream, " - prefer_linear_filtering_for_yuv: {}\n",
102              features.prefer_linear_filtering_for_yuv);
103   fmt::print(stream, " - map_unspecified_color_space_to_pass_through: {}\n",
104              features.map_unspecified_color_space_to_pass_through);
105   fmt::print(stream, " - ignore_precision_qualifiers: {}\n",
106              features.ignore_precision_qualifiers);
107   return stream;
108 }
109 
GetNeededAngleFeaturesBasedOnQuirks(const RenderingMode mode,const::gfxstream::proto::GraphicsAvailability & availability)110 Result<AngleFeatures> GetNeededAngleFeaturesBasedOnQuirks(
111     const RenderingMode mode,
112     const ::gfxstream::proto::GraphicsAvailability& availability) {
113   AngleFeatures features = {};
114   if (mode == RenderingMode::kGfxstreamGuestAngle) {
115     if (availability.has_vulkan() &&
116         !availability.vulkan().physical_devices().empty() &&
117         availability.vulkan().physical_devices(0).has_quirks() &&
118         availability.vulkan()
119             .physical_devices(0)
120             .quirks()
121             .has_issue_with_precision_qualifiers_on_yuv_samplers()) {
122       features.ignore_precision_qualifiers = true;
123     }
124   }
125 
126   if (mode == RenderingMode::kGuestSwiftShader ||
127       mode == RenderingMode::kGfxstreamGuestAngleHostSwiftshader) {
128     features.disable_expose_opengles_3_2_for_testing = true;
129   }
130 
131   return features;
132 }
133 
ToLower(const std::string & v)134 std::string ToLower(const std::string& v) {
135   std::string result = v;
136   std::transform(result.begin(), result.end(), result.begin(),
137                  [](unsigned char c) { return std::tolower(c); });
138   return result;
139 }
140 
IsLikelySoftwareRenderer(const std::string & renderer)141 bool IsLikelySoftwareRenderer(const std::string& renderer) {
142   const std::string lower_renderer = ToLower(renderer);
143   return lower_renderer.find("llvmpipe") != std::string::npos;
144 }
145 
146 CF_UNUSED_ON_MACOS
ShouldEnableAcceleratedRendering(const::gfxstream::proto::GraphicsAvailability & availability)147 bool ShouldEnableAcceleratedRendering(
148     const ::gfxstream::proto::GraphicsAvailability& availability) {
149   const bool sufficient_gles2 =
150       availability.has_egl() && availability.egl().has_gles2_availability() &&
151       !IsLikelySoftwareRenderer(
152           availability.egl().gles2_availability().renderer());
153   const bool sufficient_gles3 =
154       availability.has_egl() && availability.egl().has_gles3_availability() &&
155       !IsLikelySoftwareRenderer(
156           availability.egl().gles3_availability().renderer());
157   const bool has_discrete_gpu =
158       availability.has_vulkan() &&
159       !availability.vulkan().physical_devices().empty() &&
160       (availability.vulkan().physical_devices(0).type() ==
161        ::gfxstream::proto::VulkanPhysicalDevice::TYPE_DISCRETE_GPU);
162   return (sufficient_gles2 || sufficient_gles3) && has_discrete_gpu;
163 }
164 
165 struct AngleFeatureOverrides {
166   std::string angle_feature_overrides_enabled;
167   std::string angle_feature_overrides_disabled;
168 };
169 
170 CF_UNUSED_ON_MACOS
GetNeededAngleFeatures(const RenderingMode mode,const::gfxstream::proto::GraphicsAvailability & availability)171 Result<AngleFeatureOverrides> GetNeededAngleFeatures(
172     const RenderingMode mode,
173     const ::gfxstream::proto::GraphicsAvailability& availability) {
174   const AngleFeatures features =
175       CF_EXPECT(GetNeededAngleFeaturesBasedOnQuirks(mode, availability));
176   LOG(DEBUG) << features;
177 
178   std::vector<std::string> enable_feature_strings;
179   std::vector<std::string> disable_feature_strings;
180   if (features.prefer_linear_filtering_for_yuv) {
181     enable_feature_strings.push_back("preferLinearFilterForYUV");
182   }
183   if (features.map_unspecified_color_space_to_pass_through) {
184     enable_feature_strings.push_back("mapUnspecifiedColorSpaceToPassThrough");
185   }
186   if (features.ignore_precision_qualifiers) {
187     disable_feature_strings.push_back("enablePrecisionQualifiers");
188   }
189   if (features.disable_expose_opengles_3_2_for_testing) {
190     disable_feature_strings.push_back("exposeES32ForTesting");
191   }
192 
193   return AngleFeatureOverrides{
194       .angle_feature_overrides_enabled =
195           android::base::Join(enable_feature_strings, ':'),
196       .angle_feature_overrides_disabled =
197           android::base::Join(disable_feature_strings, ':'),
198   };
199 }
200 
201 struct VhostUserGpuHostRendererFeatures {
202   // If true, host Virtio GPU blob resources will be allocated with
203   // external memory and exported file descriptors will be shared
204   // with the VMM for mapping resources into the guest address space.
205   bool external_blob = false;
206 
207   // If true, host Virtio GPU blob resources will be allocated with
208   // shmem and exported file descriptors will be shared with the VMM
209   // for mapping resources into the guest address space.
210   //
211   // This is an extension of the above external_blob that allows the
212   // VMM to map resources without graphics API support but requires
213   // additional features (VK_EXT_external_memory_host) from the GPU
214   // driver and is potentially less performant.
215   bool system_blob = false;
216 };
217 
218 CF_UNUSED_ON_MACOS
219 Result<VhostUserGpuHostRendererFeatures>
GetNeededVhostUserGpuHostRendererFeatures(RenderingMode mode,const::gfxstream::proto::GraphicsAvailability & availability)220 GetNeededVhostUserGpuHostRendererFeatures(
221     RenderingMode mode,
222     const ::gfxstream::proto::GraphicsAvailability& availability) {
223   VhostUserGpuHostRendererFeatures features = {};
224 
225   // No features needed for guest rendering.
226   if (mode == RenderingMode::kGuestSwiftShader) {
227     return features;
228   }
229 
230   // For any passthrough graphics mode, external blob is needed for sharing
231   // buffers between the vhost-user-gpu VMM process and the main VMM process.
232   features.external_blob = true;
233 
234   // Prebuilt SwiftShader includes VK_EXT_external_memory_host.
235   if (mode == RenderingMode::kGfxstreamGuestAngleHostSwiftshader) {
236     features.system_blob = true;
237   } else {
238     const bool has_external_memory_host =
239         availability.has_vulkan() &&
240         !availability.vulkan().physical_devices().empty() &&
241         Contains(availability.vulkan().physical_devices(0).extensions(),
242                  "VK_EXT_external_memory_host");
243 
244     CF_EXPECT(
245         has_external_memory_host || mode != RenderingMode::kGfxstreamGuestAngle,
246         "VK_EXT_external_memory_host is required for running with "
247         "--gpu_mode=gfxstream_guest_angle and --enable_gpu_vhost_user=true");
248 
249     features.system_blob = has_external_memory_host;
250   }
251 
252   return features;
253 }
254 
255 #ifndef __APPLE__
SelectGpuMode(const std::string & gpu_mode_arg,VmmMode vmm,const GuestConfig & guest_config,const gfxstream::proto::GraphicsAvailability & graphics_availability)256 Result<std::string> SelectGpuMode(
257     const std::string& gpu_mode_arg, VmmMode vmm,
258     const GuestConfig& guest_config,
259     const gfxstream::proto::GraphicsAvailability& graphics_availability) {
260   if (gpu_mode_arg != kGpuModeAuto && gpu_mode_arg != kGpuModeDrmVirgl &&
261       gpu_mode_arg != kGpuModeCustom && gpu_mode_arg != kGpuModeGfxstream &&
262       gpu_mode_arg != kGpuModeGfxstreamGuestAngle &&
263       gpu_mode_arg != kGpuModeGfxstreamGuestAngleHostSwiftShader &&
264       gpu_mode_arg != kGpuModeGuestSwiftshader &&
265       gpu_mode_arg != kGpuModeNone) {
266     return CF_ERR("Invalid gpu_mode: " << gpu_mode_arg);
267   }
268 
269   if (gpu_mode_arg == kGpuModeAuto) {
270     if (ShouldEnableAcceleratedRendering(graphics_availability)) {
271       if (HostArch() == Arch::Arm64) {
272         LOG(INFO) << "GPU auto mode: detected prerequisites for accelerated "
273                      "rendering support but enabling "
274                      "--gpu_mode=guest_swiftshader until vhost-user-gpu "
275                      "based accelerated rendering on ARM has been more "
276                      "thoroughly tested. Please explicitly use "
277                      "--gpu_mode=gfxstream or "
278                      "--gpu_mode=gfxstream_guest_angle to enable for now.";
279         return kGpuModeGuestSwiftshader;
280       }
281 
282       LOG(INFO) << "GPU auto mode: detected prerequisites for accelerated "
283                 << "rendering support.";
284 
285       if (vmm == VmmMode::kQemu && !UseQemuPrebuilt()) {
286         LOG(INFO) << "Not using QEMU prebuilt (QEMU 8+): selecting guest swiftshader";
287         return kGpuModeGuestSwiftshader;
288       } else if (guest_config.prefer_drm_virgl_when_supported) {
289         LOG(INFO) << "GPU mode from guest config: drm_virgl";
290         return kGpuModeDrmVirgl;
291       } else if (!guest_config.gfxstream_supported) {
292         LOG(INFO) << "GPU auto mode: guest does not support gfxstream, "
293                      "enabling --gpu_mode=guest_swiftshader";
294         return kGpuModeGuestSwiftshader;
295       } else {
296         LOG(INFO) << "Enabling --gpu_mode=gfxstream.";
297         return kGpuModeGfxstream;
298       }
299     } else {
300       LOG(INFO) << "GPU auto mode: did not detect prerequisites for "
301                    "accelerated rendering support, enabling "
302                    "--gpu_mode=guest_swiftshader.";
303       return kGpuModeGuestSwiftshader;
304     }
305   }
306 
307   if (gpu_mode_arg == kGpuModeGfxstream ||
308       gpu_mode_arg == kGpuModeGfxstreamGuestAngle ||
309       gpu_mode_arg == kGpuModeDrmVirgl) {
310     if (!ShouldEnableAcceleratedRendering(graphics_availability)) {
311       LOG(ERROR) << "--gpu_mode=" << gpu_mode_arg
312                  << " was requested but the prerequisites for accelerated "
313                     "rendering were not detected so the device may not "
314                     "function correctly. Please consider switching to "
315                     "--gpu_mode=auto or --gpu_mode=guest_swiftshader.";
316     }
317 
318     if (vmm == VmmMode::kQemu && !UseQemuPrebuilt()) {
319       LOG(INFO) << "Not using QEMU prebuilt (QEMU 8+): selecting guest swiftshader";
320       return kGpuModeGuestSwiftshader;
321     }
322   }
323 
324   return gpu_mode_arg;
325 }
326 
SelectGpuVhostUserMode(const std::string & gpu_mode,const std::string & gpu_vhost_user_mode_arg,VmmMode vmm)327 Result<bool> SelectGpuVhostUserMode(const std::string& gpu_mode,
328                                     const std::string& gpu_vhost_user_mode_arg,
329                                     VmmMode vmm) {
330   CF_EXPECT(gpu_vhost_user_mode_arg == kGpuVhostUserModeAuto ||
331             gpu_vhost_user_mode_arg == kGpuVhostUserModeOn ||
332             gpu_vhost_user_mode_arg == kGpuVhostUserModeOff);
333   if (gpu_vhost_user_mode_arg == kGpuVhostUserModeAuto) {
334     if (gpu_mode == kGpuModeGuestSwiftshader) {
335       LOG(INFO) << "GPU vhost user auto mode: not needed for --gpu_mode="
336                 << gpu_mode << ". Not enabling vhost user gpu.";
337       return false;
338     }
339 
340     if (vmm != VmmMode::kCrosvm) {
341       LOG(INFO) << "GPU vhost user auto mode: not yet supported with " << vmm
342                 << ". Not enabling vhost user gpu.";
343       return false;
344     }
345 
346     // Android built ARM host tools seem to be incompatible with host GPU
347     // libraries. Enable vhost user gpu which will run the virtio GPU device
348     // in a separate process with a VMM prebuilt. See b/200592498.
349     const auto host_arch = HostArch();
350     if (host_arch == Arch::Arm64) {
351       LOG(INFO) << "GPU vhost user auto mode: detected arm64 host. Enabling "
352                    "vhost user gpu.";
353       return true;
354     }
355 
356     LOG(INFO) << "GPU vhost user auto mode: not needed. Not enabling vhost "
357                  "user gpu.";
358     return false;
359   }
360 
361   return gpu_vhost_user_mode_arg == kGpuVhostUserModeOn;
362 }
363 
364 #endif
365 
GraphicsDetectorBinaryPath()366 Result<std::string> GraphicsDetectorBinaryPath() {
367   const auto host_arch = HostArch();
368   switch (host_arch) {
369     case Arch::Arm64:
370       return HostBinaryPath("aarch64-linux-gnu/gfxstream_graphics_detector");
371     case Arch::X86:
372     case Arch::X86_64:
373       return HostBinaryPath("x86_64-linux-gnu/gfxstream_graphics_detector");
374     default:
375       break;
376   }
377   return CF_ERR("Graphics detector unavailable for host arch.");
378 }
379 
IsAmdGpu(const gfxstream::proto::GraphicsAvailability & availability)380 bool IsAmdGpu(const gfxstream::proto::GraphicsAvailability& availability) {
381   return (availability.has_egl() &&
382           ((availability.egl().has_gles2_availability() &&
383             availability.egl().gles2_availability().has_vendor() &&
384             availability.egl().gles2_availability().vendor().find("AMD") !=
385                 std::string::npos) ||
386            (availability.egl().has_gles3_availability() &&
387             availability.egl().gles3_availability().has_vendor() &&
388             availability.egl().gles3_availability().vendor().find("AMD") !=
389                 std::string::npos))) ||
390          (availability.has_vulkan() &&
391           !availability.vulkan().physical_devices().empty() &&
392           availability.vulkan().physical_devices(0).has_name() &&
393           availability.vulkan().physical_devices(0).name().find("AMD") !=
394               std::string::npos);
395 }
396 
397 const std::string kGfxstreamTransportAsg = "virtio-gpu-asg";
398 const std::string kGfxstreamTransportPipe = "virtio-gpu-pipe";
399 
400 CF_UNUSED_ON_MACOS
ParseGfxstreamRendererFlag(const std::string & gpu_renderer_features_arg)401 Result<std::unordered_map<std::string, bool>> ParseGfxstreamRendererFlag(
402     const std::string& gpu_renderer_features_arg) {
403   std::unordered_map<std::string, bool> features;
404 
405   for (const std::string& feature :
406        android::base::Split(gpu_renderer_features_arg, ";")) {
407     if (feature.empty()) {
408       continue;
409     }
410 
411     const std::vector<std::string> feature_parts =
412         android::base::Split(feature, ":");
413     CF_EXPECT(feature_parts.size() == 2,
414               "Failed to parse renderer features from --gpu_renderer_features="
415                   << gpu_renderer_features_arg);
416 
417     const std::string& feature_name = feature_parts[0];
418     const std::string& feature_enabled = feature_parts[1];
419     CF_EXPECT(feature_enabled == "enabled" || feature_enabled == "disabled",
420               "Failed to parse renderer features from --gpu_renderer_features="
421                   << gpu_renderer_features_arg);
422 
423     features[feature_name] = (feature_enabled == "enabled");
424   }
425 
426   return features;
427 }
428 
429 CF_UNUSED_ON_MACOS
GetGfxstreamRendererFeaturesString(const std::unordered_map<std::string,bool> & features)430 std::string GetGfxstreamRendererFeaturesString(
431     const std::unordered_map<std::string, bool>& features) {
432   std::vector<std::string> parts;
433   for (const auto& [feature_name, feature_enabled] : features) {
434     parts.push_back(feature_name + ":" +
435                     (feature_enabled ? "enabled" : "disabled"));
436   }
437   return android::base::Join(parts, ",");
438 }
439 
440 CF_UNUSED_ON_MACOS
SetGfxstreamFlags(const std::string & gpu_mode,const std::string & gpu_renderer_features_arg,const GuestConfig & guest_config,const gfxstream::proto::GraphicsAvailability & availability,CuttlefishConfig::MutableInstanceSpecific & instance)441 Result<void> SetGfxstreamFlags(
442     const std::string& gpu_mode, const std::string& gpu_renderer_features_arg,
443     const GuestConfig& guest_config,
444     const gfxstream::proto::GraphicsAvailability& availability,
445     CuttlefishConfig::MutableInstanceSpecific& instance) {
446   std::string gfxstream_transport = kGfxstreamTransportAsg;
447 
448   // Some older R branches are missing some Gfxstream backports
449   // which introduced a backward incompatible change (b/267483000).
450   if (guest_config.android_version_number == "11.0.0") {
451     gfxstream_transport = kGfxstreamTransportPipe;
452   }
453 
454   if (IsAmdGpu(availability)) {
455     // KVM does not support mapping host graphics buffers into the guest because
456     // the AMD GPU driver uses TTM memory. More info in
457     // https://lore.kernel.org/all/[email protected]
458     //
459     // TODO(b/254721007): replace with a kernel version check after KVM patches
460     // land.
461     CF_EXPECT(gpu_mode != kGpuModeGfxstreamGuestAngle,
462               "--gpu_mode=gfxstream_guest_angle is broken on AMD GPUs.");
463   }
464 
465   std::unordered_map<std::string, bool> features;
466 
467   // Apply features from host/mode requirements.
468   if (gpu_mode == kGpuModeGfxstreamGuestAngleHostSwiftShader) {
469     features["VulkanUseDedicatedAhbMemoryType"] = true;
470   }
471 
472   // Apply features from guest/mode requirements.
473   if (guest_config.gfxstream_gl_program_binary_link_status_supported) {
474     features["GlProgramBinaryLinkStatus"] = true;
475   }
476 
477   // Apply feature overrides from --gpu_renderer_features.
478   const auto feature_overrides =
479       CF_EXPECT(ParseGfxstreamRendererFlag(gpu_renderer_features_arg));
480   for (const auto& [feature_name, feature_enabled] : feature_overrides) {
481     LOG(DEBUG) << "GPU renderer feature " << feature_name << " overridden to "
482                << (feature_enabled ? "enabled" : "disabled")
483                << " via command line argument.";
484     features[feature_name] = feature_enabled;
485   }
486 
487   // Convert features back to a string for passing to the VMM.
488   const std::string features_string =
489       GetGfxstreamRendererFeaturesString(features);
490   if (!features_string.empty()) {
491     instance.set_gpu_renderer_features(features_string);
492   }
493 
494   instance.set_gpu_gfxstream_transport(gfxstream_transport);
495   return {};
496 }
497 
498 static std::unordered_set<std::string> kSupportedGpuContexts{
499     "gfxstream-vulkan", "gfxstream-composer", "cross-domain", "magma"};
500 
501 }  // namespace
502 
503 gfxstream::proto::GraphicsAvailability
GetGraphicsAvailabilityWithSubprocessCheck()504 GetGraphicsAvailabilityWithSubprocessCheck() {
505 #ifdef __APPLE__
506   return {};
507 #else
508   auto graphics_detector_binary_result = GraphicsDetectorBinaryPath();
509   if (!graphics_detector_binary_result.ok()) {
510     LOG(ERROR) << "Failed to run graphics detector, graphics detector path "
511                << " not available: "
512                << graphics_detector_binary_result.error().FormatForEnv()
513                << ". Assuming no availability.";
514     return {};
515   }
516 
517   TemporaryFile graphics_availability_file;
518 
519   Command graphics_detector_cmd(graphics_detector_binary_result.value());
520   graphics_detector_cmd.AddParameter(graphics_availability_file.path);
521 
522   std::string graphics_detector_stdout;
523   auto ret = RunWithManagedStdio(std::move(graphics_detector_cmd), nullptr,
524                                  &graphics_detector_stdout, nullptr);
525   if (ret != 0) {
526     LOG(ERROR) << "Failed to run graphics detector, bad return value: " << ret
527                << ". Assuming no availability.";
528     return {};
529   }
530   LOG(DEBUG) << graphics_detector_stdout;
531 
532   auto graphics_availability_content_result =
533       ReadFileContents(graphics_availability_file.path);
534   if (!graphics_availability_content_result.ok()) {
535     LOG(ERROR) << "Failed to read graphics availability from file "
536                << graphics_availability_file.path << ":"
537                << graphics_availability_content_result.error().FormatForEnv()
538                << ". Assuming no availability.";
539     return {};
540   }
541   const std::string& graphics_availability_content =
542       graphics_availability_content_result.value();
543 
544   gfxstream::proto::GraphicsAvailability availability;
545   google::protobuf::TextFormat::Parser parser;
546   if (!parser.ParseFromString(graphics_availability_content, &availability)) {
547     LOG(ERROR) << "Failed to parse graphics detector output: "
548                << graphics_availability_content
549                << ". Assuming no availability.";
550     return {};
551   }
552 
553   LOG(DEBUG) << "Host Graphics Availability:" << availability.DebugString();
554   return availability;
555 #endif
556 }
557 
ConfigureGpuSettings(const gfxstream::proto::GraphicsAvailability & graphics_availability,const std::string & gpu_mode_arg,const std::string & gpu_vhost_user_mode_arg,const std::string & gpu_renderer_features_arg,std::string & gpu_context_types_arg,VmmMode vmm,const GuestConfig & guest_config,CuttlefishConfig::MutableInstanceSpecific & instance)558 Result<std::string> ConfigureGpuSettings(
559     const gfxstream::proto::GraphicsAvailability& graphics_availability,
560     const std::string& gpu_mode_arg, const std::string& gpu_vhost_user_mode_arg,
561     const std::string& gpu_renderer_features_arg,
562     std::string& gpu_context_types_arg, VmmMode vmm,
563     const GuestConfig& guest_config,
564     CuttlefishConfig::MutableInstanceSpecific& instance) {
565 #ifdef __APPLE__
566   (void)graphics_availability;
567   (void)gpu_vhost_user_mode_arg;
568   (void)vmm;
569   (void)guest_config;
570   CF_EXPECT(gpu_mode_arg == kGpuModeAuto ||
571             gpu_mode_arg == kGpuModeGuestSwiftshader ||
572             gpu_mode_arg == kGpuModeDrmVirgl || gpu_mode_arg == kGpuModeNone);
573   std::string gpu_mode = gpu_mode_arg;
574   if (gpu_mode == kGpuModeAuto) {
575     gpu_mode = kGpuModeGuestSwiftshader;
576   }
577   instance.set_gpu_mode(gpu_mode);
578   instance.set_enable_gpu_vhost_user(false);
579 #else
580   const std::string gpu_mode = CF_EXPECT(
581       SelectGpuMode(gpu_mode_arg, vmm, guest_config, graphics_availability));
582   const bool enable_gpu_vhost_user =
583       CF_EXPECT(SelectGpuVhostUserMode(gpu_mode, gpu_vhost_user_mode_arg, vmm));
584 
585   if (gpu_mode == kGpuModeGfxstream ||
586       gpu_mode == kGpuModeGfxstreamGuestAngle ||
587       gpu_mode == kGpuModeGfxstreamGuestAngleHostSwiftShader) {
588     CF_EXPECT(SetGfxstreamFlags(gpu_mode, gpu_renderer_features_arg,
589                                 guest_config, graphics_availability, instance));
590   }
591 
592   if (gpu_mode == kGpuModeCustom) {
593     auto requested_types = android::base::Split(gpu_context_types_arg, ":");
594     for (const std::string& requested : requested_types) {
595       CF_EXPECT(kSupportedGpuContexts.count(requested) == 1,
596                 "unsupported context type: " + requested);
597     }
598   }
599 
600   const auto angle_features = CF_EXPECT(GetNeededAngleFeatures(
601       CF_EXPECT(GetRenderingMode(gpu_mode)), graphics_availability));
602   instance.set_gpu_angle_feature_overrides_enabled(
603       angle_features.angle_feature_overrides_enabled);
604   instance.set_gpu_angle_feature_overrides_disabled(
605       angle_features.angle_feature_overrides_disabled);
606 
607   if (enable_gpu_vhost_user) {
608     const auto gpu_vhost_user_features =
609         CF_EXPECT(GetNeededVhostUserGpuHostRendererFeatures(
610             CF_EXPECT(GetRenderingMode(gpu_mode)), graphics_availability));
611     instance.set_enable_gpu_external_blob(
612         gpu_vhost_user_features.external_blob);
613     instance.set_enable_gpu_system_blob(gpu_vhost_user_features.system_blob);
614   } else {
615     instance.set_enable_gpu_external_blob(false);
616     instance.set_enable_gpu_system_blob(false);
617   }
618 
619   instance.set_gpu_mode(gpu_mode);
620   instance.set_enable_gpu_vhost_user(enable_gpu_vhost_user);
621 #endif
622 
623   return gpu_mode;
624 }
625 
626 }  // namespace cuttlefish
627