[Piglit] [v2 3/3] tests/spec: add tests for oes image external

Topi Pohjolainen topi.pohjolainen at intel.com
Fri Mar 8 01:29:22 PST 2013


This consists of tests adapted from Khronos conformance suite and
Android surface flinger. While the former deals with "getters/setters",
enumrations and simple sampling of texture based images, the latter
addresses bilinear sampling of non-GPU written subsampled UV-planes
and conversion from YUV to RGB.

The original Android test consist of two YV12 formatted textures,
one of size 64x64 and another of size 64x66. Both represent checker
board pattern each YUV component having value 63 or 191. Instead of
filling in the entire pattern I have only written those YUV-components
that are actually checked (a dozen odd pixels) while the rest are
initialised to zero. In addition I used calculated floating point
values instead of the hardcoded found in the original. There, however,
I ended up in deviations and I would appreciate if people
understanding the domain of YUV to RGB conversion better could take
a good look.

The tests are written only for ES2 contexts, I haven't looked into
how I would separate the tests not dealing with external sampler
(samplerExternalOES is only defined for ES 2.x).

v2:
  - if platform does not support external buffers, report PIGLIT_SKIP
  - use texture based image instead of YV12 for texture creation test
  - refactored YUV content initialization
  - added 64x66 yv12 test as specified by android surface flinger
  - added 64x64 and 64x66 NV12 tests in addition to YV12
  - replaced direct calls to glGetError() by piglit_check_gl_error()

Signed-off-by: Topi Pohjolainen <topi.pohjolainen at intel.com>
---
 tests/spec/CMakeLists.txt                          |    1 +
 .../oes_egl_image_external/CMakeLists.gles2.txt    |   16 +
 tests/spec/oes_egl_image_external/CMakeLists.txt   |    3 +
 .../oes_egl_image_external.c                       |  675 ++++++++++++++++++++
 .../spec/oes_egl_image_external/yuv_test_content.h |  346 ++++++++++
 5 files changed, 1041 insertions(+)
 create mode 100644 tests/spec/oes_egl_image_external/CMakeLists.gles2.txt
 create mode 100644 tests/spec/oes_egl_image_external/CMakeLists.txt
 create mode 100644 tests/spec/oes_egl_image_external/oes_egl_image_external.c
 create mode 100644 tests/spec/oes_egl_image_external/yuv_test_content.h

diff --git a/tests/spec/CMakeLists.txt b/tests/spec/CMakeLists.txt
index 96b5a61..485cf5e 100644
--- a/tests/spec/CMakeLists.txt
+++ b/tests/spec/CMakeLists.txt
@@ -66,6 +66,7 @@ add_subdirectory (ext_texture_array)
 add_subdirectory (ext_texture_integer)
 add_subdirectory (arb_draw_buffers)
 add_subdirectory (oes_draw_texture)
+add_subdirectory (oes_egl_image_external)
 add_subdirectory (arb_blend_func_extended)
 add_subdirectory (ext_unpack_subimage)
 add_subdirectory (arb_vertex_array_object)
diff --git a/tests/spec/oes_egl_image_external/CMakeLists.gles2.txt b/tests/spec/oes_egl_image_external/CMakeLists.gles2.txt
new file mode 100644
index 0000000..1ae1792
--- /dev/null
+++ b/tests/spec/oes_egl_image_external/CMakeLists.gles2.txt
@@ -0,0 +1,16 @@
+#add_definitions(-DSOURCE_DIR="${piglit_SOURCE_DIR}/")
+
+include_directories(
+	${OPENGL_INCLUDE_PATH}
+	)
+
+link_libraries(
+	${OPENGL_egl_LIBRARY}
+	piglitutil_gles2
+	)
+
+piglit_add_executable(oes_egl_image_external_gles2
+	oes_egl_image_external.c
+	)
+
+# vim: ft=cmake:
diff --git a/tests/spec/oes_egl_image_external/CMakeLists.txt b/tests/spec/oes_egl_image_external/CMakeLists.txt
new file mode 100644
index 0000000..d9d41f2
--- /dev/null
+++ b/tests/spec/oes_egl_image_external/CMakeLists.txt
@@ -0,0 +1,3 @@
+if(OPENGL_egl_LIBRARY)
+	piglit_include_target_api()
+endif(OPENGL_egl_LIBRARY)
diff --git a/tests/spec/oes_egl_image_external/oes_egl_image_external.c b/tests/spec/oes_egl_image_external/oes_egl_image_external.c
new file mode 100644
index 0000000..fd32c4e
--- /dev/null
+++ b/tests/spec/oes_egl_image_external/oes_egl_image_external.c
@@ -0,0 +1,675 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Author: Topi Pohjolainen <topi.pohjolainen at intel.com>
+ */
+
+/** @file oes_egl_image_external.c
+ *
+ * Test EGL_OES_image_external.
+ */
+
+#define EGL_EGLEXT_PROTOTYPES 1
+#define GL_GLEXT_PROTOTYPES 1
+#include "piglit-util-gl-common.h"
+#include "piglit-util-egl.h"
+
+#include <EGL/eglext.h>
+
+#include "yuv_test_content.h"
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+
+	config.supports_gl_es_version = 20;
+
+	config.window_visual = PIGLIT_GL_VISUAL_RGBA;
+
+PIGLIT_GL_TEST_CONFIG_END
+
+static const char fs_src[] =
+	"#extension GL_OES_EGL_image_external : require\n"
+	"uniform samplerExternalOES sampler;\n"
+	"varying vec2 texcoords;\n"
+	"\n"
+	"void main()\n"
+	"{\n"
+	"gl_FragColor = texture2D(sampler, texcoords);\n"
+	"}\n";
+static const char vs_src[] =
+	"attribute vec4 position;\n"
+	"varying vec2 texcoords;\n"
+	"\n"
+	"void main()\n"
+	"{\n"
+	"texcoords = 0.5 * (position.xy + vec2(1.0, 1.0));\n"
+	"gl_Position = position;\n"
+	"}\n";
+
+static GLuint 
+make_program(const char *vs, const char *fs)
+{
+	GLuint prog, tmp;
+
+	prog = glCreateProgram();
+
+	tmp = piglit_compile_shader_text(GL_VERTEX_SHADER, vs);
+	glAttachShader(prog, tmp);
+
+	tmp = piglit_compile_shader_text(GL_FRAGMENT_SHADER, fs);
+	glAttachShader(prog, tmp);
+
+	glLinkProgram(prog);
+	if (!piglit_link_check_status(prog))
+		piglit_report_result(PIGLIT_FAIL);
+
+	glUseProgram(prog);
+
+	return prog;
+}
+
+void set_vertices(GLuint prog)
+{
+	static const GLfloat v[] = { -1.0f,  1.0f, -1.0f, -1.0f,
+				      1.0f, -1.0f,  1.0f,  1.0f };
+
+	GLint i = glGetAttribLocation(prog, "position");
+	glVertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, 0, v);
+	glEnableVertexAttribArray(i);
+}
+
+static void
+sample_and_destroy_img(unsigned w, unsigned h, EGLImageKHR img)
+{
+	GLuint prog, tex;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
+
+	glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
+				(GLeglImageOES)img);
+
+	prog = make_program(vs_src, fs_src);
+
+	glUniform1i(glGetUniformLocation(prog, "sampler"), 0);
+	set_vertices(prog);
+
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	glViewport(0, 0, w, h);
+	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+	glDeleteProgram(prog);
+	glUseProgram(0);
+
+	glDeleteTextures(1, &tex);
+	eglDestroyImageKHR(eglGetCurrentDisplay(), img);
+}
+
+static bool 
+sample_and_destroy(unsigned w, unsigned h, void *buf)
+{
+	EGLImageKHR img = eglCreateImageKHR(eglGetCurrentDisplay(), 0,
+					EGL_NATIVE_PIXMAP_KHR, buf, 0);
+
+	if (img == EGL_NO_IMAGE_KHR) {
+		piglit_destroy_ext_buf(buf);
+		return false;
+	}
+
+	sample_and_destroy_img(w, h, img);
+	piglit_destroy_ext_buf(buf);
+
+	return true;
+}
+
+static enum piglit_result
+sample_and_destroy_yv12(unsigned w, unsigned h,
+			unsigned char *y, unsigned char *u, unsigned char *v)
+{
+	void *buf;
+	enum piglit_result res;
+
+	/* Reversing the order of UV-planes gives YVU420 a.k.a. YV12 */
+	piglit_create_ext_420_buf(w, h, true, y, u, v, &res, &buf);
+	if (res == PIGLIT_SKIP)
+		return PIGLIT_SKIP;
+
+	if (res == PIGLIT_FAIL) {
+		printf("failed to create external YV12 buffer\n");
+		return PIGLIT_FAIL;
+	}
+
+	return sample_and_destroy(w, h, buf) ? PIGLIT_PASS : PIGLIT_FAIL;
+}
+
+static bool
+test_64x64_yv12(void)
+{
+	struct yuv_test_content_420 src;
+	enum piglit_result res;
+
+	init_420_64x64(&src);
+	res = sample_and_destroy_yv12(64, 64, src.y_, src.u_, src.v_);
+	free_420(&src);
+
+	if (res == PIGLIT_SKIP)
+		return true;
+
+	if (res == PIGLIT_FAIL)
+		return false;
+
+	return check_64x64_2x2_uv_subsampled();
+}
+
+static bool
+test_64x66_yv12(void)
+{
+	struct yuv_test_content_420 src;
+	enum piglit_result res;
+
+	init_420_64x66(&src);
+	res = sample_and_destroy_yv12(64, 66, src.y_, src.u_, src.v_);
+	free_420(&src);
+
+	if (res == PIGLIT_SKIP)
+		return true;
+
+	if (res == PIGLIT_FAIL)
+		return false;
+
+	return check_64x66_2x2_uv_subsampled();
+}
+
+static enum piglit_result
+sample_and_destroy_nv12(unsigned w, unsigned h,
+			unsigned char *y, unsigned char *uv)
+{
+	void *buf;
+	enum piglit_result res;
+
+	piglit_create_ext_nv12_buf(w, h, y, uv, &res, &buf);
+	if (res == PIGLIT_SKIP)
+		return PIGLIT_SKIP;
+
+	if (res == PIGLIT_FAIL) {
+		printf("failed to create external NV12 buffer\n");
+		return PIGLIT_FAIL;
+	}
+
+	return sample_and_destroy(w, h, buf) ? PIGLIT_PASS : PIGLIT_FAIL;
+}
+
+static bool
+test_64x64_nv12(void)
+{
+	struct yuv_test_content_nv12 src;
+	enum piglit_result res;
+
+	init_nv12_64x64(&src);
+	res = sample_and_destroy_nv12(64, 64, src.y_, src.uv_);
+	free_nv12(&src);
+
+	if (res == PIGLIT_SKIP)
+		return true;
+
+	if (res == PIGLIT_FAIL)
+		return false;
+
+	return check_64x64_2x2_uv_subsampled();
+}
+
+static bool
+test_64x66_nv12(void)
+{
+	struct yuv_test_content_nv12 src;
+	enum piglit_result res;
+
+	init_nv12_64x66(&src);
+	res = sample_and_destroy_nv12(64, 66, src.y_, src.uv_);
+	free_nv12(&src);
+
+	if (res == PIGLIT_SKIP)
+		return true;
+
+	if (res == PIGLIT_FAIL)
+		return false;
+
+	return check_64x66_2x2_uv_subsampled();
+}
+
+
+static bool
+test_get_binding(void)
+{
+	GLint i;
+	GLfloat f;
+
+	glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &i);
+	if (i != 0) {
+		printf("default binding EXTERNAL_OES must be zero\n");
+		return false;
+	}		
+
+	glGetFloatv(GL_TEXTURE_BINDING_EXTERNAL_OES, &f);
+	if (f != 0) {
+		printf("default binding EXTERNAL_OES must be zero\n");
+		return false;
+	}		
+
+	return true;
+}
+
+static bool
+test_required_image_units(void)
+{
+	GLint i;
+	GLfloat f;
+
+	glGetTexParameteriv(GL_TEXTURE_EXTERNAL_OES,
+		GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES, &i);
+
+	if (i < 1 || i > 3) {
+		printf("REQUIRED_TEXTURE_IMAGE_UNITS_OES "
+			"should be between 1-3\n");
+		return false;
+	}
+
+	glGetTexParameterfv(GL_TEXTURE_EXTERNAL_OES,
+		GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES, &f);
+
+	if ((GLfloat)i != f) {
+		printf("mismatch between integer and float value of "
+			"REQUIRED_TEXTURE_IMAGE_UNITS_OES %d:%f\n", i, f);
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * INVALID_ENUM should be generated for TexImage2D, TexSubImage2D,
+ * CompressedTexImager2D and CompressedTexSubImage2D with
+ * TEXTURE_EXTERNAL_OES.
+ */
+static bool
+test_disallow_image_2d(void)
+{
+	GLushort pixel_2x2_565[] = { 0, 0, 0, 0 };
+	GLuint tex;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
+
+	glTexImage2D(GL_TEXTURE_EXTERNAL_OES, 0, GL_RGB, 2, 2, 0, GL_RGB,
+		GL_UNSIGNED_SHORT_5_6_5, pixel_2x2_565);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+		
+	glTexSubImage2D(GL_TEXTURE_EXTERNAL_OES, 0, 0, 0, 2, 2, GL_RGB,
+			GL_UNSIGNED_SHORT_5_6_5, pixel_2x2_565);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glDeleteTextures(1, &tex);
+	return true;
+}
+
+static bool
+test_default_filter_mode(void)
+{
+	GLuint tex;
+	GLint param;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
+
+	glGetTexParameteriv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER,
+			&param);
+	if (param != GL_LINEAR) {
+		printf("default TEXTURE_MAG_FILTER must be LINEAR\n");
+		glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glGetTexParameteriv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
+			&param);
+	if (param != GL_LINEAR) {
+		printf("default TEXTURE_MIN_FILTER must be LINEAR\n");
+		glDeleteTextures(1, &tex);
+		return false;
+	}
+
+    	glGetTexParameteriv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, &param);
+	if (param != GL_CLAMP_TO_EDGE) {
+		printf("default TEXTURE_WRAP_S must be CLAMP_TO_EDGE\n");
+		glDeleteTextures(1, &tex);
+		return false;
+	}
+
+    	glGetTexParameteriv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, &param);
+	if (param != GL_CLAMP_TO_EDGE) {
+		printf("default TEXTURE_WRAP_T must be CLAMP_TO_EDGE\n");
+		glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glDeleteTextures(1, &tex);
+	return true;
+}
+
+static bool
+test_invalid_filter_mode(void)
+{
+	GLuint tex;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
+
+	glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
+		GL_NEAREST_MIPMAP_NEAREST);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+		
+    	glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
+		GL_NEAREST_MIPMAP_LINEAR);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+    	glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
+		GL_LINEAR_MIPMAP_NEAREST);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+    	glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
+		GL_LINEAR_MIPMAP_LINEAR);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_REPEAT);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
+		GL_MIRRORED_REPEAT);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_REPEAT);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
+		GL_MIRRORED_REPEAT);
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
+	glDeleteTextures(1, &tex);
+	return true;
+}
+
+static bool
+test_disallow_mipmap_generation(void)
+{
+	GLuint tex;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
+	glGenerateMipmap(GL_TEXTURE_EXTERNAL_OES);
+
+	if (!piglit_check_gl_error(GL_INVALID_ENUM)) {
+        	glDeleteTextures(1, &tex);
+		return false;
+	}
+
+	glDeleteTextures(1, &tex);
+    	return true;
+}
+
+static bool
+test_missing_require_directive(void)
+{
+	const GLchar *src[] = { "uniform samplerExternalOES sampler;\n"
+			        "varying vec2 p;\n"
+			        "void main()\n"
+			        "{ gl_FragColor = texture2D(sampler, p); }\n" };
+	GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
+	GLint status;
+
+	glShaderSource(fs, 1, src, 0);
+	glCompileShader(fs);
+	glGetShaderiv(fs, GL_COMPILE_STATUS, &status);
+
+	if (status != 0)
+		printf("samplerExternalOES cannot be used without requiring "
+			"OES_EGL_image_external\n");
+
+	glDeleteShader(fs);
+
+	return status == 0;
+}
+
+static bool
+test_get_active_uniform(void)
+{
+    	GLint active_n;
+	bool result = false;
+	GLuint prog = make_program(vs_src, fs_src);
+
+	glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &active_n);
+
+	if (active_n == 1) {
+    		char name[32];
+		GLint size;
+		GLenum type;
+
+		glGetActiveUniform(prog, 0, sizeof(name), 0, &size, &type,
+				name);
+
+		if (strncmp(name, "sampler", sizeof(name)) == 0 && size == 1 &&
+			type == GL_SAMPLER_EXTERNAL_OES)
+				result = true;
+        }
+
+	glDeleteProgram(prog);
+
+	return result;
+}
+
+static EGLImageKHR
+create_tex_based_egl_image(unsigned w, unsigned h, const unsigned char *pixels)
+{
+	GLuint tex;
+	EGLImageKHR img;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_2D, tex);
+
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
+		GL_UNSIGNED_BYTE, pixels);
+
+	img = eglCreateImageKHR(eglGetCurrentDisplay(), eglGetCurrentContext(),
+			EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)tex, 0);
+	if (!img)
+		printf("failed to create EGL image out of texture\n");
+
+	glDeleteTextures(1, &tex);
+
+	return img;
+}
+
+static bool
+test_create_external_texture(void)
+{
+	GLuint tex;
+	bool result = true;
+	const unsigned char src[] = { 0x00, 0x00, 0x00, 0x00 };
+	EGLImageKHR img = create_tex_based_egl_image(1, 1, src);
+
+	if (img == EGL_NO_IMAGE_KHR)
+		return false;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
+
+	glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
+				(GLeglImageOES)img);
+
+	if (!piglit_check_gl_error(GL_NO_ERROR))
+		result = false;
+
+        glDeleteTextures(1, &tex);
+	eglDestroyImageKHR(eglGetCurrentDisplay(), img);
+
+	return result;
+}
+
+static bool
+test_detect_invalid_image(void)
+{
+	GLuint tex;
+	bool result = true;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
+
+	glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)0);
+
+	if (!piglit_check_gl_error(GL_INVALID_OPERATION))
+		result = false;
+
+        glDeleteTextures(1, &tex);
+
+	return result;
+}
+
+/**
+ * Sampling an external texture which is not associated with any EGLImage
+ * sibling will return a sample value of (0,0,0,1).
+ */
+static bool
+test_no_associated_image(void)
+{
+	const float expected[] = { 0, 0, 0, 1.0 };
+	GLuint prog, tex;
+	bool result;
+
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
+
+	prog = make_program(vs_src, fs_src);
+
+	glUniform1i(glGetUniformLocation(prog, "sampler"), 0);
+	set_vertices(prog);
+
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	glViewport(0, 0, 1, 1);
+	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+	result = piglit_probe_pixel_rgba(0, 0, expected);
+
+	glDeleteProgram(prog);
+	glUseProgram(0);
+	glDeleteTextures(1, &tex);
+
+	return result;
+}
+
+static bool
+test_simple_external_from_tex_based_egl_image(void)
+{
+	const unsigned char src[] = {
+		0x00, 0xff, 0x00, 0xff,	0x12, 0x34, 0x56, 0x78,	
+		0x90, 0xab, 0xcd, 0xef,	0x1a, 0x2b, 0x3c, 0x4e };
+	const float expected[] = { src[0] / 255.0, src[1] / 255.0,
+		src[2] / 255.0, src[3] / 255.0, src[4] / 255.0, src[5] / 255.0,
+		src[6] / 255.0, src[7] / 255.0, src[8] / 255.0, src[9] / 255.0,
+		src[10] / 255.0, src[11] / 255.0, src[12] / 255.0,
+		src[13] / 255.0, src[14] / 255.0, src[15] / 255.0 };
+	EGLImageKHR img = create_tex_based_egl_image(2, 2, src);
+	bool res;
+
+	if (!img)
+		return false;
+
+	sample_and_destroy_img(2, 2, img);
+
+	res = piglit_probe_pixel_rgba(0, 0, expected +  0);
+	res = piglit_probe_pixel_rgba(1, 0, expected +  4) && res;
+	res = piglit_probe_pixel_rgba(0, 1, expected +  8) && res;
+	res = piglit_probe_pixel_rgba(1, 1, expected + 12) && res;
+
+	return res;
+}
+
+enum piglit_result
+piglit_display(void)
+{
+	bool pass = test_get_binding();
+
+	pass = test_required_image_units() && pass;
+	pass = test_disallow_image_2d() && pass;
+	pass = test_default_filter_mode() && pass;
+	pass = test_invalid_filter_mode() && pass;
+	pass = test_disallow_mipmap_generation() && pass; 
+	pass = test_missing_require_directive() && pass;
+	pass = test_get_active_uniform() && pass;
+	pass = test_create_external_texture() && pass;
+	pass = test_detect_invalid_image() && pass;
+	pass = test_no_associated_image() && pass;
+	pass = test_simple_external_from_tex_based_egl_image() && pass;
+	pass = test_64x64_yv12() && pass;
+	pass = test_64x66_yv12() && pass;
+	pass = test_64x64_nv12() && pass;
+	pass = test_64x66_nv12() && pass;
+
+	piglit_present_results();
+
+	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
+}
+
+void
+piglit_init(int argc, char **argv)
+{
+	piglit_require_extension("GL_OES_EGL_image_external");
+}
diff --git a/tests/spec/oes_egl_image_external/yuv_test_content.h b/tests/spec/oes_egl_image_external/yuv_test_content.h
new file mode 100644
index 0000000..59fd208
--- /dev/null
+++ b/tests/spec/oes_egl_image_external/yuv_test_content.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Author: Topi Pohjolainen <topi.pohjolainen at intel.com>
+ */
+
+/** @file yuv_test_content.h
+ *
+ * Test content in YUV format for EGL_OES_image_external.
+ */
+#ifndef YUV_TEST_CONTENT_H
+#define YUV_TEST_CONTENT_H
+
+struct yuv_test_content_2x2_uv_subsampled {
+	void (*set_corner)(struct yuv_test_content_2x2_uv_subsampled *base,
+			   unsigned x_pos, unsigned y_pos,
+			   int y, int u, int v);
+
+	void (*set)(struct yuv_test_content_2x2_uv_subsampled *base,
+		    unsigned x_pos, unsigned y_pos,
+		    int y,
+		    int u11, int u12, int u21, int u22,
+		    int v11, int v12, int v21, int v22);
+
+	unsigned w_;
+	unsigned h_;
+};
+
+/**
+ * Limited range:
+ *    y   [16, 235] -> 219 steps
+ *    u,v [16, 240] -> 224 steps
+ *
+ * 1.164 = 255.0 / 219
+ * 1.596 = 1.402 * 255.0 / 224
+ * 0.813 = 0.714 * 255.0 / 224
+ * 0.391 = 0.344 * 255.0 / 224
+ * 2.018 = 1.772 * 255.0 / 224
+ */
+#define YUV_TO_R_BT601_LIMITED_RANGE(y, v) \
+        (1.164f * ((y) - 16) + 1.596f * ((v) - 128))
+#define YUV_TO_G_BT601_LIMITED_RANGE(y, u, v) \
+        (1.164f * ((y) - 16) - 0.813f * ((v) - 128) - 0.391f * ((u) - 128))
+#define YUV_TO_B_BT601_LIMITED_RANGE(y, u) \
+        (1.164f * ((y) - 16) + 2.018f * ((u) - 128))
+#define CLIP_0_1(v) ((v) > 1.0 ? 1.0 : (v) < 0.0 ? 0.0 : (v))
+
+static int
+check_pixel_(int x_pos, int y_pos, int y, int u, int v)
+{
+	float expected[] = {
+		CLIP_0_1(YUV_TO_R_BT601_LIMITED_RANGE(y,    v) / 255.0f),
+		CLIP_0_1(YUV_TO_G_BT601_LIMITED_RANGE(y, u, v) / 255.0f),
+		CLIP_0_1(YUV_TO_B_BT601_LIMITED_RANGE(y, u   ) / 255.0f),
+		1.0 };
+
+	return piglit_probe_pixel_rgba(x_pos, y_pos, expected);
+}
+
+/**
+ * This is a test adapted from Android surface flinger. Whereas in the original
+ * test each of the YUV-planes are filled with a checker board pattern, here one
+ * sets only those pixels that contribute to sampling leaving the rest
+ * initialised to zero. The size 64x64 is chosen to stimulate bilinear sampling
+ * of the sub-sampled UV-planes with weights of 0.25 and 0.75 instead of just
+ * equally with weights of 0.5.
+ * The orientation of the texture is also flipped vertically, original test uses
+ * transformation matrix moving 0,0 to the lower left corner, here one samples
+ * directly keeping 0,0 in the upper left corner of the texture.
+ */
+static void
+write_64x64_2x2_uv_subsampled(struct yuv_test_content_2x2_uv_subsampled *dst)
+{
+	dst->set_corner(dst,  0,  0,  63,  63,  63);
+	dst->set_corner(dst, 63,  0, 191, 191, 191);
+	dst->set_corner(dst, 63, 63,  63,  63,  63);
+	dst->set_corner(dst,  0, 63, 191, 191, 191);
+
+	dst->set(dst, 22, 19, 191,  63,  63,  63,  63,  63,  63,  63,  63);
+	dst->set(dst, 45, 11, 191,  63,  63,  63,  63,  63,  63,  63,  63);
+	dst->set(dst, 52, 12,  63, 191, 191, 191, 191, 191, 191, 191, 191);
+	dst->set(dst,  7, 32, 191, 191,  63,  63, 191, 191, 191,  63,  63);
+	dst->set(dst, 31, 54,  63, 191,  63, 191,  63,  63, 191,  63, 191);
+	dst->set(dst, 29, 28,  63,  63,  63,  63,  63,  63,  63,  63,  63);
+	dst->set(dst, 36, 41, 191, 191, 191, 191, 191,  63,  63,  63,  63);
+}
+
+/**
+ * Deviations to values expected by surface flinger test:
+ *     (63, 0) green: 127 vs. 0.501365 * 255 = 127.848
+ *     (0 ,63) green: 127 vs. 0.501365 * 255 = 127.848
+ *     (22,19)  blue:  74 vs. 0.284431 * 255 =  72.530
+ *     (45,11)  blue:  74 vs. 0.284431 * 255 =  72.530
+ *     (52,12)  blue: 181 vs. 0.713106 * 255 = 181.842
+ *     ( 7,32)   red: 150 vs. 0.592282 * 255 = 151.032
+ *     ( 7,32)  blue: 170 vs. 0.664290 * 255 = 169.394
+ *     (31,54)   red:   0 vs. 0.008000 * 255 =   2.040
+ *     (31,54) green:  71 vs. 0.272219 * 255 =  69.416
+ */
+static bool
+check_64x64_2x2_uv_subsampled(void)
+{
+	bool pass = check_pixel_(0, 0, 63, 63, 63);
+
+	pass = check_pixel_(63,  0, 191, 191, 191) && pass;
+	pass = check_pixel_(63, 63,  63,  63,  63) && pass;
+	pass = check_pixel_( 0, 63, 191, 191, 191) && pass;
+	pass = check_pixel_(22, 19, 191,  63,  63) && pass;
+	pass = check_pixel_(45, 11, 191,  63,  63) && pass;
+	pass = check_pixel_(52, 12,  63, 191, 191) && pass;
+
+	pass = check_pixel_(7, 32,
+			191,
+			0.25 * (0.75 * 191 + 0.25 * 63) +
+			0.75 * (0.25 * 191 + 0.75 * 63),
+			0.25 * 191 + 0.75 * 63) && pass;
+
+	pass = check_pixel_(31, 54,
+			63,
+			0.75 * 191 + 0.25 * 63,
+			0.25 * 191 + 0.75 * 63) && pass;
+
+	pass = check_pixel_(29, 28,  63,  63, 63) && pass;
+	pass = check_pixel_(36, 41, 191, 191, 63) && pass;
+
+	return pass;
+}
+
+static void
+write_64x66_2x2_uv_subsampled(struct yuv_test_content_2x2_uv_subsampled *dst)
+{
+	dst->set_corner(dst,  0,  0, 191, 191, 191);
+	dst->set_corner(dst, 63,  0,  63,  63,  63);
+	dst->set_corner(dst, 63, 65,  63,  63,  63);
+	dst->set_corner(dst,  0, 65, 191, 191, 191);
+
+	dst->set(dst, 22, 44, 191, 191, 191, 191, 191, 191, 191, 191, 191);
+	dst->set(dst, 45, 52, 191, 191, 191, 191, 191, 191, 191, 191, 191);
+	dst->set(dst, 52, 51, 191,  63,  63,  63,  63,  63,  63,  63,  63);
+	dst->set(dst,  7, 31,  63, 191,  63, 191,  63, 191, 191, 191, 191);
+	dst->set(dst, 31,  9,  63, 191,  63,  63, 191, 191,  63, 191,  63);
+	dst->set(dst, 29, 35, 191, 191, 191, 191, 191, 191, 191, 191, 191);
+	dst->set(dst, 36, 22,  63,  63,  63,  63,  63, 191, 191, 191, 191);
+}
+
+/**
+ * Deviations to values expected by surface flinger test:
+ *     (31, 9)   red: 107 vs. 0.408565 * 255 = 104.184
+ *     (31, 9) green:  87 vs. 0.333247 * 255 = 84.978
+ */
+static bool
+check_64x66_2x2_uv_subsampled(void)
+{
+	bool pass = check_pixel_(0, 0, 191, 191, 191);
+
+	pass = check_pixel_(63,  0,  63,  63,  63) && pass;
+	pass = check_pixel_(63, 65,  63,  63,  63) && pass;
+	pass = check_pixel_( 0, 65, 191, 191, 191) && pass;
+	pass = check_pixel_(22, 44, 191, 191, 191) && pass;
+	pass = check_pixel_(45, 52, 191, 191, 191) && pass;
+	pass = check_pixel_(52, 51, 191,  63,  63) && pass;
+
+	pass = check_pixel_(7, 31, 63, 0.75 * 191 + 0.25 * 63, 191) && pass;
+
+	pass = check_pixel_(31, 9,
+			63,
+			0.75 * (0.75 * 191 + 0.25 * 63) +
+			0.25 * (0.25 * 191 + 0.75 * 63),
+			0.75 * 191 + 0.25 * 63) && pass;
+
+	pass = check_pixel_(36, 22, 63, 63, 191) && pass;
+
+	return pass;
+}
+
+struct yuv_test_content_420 {
+	struct yuv_test_content_2x2_uv_subsampled base;
+
+	unsigned char *y_;
+	unsigned char *u_;
+	unsigned char *v_;
+};
+
+static void
+set_420_corner(struct yuv_test_content_2x2_uv_subsampled *base,
+	unsigned x_pos, unsigned y_pos, int y, int u, int v)
+{
+	struct yuv_test_content_420 *c = (struct yuv_test_content_420 *)base;
+
+	c->y_[ y_pos      *  base->w_      + x_pos    ] = y;
+	c->u_[(y_pos / 2) * (base->w_ / 2) + x_pos / 2] = u;
+	c->v_[(y_pos / 2) * (base->w_ / 2) + x_pos / 2] = v;
+}
+
+static void
+set_420(struct yuv_test_content_2x2_uv_subsampled *base,
+	unsigned x_pos, unsigned y_pos, int y,
+	int u11, int u12, int u21, int u22, int v11, int v12, int v21, int v22)
+{
+	struct yuv_test_content_420 *c = (struct yuv_test_content_420 *)base;
+
+	c->y_[y_pos * c->base.w_ + x_pos] = y;
+
+	y_pos = (y_pos - 1) / 2;
+	x_pos = (x_pos - 1) / 2;
+
+	c->u_[(y_pos + 0) * (c->base.w_ / 2) + x_pos + 0] = u11;
+	c->u_[(y_pos + 0) * (c->base.w_ / 2) + x_pos + 1] = u12;
+	c->u_[(y_pos + 1) * (c->base.w_ / 2) + x_pos + 0] = u21;
+	c->u_[(y_pos + 1) * (c->base.w_ / 2) + x_pos + 1] = u22;
+
+	c->v_[(y_pos + 0) * (c->base.w_ / 2) + x_pos + 0] = v11;
+	c->v_[(y_pos + 0) * (c->base.w_ / 2) + x_pos + 1] = v12;
+	c->v_[(y_pos + 1) * (c->base.w_ / 2) + x_pos + 0] = v21;
+	c->v_[(y_pos + 1) * (c->base.w_ / 2) + x_pos + 1] = v22;
+}
+
+static void
+init_420(unsigned w, unsigned h, struct yuv_test_content_420 *c)
+{
+	c->base.set_corner = set_420_corner;
+	c->base.set = set_420;
+	c->base.w_ = w;
+	c->base.h_ = h;
+
+	c->y_ = calloc(sizeof(unsigned char), w * h);
+	c->u_ = calloc(sizeof(unsigned char), (w / 2) * (h / 2));
+	c->v_ = calloc(sizeof(unsigned char), (w / 2) * (h / 2));
+}
+
+static void
+free_420(struct yuv_test_content_420 *c)
+{
+	free(c->y_);	
+	free(c->u_);	
+	free(c->v_);	
+}
+
+static void
+init_420_64x64(struct yuv_test_content_420 *c)
+{
+	init_420(64, 64, c);
+	write_64x64_2x2_uv_subsampled(&c->base);
+}
+
+static void
+init_420_64x66(struct yuv_test_content_420 *c)
+{
+	init_420(64, 66, c);
+	write_64x66_2x2_uv_subsampled(&c->base);
+}
+
+struct yuv_test_content_nv12 {
+	struct yuv_test_content_2x2_uv_subsampled base;
+
+	unsigned char *y_;
+	unsigned char *uv_;
+};
+
+static void
+set_nv12_corner(struct yuv_test_content_2x2_uv_subsampled *base,
+	unsigned x_pos, unsigned y_pos, int y, int u, int v)
+{
+	struct yuv_test_content_nv12 *c = (struct yuv_test_content_nv12 *)base;
+
+	c->y_ [ y_pos      * base->w_ + x_pos    ] = y;
+	c->uv_[(y_pos / 2) * base->w_ + (x_pos / 2) * 2 + 0] = u;
+	c->uv_[(y_pos / 2) * base->w_ + (x_pos / 2) * 2 + 1] = v;
+}
+
+static void
+set_nv12(struct yuv_test_content_2x2_uv_subsampled *base,
+	unsigned x_pos, unsigned y_pos, int y,
+	int u11, int u12, int u21, int u22, int v11, int v12, int v21, int v22)
+{
+	struct yuv_test_content_nv12 *c = (struct yuv_test_content_nv12 *)base;
+
+	c->y_[y_pos * c->base.w_ + x_pos] = y;
+
+	y_pos = (y_pos - 1) / 2;
+	x_pos = (x_pos - 1) / 2;
+
+	c->uv_[(y_pos + 0) * c->base.w_ + x_pos * 2 + 0] = u11;
+	c->uv_[(y_pos + 0) * c->base.w_ + x_pos * 2 + 1] = v11;
+	c->uv_[(y_pos + 0) * c->base.w_ + x_pos * 2 + 2] = u12;
+	c->uv_[(y_pos + 0) * c->base.w_ + x_pos * 2 + 3] = v12;
+
+	c->uv_[(y_pos + 1) * c->base.w_ + x_pos * 2 + 0] = u21;
+	c->uv_[(y_pos + 1) * c->base.w_ + x_pos * 2 + 1] = v21;
+	c->uv_[(y_pos + 1) * c->base.w_ + x_pos * 2 + 2] = u22;
+	c->uv_[(y_pos + 1) * c->base.w_ + x_pos * 2 + 3] = v22;
+}
+
+static void
+init_nv12(unsigned w, unsigned h, struct yuv_test_content_nv12 *c)
+{
+	c->base.set_corner = set_nv12_corner;
+	c->base.set = set_nv12;
+	c->base.w_ = w;
+	c->base.h_ = h;
+
+	c->y_ = calloc(sizeof(unsigned char), w * h);
+	c->uv_ = calloc(sizeof(unsigned char), w * (h / 2));
+}
+
+static void
+init_nv12_64x64(struct yuv_test_content_nv12 *c)
+{
+	init_nv12(64, 64, c);
+	write_64x64_2x2_uv_subsampled(&c->base);
+}
+
+static void
+init_nv12_64x66(struct yuv_test_content_nv12 *c)
+{
+	init_nv12(64, 66, c);
+	write_64x66_2x2_uv_subsampled(&c->base);
+}
+
+static void
+free_nv12(struct yuv_test_content_nv12 *c)
+{
+	free(c->y_);	
+	free(c->uv_);	
+}
+
+#endif /* YUV_TEST_CONTENT_H */
-- 
1.7.9.5



More information about the Piglit mailing list