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