Commit 18fcb4ad by Mohan Maiya Committed by Commit Bot

Vulkan: Enable a subset of ImageTest colorspace tests

This patch does the following - 1. Expose EGL_KHR_image_gl_colorspace only if the underlying ICD supports VK_KHR_image_format_list extension 2. Create VkImageViews from EGLimage's format 3. Fix bugs in ImageTest colorspace tests Bug: angleproject:3756 Bug: angleproject:5281 Test: angle_end2end_tests.exe --gtest_filter=ImageTest*Colorspace*Vulkan Change-Id: I618ae009e83d0a6b27a0a50165f736784717e404 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2523670 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent e2ecb2dd
......@@ -212,7 +212,8 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const
getRenderer()->getFeatures().supportsAndroidHardwareBuffer.enabled;
outExtensions->surfacelessContext = true;
outExtensions->glColorspace = getRenderer()->getFeatures().supportsSwapchainColorspace.enabled;
outExtensions->imageGlColorspace = outExtensions->glColorspace;
outExtensions->imageGlColorspace =
outExtensions->glColorspace && getRenderer()->getFeatures().supportsImageFormatList.enabled;
#if defined(ANGLE_PLATFORM_ANDROID)
outExtensions->framebufferTargetANDROID = true;
......
......@@ -3660,6 +3660,22 @@ angle::Result ImageHelper::initLayerImageView(Context *context,
mFormat->vkImageFormat, nullptr);
}
angle::Result ImageHelper::initLayerImageViewWithFormat(Context *context,
gl::TextureType textureType,
const Format &format,
VkImageAspectFlags aspectMask,
const gl::SwizzleState &swizzleMap,
ImageView *imageViewOut,
LevelIndex baseMipLevelVk,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) const
{
return initLayerImageViewImpl(context, textureType, aspectMask, swizzleMap, imageViewOut,
baseMipLevelVk, levelCount, baseArrayLayer, layerCount,
format.vkImageFormat, nullptr);
}
angle::Result ImageHelper::initLayerImageViewImpl(
Context *context,
gl::TextureType textureType,
......@@ -6288,19 +6304,19 @@ angle::Result ImageViewHelper::initReadViewsImpl(ContextVk *contextVk,
if (HasBothDepthAndStencilAspects(aspectFlags))
{
ANGLE_TRY(image.initLayerImageView(contextVk, viewType, VK_IMAGE_ASPECT_DEPTH_BIT,
readSwizzle, &getReadImageView(), baseLevel, levelCount,
baseLayer, layerCount));
ANGLE_TRY(image.initLayerImageView(contextVk, viewType, VK_IMAGE_ASPECT_STENCIL_BIT,
readSwizzle,
&mPerLevelStencilReadImageViews[mCurrentMaxLevel.get()],
baseLevel, levelCount, baseLayer, layerCount));
ANGLE_TRY(image.initLayerImageViewWithFormat(
contextVk, viewType, format, VK_IMAGE_ASPECT_DEPTH_BIT, readSwizzle,
&getReadImageView(), baseLevel, levelCount, baseLayer, layerCount));
ANGLE_TRY(image.initLayerImageViewWithFormat(
contextVk, viewType, format, VK_IMAGE_ASPECT_STENCIL_BIT, readSwizzle,
&mPerLevelStencilReadImageViews[mCurrentMaxLevel.get()], baseLevel, levelCount,
baseLayer, layerCount));
}
else
{
ANGLE_TRY(image.initLayerImageView(contextVk, viewType, aspectFlags, readSwizzle,
&getReadImageView(), baseLevel, levelCount, baseLayer,
layerCount));
ANGLE_TRY(image.initLayerImageViewWithFormat(contextVk, viewType, format, aspectFlags,
readSwizzle, &getReadImageView(), baseLevel,
levelCount, baseLayer, layerCount));
}
gl::TextureType fetchType = viewType;
......@@ -6310,14 +6326,14 @@ angle::Result ImageViewHelper::initReadViewsImpl(ContextVk *contextVk,
{
fetchType = Get2DTextureType(layerCount, image.getSamples());
ANGLE_TRY(image.initLayerImageView(contextVk, fetchType, aspectFlags, readSwizzle,
&getFetchImageView(), baseLevel, levelCount, baseLayer,
layerCount));
ANGLE_TRY(image.initLayerImageViewWithFormat(contextVk, fetchType, format, aspectFlags,
readSwizzle, &getFetchImageView(), baseLevel,
levelCount, baseLayer, layerCount));
}
ANGLE_TRY(image.initLayerImageView(contextVk, fetchType, aspectFlags, formatSwizzle,
&getCopyImageView(), baseLevel, levelCount, baseLayer,
layerCount));
ANGLE_TRY(image.initLayerImageViewWithFormat(contextVk, fetchType, format, aspectFlags,
formatSwizzle, &getCopyImageView(), baseLevel,
levelCount, baseLayer, layerCount));
return angle::Result::Continue;
}
......
......@@ -1302,6 +1302,16 @@ class ImageHelper final : public Resource, public angle::Subject
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) const;
angle::Result initLayerImageViewWithFormat(Context *context,
gl::TextureType textureType,
const Format &format,
VkImageAspectFlags aspectMask,
const gl::SwizzleState &swizzleMap,
ImageView *imageViewOut,
LevelIndex baseMipLevelVk,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) const;
angle::Result initAliasedLayerImageView(Context *context,
gl::TextureType textureType,
VkImageAspectFlags aspectMask,
......
......@@ -75,6 +75,19 @@ constexpr EGLint kNativeClientBufferAttribs_RGBA8_Renderbuffer[] = {
EGL_NATIVE_BUFFER_USAGE_ANDROID,
EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID,
EGL_NONE};
// Color data in linear and sRGB colorspace
// 2D texture data
GLubyte kLinearColor[] = {132, 55, 219, 255};
GLubyte kSrgbColor[] = {59, 10, 180, 255};
// 3D texture data
GLubyte kLinearColor3D[] = {131, 242, 100, 255, 201, 89, 133, 255};
GLubyte kSrgbColor3D[] = {58, 226, 32, 255, 149, 26, 60, 255};
// Cubemap texture data
GLubyte kLinearColorCube[] = {75, 135, 205, 255, 201, 89, 133, 255, 111, 201, 108, 255,
30, 90, 230, 255, 180, 210, 70, 255, 77, 111, 99, 255};
GLubyte kSrgbColorCube[] = {18, 62, 155, 255, 149, 26, 60, 255, 41, 149, 38, 255,
3, 26, 202, 255, 117, 164, 16, 255, 19, 41, 32, 255};
constexpr int kColorspaceAttributeIndex = 2;
} // anonymous namespace
class ImageTest : public ANGLETest
......@@ -601,7 +614,7 @@ class ImageTest : public ANGLETest
void SourceRenderbufferTargetTextureExternalESSL3_helper(const EGLint *attribs);
void verifyResultsTexture(GLuint texture,
GLubyte data[4],
GLubyte referenceColor[4],
GLenum textureTarget,
GLuint program,
GLuint textureUniform)
......@@ -613,8 +626,10 @@ class ImageTest : public ANGLETest
drawQuad(program, "position", 0.5f);
// Expect that the rendered quad has the same color as the source texture
EXPECT_PIXEL_EQ(0, 0, data[0], data[1], data[2], data[3]);
// Expect that the rendered quad's color is the same as the reference color with a tolerance
// of 1
EXPECT_PIXEL_NEAR(0, 0, referenceColor[0], referenceColor[1], referenceColor[2],
referenceColor[3], 1);
}
void verifyResults2D(GLuint texture, GLubyte data[4])
......@@ -641,7 +656,7 @@ class ImageTest : public ANGLETest
mTextureExternalESSL3UniformLocation);
}
void verifyResultsRenderbuffer(GLuint renderbuffer, GLubyte data[4])
void verifyResultsRenderbuffer(GLuint renderbuffer, GLubyte referenceColor[4])
{
// Bind the renderbuffer to a framebuffer
GLuint framebuffer;
......@@ -650,8 +665,10 @@ class ImageTest : public ANGLETest
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
renderbuffer);
// Expect that the rendered quad has the same color as the source texture
EXPECT_PIXEL_EQ(0, 0, data[0], data[1], data[2], data[3]);
// Expect that the rendered quad's color is the same as the reference color with a tolerance
// of 1
EXPECT_PIXEL_NEAR(0, 0, referenceColor[0], referenceColor[1], referenceColor[2],
referenceColor[3], 1);
glDeleteFramebuffers(1, &framebuffer);
}
......@@ -719,9 +736,11 @@ class ImageTest : public ANGLETest
bool hasImageGLColorspaceExt() const
{
// Vulkan back-end bug: http://anglebug.com/5209
if (IsVulkan())
// Possible GLES driver bug on Pixel2 devices: http://anglebug.com/5321
if (IsPixel2() && IsOpenGLES())
{
return false;
}
return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kImageGLColorspaceExt);
}
......@@ -1287,12 +1306,11 @@ void ImageTest::ValidationGLEGLImage_helper(const EGLint *attribs)
{
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, data, &source, &image);
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source,
&image);
// If <target> is not TEXTURE_2D, the error INVALID_ENUM is generated.
glEGLImageTargetTexture2DOES(GL_TEXTURE_CUBE_MAP_POSITIVE_X, image);
......@@ -1466,19 +1484,26 @@ void ImageTest::Source2DTarget2D_helper(const EGLint *attribs)
EGLWindow *window = getEGLWindow();
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, data, &source, &image);
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs,
static_cast<void *>(&kLinearColor), &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResults2D(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, kLinearColor);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -1507,19 +1532,26 @@ void ImageTest::Source2DTarget2DArray_helper(const EGLint *attribs)
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() ||
!hasEglImageArrayExt());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, data, &source, &image);
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source,
&image);
// Create the target
GLuint target;
createEGLImageTargetTexture2DArray(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResults2DArray(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResults2DArray(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResults2DArray(target, kLinearColor);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -1573,6 +1605,9 @@ TEST_P(ImageTest, SourceAHBTarget2D_Colorspace)
ANGLE_SKIP_TEST_IF(!IsAndroid());
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB"));
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to AHB backed EGLImages:
// http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceAHBTarget2D_helper(kColorspaceAttribs);
}
......@@ -1583,20 +1618,27 @@ void ImageTest::SourceAHBTarget2D_helper(const EGLint *attribs)
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt());
ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport());
GLubyte data[4] = {7, 51, 197, 231};
// Create the Image
AHardwareBuffer *source;
EGLImageKHR image;
createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, attribs, data, 4, &source, &image);
createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, attribs, kLinearColor, 4, &source,
&image);
// Create a texture target to bind the egl image
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Use texture target bound to egl image as source and render to framebuffer
// Verify that data in framebuffer matches that in the egl image
verifyResults2D(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResults2D(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, kLinearColor);
}
// Clean up
eglDestroyImageKHR(window->getDisplay(), image);
......@@ -1666,6 +1708,9 @@ TEST_P(ImageTest, SourceAHBTarget2DArray_Colorspace)
ANGLE_SKIP_TEST_IF(!IsAndroid());
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB"));
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to AHB backed EGLImages:
// http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceAHBTarget2DArray_helper(kColorspaceAttribs);
}
......@@ -1677,20 +1722,27 @@ void ImageTest::SourceAHBTarget2DArray_helper(const EGLint *attribs)
!hasEglImageArrayExt());
ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport());
GLubyte data[4] = {7, 51, 197, 231};
// Create the Image
AHardwareBuffer *source;
EGLImageKHR image;
createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, attribs, data, 4, &source, &image);
createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, attribs, kLinearColor, 4, &source,
&image);
// Create a texture target to bind the egl image
GLuint target;
createEGLImageTargetTexture2DArray(image, &target);
// Use texture target bound to egl image as source and render to framebuffer
// Verify that data in framebuffer matches that in the egl image
verifyResults2DArray(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResults2DArray(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResults2DArray(target, kLinearColor);
}
// Clean up
eglDestroyImageKHR(window->getDisplay(), image);
......@@ -1711,6 +1763,9 @@ TEST_P(ImageTest, SourceAHBTargetExternal_Colorspace)
ANGLE_SKIP_TEST_IF(!IsAndroid());
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB"));
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to AHB backed EGLImages:
// http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceAHBTargetExternal_helper(kColorspaceAttribs);
}
......@@ -1723,20 +1778,27 @@ void ImageTest::SourceAHBTargetExternal_helper(const EGLint *attribs)
// Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import
ANGLE_SKIP_TEST_IF(IsOzone());
GLubyte data[4] = {7, 51, 197, 231};
// Create the Image
AHardwareBuffer *source;
EGLImageKHR image;
createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, attribs, data, 4, &source, &image);
createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, attribs, kLinearColor, 4, &source,
&image);
// Create a texture target to bind the egl image
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Use texture target bound to egl image as source and render to framebuffer
// Verify that data in framebuffer matches that in the egl image
verifyResultsExternal(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternal(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternal(target, kLinearColor);
}
// Clean up
eglDestroyImageKHR(window->getDisplay(), image);
......@@ -1757,6 +1819,9 @@ TEST_P(ImageTestES3, SourceAHBTargetExternalESSL3_Colorspace)
ANGLE_SKIP_TEST_IF(!IsAndroid());
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB"));
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to AHB backed EGLImages:
// http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceAHBTargetExternalESSL3_helper(kColorspaceAttribs);
}
......@@ -1767,20 +1832,27 @@ void ImageTest::SourceAHBTargetExternalESSL3_helper(const EGLint *attribs)
!hasExternalESSL3Ext());
ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport());
GLubyte data[4] = {7, 51, 197, 231};
// Create the Image
AHardwareBuffer *source;
EGLImageKHR image;
createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, attribs, data, 4, &source, &image);
createEGLImageAndroidHardwareBufferSource(1, 1, 1, GL_RGBA8, attribs, kLinearColor, 4, &source,
&image);
// Create a texture target to bind the egl image
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Use texture target bound to egl image as source and render to framebuffer
// Verify that data in framebuffer matches that in the egl image
verifyResultsExternalESSL3(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternalESSL3(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternalESSL3(target, kLinearColor);
}
// Clean up
eglDestroyImageKHR(window->getDisplay(), image);
......@@ -1872,6 +1944,8 @@ TEST_P(ImageTest, Source2DTargetRenderbuffer_Colorspace)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
Source2DTargetRenderbuffer_helper(kColorspaceAttribs);
}
......@@ -1881,19 +1955,26 @@ void ImageTest::Source2DTargetRenderbuffer_helper(const EGLint *attribs)
EGLWindow *window = getEGLWindow();
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, data, &source, &image);
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source,
&image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsRenderbuffer(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsRenderbuffer(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsRenderbuffer(target, kLinearColor);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -1926,20 +2007,26 @@ void ImageTest::SourceNativeClientBufferTargetExternal_helper(const EGLint *attr
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt());
ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport());
GLubyte data[] = {0, 125, 64, 250};
// Create an Image backed by a native client buffer allocated using
// EGL_ANDROID_create_native_client_buffer API
EGLImageKHR image;
createEGLImageANWBClientBufferSource(1, 1, 1, kNativeClientBufferAttribs_RGBA8_Texture, attribs,
data, 4, &image);
kLinearColor, 4, &image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target texture when sampled has the same color as the source image
verifyResultsExternal(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternal(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternal(target, kLinearColor);
}
// Clean up
glDeleteTextures(1, &target);
......@@ -1961,6 +2048,8 @@ TEST_P(ImageTest, SourceNativeClientBufferTargetRenderbuffer_Colorspace)
ANGLE_SKIP_TEST_IF(!IsAndroid());
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceNativeClientBufferTargetRenderbuffer_helper(kColorspaceAttribs);
}
......@@ -1971,20 +2060,26 @@ void ImageTest::SourceNativeClientBufferTargetRenderbuffer_helper(const EGLint *
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt());
ANGLE_SKIP_TEST_IF(!hasAndroidImageNativeBufferExt() || !hasAndroidHardwareBufferSupport());
GLubyte data[] = {0, 125, 64, 250};
// Create an Image backed by a native client buffer allocated using
// EGL_ANDROID_create_native_client_buffer API
EGLImageKHR image;
createEGLImageANWBClientBufferSource(1, 1, 1, kNativeClientBufferAttribs_RGBA8_Renderbuffer,
attribs, data, 4, &image);
attribs, kLinearColor, 4, &image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsRenderbuffer(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsRenderbuffer(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsRenderbuffer(target, kLinearColor);
}
// Clean up
glDeleteRenderbuffers(1, &target);
......@@ -2011,19 +2106,26 @@ void ImageTest::Source2DTargetExternal_helper(const EGLint *attribs)
// Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import
ANGLE_SKIP_TEST_IF(IsOzone());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, data, &source, &image);
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source,
&image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target texture when sampled has the same color as the source image
verifyResultsExternal(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternal(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternal(target, kLinearColor);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2049,19 +2151,26 @@ void ImageTest::Source2DTargetExternalESSL3_helper(const EGLint *attribs)
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !has2DTextureExt() ||
!hasExternalESSL3Ext());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, data, &source, &image);
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, kLinearColor, &source,
&image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target texture when sampled has the same color as the source image
verifyResultsExternalESSL3(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternalESSL3(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternalESSL3(target, kLinearColor);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2086,26 +2195,29 @@ void ImageTest::SourceCubeTarget2D_helper(const EGLint *attribs)
EGLWindow *window = getEGLWindow();
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasCubemapExt());
GLubyte data[24] = {
255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255,
0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255,
};
for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++)
{
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageCubemapTextureSource(
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(data),
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kLinearColorCube),
sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, &data[faceIdx * 4]);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResults2D(target, &kSrgbColorCube[faceIdx * 4]);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, &kLinearColorCube[faceIdx * 4]);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2123,6 +2235,8 @@ TEST_P(ImageTest, SourceCubeTargetRenderbuffer_Colorspace)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceCubeTargetRenderbuffer_helper(kColorspaceAttribs);
}
......@@ -2134,26 +2248,29 @@ void ImageTest::SourceCubeTargetRenderbuffer_helper(const EGLint *attribs)
// http://anglebug.com/3145
ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsFuchsia());
GLubyte data[24] = {
255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255,
0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255,
};
for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++)
{
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageCubemapTextureSource(
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(data),
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kLinearColorCube),
sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResultsRenderbuffer(target, &data[faceIdx * 4]);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsRenderbuffer(target, &kSrgbColorCube[faceIdx * 4]);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsRenderbuffer(target, &kLinearColorCube[faceIdx * 4]);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2183,26 +2300,29 @@ void ImageTest::SourceCubeTargetExternal_helper(const EGLint *attribs)
// Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import
ANGLE_SKIP_TEST_IF(IsOzone());
GLubyte data[24] = {
255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255,
0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255,
};
for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++)
{
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageCubemapTextureSource(
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(data),
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kLinearColorCube),
sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResultsExternal(target, &data[faceIdx * 4]);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternal(target, &kSrgbColorCube[faceIdx * 4]);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternal(target, &kLinearColorCube[faceIdx * 4]);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2229,26 +2349,29 @@ void ImageTest::SourceCubeTargetExternalESSL3_helper(const EGLint *attribs)
EGLWindow *window = getEGLWindow();
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalESSL3Ext() || !hasBaseExt() || !hasCubemapExt());
GLubyte data[24] = {
255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255,
0, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 255,
};
for (EGLenum faceIdx = 0; faceIdx < 6; faceIdx++)
{
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageCubemapTextureSource(
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(data),
1, 1, GL_RGBA, GL_UNSIGNED_BYTE, attribs, reinterpret_cast<uint8_t *>(kLinearColorCube),
sizeof(GLubyte) * 4, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR + faceIdx, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResultsExternalESSL3(target, &data[faceIdx * 4]);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternalESSL3(target, &kSrgbColorCube[faceIdx * 4]);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternalESSL3(target, &kLinearColorCube[faceIdx * 4]);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2276,10 +2399,7 @@ void ImageTest::Source3DTargetTexture_helper(const bool withColorspace)
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D"));
const size_t depth = 2;
GLubyte data[4 * depth] = {
255, 0, 255, 255, 255, 255, 0, 255,
};
constexpr size_t depth = 2;
for (size_t layer = 0; layer < depth; layer++)
{
......@@ -2287,15 +2407,23 @@ void ImageTest::Source3DTargetTexture_helper(const bool withColorspace)
GLuint source;
EGLImageKHR image;
createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE,
get3DAttributes(withColorspace, layer), data, &source,
&image);
get3DAttributes(withColorspace, layer), kLinearColor3D,
&source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResults2D(target, &data[layer * 4]);
if (withColorspace)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResults2D(target, &kSrgbColor3D[layer * 4]);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, &kLinearColor3D[layer * 4]);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2313,6 +2441,8 @@ TEST_P(ImageTest, Source3DTargetRenderbuffer_Colorspace)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
Source3DTargetRenderbuffer_helper(true);
}
......@@ -2328,10 +2458,7 @@ void ImageTest::Source3DTargetRenderbuffer_helper(const bool withColorspace)
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D"));
const size_t depth = 2;
GLubyte data[4 * depth] = {
255, 0, 255, 255, 255, 255, 0, 255,
};
constexpr size_t depth = 2;
for (size_t layer = 0; layer < depth; layer++)
{
......@@ -2340,15 +2467,23 @@ void ImageTest::Source3DTargetRenderbuffer_helper(const bool withColorspace)
EGLImageKHR image;
createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE,
get3DAttributes(withColorspace, layer), data, &source,
&image);
get3DAttributes(withColorspace, layer), kLinearColor3D,
&source, &image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsRenderbuffer(target, &data[layer * 4]);
if (withColorspace)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsRenderbuffer(target, &kSrgbColor3D[layer * 4]);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsRenderbuffer(target, &kLinearColor3D[layer * 4]);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2380,10 +2515,7 @@ void ImageTest::Source3DTargetExternal_helper(const bool withColorspace)
// Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import
ANGLE_SKIP_TEST_IF(IsOzone());
const size_t depth = 2;
GLubyte data[4 * depth] = {
255, 0, 255, 255, 255, 255, 0, 255,
};
constexpr size_t depth = 2;
for (size_t layer = 0; layer < depth; layer++)
{
......@@ -2391,15 +2523,23 @@ void ImageTest::Source3DTargetExternal_helper(const bool withColorspace)
GLuint source;
EGLImageKHR image;
createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE,
get3DAttributes(withColorspace, layer), data, &source,
&image);
get3DAttributes(withColorspace, layer), kLinearColor3D,
&source, &image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsExternal(target, &data[layer * 4]);
if (withColorspace)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternal(target, &kSrgbColor3D[layer * 4]);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternal(target, &kLinearColor3D[layer * 4]);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2429,10 +2569,7 @@ void ImageTest::Source3DTargetExternalESSL3_helper(const bool withColorspace)
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_OES_texture_3D"));
const size_t depth = 2;
GLubyte data[4 * depth] = {
255, 0, 255, 255, 255, 255, 0, 255,
};
constexpr size_t depth = 2;
for (size_t layer = 0; layer < depth; layer++)
{
......@@ -2441,15 +2578,23 @@ void ImageTest::Source3DTargetExternalESSL3_helper(const bool withColorspace)
EGLImageKHR image;
createEGLImage3DTextureSource(1, 1, depth, GL_RGBA, GL_UNSIGNED_BYTE,
get3DAttributes(withColorspace, layer), data, &source,
&image);
get3DAttributes(withColorspace, layer), kLinearColor3D,
&source, &image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsExternalESSL3(target, &data[layer * 4]);
if (withColorspace)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternalESSL3(target, &kSrgbColor3D[layer * 4]);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternalESSL3(target, &kLinearColor3D[layer * 4]);
}
// Clean up
glDeleteTextures(1, &source);
......@@ -2466,6 +2611,8 @@ TEST_P(ImageTest, SourceRenderbufferTargetTexture)
TEST_P(ImageTest, SourceRenderbufferTargetTexture_Colorspace)
{
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceRenderbufferTargetTexture_helper(kColorspaceAttribs);
}
......@@ -2474,19 +2621,25 @@ void ImageTest::SourceRenderbufferTargetTexture_helper(const EGLint *attribs)
EGLWindow *window = getEGLWindow();
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, data, &source, &image);
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, kLinearColor, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResults2D(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResults2D(target, kLinearColor);
}
// Clean up
glDeleteRenderbuffers(1, &source);
......@@ -2503,6 +2656,8 @@ TEST_P(ImageTest, SourceRenderbufferTargetTextureExternal)
TEST_P(ImageTest, SourceRenderbufferTargetTextureExternal_Colorspace)
{
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceRenderbufferTargetTextureExternal_helper(kColorspaceAttribs);
}
......@@ -2514,19 +2669,25 @@ void ImageTest::SourceRenderbufferTargetTextureExternal_helper(const EGLint *att
// Ozone only supports external target for images created with EGL_EXT_image_dma_buf_import
ANGLE_SKIP_TEST_IF(IsOzone());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, data, &source, &image);
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, kLinearColor, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResultsExternal(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternal(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternal(target, kLinearColor);
}
// Clean up
glDeleteRenderbuffers(1, &source);
......@@ -2543,6 +2704,8 @@ TEST_P(ImageTestES3, SourceRenderbufferTargetTextureExternalESSL3)
TEST_P(ImageTestES3, SourceRenderbufferTargetTextureExternalESSL3_Colorspace)
{
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceRenderbufferTargetTextureExternalESSL3_helper(kColorspaceAttribs);
}
......@@ -2552,19 +2715,25 @@ void ImageTest::SourceRenderbufferTargetTextureExternalESSL3_helper(const EGLint
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasExternalESSL3Ext() || !hasBaseExt() ||
!hasRenderbufferExt());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, data, &source, &image);
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, kLinearColor, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTextureExternal(image, &target);
// Expect that the target texture has the same color as the source texture
verifyResultsExternalESSL3(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsExternalESSL3(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsExternalESSL3(target, kLinearColor);
}
// Clean up
glDeleteRenderbuffers(1, &source);
......@@ -2581,6 +2750,8 @@ TEST_P(ImageTest, SourceRenderbufferTargetRenderbuffer_Colorspace)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_EXT_sRGB"));
ANGLE_SKIP_TEST_IF(!hasImageGLColorspaceExt());
// Need to add support for VK_KHR_image_format_list to Renderbuffer: http://anglebug.com/5281
ANGLE_SKIP_TEST_IF(IsVulkan());
SourceRenderbufferTargetRenderbuffer_helper(kColorspaceAttribs);
}
......@@ -2589,19 +2760,25 @@ void ImageTest::SourceRenderbufferTargetRenderbuffer_helper(const EGLint *attrib
EGLWindow *window = getEGLWindow();
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasRenderbufferExt());
GLubyte data[4] = {255, 0, 255, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, data, &source, &image);
createEGLImageRenderbufferSource(1, 1, GL_RGBA8_OES, attribs, kLinearColor, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetRenderbuffer(image, &target);
// Expect that the target renderbuffer has the same color as the source texture
verifyResultsRenderbuffer(target, data);
if (attribs[kColorspaceAttributeIndex] == EGL_GL_COLORSPACE)
{
// Expect that the target texture has the corresponding sRGB color values
verifyResultsRenderbuffer(target, kSrgbColor);
}
else
{
// Expect that the target texture has the same color as the source texture
verifyResultsRenderbuffer(target, kLinearColor);
}
// Clean up
glDeleteRenderbuffers(1, &source);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment