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