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