| // |
| // 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. |
| // |
| |
| |
| #ifndef ANGLE_ENABLE_D3D11 |
| # define ANGLE_ENABLE_D3D11 |
| #endif |
| |
| #include <d3d11.h> |
| #include "common/unsafe_buffers.h" |
| |
| #include "common/com_utils.h" |
| #include "test_utils/ANGLETest.h" |
| #include "util/EGLWindow.h" |
| #include "util/OSWindow.h" |
| #include "util/gles_loader_autogen.h" |
| |
| using namespace angle; |
| |
| class EGLDeviceCreationTest : public ANGLETest<> |
| { |
| protected: |
| EGLDeviceCreationTest() |
| : mD3D11Module(nullptr), |
| mD3D11CreateDevice(nullptr), |
| mDeviceCreationD3D11ExtAvailable(false), |
| mOSWindow(nullptr), |
| mDisplay(EGL_NO_DISPLAY), |
| mSurface(EGL_NO_SURFACE), |
| mContext(EGL_NO_CONTEXT), |
| mConfig(0) |
| {} |
| |
| void testSetUp() override |
| { |
| ASSERT_TRUE(isD3D11Renderer()); |
| |
| mD3D11Module = LoadLibrary(TEXT("d3d11.dll")); |
| if (mD3D11Module == nullptr) |
| { |
| std::cout << "Unable to LoadLibrary D3D11" << std::endl; |
| return; |
| } |
| |
| mD3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>( |
| GetProcAddress(mD3D11Module, "D3D11CreateDevice")); |
| if (mD3D11CreateDevice == nullptr) |
| { |
| std::cout << "Could not retrieve D3D11CreateDevice from d3d11.dll" << std::endl; |
| return; |
| } |
| |
| const char *extensionString = |
| static_cast<const char *>(eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)); |
| if (ANGLE_UNSAFE_TODO(strstr(extensionString, "EGL_ANGLE_device_creation"))) |
| { |
| if (ANGLE_UNSAFE_TODO(strstr(extensionString, "EGL_ANGLE_device_creation_d3d11"))) |
| { |
| mDeviceCreationD3D11ExtAvailable = true; |
| } |
| } |
| } |
| |
| void testTearDown() override |
| { |
| mDevice.Reset(); |
| mDeviceContext.Reset(); |
| |
| OSWindow::Delete(&mOSWindow); |
| |
| if (mSurface != EGL_NO_SURFACE) |
| { |
| eglDestroySurface(mDisplay, mSurface); |
| mSurface = EGL_NO_SURFACE; |
| } |
| |
| if (mContext != EGL_NO_CONTEXT) |
| { |
| eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| eglDestroyContext(mDisplay, mContext); |
| mContext = EGL_NO_CONTEXT; |
| } |
| |
| if (mDisplay != EGL_NO_DISPLAY) |
| { |
| eglTerminate(mDisplay); |
| mDisplay = EGL_NO_DISPLAY; |
| } |
| } |
| |
| void CreateD3D11Device() |
| { |
| ASSERT_EQ(nullptr, mDevice); // The device shouldn't be created twice |
| |
| HRESULT hr = |
| mD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, 0, nullptr, 0, |
| D3D11_SDK_VERSION, &mDevice, &mFeatureLevel, &mDeviceContext); |
| |
| ASSERT_TRUE(SUCCEEDED(hr)); |
| ASSERT_GE(mFeatureLevel, D3D_FEATURE_LEVEL_10_0); |
| } |
| |
| void CreateWindowSurface() |
| { |
| EGLint majorVersion, minorVersion; |
| ASSERT_EGL_TRUE(eglInitialize(mDisplay, &majorVersion, &minorVersion)); |
| |
| eglBindAPI(EGL_OPENGL_ES_API); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Choose a config |
| const EGLint configAttributes[] = {EGL_NONE}; |
| EGLint configCount = 0; |
| ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, configAttributes, &mConfig, 1, &configCount)); |
| |
| // Create an OS Window |
| mOSWindow = OSWindow::New(); |
| mOSWindow->initialize("EGLSurfaceTest", 64, 64); |
| |
| // Create window surface |
| mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), nullptr); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create EGL context |
| EGLint contextAttibutes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; |
| mContext = eglCreateContext(mDisplay, mConfig, nullptr, contextAttibutes); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Make the surface current |
| eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); |
| ASSERT_EGL_SUCCESS(); |
| } |
| |
| HMODULE mD3D11Module; |
| PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice; |
| |
| angle::ComPtr<ID3D11Device> mDevice; |
| angle::ComPtr<ID3D11DeviceContext> mDeviceContext; |
| D3D_FEATURE_LEVEL mFeatureLevel; |
| |
| bool mDeviceCreationD3D11ExtAvailable; |
| |
| OSWindow *mOSWindow; |
| |
| EGLDisplay mDisplay; |
| EGLSurface mSurface; |
| EGLContext mContext; |
| EGLConfig mConfig; |
| }; |
| |
| // Test that creating a EGLDeviceEXT from D3D11 device works, and it can be queried to retrieve |
| // D3D11 device |
| TEST_P(EGLDeviceCreationTest, BasicD3D11Device) |
| { |
| ANGLE_SKIP_TEST_IF(!mDeviceCreationD3D11ExtAvailable); |
| |
| CreateD3D11Device(); |
| |
| EGLDeviceEXT eglDevice = eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, mDevice.Get(), nullptr); |
| ASSERT_NE(EGL_NO_DEVICE_EXT, eglDevice); |
| ASSERT_EGL_SUCCESS(); |
| |
| EGLAttrib deviceAttrib; |
| eglQueryDeviceAttribEXT(eglDevice, EGL_D3D11_DEVICE_ANGLE, &deviceAttrib); |
| ASSERT_EGL_SUCCESS(); |
| |
| ID3D11Device *queriedDevice = reinterpret_cast<ID3D11Device *>(deviceAttrib); |
| ASSERT_EQ(mFeatureLevel, queriedDevice->GetFeatureLevel()); |
| |
| eglReleaseDeviceANGLE(eglDevice); |
| } |
| |
| // Test that creating a EGLDeviceEXT from D3D11 device works, and it can be queried to retrieve |
| // D3D11 device |
| TEST_P(EGLDeviceCreationTest, BasicD3D11DeviceViaFuncPointer) |
| { |
| ANGLE_SKIP_TEST_IF(!mDeviceCreationD3D11ExtAvailable); |
| |
| CreateD3D11Device(); |
| |
| EGLDeviceEXT eglDevice = eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, mDevice.Get(), nullptr); |
| ASSERT_NE(EGL_NO_DEVICE_EXT, eglDevice); |
| ASSERT_EGL_SUCCESS(); |
| |
| EGLAttrib deviceAttrib; |
| eglQueryDeviceAttribEXT(eglDevice, EGL_D3D11_DEVICE_ANGLE, &deviceAttrib); |
| ASSERT_EGL_SUCCESS(); |
| |
| ID3D11Device *queriedDevice = reinterpret_cast<ID3D11Device *>(deviceAttrib); |
| ASSERT_EQ(mFeatureLevel, queriedDevice->GetFeatureLevel()); |
| |
| eglReleaseDeviceANGLE(eglDevice); |
| } |
| |
| // Test that creating a EGLDeviceEXT from D3D11 device works, and can be used for rendering |
| TEST_P(EGLDeviceCreationTest, RenderingUsingD3D11Device) |
| { |
| CreateD3D11Device(); |
| |
| EGLDeviceEXT eglDevice = eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, mDevice.Get(), nullptr); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create an EGLDisplay using the EGLDevice |
| mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr); |
| ASSERT_NE(EGL_NO_DISPLAY, mDisplay); |
| |
| // Create a surface using the display |
| CreateWindowSurface(); |
| |
| // Perform some very basic rendering |
| glClearColor(1.0f, 0.0f, 1.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(32, 32, 255, 0, 255, 255); |
| |
| // Note that we must call TearDown() before we release the EGL device, since the display |
| // depends on the device |
| ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| testTearDown(); |
| |
| eglReleaseDeviceANGLE(eglDevice); |
| } |
| |
| // Test that calling eglGetPlatformDisplay with the same device returns the same display |
| TEST_P(EGLDeviceCreationTest, GetPlatformDisplayTwice) |
| { |
| CreateD3D11Device(); |
| |
| EGLDeviceEXT eglDevice = eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, mDevice.Get(), nullptr); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create an EGLDisplay using the EGLDevice |
| EGLDisplay display1 = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr); |
| ASSERT_NE(EGL_NO_DISPLAY, display1); |
| |
| EGLDisplay display2 = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr); |
| ASSERT_NE(EGL_NO_DISPLAY, display2); |
| |
| ASSERT_EQ(display1, display2); |
| |
| eglTerminate(display1); |
| eglReleaseDeviceANGLE(eglDevice); |
| } |
| |
| // Test that creating a EGLDeviceEXT from an invalid D3D11 device fails |
| TEST_P(EGLDeviceCreationTest, InvalidD3D11Device) |
| { |
| ANGLE_SKIP_TEST_IF(!mDeviceCreationD3D11ExtAvailable); |
| |
| CreateD3D11Device(); |
| |
| // Use mDeviceContext instead of mDevice |
| EGLDeviceEXT eglDevice = |
| eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, mDeviceContext.Get(), nullptr); |
| EXPECT_EQ(EGL_NO_DEVICE_EXT, eglDevice); |
| EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE); |
| } |
| |
| // Test that EGLDeviceEXT holds a ref to the D3D11 device |
| TEST_P(EGLDeviceCreationTest, D3D11DeviceReferenceCounting) |
| { |
| ANGLE_SKIP_TEST_IF(!mDeviceCreationD3D11ExtAvailable); |
| |
| CreateD3D11Device(); |
| |
| EGLDeviceEXT eglDevice = eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, mDevice.Get(), nullptr); |
| ASSERT_NE(EGL_NO_DEVICE_EXT, eglDevice); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Now release our D3D11 device/context |
| mDevice.Reset(); |
| mDeviceContext.Reset(); |
| |
| EGLAttrib deviceAttrib; |
| eglQueryDeviceAttribEXT(eglDevice, EGL_D3D11_DEVICE_ANGLE, &deviceAttrib); |
| ASSERT_EGL_SUCCESS(); |
| |
| ID3D11Device *queriedDevice = reinterpret_cast<ID3D11Device *>(deviceAttrib); |
| ASSERT_EQ(mFeatureLevel, queriedDevice->GetFeatureLevel()); |
| |
| eglReleaseDeviceANGLE(eglDevice); |
| } |
| |
| class EGLDeviceQueryTest : public ANGLETest<> |
| { |
| protected: |
| EGLDeviceQueryTest() {} |
| |
| void testSetUp() override |
| { |
| if (!eglQueryDeviceStringEXT) |
| { |
| FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDeviceStringEXT was not " |
| "found"; |
| } |
| |
| if (!eglQueryDisplayAttribEXT) |
| { |
| FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDisplayAttribEXT was " |
| "not found"; |
| } |
| |
| if (!eglQueryDeviceAttribEXT) |
| { |
| FAIL() << "ANGLE extension EGL_EXT_device_query export eglQueryDeviceAttribEXT was not " |
| "found"; |
| } |
| |
| EGLAttrib angleDevice = 0; |
| EXPECT_EGL_TRUE( |
| eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); |
| if (!IsEGLDeviceExtensionEnabled(reinterpret_cast<EGLDeviceEXT>(angleDevice), |
| "EGL_ANGLE_device_d3d11")) |
| { |
| FAIL() << "ANGLE extension EGL_ANGLE_device_d3d11 was not " |
| "found"; |
| } |
| } |
| }; |
| |
| // This test attempts to obtain a D3D11 device using the eglQueryDeviceAttribEXT function. |
| // If the test is configured to use D3D11 then it should succeed to obtain a D3D11 device. |
| TEST_P(EGLDeviceQueryTest, QueryDevice) |
| { |
| EGLAttrib angleDevice = 0; |
| EXPECT_EGL_TRUE( |
| eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); |
| |
| if (IsEGLDeviceExtensionEnabled(reinterpret_cast<EGLDeviceEXT>(angleDevice), |
| "EGL_ANGLE_device_d3d11")) |
| { |
| EGLAttrib device11 = 0; |
| EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice), |
| EGL_D3D11_DEVICE_ANGLE, &device11)); |
| ID3D11Device *d3d11Device = reinterpret_cast<ID3D11Device *>(device11); |
| auto dxgiDevice = angle::DynamicCastComObject<IDXGIDevice>(d3d11Device); |
| EXPECT_TRUE(dxgiDevice != nullptr); |
| } |
| } |
| |
| // This test attempts to obtain a D3D11 device from a system that isn't configured to use D3D11 |
| // using the eglQueryDeviceAttribEXT function. |
| // If the test is not configured to use D3D11 then it should fail to obtain a D3D11 device. |
| TEST_P(EGLDeviceQueryTest, QueryDeviceBadAttribute) |
| { |
| EGLAttrib angleDevice = 0; |
| EXPECT_EGL_TRUE( |
| eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); |
| |
| if (!IsEGLDeviceExtensionEnabled(reinterpret_cast<EGLDeviceEXT>(angleDevice), |
| "EGL_ANGLE_device_d3d11")) |
| { |
| EGLAttrib device11 = 0; |
| EXPECT_EGL_FALSE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice), |
| EGL_D3D11_DEVICE_ANGLE, &device11)); |
| } |
| } |
| |
| // Ensure that: |
| // - calling getPlatformDisplayEXT using ANGLE_Platform with some parameters |
| // - extracting the EGLDeviceEXT from the EGLDisplay |
| // - calling getPlatformDisplayEXT with this EGLDeviceEXT |
| // results in the same EGLDisplay being returned from getPlatformDisplayEXT both times |
| TEST_P(EGLDeviceQueryTest, GetPlatformDisplayDeviceReuse) |
| { |
| EGLAttrib eglDevice = 0; |
| EXPECT_EGL_TRUE( |
| eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &eglDevice)); |
| |
| EGLDisplay display2 = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, |
| reinterpret_cast<EGLDeviceEXT>(eglDevice), nullptr); |
| EXPECT_EQ(getEGLWindow()->getDisplay(), display2); |
| } |
| |
| // Use this to select which configurations (e.g. which renderer, which GLES major version) these |
| // tests should be run against. |
| ANGLE_INSTANTIATE_TEST(EGLDeviceCreationTest, WithNoFixture(ES2_D3D11())); |
| ANGLE_INSTANTIATE_TEST(EGLDeviceQueryTest, ES2_D3D11()); |