blob: 6eeb6394d9dc6ac0c6f1d63f5d0339934a0328b0 [file] [log] [blame]
//
// Copyright 2014 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// angle_test_instantiate.cpp: Adds support for filtering parameterized
// tests by platform, so we skip unsupported configs.
#include "test_utils/angle_test_instantiate.h"
#include <algorithm>
#include <array>
#include <iostream>
#include <map>
#include "angle_gl.h"
#include "common/debug.h"
#include "common/platform.h"
#include "common/system_utils.h"
#include "common/third_party/base/anglebase/no_destructor.h"
#include "gpu_info_util/SystemInfo.h"
#include "test_utils/angle_test_configs.h"
#include "util/EGLWindow.h"
#include "util/OSWindow.h"
#include "util/test_utils.h"
#if defined(ANGLE_PLATFORM_WINDOWS)
# include <VersionHelpers.h>
# include "util/windows/WGLWindow.h"
#endif // defined(ANGLE_PLATFORM_WINDOWS)
#if defined(ANGLE_PLATFORM_APPLE)
# include "test_utils/angle_test_instantiate_apple.h"
#endif
namespace angle
{
namespace
{
bool IsAngleEGLConfigSupported(const PlatformParameters &param, OSWindow *osWindow)
{
std::unique_ptr<angle::Library> eglLibrary;
#if defined(ANGLE_USE_UTIL_LOADER)
eglLibrary.reset(
angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, angle::SearchType::ModuleDir));
#endif
EGLWindow *eglWindow = EGLWindow::New(param.majorVersion, param.minorVersion);
ConfigParameters configParams;
bool result =
eglWindow->initializeGL(osWindow, eglLibrary.get(), angle::GLESDriverType::AngleEGL,
param.eglParameters, configParams);
eglWindow->destroyGL();
EGLWindow::Delete(&eglWindow);
return result;
}
bool IsSystemWGLConfigSupported(const PlatformParameters &param, OSWindow *osWindow)
{
#if defined(ANGLE_PLATFORM_WINDOWS) && defined(ANGLE_USE_UTIL_LOADER)
std::unique_ptr<angle::Library> openglLibrary(
angle::OpenSharedLibrary("opengl32", angle::SearchType::SystemDir));
WGLWindow *wglWindow = WGLWindow::New(param.majorVersion, param.minorVersion);
ConfigParameters configParams;
bool result =
wglWindow->initializeGL(osWindow, openglLibrary.get(), angle::GLESDriverType::SystemWGL,
param.eglParameters, configParams);
wglWindow->destroyGL();
WGLWindow::Delete(&wglWindow);
return result;
#else
return false;
#endif // defined(ANGLE_PLATFORM_WINDOWS) && defined(ANGLE_USE_UTIL_LOADER)
}
bool IsSystemEGLConfigSupported(const PlatformParameters &param, OSWindow *osWindow)
{
#if defined(ANGLE_USE_UTIL_LOADER)
std::unique_ptr<angle::Library> eglLibrary;
eglLibrary.reset(OpenSharedLibraryWithExtension(GetNativeEGLLibraryNameWithExtension(),
SearchType::SystemDir));
EGLWindow *eglWindow = EGLWindow::New(param.majorVersion, param.minorVersion);
ConfigParameters configParams;
bool result =
eglWindow->initializeGL(osWindow, eglLibrary.get(), angle::GLESDriverType::SystemEGL,
param.eglParameters, configParams);
eglWindow->destroyGL();
EGLWindow::Delete(&eglWindow);
return result;
#else
return false;
#endif
}
bool IsAndroidDevice(const std::string &deviceName)
{
if (!IsAndroid())
{
return false;
}
SystemInfo *systemInfo = GetTestSystemInfo();
if (systemInfo->machineModelName == deviceName)
{
return true;
}
return false;
}
bool IsAndroid9OrNewer()
{
if (!IsAndroid())
{
return false;
}
SystemInfo *systemInfo = GetTestSystemInfo();
if (systemInfo->androidSdkLevel >= 28)
{
return true;
}
return false;
}
GPUDeviceInfo *GetActiveGPUDeviceInfo()
{
SystemInfo *systemInfo = GetTestSystemInfo();
// Unfortunately sometimes GPU info collection can fail.
if (systemInfo->gpus.empty())
{
return nullptr;
}
return &systemInfo->gpus[systemInfo->activeGPUIndex];
}
bool HasSystemVendorID(VendorID vendorID)
{
GPUDeviceInfo *gpuInfo = GetActiveGPUDeviceInfo();
return gpuInfo && gpuInfo->vendorId == vendorID;
}
bool HasSystemDeviceID(VendorID vendorID, DeviceID deviceID)
{
GPUDeviceInfo *gpuInfo = GetActiveGPUDeviceInfo();
return gpuInfo && gpuInfo->vendorId == vendorID && gpuInfo->deviceId == deviceID;
}
using ParamAvailabilityCache = std::map<PlatformParameters, bool>;
ParamAvailabilityCache &GetAvailabilityCache()
{
static angle::base::NoDestructor<std::unique_ptr<ParamAvailabilityCache>>
sParamAvailabilityCache(new ParamAvailabilityCache());
return **sParamAvailabilityCache;
}
constexpr size_t kMaxConfigNameLen = 100;
std::array<char, kMaxConfigNameLen> gSelectedConfig;
} // namespace
bool gEnableANGLEPerTestCaptureLabel = false;
bool IsConfigSelected()
{
return gSelectedConfig[0] != 0;
}
#if !defined(ANGLE_PLATFORM_APPLE)
// For Apple platform, see angle_test_instantiate_apple.mm
bool IsMetalTextureSwizzleAvailable()
{
return false;
}
bool IsMetalCompressedTexture3DAvailable()
{
return false;
}
#endif
SystemInfo *GetTestSystemInfo()
{
static SystemInfo *sSystemInfo = nullptr;
if (sSystemInfo == nullptr)
{
sSystemInfo = new SystemInfo;
if (!GetSystemInfo(sSystemInfo))
{
std::cerr << "Warning: incomplete system info collection.\n";
}
// On dual-GPU Macs we want the active GPU to always appear to be the
// high-performance GPU for tests.
// We can call the generic GPU info collector which selects the
// non-Intel GPU as the active one on dual-GPU machines.
if (IsOSX())
{
GetDualGPUInfo(sSystemInfo);
}
// Print complete system info when available.
// Seems to trip up Android test expectation parsing.
// Also don't print info when a config is selected to prevent test spam.
if (!IsAndroid() && !IsConfigSelected())
{
PrintSystemInfo(*sSystemInfo);
}
}
return sSystemInfo;
}
bool IsAndroid()
{
#if defined(ANGLE_PLATFORM_ANDROID)
return true;
#else
return false;
#endif
}
bool IsLinux()
{
#if defined(ANGLE_PLATFORM_LINUX)
return true;
#else
return false;
#endif
}
bool IsOSX()
{
#if defined(ANGLE_PLATFORM_MACOS)
return true;
#else
return false;
#endif
}
bool IsIOS()
{
#if defined(ANGLE_PLATFORM_IOS)
return true;
#else
return false;
#endif
}
bool IsARM64()
{
// _M_ARM64 is Windows-specific, while __aarch64__ is for other platforms.
#if defined(_M_ARM64) || defined(__aarch64__)
return true;
#else
return false;
#endif
}
bool IsOzone()
{
#if defined(USE_OZONE) && (defined(USE_X11) || defined(ANGLE_USE_VULKAN_DISPLAY))
// We do not have a proper support for Ozone/Linux yet. Still, we need to figure out how to
// properly initialize tests and differentiate between X11 and Wayland. Probably, passing a
// command line argument could be sufficient. At the moment, run tests only for X11 backend
// as we don't have Wayland support in Angle. Yes, this is a bit weird to return false, but
// it makes it possible to continue angle tests with X11 regardless of the Chromium config
// for linux, which is use_x11 && use_ozone. Also, IsOzone is a bit vague now. It was only
// expected that angle could run with ozone/drm backend for ChromeOS. And returning true
// for desktop Linux when USE_OZONE && USE_X11 are both defined results in incorrect tests'
// expectations. We should also rework them and make IsOzone less vague.
//
// TODO(crbug.com/angleproject/4977): make it possible to switch between X11 and Wayland on
// Ozone/Linux builds. Probably, it's possible to identify the WAYLAND backend by checking
// the WAYLAND_DISPLAY or XDG_SESSION_TYPE env vars. And also make the IsOzone method less
// vague (read the comment above).
return false;
#elif defined(USE_OZONE)
return true;
#else
return false;
#endif
}
bool IsWindows()
{
#if defined(ANGLE_PLATFORM_WINDOWS)
return true;
#else
return false;
#endif
}
bool IsWindows7()
{
#if defined(ANGLE_PLATFORM_WINDOWS)
return ::IsWindows7OrGreater() && !::IsWindows8OrGreater();
#else
return false;
#endif
}
bool IsFuchsia()
{
#if defined(ANGLE_PLATFORM_FUCHSIA)
return true;
#else
return false;
#endif
}
bool IsNexus5X()
{
return IsAndroidDevice("Nexus 5X");
}
bool IsNexus9()
{
return IsAndroidDevice("Nexus 9");
}
bool IsPixelXL()
{
return IsAndroidDevice("Pixel XL");
}
bool IsPixel2()
{
return IsAndroidDevice("Pixel 2");
}
bool IsPixel2XL()
{
return IsAndroidDevice("Pixel 2 XL");
}
bool IsPixel4()
{
return IsAndroidDevice("Pixel 4");
}
bool IsNVIDIAShield()
{
return IsAndroidDevice("SHIELD Android TV");
}
bool IsIntel()
{
return HasSystemVendorID(kVendorID_Intel);
}
bool IsIntelUHD630Mobile()
{
return HasSystemDeviceID(kVendorID_Intel, kDeviceID_UHD630Mobile);
}
bool IsAMD()
{
return HasSystemVendorID(kVendorID_AMD);
}
bool IsARM()
{
return HasSystemVendorID(kVendorID_ARM);
}
bool IsSwiftshaderDevice()
{
return HasSystemDeviceID(kVendorID_GOOGLE, kDeviceID_Swiftshader);
}
bool IsNVIDIA()
{
#if defined(ANGLE_PLATFORM_ANDROID)
// NVIDIA Shield cannot detect vendor ID (http://anglebug.com/3541)
if (IsNVIDIAShield())
{
return true;
}
#endif
return HasSystemVendorID(kVendorID_NVIDIA);
}
bool IsQualcomm()
{
return IsNexus5X() || IsNexus9() || IsPixelXL() || IsPixel2() || IsPixel2XL();
}
bool IsConfigAllowlisted(const SystemInfo &systemInfo, const PlatformParameters &param)
{
VendorID vendorID =
systemInfo.gpus.empty() ? 0 : systemInfo.gpus[systemInfo.activeGPUIndex].vendorId;
// We support the default and null back-ends on every platform.
if (param.driver == GLESDriverType::AngleEGL)
{
if (param.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE)
return true;
if (param.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE)
return true;
}
// TODO: http://crbug.com/swiftshader/145
// Swiftshader does not currently have all the robustness features
// we need for ANGLE. In particular, it is unable to detect and recover
// from infinitely looping shaders. That bug is the tracker for fixing
// that and when resolved we can remove the following code.
// This test will disable tests marked with the config WithRobustness
// when run with the swiftshader Vulkan driver and on Android.
if ((param.isSwiftshader() || IsSwiftshaderDevice()) &&
param.eglParameters.robustness == EGL_TRUE)
{
return false;
}
if (IsWindows())
{
switch (param.driver)
{
case GLESDriverType::AngleEGL:
switch (param.getRenderer())
{
case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
return true;
case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
// Note we disable AMD OpenGL testing on Windows due to using a very old and
// outdated card with many driver bugs. See http://anglebug.com/5123
return !IsAMD();
case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
if (IsARM64())
{
return param.getDeviceType() ==
EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE;
}
return true;
case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
// ES 3.1+ back-end is not supported properly.
if (param.eglParameters.majorVersion == 3 &&
param.eglParameters.minorVersion > 0)
{
return false;
}
// Win ES emulation is currently only supported on NVIDIA.
return IsNVIDIA(vendorID);
default:
return false;
}
case GLESDriverType::SystemWGL:
// AMD does not support the ES compatibility extensions.
return !IsAMD(vendorID);
default:
return false;
}
}
#if defined(ANGLE_PLATFORM_APPLE)
if (IsOSX() || IsIOS())
{
// We do not support non-ANGLE bindings on OSX.
if (param.driver != GLESDriverType::AngleEGL)
{
return false;
}
switch (param.getRenderer())
{
case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
// ES 3.1+ back-end is not supported properly.
if (param.majorVersion == 3 && param.minorVersion > 0)
{
return false;
}
return true;
case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
if (!IsMetalRendererAvailable())
{
return false;
}
return true;
case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
// OSX does not support native vulkan
return param.getDeviceType() == EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE;
default:
return false;
}
}
#endif // #if defined(ANGLE_PLATFORM_APPLE)
if (IsFuchsia())
{
// We do not support non-ANGLE bindings on Fuchsia.
if (param.driver != GLESDriverType::AngleEGL)
{
return false;
}
// ES 3 configs do not work properly on Fuchsia ARM.
// TODO(anglebug.com/4352): Investigate missing features.
if (param.majorVersion > 2 && IsARM())
return false;
// Loading swiftshader is not brought up on Fuchsia.
// TODO(anglebug.com/4353): Support loading swiftshader vulkan ICD.
if (param.getDeviceType() == EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE)
return false;
// Currently we only support the Vulkan back-end on Fuchsia.
return (param.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
}
if (IsOzone())
{
// We do not support non-ANGLE bindings on Ozone.
if (param.driver != GLESDriverType::AngleEGL)
return false;
// ES 3 configs do not work properly on Ozone.
if (param.majorVersion > 2)
return false;
// Currently we only support the GLES back-end on Ozone.
return (param.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE);
}
if (IsLinux() || IsAndroid())
{
// We do not support WGL bindings on Linux/Android. We do support system EGL.
switch (param.driver)
{
case GLESDriverType::SystemEGL:
return param.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
case GLESDriverType::SystemWGL:
return false;
default:
break;
}
}
if (IsLinux())
{
ASSERT(param.driver == GLESDriverType::AngleEGL);
// Currently we support the OpenGL and Vulkan back-ends on Linux.
switch (param.getRenderer())
{
case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
return true;
case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
// http://issuetracker.google.com/173004081
return !IsIntel() || param.eglParameters.asyncCommandQueueFeatureVulkan != EGL_TRUE;
default:
return false;
}
}
if (IsAndroid())
{
ASSERT(param.driver == GLESDriverType::AngleEGL);
// Nexus Android devices don't support backing 3.2 contexts
if (param.eglParameters.majorVersion == 3 && param.eglParameters.minorVersion == 2)
{
if (IsNexus5X())
{
return false;
}
}
// Currently we support the GLES and Vulkan back-ends on Android.
switch (param.getRenderer())
{
case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
return true;
case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
// Swiftshader's vulkan frontend doesn't build on Android.
if (param.getDeviceType() == EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE)
{
return false;
}
if (!IsAndroid9OrNewer())
{
return false;
}
if (param.eglParameters.supportsVulkanViewportFlip == EGL_FALSE)
{
return false;
}
return true;
default:
return false;
}
}
// Unknown platform.
return false;
}
bool IsConfigSupported(const PlatformParameters &param)
{
OSWindow *osWindow = OSWindow::New();
bool result = false;
if (osWindow->initialize("CONFIG_TESTER", 1, 1))
{
switch (param.driver)
{
case GLESDriverType::AngleEGL:
result = IsAngleEGLConfigSupported(param, osWindow);
break;
case GLESDriverType::SystemEGL:
result = IsSystemEGLConfigSupported(param, osWindow);
break;
case GLESDriverType::SystemWGL:
result = IsSystemWGLConfigSupported(param, osWindow);
break;
}
osWindow->destroy();
}
OSWindow::Delete(&osWindow);
return result;
}
bool IsPlatformAvailable(const PlatformParameters &param)
{
// Disable "null" device when not on ANGLE or in D3D9.
if (param.getDeviceType() == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
{
if (param.driver != GLESDriverType::AngleEGL)
return false;
if (param.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE)
return false;
}
switch (param.getRenderer())
{
case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
break;
case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
#if !defined(ANGLE_ENABLE_D3D9)
return false;
#else
break;
#endif
case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
#if !defined(ANGLE_ENABLE_D3D11)
return false;
#else
break;
#endif
case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
#if !defined(ANGLE_ENABLE_OPENGL)
return false;
#else
break;
#endif
case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
#if !defined(ANGLE_ENABLE_VULKAN)
return false;
#else
break;
#endif
case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
#if !defined(ANGLE_ENABLE_METAL)
return false;
#else
break;
#endif
case EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE:
#if !defined(ANGLE_ENABLE_NULL)
return false;
#else
break;
#endif
default:
std::cout << "Unknown test platform: " << param << std::endl;
return false;
}
bool result = false;
auto iter = GetAvailabilityCache().find(param);
if (iter != GetAvailabilityCache().end())
{
result = iter->second;
}
else
{
if (IsConfigSelected())
{
std::stringstream strstr;
strstr << param;
if (strstr.str() == std::string(gSelectedConfig.data()))
{
result = true;
}
}
else
{
const SystemInfo *systemInfo = GetTestSystemInfo();
if (systemInfo)
{
result = IsConfigAllowlisted(*systemInfo, param);
}
else
{
result = IsConfigSupported(param);
}
}
GetAvailabilityCache()[param] = result;
// Enable this unconditionally to print available platforms.
if (IsConfigSelected())
{
if (result)
{
std::cout << "Test Config: " << param << "\n";
}
}
else if (!result)
{
std::cout << "Skipping tests using configuration " << param
<< " because it is not available.\n";
}
}
return result;
}
std::vector<std::string> GetAvailableTestPlatformNames()
{
std::vector<std::string> platformNames;
for (const auto &iter : GetAvailabilityCache())
{
if (iter.second)
{
std::stringstream strstr;
strstr << iter.first;
platformNames.push_back(strstr.str());
}
}
// Keep the list sorted.
std::sort(platformNames.begin(), platformNames.end());
return platformNames;
}
void SetSelectedConfig(const char *selectedConfig)
{
gSelectedConfig.fill(0);
strncpy(gSelectedConfig.data(), selectedConfig, kMaxConfigNameLen - 1);
}
} // namespace angle