mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-23 05:26:11 +00:00
Tests are based on the IOSurfaceWrappingTests. Sampling tests are not implemented, since they would require support for the samplerExternalOES sampler type in WGSL. Bug: chromium:1205155 Change-Id: Icc114eaf6efaee93f1b8486e615f0fd307f23080 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/50201 Commit-Queue: Stephen White <senorblanco@chromium.org> Reviewed-by: Corentin Wallez <cwallez@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
357 lines
13 KiB
C++
357 lines
13 KiB
C++
// Copyright 2021 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "tests/DawnTest.h"
|
|
|
|
#include "common/DynamicLib.h"
|
|
#include "dawn_native/OpenGLBackend.h"
|
|
#include "dawn_native/opengl/DeviceGL.h"
|
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
#include <EGL/egl.h>
|
|
|
|
namespace {
|
|
|
|
class EGLFunctions {
|
|
public:
|
|
EGLFunctions() {
|
|
#ifdef DAWN_PLATFORM_WINDOWS
|
|
const char* eglLib = "libEGL.dll";
|
|
#else
|
|
const char* eglLib = "libEGL.so";
|
|
#endif
|
|
EXPECT_TRUE(mlibEGL.Open(eglLib));
|
|
CreateImage = reinterpret_cast<PFNEGLCREATEIMAGEPROC>(LoadProc("eglCreateImage"));
|
|
DestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEPROC>(LoadProc("eglDestroyImage"));
|
|
GetCurrentContext =
|
|
reinterpret_cast<PFNEGLGETCURRENTCONTEXTPROC>(LoadProc("eglGetCurrentContext"));
|
|
GetCurrentDisplay =
|
|
reinterpret_cast<PFNEGLGETCURRENTDISPLAYPROC>(LoadProc("eglGetCurrentDisplay"));
|
|
}
|
|
|
|
private:
|
|
void* LoadProc(const char* name) {
|
|
void* proc = mlibEGL.GetProc(name);
|
|
EXPECT_NE(proc, nullptr);
|
|
return proc;
|
|
}
|
|
|
|
public:
|
|
PFNEGLCREATEIMAGEPROC CreateImage;
|
|
PFNEGLDESTROYIMAGEPROC DestroyImage;
|
|
PFNEGLGETCURRENTCONTEXTPROC GetCurrentContext;
|
|
PFNEGLGETCURRENTDISPLAYPROC GetCurrentDisplay;
|
|
|
|
private:
|
|
DynamicLib mlibEGL;
|
|
};
|
|
|
|
class ScopedEGLImage {
|
|
public:
|
|
ScopedEGLImage(PFNEGLDESTROYIMAGEPROC destroyImage,
|
|
PFNGLDELETETEXTURESPROC deleteTextures,
|
|
EGLDisplay display,
|
|
EGLImage image,
|
|
GLuint texture)
|
|
: mDestroyImage(destroyImage),
|
|
mDeleteTextures(deleteTextures),
|
|
mDisplay(display),
|
|
mImage(image),
|
|
mTexture(texture) {
|
|
}
|
|
|
|
ScopedEGLImage(ScopedEGLImage&& other) {
|
|
if (mImage != nullptr) {
|
|
mDestroyImage(mDisplay, mImage);
|
|
}
|
|
if (mTexture != 0) {
|
|
mDeleteTextures(1, &mTexture);
|
|
}
|
|
mDestroyImage = std::move(other.mDestroyImage);
|
|
mDeleteTextures = std::move(other.mDeleteTextures);
|
|
mDisplay = std::move(other.mDisplay);
|
|
mImage = std::move(other.mImage);
|
|
mTexture = std::move(other.mTexture);
|
|
}
|
|
|
|
~ScopedEGLImage() {
|
|
if (mTexture != 0) {
|
|
mDeleteTextures(1, &mTexture);
|
|
}
|
|
if (mImage != nullptr) {
|
|
mDestroyImage(mDisplay, mImage);
|
|
}
|
|
}
|
|
|
|
EGLImage getImage() const {
|
|
return mImage;
|
|
}
|
|
|
|
GLuint getTexture() const {
|
|
return mTexture;
|
|
}
|
|
|
|
private:
|
|
PFNEGLDESTROYIMAGEPROC mDestroyImage = nullptr;
|
|
PFNGLDELETETEXTURESPROC mDeleteTextures = nullptr;
|
|
EGLDisplay mDisplay = nullptr;
|
|
EGLImage mImage = nullptr;
|
|
GLuint mTexture = 0;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
class EGLImageTestBase : public DawnTest {
|
|
public:
|
|
ScopedEGLImage CreateEGLImage(uint32_t width,
|
|
uint32_t height,
|
|
GLenum internalFormat,
|
|
GLenum format,
|
|
GLenum type,
|
|
void* data,
|
|
size_t size) {
|
|
dawn_native::opengl::Device* openglDevice =
|
|
reinterpret_cast<dawn_native::opengl::Device*>(device.Get());
|
|
const dawn_native::opengl::OpenGLFunctions& gl = openglDevice->gl;
|
|
GLuint tex;
|
|
gl.GenTextures(1, &tex);
|
|
gl.BindTexture(GL_TEXTURE_2D, tex);
|
|
gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
|
|
EGLAttrib attribs[1] = {EGL_NONE};
|
|
EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(tex);
|
|
EGLDisplay dpy = egl.GetCurrentDisplay();
|
|
EGLContext ctx = egl.GetCurrentContext();
|
|
EGLImage eglImage = egl.CreateImage(dpy, ctx, EGL_GL_TEXTURE_2D, buffer, attribs);
|
|
EXPECT_NE(nullptr, eglImage);
|
|
|
|
return ScopedEGLImage(egl.DestroyImage, gl.DeleteTextures, dpy, eglImage, tex);
|
|
}
|
|
wgpu::Texture WrapEGLImage(const wgpu::TextureDescriptor* descriptor, EGLImage eglImage) {
|
|
dawn_native::opengl::ExternalImageDescriptorEGLImage externDesc;
|
|
externDesc.cTextureDescriptor = reinterpret_cast<const WGPUTextureDescriptor*>(descriptor);
|
|
externDesc.image = eglImage;
|
|
WGPUTexture texture = dawn_native::opengl::WrapExternalEGLImage(device.Get(), &externDesc);
|
|
return wgpu::Texture::Acquire(texture);
|
|
}
|
|
EGLFunctions egl;
|
|
};
|
|
|
|
// A small fixture used to initialize default data for the EGLImage validation tests.
|
|
// These tests are skipped if the harness is using the wire.
|
|
class EGLImageValidationTests : public EGLImageTestBase {
|
|
public:
|
|
EGLImageValidationTests() {
|
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
|
descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
descriptor.size = {10, 10, 1};
|
|
descriptor.sampleCount = 1;
|
|
descriptor.mipLevelCount = 1;
|
|
descriptor.usage = wgpu::TextureUsage::RenderAttachment;
|
|
}
|
|
|
|
ScopedEGLImage CreateDefaultEGLImage() {
|
|
return CreateEGLImage(10, 10, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, 0);
|
|
}
|
|
|
|
protected:
|
|
wgpu::TextureDescriptor descriptor;
|
|
};
|
|
|
|
// Test a successful wrapping of an EGLImage in a texture
|
|
TEST_P(EGLImageValidationTests, Success) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage());
|
|
ASSERT_NE(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Test an error occurs if the texture descriptor is invalid
|
|
TEST_P(EGLImageValidationTests, InvalidTextureDescriptor) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
|
|
wgpu::ChainedStruct chainedDescriptor;
|
|
descriptor.nextInChain = &chainedDescriptor;
|
|
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Test an error occurs if the descriptor dimension isn't 2D
|
|
TEST_P(EGLImageValidationTests, InvalidTextureDimension) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
descriptor.dimension = wgpu::TextureDimension::e3D;
|
|
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Test an error occurs if the texture usage is not RenderAttachment
|
|
TEST_P(EGLImageValidationTests, InvalidTextureUsage) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
descriptor.usage = wgpu::TextureUsage::Sampled;
|
|
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
wgpu::Texture texture;
|
|
ASSERT_DEVICE_ERROR(texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
descriptor.usage = wgpu::TextureUsage::Storage;
|
|
|
|
ASSERT_DEVICE_ERROR(texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Test an error occurs if the descriptor mip level count isn't 1
|
|
TEST_P(EGLImageValidationTests, InvalidMipLevelCount) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
descriptor.mipLevelCount = 2;
|
|
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Test an error occurs if the descriptor depth isn't 1
|
|
TEST_P(EGLImageValidationTests, InvalidDepth) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
descriptor.size.depthOrArrayLayers = 2;
|
|
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Test an error occurs if the descriptor sample count isn't 1
|
|
TEST_P(EGLImageValidationTests, InvalidSampleCount) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
descriptor.sampleCount = 4;
|
|
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Test an error occurs if the descriptor width doesn't match the surface's
|
|
TEST_P(EGLImageValidationTests, InvalidWidth) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
descriptor.size.width = 11;
|
|
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Test an error occurs if the descriptor height doesn't match the surface's
|
|
TEST_P(EGLImageValidationTests, InvalidHeight) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
descriptor.size.height = 11;
|
|
|
|
ScopedEGLImage image = CreateDefaultEGLImage();
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapEGLImage(&descriptor, image.getImage()));
|
|
ASSERT_EQ(texture.Get(), nullptr);
|
|
}
|
|
|
|
// Fixture to test using EGLImages through different usages.
|
|
// These tests are skipped if the harness is using the wire.
|
|
class EGLImageUsageTests : public EGLImageTestBase {
|
|
public:
|
|
// Test that clearing using BeginRenderPass writes correct data in the eglImage.
|
|
void DoClearTest(EGLImage eglImage,
|
|
GLuint texture,
|
|
wgpu::TextureFormat format,
|
|
GLenum glFormat,
|
|
GLenum glType,
|
|
void* data,
|
|
size_t dataSize) {
|
|
dawn_native::opengl::Device* openglDevice =
|
|
reinterpret_cast<dawn_native::opengl::Device*>(device.Get());
|
|
const dawn_native::opengl::OpenGLFunctions& gl = openglDevice->gl;
|
|
|
|
// Get a texture view for the eglImage
|
|
wgpu::TextureDescriptor textureDescriptor;
|
|
textureDescriptor.dimension = wgpu::TextureDimension::e2D;
|
|
textureDescriptor.format = format;
|
|
textureDescriptor.size = {1, 1, 1};
|
|
textureDescriptor.sampleCount = 1;
|
|
textureDescriptor.mipLevelCount = 1;
|
|
textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
|
|
wgpu::Texture eglImageTexture = WrapEGLImage(&textureDescriptor, eglImage);
|
|
ASSERT_NE(eglImageTexture, nullptr);
|
|
|
|
wgpu::TextureView eglImageView = eglImageTexture.CreateView();
|
|
|
|
utils::ComboRenderPassDescriptor renderPassDescriptor({eglImageView}, {});
|
|
renderPassDescriptor.cColorAttachments[0].clearColor = {1 / 255.0f, 2 / 255.0f, 3 / 255.0f,
|
|
4 / 255.0f};
|
|
|
|
// Execute commands to clear the eglImage
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
|
|
pass.EndPass();
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
// Check the correct data was written
|
|
std::vector<uint8_t> result(dataSize);
|
|
GLuint fbo;
|
|
gl.GenFramebuffers(1, &fbo);
|
|
gl.BindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
|
|
0);
|
|
gl.ReadPixels(0, 0, 1, 1, glFormat, glType, result.data());
|
|
gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
gl.DeleteFramebuffers(1, &fbo);
|
|
ASSERT_EQ(0, memcmp(result.data(), data, dataSize));
|
|
}
|
|
};
|
|
|
|
// Test clearing a R8 EGLImage
|
|
TEST_P(EGLImageUsageTests, ClearR8EGLImage) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
ScopedEGLImage eglImage = CreateEGLImage(1, 1, GL_R8, GL_RED, GL_UNSIGNED_BYTE, nullptr, 0);
|
|
|
|
uint8_t data = 0x01;
|
|
DoClearTest(eglImage.getImage(), eglImage.getTexture(), wgpu::TextureFormat::R8Unorm, GL_RED,
|
|
GL_UNSIGNED_BYTE, &data, sizeof(data));
|
|
}
|
|
|
|
// Test clearing a RG8 EGLImage
|
|
TEST_P(EGLImageUsageTests, ClearRG8EGLImage) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
ScopedEGLImage eglImage = CreateEGLImage(1, 1, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, nullptr, 0);
|
|
|
|
uint16_t data = 0x0201;
|
|
DoClearTest(eglImage.getImage(), eglImage.getTexture(), wgpu::TextureFormat::RG8Unorm, GL_RG,
|
|
GL_UNSIGNED_BYTE, &data, sizeof(data));
|
|
}
|
|
|
|
// Test clearing an RGBA8 EGLImage
|
|
TEST_P(EGLImageUsageTests, ClearRGBA8EGLImage) {
|
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
|
ScopedEGLImage eglImage = CreateEGLImage(1, 1, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, 0);
|
|
|
|
uint32_t data = 0x04030201;
|
|
DoClearTest(eglImage.getImage(), eglImage.getTexture(), wgpu::TextureFormat::RGBA8Unorm,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, &data, sizeof(data));
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(EGLImageValidationTests, OpenGLESBackend());
|
|
DAWN_INSTANTIATE_TEST(EGLImageUsageTests, OpenGLESBackend());
|