| // | |
| // Copyright 2015 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. | |
| // | |
| // ANGLETest: | |
| // Implementation of common ANGLE testing fixture. | |
| // | |
| #include "ANGLETest.h" | |
| #include <algorithm> | |
| #include <cstdlib> | |
| #include "common/PackedEnums.h" | |
| #include "common/platform.h" | |
| #include "gpu_info_util/SystemInfo.h" | |
| #include "test_utils/runner/TestSuite.h" | |
| #include "util/EGLWindow.h" | |
| #include "util/OSWindow.h" | |
| #include "util/random_utils.h" | |
| #include "util/test_utils.h" | |
| #if defined(ANGLE_PLATFORM_WINDOWS) | |
| # include <VersionHelpers.h> | |
| #endif // defined(ANGLE_PLATFORM_WINDOWS) | |
| namespace angle | |
| { | |
| const GLColorRGB GLColorRGB::black(0u, 0u, 0u); | |
| const GLColorRGB GLColorRGB::blue(0u, 0u, 255u); | |
| const GLColorRGB GLColorRGB::green(0u, 255u, 0u); | |
| const GLColorRGB GLColorRGB::red(255u, 0u, 0u); | |
| const GLColorRGB GLColorRGB::yellow(255u, 255u, 0); | |
| const GLColor GLColor::black = GLColor(0u, 0u, 0u, 255u); | |
| const GLColor GLColor::blue = GLColor(0u, 0u, 255u, 255u); | |
| const GLColor GLColor::cyan = GLColor(0u, 255u, 255u, 255u); | |
| const GLColor GLColor::green = GLColor(0u, 255u, 0u, 255u); | |
| const GLColor GLColor::red = GLColor(255u, 0u, 0u, 255u); | |
| const GLColor GLColor::transparentBlack = GLColor(0u, 0u, 0u, 0u); | |
| const GLColor GLColor::white = GLColor(255u, 255u, 255u, 255u); | |
| const GLColor GLColor::yellow = GLColor(255u, 255u, 0, 255u); | |
| const GLColor GLColor::magenta = GLColor(255u, 0u, 255u, 255u); | |
| namespace | |
| { | |
| float ColorNorm(GLubyte channelValue) | |
| { | |
| return static_cast<float>(channelValue) / 255.0f; | |
| } | |
| GLubyte ColorDenorm(float colorValue) | |
| { | |
| return static_cast<GLubyte>(colorValue * 255.0f); | |
| } | |
| void TestPlatform_logError(PlatformMethods *platform, const char *errorMessage) | |
| { | |
| auto *testPlatformContext = static_cast<TestPlatformContext *>(platform->context); | |
| if (testPlatformContext->ignoreMessages) | |
| return; | |
| GTEST_NONFATAL_FAILURE_(errorMessage); | |
| PrintStackBacktrace(); | |
| } | |
| void TestPlatform_logWarning(PlatformMethods *platform, const char *warningMessage) | |
| { | |
| auto *testPlatformContext = static_cast<TestPlatformContext *>(platform->context); | |
| if (testPlatformContext->ignoreMessages) | |
| return; | |
| if (testPlatformContext->warningsAsErrors) | |
| { | |
| FAIL() << warningMessage; | |
| } | |
| else | |
| { | |
| std::cerr << "Warning: " << warningMessage << std::endl; | |
| } | |
| } | |
| void TestPlatform_logInfo(PlatformMethods *platform, const char *infoMessage) {} | |
| const std::array<Vector3, 6> kQuadVertices = {{ | |
| Vector3(-1.0f, 1.0f, 0.5f), | |
| Vector3(-1.0f, -1.0f, 0.5f), | |
| Vector3(1.0f, -1.0f, 0.5f), | |
| Vector3(-1.0f, 1.0f, 0.5f), | |
| Vector3(1.0f, -1.0f, 0.5f), | |
| Vector3(1.0f, 1.0f, 0.5f), | |
| }}; | |
| const std::array<Vector3, 4> kIndexedQuadVertices = {{ | |
| Vector3(-1.0f, 1.0f, 0.5f), | |
| Vector3(-1.0f, -1.0f, 0.5f), | |
| Vector3(1.0f, -1.0f, 0.5f), | |
| Vector3(1.0f, 1.0f, 0.5f), | |
| }}; | |
| constexpr std::array<GLushort, 6> kIndexedQuadIndices = {{0, 1, 2, 0, 2, 3}}; | |
| const char *GetColorName(GLColor color) | |
| { | |
| if (color == GLColor::red) | |
| { | |
| return "Red"; | |
| } | |
| if (color == GLColor::green) | |
| { | |
| return "Green"; | |
| } | |
| if (color == GLColor::blue) | |
| { | |
| return "Blue"; | |
| } | |
| if (color == GLColor::white) | |
| { | |
| return "White"; | |
| } | |
| if (color == GLColor::black) | |
| { | |
| return "Black"; | |
| } | |
| if (color == GLColor::transparentBlack) | |
| { | |
| return "Transparent Black"; | |
| } | |
| if (color == GLColor::yellow) | |
| { | |
| return "Yellow"; | |
| } | |
| if (color == GLColor::magenta) | |
| { | |
| return "Magenta"; | |
| } | |
| if (color == GLColor::cyan) | |
| { | |
| return "Cyan"; | |
| } | |
| return nullptr; | |
| } | |
| const char *GetColorName(GLColorRGB color) | |
| { | |
| return GetColorName(GLColor(color.R, color.G, color.B, 255)); | |
| } | |
| // Always re-use displays when using --bot-mode in the test runner. | |
| bool gReuseDisplays = false; | |
| bool ShouldAlwaysForceNewDisplay() | |
| { | |
| if (gReuseDisplays) | |
| return false; | |
| // We prefer to reuse config displays. This is faster and solves a driver issue where creating | |
| // many displays causes crashes. However this exposes other driver bugs on many other platforms. | |
| // Conservatively enable the feature only on Windows Intel and NVIDIA for now. | |
| SystemInfo *systemInfo = GetTestSystemInfo(); | |
| return (!systemInfo || !IsWindows() || systemInfo->hasAMDGPU()); | |
| } | |
| GPUTestConfig::API GetTestConfigAPIFromRenderer(angle::GLESDriverType driverType, | |
| EGLenum renderer, | |
| EGLenum deviceType) | |
| { | |
| if (driverType != angle::GLESDriverType::AngleEGL && | |
| driverType != angle::GLESDriverType::AngleVulkanSecondariesEGL) | |
| { | |
| return GPUTestConfig::kAPIUnknown; | |
| } | |
| switch (renderer) | |
| { | |
| case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE: | |
| return GPUTestConfig::kAPID3D11; | |
| case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE: | |
| return GPUTestConfig::kAPID3D9; | |
| case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE: | |
| return GPUTestConfig::kAPIGLDesktop; | |
| case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: | |
| return GPUTestConfig::kAPIGLES; | |
| case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE: | |
| if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE) | |
| { | |
| return GPUTestConfig::kAPISwiftShader; | |
| } | |
| else | |
| { | |
| return GPUTestConfig::kAPIVulkan; | |
| } | |
| case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE: | |
| return GPUTestConfig::kAPIMetal; | |
| default: | |
| std::cerr << "Unknown Renderer enum: 0x" << std::hex << renderer << "\n"; | |
| return GPUTestConfig::kAPIUnknown; | |
| } | |
| } | |
| } // anonymous namespace | |
| GLColorRGB::GLColorRGB(const Vector3 &floatColor) | |
| : R(ColorDenorm(floatColor.x())), G(ColorDenorm(floatColor.y())), B(ColorDenorm(floatColor.z())) | |
| {} | |
| GLColor::GLColor(const Vector3 &floatColor) | |
| : R(ColorDenorm(floatColor.x())), | |
| G(ColorDenorm(floatColor.y())), | |
| B(ColorDenorm(floatColor.z())), | |
| A(255) | |
| {} | |
| GLColor::GLColor(const Vector4 &floatColor) | |
| : R(ColorDenorm(floatColor.x())), | |
| G(ColorDenorm(floatColor.y())), | |
| B(ColorDenorm(floatColor.z())), | |
| A(ColorDenorm(floatColor.w())) | |
| {} | |
| GLColor::GLColor(GLuint colorValue) : R(0), G(0), B(0), A(0) | |
| { | |
| memcpy(&R, &colorValue, sizeof(GLuint)); | |
| } | |
| GLuint GLColor::asUint() const | |
| { | |
| GLuint uint = 0; | |
| memcpy(&uint, &R, sizeof(GLuint)); | |
| return uint; | |
| } | |
| testing::AssertionResult GLColor::ExpectNear(const GLColor &expected, const GLColor &err) const | |
| { | |
| testing::AssertionResult result( | |
| abs(int(expected.R) - this->R) <= err.R && abs(int(expected.G) - this->G) <= err.G && | |
| abs(int(expected.B) - this->B) <= err.B && abs(int(expected.A) - this->A) <= err.A); | |
| if (!bool(result)) | |
| { | |
| result << "Expected " << expected << "+/-" << err << ", was " << *this; | |
| } | |
| return result; | |
| } | |
| void CreatePixelCenterWindowCoords(const std::vector<Vector2> &pixelPoints, | |
| int windowWidth, | |
| int windowHeight, | |
| std::vector<Vector3> *outVertices) | |
| { | |
| for (Vector2 pixelPoint : pixelPoints) | |
| { | |
| outVertices->emplace_back(Vector3((pixelPoint[0] + 0.5f) * 2.0f / windowWidth - 1.0f, | |
| (pixelPoint[1] + 0.5f) * 2.0f / windowHeight - 1.0f, | |
| 0.0f)); | |
| } | |
| } | |
| Vector4 GLColor::toNormalizedVector() const | |
| { | |
| return Vector4(ColorNorm(R), ColorNorm(G), ColorNorm(B), ColorNorm(A)); | |
| } | |
| GLColor RandomColor(angle::RNG *rng) | |
| { | |
| return GLColor(rng->randomIntBetween(0, 255), rng->randomIntBetween(0, 255), | |
| rng->randomIntBetween(0, 255), rng->randomIntBetween(0, 255)); | |
| } | |
| GLColor ReadColor(GLint x, GLint y) | |
| { | |
| GLColor actual; | |
| glReadPixels((x), (y), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &actual.R); | |
| EXPECT_GL_NO_ERROR(); | |
| return actual; | |
| } | |
| bool operator==(const GLColor &a, const GLColor &b) | |
| { | |
| return a.R == b.R && a.G == b.G && a.B == b.B && a.A == b.A; | |
| } | |
| bool operator!=(const GLColor &a, const GLColor &b) | |
| { | |
| return !(a == b); | |
| } | |
| std::ostream &operator<<(std::ostream &ostream, const GLColor &color) | |
| { | |
| const char *colorName = GetColorName(color); | |
| if (colorName) | |
| { | |
| return ostream << colorName; | |
| } | |
| ostream << "(" << static_cast<unsigned int>(color.R) << ", " | |
| << static_cast<unsigned int>(color.G) << ", " << static_cast<unsigned int>(color.B) | |
| << ", " << static_cast<unsigned int>(color.A) << ")"; | |
| return ostream; | |
| } | |
| bool operator==(const GLColorRGB &a, const GLColorRGB &b) | |
| { | |
| return a.R == b.R && a.G == b.G && a.B == b.B; | |
| } | |
| bool operator!=(const GLColorRGB &a, const GLColorRGB &b) | |
| { | |
| return !(a == b); | |
| } | |
| std::ostream &operator<<(std::ostream &ostream, const GLColorRGB &color) | |
| { | |
| const char *colorName = GetColorName(color); | |
| if (colorName) | |
| { | |
| return ostream << colorName; | |
| } | |
| ostream << "(" << static_cast<unsigned int>(color.R) << ", " | |
| << static_cast<unsigned int>(color.G) << ", " << static_cast<unsigned int>(color.B) | |
| << ")"; | |
| return ostream; | |
| } | |
| std::ostream &operator<<(std::ostream &ostream, const GLColor32F &color) | |
| { | |
| ostream << "(" << color.R << ", " << color.G << ", " << color.B << ", " << color.A << ")"; | |
| return ostream; | |
| } | |
| GLColor32F ReadColor32F(GLint x, GLint y) | |
| { | |
| GLColor32F actual; | |
| glReadPixels((x), (y), 1, 1, GL_RGBA, GL_FLOAT, &actual.R); | |
| EXPECT_GL_NO_ERROR(); | |
| return actual; | |
| } | |
| void LoadEntryPointsWithUtilLoader(angle::GLESDriverType driverType) | |
| { | |
| #if defined(ANGLE_USE_UTIL_LOADER) | |
| PFNEGLGETPROCADDRESSPROC getProcAddress; | |
| ANGLETestEnvironment::GetDriverLibrary(driverType)->getAs("eglGetProcAddress", &getProcAddress); | |
| ASSERT(nullptr != getProcAddress); | |
| LoadUtilEGL(getProcAddress); | |
| LoadUtilGLES(getProcAddress); | |
| #endif // defined(ANGLE_USE_UTIL_LOADER) | |
| } | |
| bool IsFormatEmulated(GLenum target) | |
| { | |
| GLint readFormat; | |
| glGetTexParameteriv(target, GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat); | |
| // This helper only works for compressed formats | |
| return gl::IsEmulatedCompressedFormat(readFormat); | |
| } | |
| } // namespace angle | |
| using namespace angle; | |
| PlatformMethods gDefaultPlatformMethods; | |
| namespace | |
| { | |
| TestPlatformContext gPlatformContext; | |
| // After a fixed number of iterations we reset the test window. This works around some driver bugs. | |
| constexpr uint32_t kWindowReuseLimit = 50; | |
| constexpr char kUseConfig[] = "--use-config="; | |
| constexpr char kReuseDisplays[] = "--reuse-displays"; | |
| constexpr char kEnableANGLEPerTestCaptureLabel[] = "--angle-per-test-capture-label"; | |
| constexpr char kBatchId[] = "--batch-id="; | |
| constexpr char kDelayTestStart[] = "--delay-test-start="; | |
| constexpr char kRenderDoc[] = "--renderdoc"; | |
| constexpr char kNoRenderDoc[] = "--no-renderdoc"; | |
| void SetupEnvironmentVarsForCaptureReplay() | |
| { | |
| const ::testing::TestInfo *const testInfo = | |
| ::testing::UnitTest::GetInstance()->current_test_info(); | |
| std::string testName = std::string{testInfo->name()}; | |
| std::replace(testName.begin(), testName.end(), '/', '_'); | |
| SetEnvironmentVar("ANGLE_CAPTURE_LABEL", | |
| (std::string{testInfo->test_case_name()} + "_" + testName).c_str()); | |
| } | |
| } // anonymous namespace | |
| int gTestStartDelaySeconds = 0; | |
| int GetTestStartDelaySeconds() | |
| { | |
| return gTestStartDelaySeconds; | |
| } | |
| void SetTestStartDelay(const char *testStartDelay) | |
| { | |
| gTestStartDelaySeconds = std::stoi(testStartDelay); | |
| } | |
| #if defined(ANGLE_TEST_ENABLE_RENDERDOC_CAPTURE) | |
| bool gEnableRenderDocCapture = true; | |
| #else | |
| bool gEnableRenderDocCapture = false; | |
| #endif | |
| // static | |
| std::array<Vector3, 6> ANGLETestBase::GetQuadVertices() | |
| { | |
| return kQuadVertices; | |
| } | |
| // static | |
| std::array<GLushort, 6> ANGLETestBase::GetQuadIndices() | |
| { | |
| return kIndexedQuadIndices; | |
| } | |
| // static | |
| std::array<Vector3, 4> ANGLETestBase::GetIndexedQuadVertices() | |
| { | |
| return kIndexedQuadVertices; | |
| } | |
| testing::AssertionResult AssertEGLEnumsEqual(const char *lhsExpr, | |
| const char *rhsExpr, | |
| EGLenum lhs, | |
| EGLenum rhs) | |
| { | |
| if (lhs == rhs) | |
| { | |
| return testing::AssertionSuccess(); | |
| } | |
| else | |
| { | |
| std::stringstream strstr; | |
| strstr << std::hex << lhsExpr << " (0x" << int(lhs) << ") != " << rhsExpr << " (0x" | |
| << int(rhs) << ")"; | |
| return testing::AssertionFailure() << strstr.str(); | |
| } | |
| } | |
| ANGLETestBase::ANGLETestBase(const PlatformParameters ¶ms) | |
| : mWidth(16), | |
| mHeight(16), | |
| mIgnoreD3D11SDKLayersWarnings(false), | |
| mQuadVertexBuffer(0), | |
| mQuadIndexBuffer(0), | |
| m2DTexturedQuadProgram(0), | |
| m3DTexturedQuadProgram(0), | |
| mDeferContextInit(false), | |
| mAlwaysForceNewDisplay(ShouldAlwaysForceNewDisplay()), | |
| mForceNewDisplay(mAlwaysForceNewDisplay), | |
| mSetUpCalled(false), | |
| mTearDownCalled(false), | |
| mCurrentParams(nullptr), | |
| mFixture(nullptr) | |
| { | |
| // Override the default platform methods with the ANGLE test methods pointer. | |
| PlatformParameters withMethods = params; | |
| withMethods.eglParameters.platformMethods = &gDefaultPlatformMethods; | |
| if (withMethods.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) | |
| { | |
| #if defined(ANGLE_ENABLE_VULKAN_VALIDATION_LAYERS) | |
| withMethods.eglParameters.debugLayersEnabled = true; | |
| #else | |
| withMethods.eglParameters.debugLayersEnabled = false; | |
| #endif | |
| } | |
| if (gEnableRenderDocCapture) | |
| { | |
| mRenderDoc.attach(); | |
| } | |
| auto iter = gFixtures.find(withMethods); | |
| if (iter != gFixtures.end()) | |
| { | |
| mCurrentParams = &iter->first; | |
| if (!params.noFixture) | |
| { | |
| mFixture = &iter->second; | |
| mFixture->configParams.reset(); | |
| } | |
| return; | |
| } | |
| TestFixture platform; | |
| auto insertIter = gFixtures.emplace(withMethods, platform); | |
| mCurrentParams = &insertIter.first->first; | |
| if (!params.noFixture) | |
| { | |
| mFixture = &insertIter.first->second; | |
| initOSWindow(); | |
| } | |
| } | |
| void ANGLETestBase::initOSWindow() | |
| { | |
| std::stringstream windowNameStream; | |
| windowNameStream << "ANGLE Tests - " << *mCurrentParams; | |
| std::string windowName = windowNameStream.str(); | |
| if (IsAndroid()) | |
| { | |
| // Only one window per test application on Android, shared among all fixtures | |
| mFixture->osWindow = mOSWindowSingleton; | |
| } | |
| if (mFixture->osWindow == nullptr) | |
| { | |
| mFixture->osWindow = OSWindow::New(); | |
| if (mFixture->osWindow == nullptr) | |
| { | |
| FATAL() << "Failed to create a new window"; | |
| } | |
| mFixture->osWindow->disableErrorMessageDialog(); | |
| if (!mFixture->osWindow->initialize(windowName.c_str(), 128, 128)) | |
| { | |
| std::cerr << "Failed to initialize OS Window.\n"; | |
| } | |
| if (IsAndroid()) | |
| { | |
| // Initialize the single window on Andoird only once | |
| mOSWindowSingleton = mFixture->osWindow; | |
| } | |
| } | |
| if (!mFixture->osWindow->valid()) | |
| { | |
| return; | |
| } | |
| // On Linux we must keep the test windows visible. On Windows it doesn't seem to need it. | |
| setWindowVisible(getOSWindow(), !IsWindows()); | |
| switch (mCurrentParams->driver) | |
| { | |
| case GLESDriverType::AngleEGL: | |
| case GLESDriverType::AngleVulkanSecondariesEGL: | |
| case GLESDriverType::SystemEGL: | |
| case GLESDriverType::ZinkEGL: | |
| { | |
| mFixture->eglWindow = | |
| EGLWindow::New(mCurrentParams->clientType, mCurrentParams->majorVersion, | |
| mCurrentParams->minorVersion, mCurrentParams->profileMask); | |
| break; | |
| } | |
| case GLESDriverType::SystemWGL: | |
| { | |
| // WGL tests are currently disabled. | |
| std::cerr << "Unsupported driver." << std::endl; | |
| break; | |
| } | |
| } | |
| } | |
| ANGLETestBase::~ANGLETestBase() | |
| { | |
| if (mQuadVertexBuffer) | |
| { | |
| glDeleteBuffers(1, &mQuadVertexBuffer); | |
| } | |
| if (mQuadIndexBuffer) | |
| { | |
| glDeleteBuffers(1, &mQuadIndexBuffer); | |
| } | |
| if (m2DTexturedQuadProgram) | |
| { | |
| glDeleteProgram(m2DTexturedQuadProgram); | |
| } | |
| if (m3DTexturedQuadProgram) | |
| { | |
| glDeleteProgram(m3DTexturedQuadProgram); | |
| } | |
| if (!mSetUpCalled) | |
| { | |
| GTEST_NONFATAL_FAILURE_("SetUp not called."); | |
| } | |
| if (!mTearDownCalled) | |
| { | |
| GTEST_NONFATAL_FAILURE_("TearDown not called."); | |
| } | |
| } | |
| void ANGLETestBase::ANGLETestSetUp() | |
| { | |
| mSetUpCalled = true; | |
| // Delay test startup to allow a debugger to attach. | |
| if (GetTestStartDelaySeconds()) | |
| { | |
| angle::Sleep(GetTestStartDelaySeconds() * 1000); | |
| } | |
| gDefaultPlatformMethods.logError = TestPlatform_logError; | |
| gDefaultPlatformMethods.logWarning = TestPlatform_logWarning; | |
| gDefaultPlatformMethods.logInfo = TestPlatform_logInfo; | |
| gDefaultPlatformMethods.context = &gPlatformContext; | |
| gPlatformContext.ignoreMessages = false; | |
| gPlatformContext.warningsAsErrors = false; | |
| gPlatformContext.currentTest = this; | |
| const testing::TestInfo *testInfo = testing::UnitTest::GetInstance()->current_test_info(); | |
| // Check the skip list. | |
| angle::GPUTestConfig::API api = GetTestConfigAPIFromRenderer( | |
| mCurrentParams->driver, mCurrentParams->getRenderer(), mCurrentParams->getDeviceType()); | |
| GPUTestConfig testConfig = GPUTestConfig(api, 0); | |
| std::stringstream fullTestNameStr; | |
| fullTestNameStr << testInfo->test_case_name() << "." << testInfo->name(); | |
| std::string fullTestName = fullTestNameStr.str(); | |
| TestSuite *testSuite = TestSuite::GetInstance(); | |
| int32_t testExpectation = | |
| testSuite->getTestExpectationWithConfigAndUpdateTimeout(testConfig, fullTestName); | |
| if (testExpectation == GPUTestExpectationsParser::kGpuTestSkip) | |
| { | |
| GTEST_SKIP() << "Test skipped on this config"; | |
| } | |
| if (IsWindows()) | |
| { | |
| WriteDebugMessage("Entering %s\n", fullTestName.c_str()); | |
| } | |
| if (mCurrentParams->noFixture) | |
| { | |
| LoadEntryPointsWithUtilLoader(mCurrentParams->driver); | |
| mIsSetUp = true; | |
| return; | |
| } | |
| if (mLastLoadedDriver.valid() && mCurrentParams->driver != mLastLoadedDriver.value()) | |
| { | |
| LoadEntryPointsWithUtilLoader(mCurrentParams->driver); | |
| mLastLoadedDriver = mCurrentParams->driver; | |
| } | |
| if (gEnableANGLEPerTestCaptureLabel) | |
| { | |
| SetupEnvironmentVarsForCaptureReplay(); | |
| } | |
| if (!mFixture->osWindow->valid()) | |
| { | |
| mIsSetUp = true; | |
| return; | |
| } | |
| // Resize the window before creating the context so that the first make current | |
| // sets the viewport and scissor box to the right size. | |
| bool needSwap = false; | |
| int osWindowWidth = mFixture->osWindow->getWidth(); | |
| int osWindowHeight = mFixture->osWindow->getHeight(); | |
| const bool isRotated = mCurrentParams->isEnableRequested(Feature::EmulatedPrerotation90) || | |
| mCurrentParams->isEnableRequested(Feature::EmulatedPrerotation270); | |
| if (isRotated) | |
| { | |
| std::swap(osWindowWidth, osWindowHeight); | |
| } | |
| if (osWindowWidth != mWidth || osWindowHeight != mHeight) | |
| { | |
| int newWindowWidth = mWidth; | |
| int newWindowHeight = mHeight; | |
| if (isRotated) | |
| { | |
| std::swap(newWindowWidth, newWindowHeight); | |
| } | |
| if (!mFixture->osWindow->resize(newWindowWidth, newWindowHeight)) | |
| { | |
| FAIL() << "Failed to resize ANGLE test window."; | |
| } | |
| needSwap = true; | |
| } | |
| // WGL tests are currently disabled. | |
| if (mFixture->wglWindow) | |
| { | |
| FAIL() << "Unsupported driver."; | |
| } | |
| else | |
| { | |
| Library *driverLib = ANGLETestEnvironment::GetDriverLibrary(mCurrentParams->driver); | |
| if (mForceNewDisplay || !mFixture->eglWindow->isDisplayInitialized()) | |
| { | |
| mFixture->eglWindow->destroyGL(); | |
| if (!mFixture->eglWindow->initializeDisplay(mFixture->osWindow, driverLib, | |
| mCurrentParams->driver, | |
| mCurrentParams->eglParameters)) | |
| { | |
| FAIL() << "EGL Display init failed."; | |
| } | |
| } | |
| else if (mCurrentParams->eglParameters != mFixture->eglWindow->getPlatform()) | |
| { | |
| FAIL() << "Internal parameter conflict error."; | |
| } | |
| const GLWindowResult windowResult = mFixture->eglWindow->initializeSurface( | |
| mFixture->osWindow, driverLib, mFixture->configParams); | |
| if (windowResult != GLWindowResult::NoError) | |
| { | |
| if (windowResult != GLWindowResult::Error) | |
| { | |
| // If the test requests an extension that isn't supported, automatically skip the | |
| // test. | |
| GTEST_SKIP() << "Test skipped due to missing extension"; | |
| } | |
| else if (mFixture->configParams.multisample) | |
| { | |
| // If the test requests a multisampled window that isn't supported, automatically | |
| // skip the test. | |
| GTEST_SKIP() << "Test skipped due to no multisampled configs available"; | |
| } | |
| else | |
| { | |
| // Otherwise fail the test. | |
| FAIL() << "egl surface init failed."; | |
| } | |
| } | |
| if (!mDeferContextInit && !mFixture->eglWindow->initializeContext()) | |
| { | |
| FAIL() << "GL Context init failed."; | |
| } | |
| } | |
| if (needSwap) | |
| { | |
| // Swap the buffers so that the default framebuffer picks up the resize | |
| // which will allow follow-up test code to assume the framebuffer covers | |
| // the whole window. | |
| swapBuffers(); | |
| } | |
| // This Viewport command is not strictly necessary but we add it so that programs | |
| // taking OpenGL traces can guess the size of the default framebuffer and show it | |
| // in their UIs | |
| glViewport(0, 0, mWidth, mHeight); | |
| mIsSetUp = true; | |
| mRenderDoc.startFrame(); | |
| } | |
| void ANGLETestBase::ANGLETestPreTearDown() | |
| { | |
| // We swap an extra time before we call "tearDown" to capture resources before they're freed. | |
| if (gEnableANGLEPerTestCaptureLabel) | |
| { | |
| swapBuffers(); | |
| } | |
| } | |
| void ANGLETestBase::ANGLETestTearDown() | |
| { | |
| mTearDownCalled = true; | |
| gPlatformContext.currentTest = nullptr; | |
| if (IsWindows()) | |
| { | |
| const testing::TestInfo *info = testing::UnitTest::GetInstance()->current_test_info(); | |
| WriteDebugMessage("Exiting %s.%s\n", info->test_case_name(), info->name()); | |
| } | |
| if (mCurrentParams->noFixture || !mFixture->osWindow->valid()) | |
| { | |
| mRenderDoc.endFrame(); | |
| return; | |
| } | |
| swapBuffers(); | |
| mFixture->osWindow->messageLoop(); | |
| mRenderDoc.endFrame(); | |
| if (mFixture->eglWindow) | |
| { | |
| checkD3D11SDKLayersMessages(); | |
| } | |
| if (mFixture->reuseCounter++ >= kWindowReuseLimit || mForceNewDisplay) | |
| { | |
| if (!mForceNewDisplay) | |
| { | |
| printf("Recreating test window because of reuse limit of %d\n", kWindowReuseLimit); | |
| } | |
| mFixture->reuseCounter = 0; | |
| getGLWindow()->destroyGL(); | |
| } | |
| else | |
| { | |
| mFixture->eglWindow->destroyContext(); | |
| mFixture->eglWindow->destroySurface(); | |
| } | |
| Event myEvent; | |
| while (mFixture->osWindow->popEvent(&myEvent)) | |
| { | |
| if (myEvent.Type == Event::EVENT_CLOSED) | |
| { | |
| exit(0); | |
| } | |
| } | |
| } | |
| void ANGLETestBase::ReleaseFixtures() | |
| { | |
| for (auto it = gFixtures.begin(); it != gFixtures.end(); it++) | |
| { | |
| TestFixture &fixture = it->second; | |
| if (fixture.eglWindow != nullptr) | |
| { | |
| fixture.eglWindow->destroyGL(); | |
| EGLWindow::Delete(&fixture.eglWindow); | |
| } | |
| if (IsAndroid()) | |
| { | |
| if (mOSWindowSingleton != nullptr) | |
| { | |
| OSWindow::Delete(&mOSWindowSingleton); | |
| } | |
| fixture.osWindow = nullptr; | |
| } | |
| else | |
| { | |
| if (fixture.osWindow != nullptr) | |
| { | |
| OSWindow::Delete(&fixture.osWindow); | |
| } | |
| } | |
| } | |
| gFixtures.clear(); | |
| } | |
| void ANGLETestBase::swapBuffers() | |
| { | |
| if (getGLWindow()->isGLInitialized()) | |
| { | |
| getGLWindow()->swap(); | |
| if (mFixture->eglWindow) | |
| { | |
| EXPECT_EGL_SUCCESS(); | |
| } | |
| } | |
| } | |
| void ANGLETestBase::setupQuadVertexBuffer(GLfloat positionAttribZ, GLfloat positionAttribXYScale) | |
| { | |
| if (mQuadVertexBuffer == 0) | |
| { | |
| glGenBuffers(1, &mQuadVertexBuffer); | |
| } | |
| auto quadVertices = GetQuadVertices(); | |
| for (Vector3 &vertex : quadVertices) | |
| { | |
| vertex.x() *= positionAttribXYScale; | |
| vertex.y() *= positionAttribXYScale; | |
| vertex.z() = positionAttribZ; | |
| } | |
| glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer); | |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW); | |
| } | |
| void ANGLETestBase::setupIndexedQuadVertexBuffer(GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale) | |
| { | |
| if (mQuadVertexBuffer == 0) | |
| { | |
| glGenBuffers(1, &mQuadVertexBuffer); | |
| } | |
| auto quadVertices = kIndexedQuadVertices; | |
| for (Vector3 &vertex : quadVertices) | |
| { | |
| vertex.x() *= positionAttribXYScale; | |
| vertex.y() *= positionAttribXYScale; | |
| vertex.z() = positionAttribZ; | |
| } | |
| glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer); | |
| glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 4, quadVertices.data(), GL_STATIC_DRAW); | |
| } | |
| void ANGLETestBase::setupIndexedQuadIndexBuffer() | |
| { | |
| if (mQuadIndexBuffer == 0) | |
| { | |
| glGenBuffers(1, &mQuadIndexBuffer); | |
| } | |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadIndexBuffer); | |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndexedQuadIndices), kIndexedQuadIndices.data(), | |
| GL_STATIC_DRAW); | |
| } | |
| // static | |
| void ANGLETestBase::drawQuad(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ) | |
| { | |
| drawQuad(program, positionAttribName, positionAttribZ, 1.0f); | |
| } | |
| // static | |
| void ANGLETestBase::drawQuad(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale) | |
| { | |
| drawQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, false); | |
| } | |
| void ANGLETestBase::drawQuad(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale, | |
| bool useVertexBuffer) | |
| { | |
| drawQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, useVertexBuffer, | |
| false, false, 0u); | |
| } | |
| void ANGLETestBase::drawQuadInstanced(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale, | |
| bool useVertexBuffer, | |
| GLuint numInstances) | |
| { | |
| drawQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, useVertexBuffer, | |
| true, false, numInstances); | |
| } | |
| void ANGLETestBase::drawPatches(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale, | |
| bool useVertexBuffer) | |
| { | |
| drawQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, useVertexBuffer, | |
| false, true, 0u); | |
| } | |
| void ANGLETestBase::drawQuad(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale, | |
| bool useVertexBuffer, | |
| bool useInstancedDrawCalls, | |
| bool useTessellationPatches, | |
| GLuint numInstances) | |
| { | |
| GLint previousProgram = 0; | |
| glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgram); | |
| if (previousProgram != static_cast<GLint>(program)) | |
| { | |
| glUseProgram(program); | |
| } | |
| GLint previousBuffer = 0; | |
| glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &previousBuffer); | |
| GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str()); | |
| std::array<Vector3, 6> quadVertices = GetQuadVertices(); | |
| if (useVertexBuffer) | |
| { | |
| setupQuadVertexBuffer(positionAttribZ, positionAttribXYScale); | |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); | |
| glBindBuffer(GL_ARRAY_BUFFER, previousBuffer); | |
| } | |
| else | |
| { | |
| for (Vector3 &vertex : quadVertices) | |
| { | |
| vertex.x() *= positionAttribXYScale; | |
| vertex.y() *= positionAttribXYScale; | |
| vertex.z() = positionAttribZ; | |
| } | |
| if (previousBuffer != 0) | |
| { | |
| glBindBuffer(GL_ARRAY_BUFFER, 0); | |
| } | |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data()); | |
| if (previousBuffer != 0) | |
| { | |
| glBindBuffer(GL_ARRAY_BUFFER, previousBuffer); | |
| } | |
| } | |
| glEnableVertexAttribArray(positionLocation); | |
| GLenum drawMode = (useTessellationPatches) ? GL_PATCHES : GL_TRIANGLES; | |
| if (useInstancedDrawCalls) | |
| { | |
| glDrawArraysInstanced(drawMode, 0, 6, numInstances); | |
| } | |
| else | |
| { | |
| glDrawArrays(drawMode, 0, 6); | |
| } | |
| glDisableVertexAttribArray(positionLocation); | |
| glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr); | |
| if (previousProgram != static_cast<GLint>(program)) | |
| { | |
| glUseProgram(previousProgram); | |
| } | |
| } | |
| void ANGLETestBase::drawQuadPPO(GLuint vertProgram, | |
| const std::string &positionAttribName, | |
| const GLfloat positionAttribZ, | |
| const GLfloat positionAttribXYScale) | |
| { | |
| GLint activeProgram = 0; | |
| glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgram); | |
| if (activeProgram) | |
| { | |
| glUseProgram(0); | |
| } | |
| std::array<Vector3, 6> quadVertices = GetQuadVertices(); | |
| for (Vector3 &vertex : quadVertices) | |
| { | |
| vertex.x() *= positionAttribXYScale; | |
| vertex.y() *= positionAttribXYScale; | |
| vertex.z() = positionAttribZ; | |
| } | |
| GLint positionLocation = glGetAttribLocation(vertProgram, positionAttribName.c_str()); | |
| glBindBuffer(GL_ARRAY_BUFFER, 0); | |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data()); | |
| glEnableVertexAttribArray(positionLocation); | |
| glDrawArrays(GL_TRIANGLES, 0, 6); | |
| glDisableVertexAttribArray(positionLocation); | |
| glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr); | |
| if (activeProgram) | |
| { | |
| glUseProgram(static_cast<GLuint>(activeProgram)); | |
| } | |
| } | |
| void ANGLETestBase::drawIndexedQuad(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ) | |
| { | |
| drawIndexedQuad(program, positionAttribName, positionAttribZ, 1.0f); | |
| } | |
| void ANGLETestBase::drawIndexedQuad(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale) | |
| { | |
| ASSERT(!mFixture || !mFixture->configParams.webGLCompatibility.valid() || | |
| !mFixture->configParams.webGLCompatibility.value()); | |
| drawIndexedQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, false); | |
| } | |
| void ANGLETestBase::drawIndexedQuad(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale, | |
| bool useIndexBuffer) | |
| { | |
| drawIndexedQuad(program, positionAttribName, positionAttribZ, positionAttribXYScale, | |
| useIndexBuffer, false); | |
| } | |
| void ANGLETestBase::drawIndexedQuad(GLuint program, | |
| const std::string &positionAttribName, | |
| GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale, | |
| bool useIndexBuffer, | |
| bool restrictedRange) | |
| { | |
| GLint positionLocation = glGetAttribLocation(program, positionAttribName.c_str()); | |
| GLint activeProgram = 0; | |
| glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgram); | |
| if (static_cast<GLuint>(activeProgram) != program) | |
| { | |
| glUseProgram(program); | |
| } | |
| GLuint prevCoordBinding = 0; | |
| glGetIntegerv(GL_ARRAY_BUFFER_BINDING, reinterpret_cast<GLint *>(&prevCoordBinding)); | |
| setupIndexedQuadVertexBuffer(positionAttribZ, positionAttribXYScale); | |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr); | |
| glEnableVertexAttribArray(positionLocation); | |
| glBindBuffer(GL_ARRAY_BUFFER, prevCoordBinding); | |
| GLuint prevIndexBinding = 0; | |
| const GLvoid *indices; | |
| if (useIndexBuffer) | |
| { | |
| glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, | |
| reinterpret_cast<GLint *>(&prevIndexBinding)); | |
| setupIndexedQuadIndexBuffer(); | |
| indices = 0; | |
| } | |
| else | |
| { | |
| indices = kIndexedQuadIndices.data(); | |
| } | |
| if (!restrictedRange) | |
| { | |
| glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); | |
| } | |
| else | |
| { | |
| glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, indices); | |
| } | |
| if (useIndexBuffer) | |
| { | |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prevIndexBinding); | |
| } | |
| glDisableVertexAttribArray(positionLocation); | |
| glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr); | |
| if (static_cast<GLuint>(activeProgram) != program) | |
| { | |
| glUseProgram(static_cast<GLuint>(activeProgram)); | |
| } | |
| } | |
| GLuint ANGLETestBase::get2DTexturedQuadProgram() | |
| { | |
| if (m2DTexturedQuadProgram) | |
| { | |
| return m2DTexturedQuadProgram; | |
| } | |
| constexpr char kVS[] = | |
| "attribute vec2 position;\n" | |
| "varying mediump vec2 texCoord;\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_Position = vec4(position, 0, 1);\n" | |
| " texCoord = position * 0.5 + vec2(0.5);\n" | |
| "}\n"; | |
| constexpr char kFS[] = | |
| "varying mediump vec2 texCoord;\n" | |
| "uniform sampler2D tex;\n" | |
| "void main()\n" | |
| "{\n" | |
| " gl_FragColor = texture2D(tex, texCoord);\n" | |
| "}\n"; | |
| m2DTexturedQuadProgram = CompileProgram(kVS, kFS); | |
| return m2DTexturedQuadProgram; | |
| } | |
| GLuint ANGLETestBase::get3DTexturedQuadProgram() | |
| { | |
| if (m3DTexturedQuadProgram) | |
| { | |
| return m3DTexturedQuadProgram; | |
| } | |
| constexpr char kVS[] = R"(#version 300 es | |
| in vec2 position; | |
| out vec2 texCoord; | |
| void main() | |
| { | |
| gl_Position = vec4(position, 0, 1); | |
| texCoord = position * 0.5 + vec2(0.5); | |
| })"; | |
| constexpr char kFS[] = R"(#version 300 es | |
| precision highp float; | |
| in vec2 texCoord; | |
| out vec4 my_FragColor; | |
| uniform highp sampler3D tex; | |
| uniform float u_layer; | |
| void main() | |
| { | |
| my_FragColor = texture(tex, vec3(texCoord, u_layer)); | |
| })"; | |
| m3DTexturedQuadProgram = CompileProgram(kVS, kFS); | |
| return m3DTexturedQuadProgram; | |
| } | |
| void ANGLETestBase::draw2DTexturedQuad(GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale, | |
| bool useVertexBuffer) | |
| { | |
| ASSERT_NE(0u, get2DTexturedQuadProgram()); | |
| drawQuad(get2DTexturedQuadProgram(), "position", positionAttribZ, positionAttribXYScale, | |
| useVertexBuffer); | |
| } | |
| void ANGLETestBase::draw3DTexturedQuad(GLfloat positionAttribZ, | |
| GLfloat positionAttribXYScale, | |
| bool useVertexBuffer, | |
| float layer) | |
| { | |
| GLuint program = get3DTexturedQuadProgram(); | |
| ASSERT_NE(0u, program); | |
| GLint activeProgram = 0; | |
| glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgram); | |
| if (static_cast<GLuint>(activeProgram) != program) | |
| { | |
| glUseProgram(program); | |
| } | |
| glUniform1f(glGetUniformLocation(program, "u_layer"), layer); | |
| drawQuad(program, "position", positionAttribZ, positionAttribXYScale, useVertexBuffer); | |
| if (static_cast<GLuint>(activeProgram) != program) | |
| { | |
| glUseProgram(static_cast<GLuint>(activeProgram)); | |
| } | |
| } | |
| bool ANGLETestBase::platformSupportsMultithreading() const | |
| { | |
| return (mFixture && mFixture->eglWindow && | |
| IsEGLDisplayExtensionEnabled(mFixture->eglWindow->getDisplay(), | |
| "EGL_ANGLE_context_virtualization")) || | |
| IsVulkan(); | |
| } | |
| void ANGLETestBase::checkD3D11SDKLayersMessages() | |
| { | |
| #if defined(ANGLE_ENABLE_D3D11) | |
| // On Windows D3D11, check ID3D11InfoQueue to see if any D3D11 SDK Layers messages | |
| // were outputted by the test. We enable the Debug layers in Release tests as well. | |
| if (mIgnoreD3D11SDKLayersWarnings || | |
| mFixture->eglWindow->getPlatform().renderer != EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE || | |
| mFixture->eglWindow->getDisplay() == EGL_NO_DISPLAY) | |
| { | |
| return; | |
| } | |
| const char *extensionString = static_cast<const char *>( | |
| eglQueryString(mFixture->eglWindow->getDisplay(), EGL_EXTENSIONS)); | |
| if (!extensionString) | |
| { | |
| std::cout << "Error getting extension string from EGL Window." << std::endl; | |
| return; | |
| } | |
| if (!strstr(extensionString, "EGL_EXT_device_query")) | |
| { | |
| return; | |
| } | |
| EGLAttrib device = 0; | |
| EGLAttrib angleDevice = 0; | |
| ASSERT_EGL_TRUE( | |
| eglQueryDisplayAttribEXT(mFixture->eglWindow->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); | |
| ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice), | |
| EGL_D3D11_DEVICE_ANGLE, &device)); | |
| ID3D11Device *d3d11Device = reinterpret_cast<ID3D11Device *>(device); | |
| ID3D11InfoQueue *infoQueue = nullptr; | |
| HRESULT hr = | |
| d3d11Device->QueryInterface(__uuidof(infoQueue), reinterpret_cast<void **>(&infoQueue)); | |
| if (SUCCEEDED(hr)) | |
| { | |
| UINT64 numStoredD3DDebugMessages = | |
| infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); | |
| if (numStoredD3DDebugMessages > 0) | |
| { | |
| for (UINT64 i = 0; i < numStoredD3DDebugMessages; i++) | |
| { | |
| SIZE_T messageLength = 0; | |
| hr = infoQueue->GetMessage(i, nullptr, &messageLength); | |
| if (SUCCEEDED(hr)) | |
| { | |
| D3D11_MESSAGE *pMessage = | |
| reinterpret_cast<D3D11_MESSAGE *>(malloc(messageLength)); | |
| infoQueue->GetMessage(i, pMessage, &messageLength); | |
| std::cout << "Message " << i << ":" | |
| << " " << pMessage->pDescription << "\n"; | |
| free(pMessage); | |
| } | |
| } | |
| // Clear the queue, so that previous failures are not reported | |
| // for subsequent, otherwise passing, tests | |
| infoQueue->ClearStoredMessages(); | |
| FAIL() << numStoredD3DDebugMessages | |
| << " D3D11 SDK Layers message(s) detected! Test Failed.\n"; | |
| } | |
| } | |
| SafeRelease(infoQueue); | |
| #endif // defined(ANGLE_ENABLE_D3D11) | |
| } | |
| void ANGLETestBase::setWindowWidth(int width) | |
| { | |
| mWidth = width; | |
| } | |
| void ANGLETestBase::setWindowHeight(int height) | |
| { | |
| mHeight = height; | |
| } | |
| GLWindowBase *ANGLETestBase::getGLWindow() const | |
| { | |
| // WGL tests are currently disabled. | |
| assert(!mFixture->wglWindow); | |
| return mFixture->eglWindow; | |
| } | |
| void ANGLETestBase::setConfigRedBits(int bits) | |
| { | |
| mFixture->configParams.redBits = bits; | |
| } | |
| void ANGLETestBase::setConfigGreenBits(int bits) | |
| { | |
| mFixture->configParams.greenBits = bits; | |
| } | |
| void ANGLETestBase::setConfigBlueBits(int bits) | |
| { | |
| mFixture->configParams.blueBits = bits; | |
| } | |
| void ANGLETestBase::setConfigAlphaBits(int bits) | |
| { | |
| mFixture->configParams.alphaBits = bits; | |
| } | |
| void ANGLETestBase::setConfigDepthBits(int bits) | |
| { | |
| mFixture->configParams.depthBits = bits; | |
| } | |
| void ANGLETestBase::setConfigStencilBits(int bits) | |
| { | |
| mFixture->configParams.stencilBits = bits; | |
| } | |
| void ANGLETestBase::setConfigComponentType(EGLenum componentType) | |
| { | |
| mFixture->configParams.componentType = componentType; | |
| } | |
| void ANGLETestBase::setMultisampleEnabled(bool enabled) | |
| { | |
| mFixture->configParams.multisample = enabled; | |
| } | |
| void ANGLETestBase::setSamples(EGLint samples) | |
| { | |
| mFixture->configParams.samples = samples; | |
| } | |
| void ANGLETestBase::setDebugEnabled(bool enabled) | |
| { | |
| mFixture->configParams.debug = enabled; | |
| } | |
| void ANGLETestBase::setNoErrorEnabled(bool enabled) | |
| { | |
| mFixture->configParams.noError = enabled; | |
| } | |
| void ANGLETestBase::setWebGLCompatibilityEnabled(bool webglCompatibility) | |
| { | |
| mFixture->configParams.webGLCompatibility = webglCompatibility; | |
| } | |
| void ANGLETestBase::setExtensionsEnabled(bool extensionsEnabled) | |
| { | |
| mFixture->configParams.extensionsEnabled = extensionsEnabled; | |
| } | |
| void ANGLETestBase::setRobustAccess(bool enabled) | |
| { | |
| mFixture->configParams.robustAccess = enabled; | |
| } | |
| void ANGLETestBase::setBindGeneratesResource(bool bindGeneratesResource) | |
| { | |
| mFixture->configParams.bindGeneratesResource = bindGeneratesResource; | |
| } | |
| void ANGLETestBase::setClientArraysEnabled(bool enabled) | |
| { | |
| mFixture->configParams.clientArraysEnabled = enabled; | |
| } | |
| void ANGLETestBase::setRobustResourceInit(bool enabled) | |
| { | |
| mFixture->configParams.robustResourceInit = enabled; | |
| } | |
| void ANGLETestBase::setMutableRenderBuffer(bool enabled) | |
| { | |
| mFixture->configParams.mutableRenderBuffer = enabled; | |
| } | |
| void ANGLETestBase::setContextProgramCacheEnabled(bool enabled) | |
| { | |
| mFixture->configParams.contextProgramCacheEnabled = enabled; | |
| } | |
| void ANGLETestBase::setContextResetStrategy(EGLenum resetStrategy) | |
| { | |
| mFixture->configParams.resetStrategy = resetStrategy; | |
| } | |
| void ANGLETestBase::forceNewDisplay() | |
| { | |
| mForceNewDisplay = true; | |
| } | |
| void ANGLETestBase::setDeferContextInit(bool enabled) | |
| { | |
| mDeferContextInit = enabled; | |
| } | |
| int ANGLETestBase::getClientMajorVersion() const | |
| { | |
| return getGLWindow()->getClientMajorVersion(); | |
| } | |
| int ANGLETestBase::getClientMinorVersion() const | |
| { | |
| return getGLWindow()->getClientMinorVersion(); | |
| } | |
| EGLWindow *ANGLETestBase::getEGLWindow() const | |
| { | |
| return mFixture->eglWindow; | |
| } | |
| int ANGLETestBase::getWindowWidth() const | |
| { | |
| return mWidth; | |
| } | |
| int ANGLETestBase::getWindowHeight() const | |
| { | |
| return mHeight; | |
| } | |
| void ANGLETestBase::setWindowVisible(OSWindow *osWindow, bool isVisible) | |
| { | |
| // SwiftShader windows are not required to be visible for test correctness, | |
| // moreover, making a SwiftShader window visible flaky hangs on Xvfb, so we keep them hidden. | |
| if (isSwiftshader()) | |
| { | |
| return; | |
| } | |
| osWindow->setVisible(isVisible); | |
| } | |
| ANGLETestBase::TestFixture::TestFixture() = default; | |
| ANGLETestBase::TestFixture::~TestFixture() = default; | |
| EGLint ANGLETestBase::getPlatformRenderer() const | |
| { | |
| assert(mFixture->eglWindow); | |
| return mFixture->eglWindow->getPlatform().renderer; | |
| } | |
| void ANGLETestBase::ignoreD3D11SDKLayersWarnings() | |
| { | |
| // Some tests may need to disable the D3D11 SDK Layers Warnings checks | |
| mIgnoreD3D11SDKLayersWarnings = true; | |
| } | |
| ANGLETestBase::ScopedIgnorePlatformMessages::ScopedIgnorePlatformMessages() | |
| { | |
| gPlatformContext.ignoreMessages = true; | |
| } | |
| ANGLETestBase::ScopedIgnorePlatformMessages::~ScopedIgnorePlatformMessages() | |
| { | |
| gPlatformContext.ignoreMessages = false; | |
| } | |
| OSWindow *ANGLETestBase::mOSWindowSingleton = nullptr; | |
| std::map<angle::PlatformParameters, ANGLETestBase::TestFixture> ANGLETestBase::gFixtures; | |
| Optional<EGLint> ANGLETestBase::mLastRendererType; | |
| Optional<angle::GLESDriverType> ANGLETestBase::mLastLoadedDriver; | |
| std::unique_ptr<Library> ANGLETestEnvironment::gAngleEGLLibrary; | |
| std::unique_ptr<Library> ANGLETestEnvironment::gAngleVulkanSecondariesEGLLibrary; | |
| std::unique_ptr<Library> ANGLETestEnvironment::gMesaEGLLibrary; | |
| std::unique_ptr<Library> ANGLETestEnvironment::gSystemEGLLibrary; | |
| std::unique_ptr<Library> ANGLETestEnvironment::gSystemWGLLibrary; | |
| void ANGLETestEnvironment::SetUp() {} | |
| void ANGLETestEnvironment::TearDown() | |
| { | |
| ANGLETestBase::ReleaseFixtures(); | |
| } | |
| // static | |
| Library *ANGLETestEnvironment::GetDriverLibrary(angle::GLESDriverType driver) | |
| { | |
| switch (driver) | |
| { | |
| case angle::GLESDriverType::AngleEGL: | |
| return GetAngleEGLLibrary(); | |
| case angle::GLESDriverType::AngleVulkanSecondariesEGL: | |
| return GetAngleVulkanSecondariesEGLLibrary(); | |
| case angle::GLESDriverType::SystemEGL: | |
| return GetSystemEGLLibrary(); | |
| case angle::GLESDriverType::SystemWGL: | |
| return GetSystemWGLLibrary(); | |
| case angle::GLESDriverType::ZinkEGL: | |
| return GetMesaEGLLibrary(); | |
| default: | |
| return nullptr; | |
| } | |
| } | |
| // static | |
| Library *ANGLETestEnvironment::GetAngleEGLLibrary() | |
| { | |
| #if defined(ANGLE_USE_UTIL_LOADER) | |
| if (!gAngleEGLLibrary) | |
| { | |
| gAngleEGLLibrary.reset(OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, SearchType::ModuleDir)); | |
| } | |
| #endif // defined(ANGLE_USE_UTIL_LOADER) | |
| return gAngleEGLLibrary.get(); | |
| } | |
| // static | |
| Library *ANGLETestEnvironment::GetAngleVulkanSecondariesEGLLibrary() | |
| { | |
| #if defined(ANGLE_USE_UTIL_LOADER) | |
| if (!gAngleVulkanSecondariesEGLLibrary) | |
| { | |
| gAngleVulkanSecondariesEGLLibrary.reset( | |
| OpenSharedLibrary(ANGLE_VULKAN_SECONDARIES_EGL_LIBRARY_NAME, SearchType::ModuleDir)); | |
| } | |
| #endif // defined(ANGLE_USE_UTIL_LOADER) | |
| return gAngleVulkanSecondariesEGLLibrary.get(); | |
| } | |
| // static | |
| Library *ANGLETestEnvironment::GetMesaEGLLibrary() | |
| { | |
| #if defined(ANGLE_USE_UTIL_LOADER) | |
| if (!gMesaEGLLibrary) | |
| { | |
| gMesaEGLLibrary.reset( | |
| OpenSharedLibrary(ANGLE_MESA_EGL_LIBRARY_NAME, SearchType::ModuleDir)); | |
| } | |
| #endif // defined(ANGLE_USE_UTIL_LOADER) | |
| return gMesaEGLLibrary.get(); | |
| } | |
| // static | |
| Library *ANGLETestEnvironment::GetSystemEGLLibrary() | |
| { | |
| #if defined(ANGLE_USE_UTIL_LOADER) | |
| if (!gSystemEGLLibrary) | |
| { | |
| gSystemEGLLibrary.reset(OpenSharedLibraryWithExtension( | |
| GetNativeEGLLibraryNameWithExtension(), SearchType::SystemDir)); | |
| } | |
| #endif // defined(ANGLE_USE_UTIL_LOADER) | |
| return gSystemEGLLibrary.get(); | |
| } | |
| // static | |
| Library *ANGLETestEnvironment::GetSystemWGLLibrary() | |
| { | |
| #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS) | |
| if (!gSystemWGLLibrary) | |
| { | |
| gSystemWGLLibrary.reset(OpenSharedLibrary("opengl32", SearchType::SystemDir)); | |
| } | |
| #endif // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS) | |
| return gSystemWGLLibrary.get(); | |
| } | |
| void ANGLEProcessTestArgs(int *argc, char *argv[]) | |
| { | |
| testing::AddGlobalTestEnvironment(new ANGLETestEnvironment()); | |
| for (int argIndex = 1; argIndex < *argc; argIndex++) | |
| { | |
| if (strncmp(argv[argIndex], kUseConfig, strlen(kUseConfig)) == 0) | |
| { | |
| SetSelectedConfig(argv[argIndex] + strlen(kUseConfig)); | |
| } | |
| else if (strncmp(argv[argIndex], kReuseDisplays, strlen(kReuseDisplays)) == 0) | |
| { | |
| gReuseDisplays = true; | |
| } | |
| else if (strncmp(argv[argIndex], kBatchId, strlen(kBatchId)) == 0) | |
| { | |
| // Enable display reuse when running under --bot-mode. | |
| gReuseDisplays = true; | |
| } | |
| else if (strncmp(argv[argIndex], kEnableANGLEPerTestCaptureLabel, | |
| strlen(kEnableANGLEPerTestCaptureLabel)) == 0) | |
| { | |
| gEnableANGLEPerTestCaptureLabel = true; | |
| } | |
| else if (strncmp(argv[argIndex], kDelayTestStart, strlen(kDelayTestStart)) == 0) | |
| { | |
| SetTestStartDelay(argv[argIndex] + strlen(kDelayTestStart)); | |
| } | |
| else if (strncmp(argv[argIndex], kRenderDoc, strlen(kRenderDoc)) == 0) | |
| { | |
| gEnableRenderDocCapture = true; | |
| } | |
| else if (strncmp(argv[argIndex], kNoRenderDoc, strlen(kNoRenderDoc)) == 0) | |
| { | |
| gEnableRenderDocCapture = false; | |
| } | |
| } | |
| } |