| // |
| // 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. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| |
| #include <d3d11.h> |
| #include <cstdint> |
| |
| #include "util/OSWindow.h" |
| #include "util/com_utils.h" |
| |
| using namespace angle; |
| |
| class EGLPresentPathD3D11 : public ANGLETest |
| { |
| protected: |
| EGLPresentPathD3D11() |
| : mDisplay(EGL_NO_DISPLAY), |
| mContext(EGL_NO_CONTEXT), |
| mSurface(EGL_NO_SURFACE), |
| mOffscreenSurfaceD3D11Texture(nullptr), |
| mConfig(0), |
| mOSWindow(nullptr), |
| mWindowWidth(0) |
| {} |
| |
| void testSetUp() override |
| { |
| mOSWindow = OSWindow::New(); |
| mWindowWidth = 64; |
| mOSWindow->initialize("EGLPresentPathD3D11", mWindowWidth, mWindowWidth); |
| } |
| |
| void initializeEGL(bool usePresentPathFast) |
| { |
| int clientVersion = GetParam().majorVersion; |
| |
| // Set up EGL Display |
| EGLint displayAttribs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, |
| GetParam().getRenderer(), |
| EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, |
| GetParam().eglParameters.majorVersion, |
| EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, |
| GetParam().eglParameters.majorVersion, |
| EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, |
| usePresentPathFast ? EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE |
| : EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE, |
| EGL_NONE}; |
| mDisplay = |
| eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttribs); |
| ASSERT_TRUE(EGL_NO_DISPLAY != mDisplay); |
| ASSERT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr)); |
| |
| // Choose the EGL config |
| EGLint numConfigs; |
| EGLint configAttribs[] = {EGL_RED_SIZE, |
| 8, |
| EGL_GREEN_SIZE, |
| 8, |
| EGL_BLUE_SIZE, |
| 8, |
| EGL_ALPHA_SIZE, |
| 8, |
| EGL_RENDERABLE_TYPE, |
| clientVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, |
| EGL_SURFACE_TYPE, |
| EGL_PBUFFER_BIT, |
| EGL_NONE}; |
| ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, configAttribs, &mConfig, 1, &numConfigs)); |
| ASSERT_EQ(1, numConfigs); |
| |
| // Set up the EGL context |
| EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, clientVersion, EGL_NONE}; |
| mContext = eglCreateContext(mDisplay, mConfig, nullptr, contextAttribs); |
| ASSERT_TRUE(EGL_NO_CONTEXT != mContext); |
| } |
| |
| void createWindowSurface() |
| { |
| mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), nullptr); |
| } |
| |
| void createPbufferFromClientBufferSurface() |
| { |
| EGLAttrib device = 0; |
| EGLAttrib angleDevice = 0; |
| |
| const char *extensionString = |
| static_cast<const char *>(eglQueryString(mDisplay, EGL_EXTENSIONS)); |
| EXPECT_TRUE(strstr(extensionString, "EGL_EXT_device_query")); |
| |
| ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &angleDevice)); |
| ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice), |
| EGL_D3D11_DEVICE_ANGLE, &device)); |
| ID3D11Device *d3d11Device = reinterpret_cast<ID3D11Device *>(device); |
| |
| D3D11_TEXTURE2D_DESC textureDesc = {0}; |
| textureDesc.Width = mWindowWidth; |
| textureDesc.Height = mWindowWidth; |
| textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; |
| textureDesc.MipLevels = 1; |
| textureDesc.ArraySize = 1; |
| textureDesc.SampleDesc.Count = 1; |
| textureDesc.SampleDesc.Quality = 0; |
| textureDesc.Usage = D3D11_USAGE_DEFAULT; |
| textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; |
| textureDesc.CPUAccessFlags = 0; |
| textureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; |
| |
| ASSERT_TRUE(SUCCEEDED( |
| d3d11Device->CreateTexture2D(&textureDesc, nullptr, &mOffscreenSurfaceD3D11Texture))); |
| |
| IDXGIResource *dxgiResource = |
| DynamicCastComObject<IDXGIResource>(mOffscreenSurfaceD3D11Texture); |
| ASSERT_NE(nullptr, dxgiResource); |
| |
| HANDLE sharedHandle = 0; |
| ASSERT_TRUE(SUCCEEDED(dxgiResource->GetSharedHandle(&sharedHandle))); |
| SafeRelease(dxgiResource); |
| |
| EGLint pBufferAttributes[] = {EGL_WIDTH, mWindowWidth, EGL_HEIGHT, |
| mWindowWidth, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, EGL_NONE}; |
| |
| mSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, |
| sharedHandle, mConfig, pBufferAttributes); |
| ASSERT_TRUE(EGL_NO_SURFACE != mSurface); |
| } |
| |
| void makeCurrent() { ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)); } |
| |
| void testTearDown() override |
| { |
| SafeRelease(mOffscreenSurfaceD3D11Texture); |
| |
| if (mDisplay != EGL_NO_DISPLAY) |
| { |
| eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| |
| if (mSurface != EGL_NO_SURFACE) |
| { |
| eglDestroySurface(mDisplay, mSurface); |
| mSurface = EGL_NO_SURFACE; |
| } |
| |
| if (mContext != EGL_NO_CONTEXT) |
| { |
| eglDestroyContext(mDisplay, mContext); |
| mContext = EGL_NO_CONTEXT; |
| } |
| |
| eglTerminate(mDisplay); |
| mDisplay = EGL_NO_DISPLAY; |
| } |
| |
| mOSWindow->destroy(); |
| OSWindow::Delete(&mOSWindow); |
| } |
| |
| void drawQuadUsingGL() |
| { |
| GLuint m2DProgram; |
| GLint mTexture2DUniformLocation; |
| |
| constexpr char kVS[] = |
| R"(precision highp float; |
| attribute vec4 position; |
| varying vec2 texcoord; |
| |
| void main() |
| { |
| gl_Position = vec4(position.xy, 0.0, 1.0); |
| texcoord = (position.xy * 0.5) + 0.5; |
| })"; |
| |
| constexpr char kFS[] = |
| R"(precision highp float; |
| uniform sampler2D tex; |
| varying vec2 texcoord; |
| |
| void main() |
| { |
| gl_FragColor = texture2D(tex, texcoord); |
| })"; |
| |
| m2DProgram = CompileProgram(kVS, kFS); |
| mTexture2DUniformLocation = glGetUniformLocation(m2DProgram, "tex"); |
| |
| uint8_t textureInitData[16] = { |
| 255, 0, 0, 255, // Red |
| 0, 255, 0, 255, // Green |
| 0, 0, 255, 255, // Blue |
| 255, 255, 0, 255 // Red + Green |
| }; |
| |
| // Create a simple RGBA texture |
| GLuint tex = 0; |
| glGenTextures(1, &tex); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| textureInitData); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw a quad using the texture |
| glClear(GL_COLOR_BUFFER_BIT); |
| glUseProgram(m2DProgram); |
| glUniform1i(mTexture2DUniformLocation, 0); |
| |
| GLint positionLocation = glGetAttribLocation(m2DProgram, "position"); |
| glUseProgram(m2DProgram); |
| const GLfloat vertices[] = { |
| -1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f, |
| -1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f, |
| }; |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| glDeleteProgram(m2DProgram); |
| } |
| |
| void checkPixelsUsingGL() |
| { |
| // Note that the texture is in BGRA format |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); // Red |
| EXPECT_PIXEL_EQ(mWindowWidth - 1, 0, 0, 255, 0, 255); // Green |
| EXPECT_PIXEL_EQ(0, mWindowWidth - 1, 0, 0, 255, 255); // Blue |
| EXPECT_PIXEL_EQ(mWindowWidth - 1, mWindowWidth - 1, 255, 255, 0, 255); // Red + green |
| } |
| |
| void checkPixelsUsingD3D(bool usingPresentPathFast) |
| { |
| ASSERT_NE(nullptr, mOffscreenSurfaceD3D11Texture); |
| |
| D3D11_TEXTURE2D_DESC textureDesc = {0}; |
| ID3D11Device *device; |
| ID3D11DeviceContext *context; |
| mOffscreenSurfaceD3D11Texture->GetDesc(&textureDesc); |
| mOffscreenSurfaceD3D11Texture->GetDevice(&device); |
| device->GetImmediateContext(&context); |
| ASSERT_NE(nullptr, device); |
| ASSERT_NE(nullptr, context); |
| |
| textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; |
| textureDesc.Usage = D3D11_USAGE_STAGING; |
| textureDesc.BindFlags = 0; |
| textureDesc.MiscFlags = 0; |
| ID3D11Texture2D *cpuTexture = nullptr; |
| ASSERT_TRUE(SUCCEEDED(device->CreateTexture2D(&textureDesc, nullptr, &cpuTexture))); |
| |
| context->CopyResource(cpuTexture, mOffscreenSurfaceD3D11Texture); |
| |
| D3D11_MAPPED_SUBRESOURCE mappedSubresource; |
| context->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mappedSubresource); |
| ASSERT_EQ(static_cast<UINT>(mWindowWidth * 4), mappedSubresource.RowPitch); |
| ASSERT_EQ(static_cast<UINT>(mWindowWidth * mWindowWidth * 4), mappedSubresource.DepthPitch); |
| |
| angle::GLColor *byteData = reinterpret_cast<angle::GLColor *>(mappedSubresource.pData); |
| |
| // Note that the texture is in BGRA format, although the GLColor struct is RGBA |
| GLColor expectedTopLeftPixel = GLColor(0, 0, 255, 255); // Red |
| GLColor expectedTopRightPixel = GLColor(0, 255, 0, 255); // Green |
| GLColor expectedBottomLeftPixel = GLColor(255, 0, 0, 255); // Blue |
| GLColor expectedBottomRightPixel = GLColor(0, 255, 255, 255); // Red + Green |
| |
| if (usingPresentPathFast) |
| { |
| // Invert the expected values |
| GLColor tempTopLeft = expectedTopLeftPixel; |
| GLColor tempTopRight = expectedTopRightPixel; |
| expectedTopLeftPixel = expectedBottomLeftPixel; |
| expectedTopRightPixel = expectedBottomRightPixel; |
| expectedBottomLeftPixel = tempTopLeft; |
| expectedBottomRightPixel = tempTopRight; |
| } |
| |
| EXPECT_EQ(expectedTopLeftPixel, byteData[0]); |
| EXPECT_EQ(expectedTopRightPixel, byteData[(mWindowWidth - 1)]); |
| EXPECT_EQ(expectedBottomLeftPixel, byteData[(mWindowWidth - 1) * mWindowWidth]); |
| EXPECT_EQ(expectedBottomRightPixel, |
| byteData[(mWindowWidth - 1) * mWindowWidth + (mWindowWidth - 1)]); |
| |
| context->Unmap(cpuTexture, 0); |
| SafeRelease(cpuTexture); |
| SafeRelease(device); |
| SafeRelease(context); |
| } |
| |
| EGLDisplay mDisplay; |
| EGLContext mContext; |
| EGLSurface mSurface; |
| ID3D11Texture2D *mOffscreenSurfaceD3D11Texture; |
| EGLConfig mConfig; |
| OSWindow *mOSWindow; |
| GLint mWindowWidth; |
| }; |
| |
| // Test that rendering basic content onto a window surface when present path fast |
| // is enabled works as expected |
| TEST_P(EGLPresentPathD3D11, WindowPresentPathFast) |
| { |
| initializeEGL(true); |
| createWindowSurface(); |
| makeCurrent(); |
| |
| drawQuadUsingGL(); |
| |
| checkPixelsUsingGL(); |
| } |
| |
| // Test that rendering basic content onto a client buffer surface when present path fast |
| // works as expected, and is also oriented the correct way around |
| TEST_P(EGLPresentPathD3D11, ClientBufferPresentPathFast) |
| { |
| initializeEGL(true); |
| createPbufferFromClientBufferSurface(); |
| makeCurrent(); |
| |
| drawQuadUsingGL(); |
| |
| checkPixelsUsingGL(); |
| checkPixelsUsingD3D(true); |
| } |
| |
| // Test that rendering basic content onto a window surface when present path fast |
| // is disabled works as expected |
| TEST_P(EGLPresentPathD3D11, WindowPresentPathCopy) |
| { |
| initializeEGL(false); |
| createWindowSurface(); |
| makeCurrent(); |
| |
| drawQuadUsingGL(); |
| |
| checkPixelsUsingGL(); |
| } |
| |
| // Test that rendering basic content onto a client buffer surface when present path |
| // fast is disabled works as expected, and is also oriented the correct way around |
| TEST_P(EGLPresentPathD3D11, ClientBufferPresentPathCopy) |
| { |
| initializeEGL(false); |
| createPbufferFromClientBufferSurface(); |
| makeCurrent(); |
| |
| drawQuadUsingGL(); |
| |
| checkPixelsUsingGL(); |
| checkPixelsUsingD3D(false); |
| } |
| |
| ANGLE_INSTANTIATE_TEST(EGLPresentPathD3D11, WithNoFixture(ES2_D3D11())); |