1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "test_utils/ANGLETest.h"
8
9 #include <d3d11.h>
10 #include <cstdint>
11
12 #include "util/OSWindow.h"
13 #include "util/com_utils.h"
14
15 using namespace angle;
16
17 class EGLPresentPathD3D11 : public ANGLETest<>
18 {
19 protected:
EGLPresentPathD3D11()20 EGLPresentPathD3D11()
21 : mDisplay(EGL_NO_DISPLAY),
22 mContext(EGL_NO_CONTEXT),
23 mSurface(EGL_NO_SURFACE),
24 mOffscreenSurfaceD3D11Texture(nullptr),
25 mConfig(0),
26 mOSWindow(nullptr),
27 mWindowWidth(0)
28 {}
29
testSetUp()30 void testSetUp() override
31 {
32 mOSWindow = OSWindow::New();
33 mWindowWidth = 64;
34 mOSWindow->initialize("EGLPresentPathD3D11", mWindowWidth, mWindowWidth);
35 }
36
initializeEGL(bool usePresentPathFast)37 void initializeEGL(bool usePresentPathFast)
38 {
39 int clientVersion = GetParam().majorVersion;
40
41 // Set up EGL Display
42 EGLint displayAttribs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE,
43 GetParam().getRenderer(),
44 EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
45 GetParam().eglParameters.majorVersion,
46 EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
47 GetParam().eglParameters.majorVersion,
48 EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
49 usePresentPathFast ? EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE
50 : EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE,
51 EGL_NONE};
52 mDisplay =
53 eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttribs);
54 ASSERT_TRUE(EGL_NO_DISPLAY != mDisplay);
55 ASSERT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
56
57 // Choose the EGL config
58 EGLint numConfigs;
59 EGLint configAttribs[] = {EGL_RED_SIZE,
60 8,
61 EGL_GREEN_SIZE,
62 8,
63 EGL_BLUE_SIZE,
64 8,
65 EGL_ALPHA_SIZE,
66 8,
67 EGL_RENDERABLE_TYPE,
68 clientVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT,
69 EGL_SURFACE_TYPE,
70 EGL_PBUFFER_BIT,
71 EGL_NONE};
72 ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, configAttribs, &mConfig, 1, &numConfigs));
73 ASSERT_EQ(1, numConfigs);
74
75 // Set up the EGL context
76 EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, clientVersion, EGL_NONE};
77 mContext = eglCreateContext(mDisplay, mConfig, nullptr, contextAttribs);
78 ASSERT_TRUE(EGL_NO_CONTEXT != mContext);
79 }
80
createWindowSurface()81 void createWindowSurface()
82 {
83 mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), nullptr);
84 }
85
createPbufferFromClientBufferSurface()86 void createPbufferFromClientBufferSurface()
87 {
88 EGLAttrib device = 0;
89 EGLAttrib angleDevice = 0;
90
91 EXPECT_TRUE(IsEGLClientExtensionEnabled("EGL_EXT_device_query"));
92
93 ASSERT_EGL_TRUE(eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, &angleDevice));
94 ASSERT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice),
95 EGL_D3D11_DEVICE_ANGLE, &device));
96 ID3D11Device *d3d11Device = reinterpret_cast<ID3D11Device *>(device);
97
98 D3D11_TEXTURE2D_DESC textureDesc = {0};
99 textureDesc.Width = mWindowWidth;
100 textureDesc.Height = mWindowWidth;
101 textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
102 textureDesc.MipLevels = 1;
103 textureDesc.ArraySize = 1;
104 textureDesc.SampleDesc.Count = 1;
105 textureDesc.SampleDesc.Quality = 0;
106 textureDesc.Usage = D3D11_USAGE_DEFAULT;
107 textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
108 textureDesc.CPUAccessFlags = 0;
109 textureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
110
111 ASSERT_TRUE(SUCCEEDED(
112 d3d11Device->CreateTexture2D(&textureDesc, nullptr, &mOffscreenSurfaceD3D11Texture)));
113
114 IDXGIResource *dxgiResource =
115 DynamicCastComObject<IDXGIResource>(mOffscreenSurfaceD3D11Texture);
116 ASSERT_NE(nullptr, dxgiResource);
117
118 HANDLE sharedHandle = 0;
119 ASSERT_TRUE(SUCCEEDED(dxgiResource->GetSharedHandle(&sharedHandle)));
120 SafeRelease(dxgiResource);
121
122 EGLint pBufferAttributes[] = {EGL_WIDTH, mWindowWidth, EGL_HEIGHT,
123 mWindowWidth, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
124 EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, EGL_NONE};
125
126 mSurface = eglCreatePbufferFromClientBuffer(mDisplay, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
127 sharedHandle, mConfig, pBufferAttributes);
128 ASSERT_TRUE(EGL_NO_SURFACE != mSurface);
129 }
130
makeCurrent()131 void makeCurrent() { ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)); }
132
testTearDown()133 void testTearDown() override
134 {
135 SafeRelease(mOffscreenSurfaceD3D11Texture);
136
137 if (mDisplay != EGL_NO_DISPLAY)
138 {
139 eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
140
141 if (mSurface != EGL_NO_SURFACE)
142 {
143 eglDestroySurface(mDisplay, mSurface);
144 mSurface = EGL_NO_SURFACE;
145 }
146
147 if (mContext != EGL_NO_CONTEXT)
148 {
149 ASSERT_EGL_TRUE(
150 eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
151 eglDestroyContext(mDisplay, mContext);
152 mContext = EGL_NO_CONTEXT;
153 }
154
155 eglTerminate(mDisplay);
156 mDisplay = EGL_NO_DISPLAY;
157 }
158
159 mOSWindow->destroy();
160 OSWindow::Delete(&mOSWindow);
161 }
162
drawQuadUsingGL()163 void drawQuadUsingGL()
164 {
165 GLuint m2DProgram;
166 GLint mTexture2DUniformLocation;
167
168 constexpr char kVS[] =
169 R"(precision highp float;
170 attribute vec4 position;
171 varying vec2 texcoord;
172
173 void main()
174 {
175 gl_Position = vec4(position.xy, 0.0, 1.0);
176 texcoord = (position.xy * 0.5) + 0.5;
177 })";
178
179 constexpr char kFS[] =
180 R"(precision highp float;
181 uniform sampler2D tex;
182 varying vec2 texcoord;
183
184 void main()
185 {
186 gl_FragColor = texture2D(tex, texcoord);
187 })";
188
189 m2DProgram = CompileProgram(kVS, kFS);
190 mTexture2DUniformLocation = glGetUniformLocation(m2DProgram, "tex");
191
192 uint8_t textureInitData[16] = {
193 255, 0, 0, 255, // Red
194 0, 255, 0, 255, // Green
195 0, 0, 255, 255, // Blue
196 255, 255, 0, 255 // Red + Green
197 };
198
199 // Create a simple RGBA texture
200 GLuint tex = 0;
201 glGenTextures(1, &tex);
202 glBindTexture(GL_TEXTURE_2D, tex);
203 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
204 textureInitData);
205 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
207 ASSERT_GL_NO_ERROR();
208
209 // Draw a quad using the texture
210 glClear(GL_COLOR_BUFFER_BIT);
211 glUseProgram(m2DProgram);
212 glUniform1i(mTexture2DUniformLocation, 0);
213
214 GLint positionLocation = glGetAttribLocation(m2DProgram, "position");
215 glUseProgram(m2DProgram);
216 const GLfloat vertices[] = {
217 -1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f,
218 -1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f,
219 };
220
221 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
222 glEnableVertexAttribArray(positionLocation);
223
224 glDrawArrays(GL_TRIANGLES, 0, 6);
225 ASSERT_GL_NO_ERROR();
226
227 glDeleteProgram(m2DProgram);
228 }
229
checkPixelsUsingGL()230 void checkPixelsUsingGL()
231 {
232 // Note that the texture is in BGRA format
233 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); // Red
234 EXPECT_PIXEL_EQ(mWindowWidth - 1, 0, 0, 255, 0, 255); // Green
235 EXPECT_PIXEL_EQ(0, mWindowWidth - 1, 0, 0, 255, 255); // Blue
236 EXPECT_PIXEL_EQ(mWindowWidth - 1, mWindowWidth - 1, 255, 255, 0, 255); // Red + green
237 }
238
checkPixelsUsingD3D(bool usingPresentPathFast)239 void checkPixelsUsingD3D(bool usingPresentPathFast)
240 {
241 ASSERT_NE(nullptr, mOffscreenSurfaceD3D11Texture);
242
243 D3D11_TEXTURE2D_DESC textureDesc = {0};
244 ID3D11Device *device;
245 ID3D11DeviceContext *context;
246 mOffscreenSurfaceD3D11Texture->GetDesc(&textureDesc);
247 mOffscreenSurfaceD3D11Texture->GetDevice(&device);
248 device->GetImmediateContext(&context);
249 ASSERT_NE(nullptr, device);
250 ASSERT_NE(nullptr, context);
251
252 textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
253 textureDesc.Usage = D3D11_USAGE_STAGING;
254 textureDesc.BindFlags = 0;
255 textureDesc.MiscFlags = 0;
256 ID3D11Texture2D *cpuTexture = nullptr;
257 ASSERT_TRUE(SUCCEEDED(device->CreateTexture2D(&textureDesc, nullptr, &cpuTexture)));
258
259 context->CopyResource(cpuTexture, mOffscreenSurfaceD3D11Texture);
260
261 D3D11_MAPPED_SUBRESOURCE mappedSubresource;
262 context->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mappedSubresource);
263 ASSERT_EQ(static_cast<UINT>(mWindowWidth * 4), mappedSubresource.RowPitch);
264 ASSERT_EQ(static_cast<UINT>(mWindowWidth * mWindowWidth * 4), mappedSubresource.DepthPitch);
265
266 angle::GLColor *byteData = reinterpret_cast<angle::GLColor *>(mappedSubresource.pData);
267
268 // Note that the texture is in BGRA format, although the GLColor struct is RGBA
269 GLColor expectedTopLeftPixel = GLColor(0, 0, 255, 255); // Red
270 GLColor expectedTopRightPixel = GLColor(0, 255, 0, 255); // Green
271 GLColor expectedBottomLeftPixel = GLColor(255, 0, 0, 255); // Blue
272 GLColor expectedBottomRightPixel = GLColor(0, 255, 255, 255); // Red + Green
273
274 if (usingPresentPathFast)
275 {
276 // Invert the expected values
277 GLColor tempTopLeft = expectedTopLeftPixel;
278 GLColor tempTopRight = expectedTopRightPixel;
279 expectedTopLeftPixel = expectedBottomLeftPixel;
280 expectedTopRightPixel = expectedBottomRightPixel;
281 expectedBottomLeftPixel = tempTopLeft;
282 expectedBottomRightPixel = tempTopRight;
283 }
284
285 EXPECT_EQ(expectedTopLeftPixel, byteData[0]);
286 EXPECT_EQ(expectedTopRightPixel, byteData[(mWindowWidth - 1)]);
287 EXPECT_EQ(expectedBottomLeftPixel, byteData[(mWindowWidth - 1) * mWindowWidth]);
288 EXPECT_EQ(expectedBottomRightPixel,
289 byteData[(mWindowWidth - 1) * mWindowWidth + (mWindowWidth - 1)]);
290
291 context->Unmap(cpuTexture, 0);
292 SafeRelease(cpuTexture);
293 SafeRelease(device);
294 SafeRelease(context);
295 }
296
297 EGLDisplay mDisplay;
298 EGLContext mContext;
299 EGLSurface mSurface;
300 ID3D11Texture2D *mOffscreenSurfaceD3D11Texture;
301 EGLConfig mConfig;
302 OSWindow *mOSWindow;
303 GLint mWindowWidth;
304 };
305
306 // Test that rendering basic content onto a window surface when present path fast
307 // is enabled works as expected
TEST_P(EGLPresentPathD3D11,WindowPresentPathFast)308 TEST_P(EGLPresentPathD3D11, WindowPresentPathFast)
309 {
310 initializeEGL(true);
311 createWindowSurface();
312 makeCurrent();
313
314 drawQuadUsingGL();
315
316 checkPixelsUsingGL();
317 }
318
319 // Test that rendering basic content onto a client buffer surface when present path fast
320 // works as expected, and is also oriented the correct way around
TEST_P(EGLPresentPathD3D11,ClientBufferPresentPathFast)321 TEST_P(EGLPresentPathD3D11, ClientBufferPresentPathFast)
322 {
323 initializeEGL(true);
324 createPbufferFromClientBufferSurface();
325 makeCurrent();
326
327 drawQuadUsingGL();
328
329 checkPixelsUsingGL();
330 checkPixelsUsingD3D(true);
331 }
332
333 // Test that rendering basic content onto a window surface when present path fast
334 // is disabled works as expected
TEST_P(EGLPresentPathD3D11,WindowPresentPathCopy)335 TEST_P(EGLPresentPathD3D11, WindowPresentPathCopy)
336 {
337 initializeEGL(false);
338 createWindowSurface();
339 makeCurrent();
340
341 drawQuadUsingGL();
342
343 checkPixelsUsingGL();
344 }
345
346 // Test that rendering basic content onto a client buffer surface when present path
347 // fast is disabled works as expected, and is also oriented the correct way around
TEST_P(EGLPresentPathD3D11,ClientBufferPresentPathCopy)348 TEST_P(EGLPresentPathD3D11, ClientBufferPresentPathCopy)
349 {
350 initializeEGL(false);
351 createPbufferFromClientBufferSurface();
352 makeCurrent();
353
354 drawQuadUsingGL();
355
356 checkPixelsUsingGL();
357 checkPixelsUsingD3D(false);
358 }
359
360 ANGLE_INSTANTIATE_TEST(EGLPresentPathD3D11, WithNoFixture(ES2_D3D11()));
361