• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/DawnTest.h"
16 
17 #include "common/DynamicLib.h"
18 #include "dawn_native/OpenGLBackend.h"
19 #include "dawn_native/opengl/DeviceGL.h"
20 #include "utils/ComboRenderPipelineDescriptor.h"
21 #include "utils/WGPUHelpers.h"
22 
23 #include <EGL/egl.h>
24 
25 namespace {
26 
27     class EGLFunctions {
28       public:
EGLFunctions()29         EGLFunctions() {
30 #ifdef DAWN_PLATFORM_WINDOWS
31             const char* eglLib = "libEGL.dll";
32 #else
33             const char* eglLib = "libEGL.so";
34 #endif
35             EXPECT_TRUE(mlibEGL.Open(eglLib));
36             CreateImage = reinterpret_cast<PFNEGLCREATEIMAGEPROC>(LoadProc("eglCreateImage"));
37             DestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEPROC>(LoadProc("eglDestroyImage"));
38             GetCurrentContext =
39                 reinterpret_cast<PFNEGLGETCURRENTCONTEXTPROC>(LoadProc("eglGetCurrentContext"));
40             GetCurrentDisplay =
41                 reinterpret_cast<PFNEGLGETCURRENTDISPLAYPROC>(LoadProc("eglGetCurrentDisplay"));
42         }
43 
44       private:
LoadProc(const char * name)45         void* LoadProc(const char* name) {
46             void* proc = mlibEGL.GetProc(name);
47             EXPECT_NE(proc, nullptr);
48             return proc;
49         }
50 
51       public:
52         PFNEGLCREATEIMAGEPROC CreateImage;
53         PFNEGLDESTROYIMAGEPROC DestroyImage;
54         PFNEGLGETCURRENTCONTEXTPROC GetCurrentContext;
55         PFNEGLGETCURRENTDISPLAYPROC GetCurrentDisplay;
56 
57       private:
58         DynamicLib mlibEGL;
59     };
60 
61     class ScopedEGLImage {
62       public:
ScopedEGLImage(PFNEGLDESTROYIMAGEPROC destroyImage,PFNGLDELETETEXTURESPROC deleteTextures,EGLDisplay display,EGLImage image,GLuint texture)63         ScopedEGLImage(PFNEGLDESTROYIMAGEPROC destroyImage,
64                        PFNGLDELETETEXTURESPROC deleteTextures,
65                        EGLDisplay display,
66                        EGLImage image,
67                        GLuint texture)
68             : mDestroyImage(destroyImage),
69               mDeleteTextures(deleteTextures),
70               mDisplay(display),
71               mImage(image),
72               mTexture(texture) {
73         }
74 
ScopedEGLImage(ScopedEGLImage && other)75         ScopedEGLImage(ScopedEGLImage&& other) {
76             if (mImage != nullptr) {
77                 mDestroyImage(mDisplay, mImage);
78             }
79             if (mTexture != 0) {
80                 mDeleteTextures(1, &mTexture);
81             }
82             mDestroyImage = std::move(other.mDestroyImage);
83             mDeleteTextures = std::move(other.mDeleteTextures);
84             mDisplay = std::move(other.mDisplay);
85             mImage = std::move(other.mImage);
86             mTexture = std::move(other.mTexture);
87         }
88 
~ScopedEGLImage()89         ~ScopedEGLImage() {
90             if (mTexture != 0) {
91                 mDeleteTextures(1, &mTexture);
92             }
93             if (mImage != nullptr) {
94                 mDestroyImage(mDisplay, mImage);
95             }
96         }
97 
getImage() const98         EGLImage getImage() const {
99             return mImage;
100         }
101 
getTexture() const102         GLuint getTexture() const {
103             return mTexture;
104         }
105 
106       private:
107         PFNEGLDESTROYIMAGEPROC mDestroyImage = nullptr;
108         PFNGLDELETETEXTURESPROC mDeleteTextures = nullptr;
109         EGLDisplay mDisplay = nullptr;
110         EGLImage mImage = nullptr;
111         GLuint mTexture = 0;
112     };
113 
114 }  // anonymous namespace
115 
116 class EGLImageTestBase : public DawnTest {
117   protected:
GetRequiredFeatures()118     std::vector<const char*> GetRequiredFeatures() override {
119         return {"dawn-internal-usages"};
120     }
121 
122   public:
CreateEGLImage(uint32_t width,uint32_t height,GLenum internalFormat,GLenum format,GLenum type,void * data,size_t size)123     ScopedEGLImage CreateEGLImage(uint32_t width,
124                                   uint32_t height,
125                                   GLenum internalFormat,
126                                   GLenum format,
127                                   GLenum type,
128                                   void* data,
129                                   size_t size) {
130         dawn_native::opengl::Device* openglDevice =
131             dawn_native::opengl::ToBackend(dawn_native::FromAPI(device.Get()));
132         const dawn_native::opengl::OpenGLFunctions& gl = openglDevice->gl;
133         GLuint tex;
134         gl.GenTextures(1, &tex);
135         gl.BindTexture(GL_TEXTURE_2D, tex);
136         gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
137         EGLAttrib attribs[1] = {EGL_NONE};
138         EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(tex);
139         EGLDisplay dpy = egl.GetCurrentDisplay();
140         EGLContext ctx = egl.GetCurrentContext();
141         EGLImage eglImage = egl.CreateImage(dpy, ctx, EGL_GL_TEXTURE_2D, buffer, attribs);
142         EXPECT_NE(nullptr, eglImage);
143 
144         return ScopedEGLImage(egl.DestroyImage, gl.DeleteTextures, dpy, eglImage, tex);
145     }
WrapEGLImage(const wgpu::TextureDescriptor * descriptor,EGLImage eglImage)146     wgpu::Texture WrapEGLImage(const wgpu::TextureDescriptor* descriptor, EGLImage eglImage) {
147         dawn_native::opengl::ExternalImageDescriptorEGLImage externDesc;
148         externDesc.cTextureDescriptor = reinterpret_cast<const WGPUTextureDescriptor*>(descriptor);
149         externDesc.image = eglImage;
150         WGPUTexture texture = dawn_native::opengl::WrapExternalEGLImage(device.Get(), &externDesc);
151         return wgpu::Texture::Acquire(texture);
152     }
153     EGLFunctions egl;
154 };
155 
156 // A small fixture used to initialize default data for the EGLImage validation tests.
157 // These tests are skipped if the harness is using the wire.
158 class EGLImageValidationTests : public EGLImageTestBase {
159   public:
EGLImageValidationTests()160     EGLImageValidationTests() {
161         descriptor.dimension = wgpu::TextureDimension::e2D;
162         descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
163         descriptor.size = {10, 10, 1};
164         descriptor.sampleCount = 1;
165         descriptor.mipLevelCount = 1;
166         descriptor.usage = wgpu::TextureUsage::RenderAttachment;
167     }
168 
CreateDefaultEGLImage()169     ScopedEGLImage CreateDefaultEGLImage() {
170         return CreateEGLImage(10, 10, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, 0);
171     }
172 
173   protected:
174     wgpu::TextureDescriptor descriptor;
175 };
176 
177 // Test a successful wrapping of an EGLImage in a texture
TEST_P(EGLImageValidationTests,Success)178 TEST_P(EGLImageValidationTests, Success) {
179     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
180     ScopedEGLImage image = CreateDefaultEGLImage();
181     wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage());
182     ASSERT_NE(texture.Get(), nullptr);
183 }
184 
185 // Test a successful wrapping of an EGLImage in a texture with DawnTextureInternalUsageDescriptor
TEST_P(EGLImageValidationTests,SuccessWithInternalUsageDescriptor)186 TEST_P(EGLImageValidationTests, SuccessWithInternalUsageDescriptor) {
187     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
188     wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
189     descriptor.nextInChain = &internalDesc;
190     internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
191     internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
192 
193     ScopedEGLImage image = CreateDefaultEGLImage();
194     wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage());
195     ASSERT_NE(texture.Get(), nullptr);
196 }
197 
198 // Test an error occurs if an invalid sType is the nextInChain
TEST_P(EGLImageValidationTests,InvalidTextureDescriptor)199 TEST_P(EGLImageValidationTests, InvalidTextureDescriptor) {
200     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
201 
202     wgpu::ChainedStruct chainedDescriptor;
203     chainedDescriptor.sType = wgpu::SType::SurfaceDescriptorFromWindowsSwapChainPanel;
204     descriptor.nextInChain = &chainedDescriptor;
205 
206     ScopedEGLImage image = CreateDefaultEGLImage();
207     ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
208 
209     ASSERT_EQ(texture.Get(), nullptr);
210 }
211 
212 // Test an error occurs if the descriptor dimension isn't 2D
TEST_P(EGLImageValidationTests,InvalidTextureDimension)213 TEST_P(EGLImageValidationTests, InvalidTextureDimension) {
214     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
215     descriptor.dimension = wgpu::TextureDimension::e3D;
216 
217     ScopedEGLImage image = CreateDefaultEGLImage();
218     ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
219 
220     ASSERT_EQ(texture.Get(), nullptr);
221 }
222 
223 // Test an error occurs if the texture usage is not RenderAttachment
TEST_P(EGLImageValidationTests,InvalidTextureUsage)224 TEST_P(EGLImageValidationTests, InvalidTextureUsage) {
225     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
226     descriptor.usage = wgpu::TextureUsage::TextureBinding;
227 
228     ScopedEGLImage image = CreateDefaultEGLImage();
229     wgpu::Texture texture;
230     ASSERT_DEVICE_ERROR(texture = WrapEGLImage(&descriptor, image.getImage()));
231 
232     ASSERT_EQ(texture.Get(), nullptr);
233     descriptor.usage = wgpu::TextureUsage::StorageBinding;
234 
235     ASSERT_DEVICE_ERROR(texture = WrapEGLImage(&descriptor, image.getImage()));
236 
237     ASSERT_EQ(texture.Get(), nullptr);
238 }
239 
240 // Test an error occurs if the descriptor mip level count isn't 1
TEST_P(EGLImageValidationTests,InvalidMipLevelCount)241 TEST_P(EGLImageValidationTests, InvalidMipLevelCount) {
242     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
243     descriptor.mipLevelCount = 2;
244 
245     ScopedEGLImage image = CreateDefaultEGLImage();
246     ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
247     ASSERT_EQ(texture.Get(), nullptr);
248 }
249 
250 // Test an error occurs if the descriptor depth isn't 1
TEST_P(EGLImageValidationTests,InvalidDepth)251 TEST_P(EGLImageValidationTests, InvalidDepth) {
252     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
253     descriptor.size.depthOrArrayLayers = 2;
254 
255     ScopedEGLImage image = CreateDefaultEGLImage();
256     ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
257     ASSERT_EQ(texture.Get(), nullptr);
258 }
259 
260 // Test an error occurs if the descriptor sample count isn't 1
TEST_P(EGLImageValidationTests,InvalidSampleCount)261 TEST_P(EGLImageValidationTests, InvalidSampleCount) {
262     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
263     descriptor.sampleCount = 4;
264 
265     ScopedEGLImage image = CreateDefaultEGLImage();
266     ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
267     ASSERT_EQ(texture.Get(), nullptr);
268 }
269 
270 // Test an error occurs if the descriptor width doesn't match the surface's
TEST_P(EGLImageValidationTests,InvalidWidth)271 TEST_P(EGLImageValidationTests, InvalidWidth) {
272     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
273     descriptor.size.width = 11;
274 
275     ScopedEGLImage image = CreateDefaultEGLImage();
276     ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
277     ASSERT_EQ(texture.Get(), nullptr);
278 }
279 
280 // Test an error occurs if the descriptor height doesn't match the surface's
TEST_P(EGLImageValidationTests,InvalidHeight)281 TEST_P(EGLImageValidationTests, InvalidHeight) {
282     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
283     descriptor.size.height = 11;
284 
285     ScopedEGLImage image = CreateDefaultEGLImage();
286     ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
287     ASSERT_EQ(texture.Get(), nullptr);
288 }
289 
290 // Fixture to test using EGLImages through different usages.
291 // These tests are skipped if the harness is using the wire.
292 class EGLImageUsageTests : public EGLImageTestBase {
293   public:
294     // Test that clearing using BeginRenderPass writes correct data in the eglImage.
DoClearTest(EGLImage eglImage,GLuint texture,wgpu::TextureFormat format,GLenum glFormat,GLenum glType,void * data,size_t dataSize)295     void DoClearTest(EGLImage eglImage,
296                      GLuint texture,
297                      wgpu::TextureFormat format,
298                      GLenum glFormat,
299                      GLenum glType,
300                      void* data,
301                      size_t dataSize) {
302         dawn_native::opengl::Device* openglDevice =
303             dawn_native::opengl::ToBackend(dawn_native::FromAPI(device.Get()));
304         const dawn_native::opengl::OpenGLFunctions& gl = openglDevice->gl;
305 
306         // Get a texture view for the eglImage
307         wgpu::TextureDescriptor textureDescriptor;
308         textureDescriptor.dimension = wgpu::TextureDimension::e2D;
309         textureDescriptor.format = format;
310         textureDescriptor.size = {1, 1, 1};
311         textureDescriptor.sampleCount = 1;
312         textureDescriptor.mipLevelCount = 1;
313         textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
314 
315         wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
316         textureDescriptor.nextInChain = &internalDesc;
317         internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
318         internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
319 
320         wgpu::Texture eglImageTexture = WrapEGLImage(&textureDescriptor, eglImage);
321         ASSERT_NE(eglImageTexture, nullptr);
322 
323         wgpu::TextureView eglImageView = eglImageTexture.CreateView();
324 
325         utils::ComboRenderPassDescriptor renderPassDescriptor({eglImageView}, {});
326         renderPassDescriptor.cColorAttachments[0].clearColor = {1 / 255.0f, 2 / 255.0f, 3 / 255.0f,
327                                                                 4 / 255.0f};
328 
329         // Execute commands to clear the eglImage
330         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
331         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
332         pass.EndPass();
333 
334         wgpu::CommandBuffer commands = encoder.Finish();
335         queue.Submit(1, &commands);
336 
337         // Check the correct data was written
338         std::vector<uint8_t> result(dataSize);
339         GLuint fbo;
340         gl.GenFramebuffers(1, &fbo);
341         gl.BindFramebuffer(GL_FRAMEBUFFER, fbo);
342         gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
343                                 0);
344         gl.ReadPixels(0, 0, 1, 1, glFormat, glType, result.data());
345         gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
346         gl.DeleteFramebuffers(1, &fbo);
347         ASSERT_EQ(0, memcmp(result.data(), data, dataSize));
348     }
349 };
350 
351 // Test clearing a R8 EGLImage
TEST_P(EGLImageUsageTests,ClearR8EGLImage)352 TEST_P(EGLImageUsageTests, ClearR8EGLImage) {
353     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
354     ScopedEGLImage eglImage = CreateEGLImage(1, 1, GL_R8, GL_RED, GL_UNSIGNED_BYTE, nullptr, 0);
355 
356     uint8_t data = 0x01;
357     DoClearTest(eglImage.getImage(), eglImage.getTexture(), wgpu::TextureFormat::R8Unorm, GL_RED,
358                 GL_UNSIGNED_BYTE, &data, sizeof(data));
359 }
360 
361 // Test clearing a RG8 EGLImage
TEST_P(EGLImageUsageTests,ClearRG8EGLImage)362 TEST_P(EGLImageUsageTests, ClearRG8EGLImage) {
363     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
364     ScopedEGLImage eglImage = CreateEGLImage(1, 1, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, nullptr, 0);
365 
366     uint16_t data = 0x0201;
367     DoClearTest(eglImage.getImage(), eglImage.getTexture(), wgpu::TextureFormat::RG8Unorm, GL_RG,
368                 GL_UNSIGNED_BYTE, &data, sizeof(data));
369 }
370 
371 // Test clearing an RGBA8 EGLImage
TEST_P(EGLImageUsageTests,ClearRGBA8EGLImage)372 TEST_P(EGLImageUsageTests, ClearRGBA8EGLImage) {
373     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
374     ScopedEGLImage eglImage = CreateEGLImage(1, 1, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, 0);
375 
376     uint32_t data = 0x04030201;
377     DoClearTest(eglImage.getImage(), eglImage.getTexture(), wgpu::TextureFormat::RGBA8Unorm,
378                 GL_RGBA, GL_UNSIGNED_BYTE, &data, sizeof(data));
379 }
380 
381 DAWN_INSTANTIATE_TEST(EGLImageValidationTests, OpenGLESBackend());
382 DAWN_INSTANTIATE_TEST(EGLImageUsageTests, OpenGLESBackend());
383