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 // D3DTextureTest:
7 //   Tests of the EGL_ANGLE_d3d_texture_client_buffer extension
8 
9 #include "test_utils/ANGLETest.h"
10 
11 #include <d3d11.h>
12 #include <windows.h>
13 
14 #include "com_utils.h"
15 
16 namespace angle
17 {
18 
19 class D3DTextureTest : public ANGLETest
20 {
21   protected:
D3DTextureTest()22     D3DTextureTest()
23     {
24         setWindowWidth(128);
25         setWindowHeight(128);
26         setConfigRedBits(8);
27         setConfigGreenBits(8);
28         setConfigBlueBits(8);
29         setConfigAlphaBits(8);
30     }
31 
SetUp()32     void SetUp() override
33     {
34         ANGLETest::SetUp();
35 
36         // clang-format off
37         const std::string vsSource = SHADER_SOURCE
38         (
39             precision highp float;
40             attribute vec4 position;
41             varying vec2 texcoord;
42 
43             void main()
44             {
45                 gl_Position = position;
46                 texcoord = (position.xy * 0.5) + 0.5;
47                 texcoord.y = 1.0 - texcoord.y;
48             }
49         );
50 
51         const std::string textureFSSource = SHADER_SOURCE
52         (
53             precision highp float;
54             uniform sampler2D tex;
55             varying vec2 texcoord;
56 
57             void main()
58             {
59                 gl_FragColor = texture2D(tex, texcoord);
60             }
61         );
62         // clang-format on
63 
64         mTextureProgram = CompileProgram(vsSource, textureFSSource);
65         ASSERT_NE(0u, mTextureProgram) << "shader compilation failed.";
66 
67         mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex");
68         ASSERT_NE(-1, mTextureUniformLocation);
69 
70         mD3D11Module = LoadLibrary(TEXT("d3d11.dll"));
71         ASSERT_NE(nullptr, mD3D11Module);
72 
73         PFN_D3D11_CREATE_DEVICE createDeviceFunc = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(
74             GetProcAddress(mD3D11Module, "D3D11CreateDevice"));
75 
76         EGLWindow *window  = getEGLWindow();
77         EGLDisplay display = window->getDisplay();
78         if (eglDisplayExtensionEnabled(display, "EGL_EXT_device_query"))
79         {
80             PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT =
81                 reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
82                     eglGetProcAddress("eglQueryDisplayAttribEXT"));
83             PFNEGLQUERYDEVICEATTRIBEXTPROC eglQueryDeviceAttribEXT =
84                 reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
85                     eglGetProcAddress("eglQueryDeviceAttribEXT"));
86 
87             EGLDeviceEXT device = 0;
88             {
89                 EGLAttrib result = 0;
90                 EXPECT_EGL_TRUE(eglQueryDisplayAttribEXT(display, EGL_DEVICE_EXT, &result));
91                 device = reinterpret_cast<EGLDeviceEXT>(result);
92             }
93 
94             if (eglDeviceExtensionEnabled(device, "EGL_ANGLE_device_d3d"))
95             {
96                 EGLAttrib result = 0;
97                 if (eglQueryDeviceAttribEXT(device, EGL_D3D11_DEVICE_ANGLE, &result))
98                 {
99                     mD3D11Device = reinterpret_cast<ID3D11Device *>(result);
100                     mD3D11Device->AddRef();
101                 }
102                 else if (eglQueryDeviceAttribEXT(device, EGL_D3D9_DEVICE_ANGLE, &result))
103                 {
104                     mD3D9Device = reinterpret_cast<IDirect3DDevice9 *>(result);
105                     mD3D9Device->AddRef();
106                 }
107             }
108         }
109         else
110         {
111             ASSERT_TRUE(
112                 SUCCEEDED(createDeviceFunc(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr,
113                                            0, D3D11_SDK_VERSION, &mD3D11Device, nullptr, nullptr)));
114         }
115     }
116 
TearDown()117     void TearDown() override
118     {
119         glDeleteProgram(mTextureProgram);
120 
121         if (mD3D11Device)
122         {
123             mD3D11Device->Release();
124             mD3D11Device = nullptr;
125         }
126 
127         FreeLibrary(mD3D11Module);
128         mD3D11Module = nullptr;
129 
130         if (mD3D9Device)
131         {
132             mD3D9Device->Release();
133             mD3D9Device = nullptr;
134         }
135 
136         ANGLETest::TearDown();
137     }
138 
createPBuffer(size_t width,size_t height,EGLint eglTextureFormat,EGLint eglTextureTarget)139     EGLSurface createPBuffer(size_t width,
140                              size_t height,
141                              EGLint eglTextureFormat,
142                              EGLint eglTextureTarget)
143     {
144         EGLWindow *window  = getEGLWindow();
145         EGLDisplay display = window->getDisplay();
146         EGLConfig config   = window->getConfig();
147 
148         EGLint attribs[] = {
149             EGL_TEXTURE_FORMAT, eglTextureFormat, EGL_TEXTURE_TARGET,
150             eglTextureTarget,   EGL_NONE,         EGL_NONE,
151         };
152 
153         if (mD3D11Device)
154         {
155             ID3D11Texture2D *texture = nullptr;
156             CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, static_cast<UINT>(width),
157                                        static_cast<UINT>(height), 1, 1,
158                                        D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
159             EXPECT_TRUE(SUCCEEDED(mD3D11Device->CreateTexture2D(&desc, nullptr, &texture)));
160 
161             EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
162                                                                   texture, config, attribs);
163 
164             texture->Release();
165 
166             return pbuffer;
167         }
168         else if (mD3D9Device)
169         {
170             IDirect3DTexture9 *texture = nullptr;
171             EXPECT_TRUE(SUCCEEDED(mD3D9Device->CreateTexture(
172                 static_cast<UINT>(width), static_cast<UINT>(height), 1, D3DUSAGE_RENDERTARGET,
173                 D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture, nullptr)));
174 
175             EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
176                                                                   texture, config, attribs);
177 
178             texture->Release();
179 
180             return pbuffer;
181         }
182         else
183         {
184             return EGL_NO_SURFACE;
185         }
186     }
187 
valid() const188     bool valid() const
189     {
190         EGLWindow *window  = getEGLWindow();
191         EGLDisplay display = window->getDisplay();
192         if (!eglDisplayExtensionEnabled(display, "EGL_ANGLE_d3d_texture_client_buffer"))
193         {
194             std::cout << "Test skipped due to missing EGL_ANGLE_d3d_texture_client_buffer"
195                       << std::endl;
196             return false;
197         }
198 
199         if (!mD3D11Device && !mD3D9Device)
200         {
201             std::cout << "Test skipped due to no D3D devices being available." << std::endl;
202             return false;
203         }
204 
205         if (IsWindows() && IsAMD() && IsOpenGL())
206         {
207             std::cout << "Test skipped on Windows AMD OpenGL." << std::endl;
208             return false;
209         }
210 
211         if (IsWindows() && IsIntel() && IsOpenGL())
212         {
213             std::cout << "Test skipped on Windows Intel OpenGL." << std::endl;
214             return false;
215         }
216         return true;
217     }
218 
219     GLuint mTextureProgram;
220     GLint mTextureUniformLocation;
221 
222     HMODULE mD3D11Module       = nullptr;
223     ID3D11Device *mD3D11Device = nullptr;
224 
225     IDirect3DDevice9 *mD3D9Device = nullptr;
226 };
227 
228 // Test creating a pbuffer from a d3d surface and clearing it
TEST_P(D3DTextureTest,Clear)229 TEST_P(D3DTextureTest, Clear)
230 {
231     if (!valid())
232     {
233         return;
234     }
235 
236     EGLWindow *window  = getEGLWindow();
237     EGLDisplay display = window->getDisplay();
238 
239     const size_t bufferSize = 32;
240 
241     EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_NO_TEXTURE, EGL_NO_TEXTURE);
242     ASSERT_EGL_SUCCESS();
243     ASSERT_NE(pbuffer, EGL_NO_SURFACE);
244 
245     // Apply the Pbuffer and clear it to purple and verify
246     eglMakeCurrent(display, pbuffer, pbuffer, window->getContext());
247     ASSERT_EGL_SUCCESS();
248 
249     glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
250     glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
251     glClear(GL_COLOR_BUFFER_BIT);
252     ASSERT_GL_NO_ERROR();
253     EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 255, 0,
254                     255, 255);
255 
256     eglDestroySurface(display, pbuffer);
257 }
258 
259 // Test creating a pbuffer from a d3d surface and binding it to a texture
TEST_P(D3DTextureTest,BindTexImage)260 TEST_P(D3DTextureTest, BindTexImage)
261 {
262     if (!valid())
263     {
264         return;
265     }
266 
267     EGLWindow *window = getEGLWindow();
268 
269     const size_t bufferSize = 32;
270 
271     EGLSurface pbuffer = createPBuffer(bufferSize, bufferSize, EGL_TEXTURE_RGBA, EGL_TEXTURE_2D);
272     ASSERT_EGL_SUCCESS();
273     ASSERT_NE(pbuffer, EGL_NO_SURFACE);
274 
275     // Apply the Pbuffer and clear it to purple
276     eglMakeCurrent(window->getDisplay(), pbuffer, pbuffer, window->getContext());
277     ASSERT_EGL_SUCCESS();
278 
279     glViewport(0, 0, static_cast<GLsizei>(bufferSize), static_cast<GLsizei>(bufferSize));
280     glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
281     glClear(GL_COLOR_BUFFER_BIT);
282     ASSERT_GL_NO_ERROR();
283 
284     EXPECT_PIXEL_EQ(static_cast<GLint>(bufferSize) / 2, static_cast<GLint>(bufferSize) / 2, 255, 0,
285                     255, 255);
286 
287     // Apply the window surface
288     eglMakeCurrent(window->getDisplay(), window->getSurface(), window->getSurface(),
289                    window->getContext());
290 
291     // Create a texture and bind the Pbuffer to it
292     GLuint texture = 0;
293     glGenTextures(1, &texture);
294     glBindTexture(GL_TEXTURE_2D, texture);
295     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
296     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
297     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
298     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
299     EXPECT_GL_NO_ERROR();
300 
301     eglBindTexImage(window->getDisplay(), pbuffer, EGL_BACK_BUFFER);
302     glViewport(0, 0, getWindowWidth(), getWindowHeight());
303     ASSERT_EGL_SUCCESS();
304 
305     // Draw a quad and verify that it is purple
306     glUseProgram(mTextureProgram);
307     glUniform1i(mTextureUniformLocation, 0);
308 
309     drawQuad(mTextureProgram, "position", 0.5f);
310     EXPECT_GL_NO_ERROR();
311 
312     // Unbind the texture
313     eglReleaseTexImage(window->getDisplay(), pbuffer, EGL_BACK_BUFFER);
314     ASSERT_EGL_SUCCESS();
315 
316     // Verify that purple was drawn
317     EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 0, 255, 255);
318 
319     glDeleteTextures(1, &texture);
320 }
321 
322 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
323 // tests should be run against.
324 ANGLE_INSTANTIATE_TEST(D3DTextureTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL());
325 
326 }  // namespace
327