/*------------------------------------------------------------------------- * drawElements Quality Program EGL Module * --------------------------------------- * * Copyright 2014 The Android Open Source Project * * 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. * *//*! * \file * \brief EGL image tests. *//*--------------------------------------------------------------------*/ #include "teglImageFormatTests.hpp" #include "deStringUtil.hpp" #include "deSTLUtil.hpp" #include "tcuTestLog.hpp" #include "tcuSurface.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" #include "tcuImageCompare.hpp" #include "tcuCommandLine.hpp" #include "egluNativeDisplay.hpp" #include "egluNativeWindow.hpp" #include "egluNativePixmap.hpp" #include "egluConfigFilter.hpp" #include "egluUnique.hpp" #include "egluUtil.hpp" #include "eglwLibrary.hpp" #include "eglwEnums.hpp" #include "gluCallLogWrapper.hpp" #include "gluShaderProgram.hpp" #include "gluStrUtil.hpp" #include "gluTexture.hpp" #include "gluPixelTransfer.hpp" #include "gluObjectWrapper.hpp" #include "gluTextureUtil.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "teglImageUtil.hpp" #include "teglAndroidUtil.hpp" #include <vector> #include <string> #include <set> using std::vector; using std::set; using std::string; using de::MovePtr; using de::UniquePtr; using glu::Framebuffer; using glu::Renderbuffer; using glu::Texture; using eglu::UniqueImage; using tcu::ConstPixelBufferAccess; using namespace glw; using namespace eglw; namespace deqp { namespace egl { namespace { glu::ProgramSources programSources (const string& vertexSource, const string& fragmentSource) { glu::ProgramSources sources; sources << glu::VertexSource(vertexSource) << glu::FragmentSource(fragmentSource); return sources; } class Program : public glu::ShaderProgram { public: Program (const glw::Functions& gl, const char* vertexSource, const char* fragmentSource) : glu::ShaderProgram(gl, programSources(vertexSource, fragmentSource)) {} }; } // anonymous namespace Image { class ImageApi; class IllegalRendererException : public std::exception { }; class Action { public: virtual ~Action (void) {} virtual bool invoke (ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& refImg) const = 0; virtual string getRequiredExtension (void) const = 0; }; struct TestSpec { std::string name; std::string desc; enum ApiContext { API_GLES2 = 0, //API_VG //API_GLES1 API_LAST }; struct Operation { Operation (int apiIndex_, const Action& action_) : apiIndex(apiIndex_), action(&action_) {} int apiIndex; const Action* action; }; vector<ApiContext> contexts; vector<Operation> operations; }; class ImageApi { public: ImageApi (const Library& egl, int contextId, EGLDisplay display, EGLSurface surface); virtual ~ImageApi (void) {} protected: const Library& m_egl; int m_contextId; EGLDisplay m_display; EGLSurface m_surface; }; ImageApi::ImageApi (const Library& egl, int contextId, EGLDisplay display, EGLSurface surface) : m_egl (egl) , m_contextId (contextId) , m_display (display) , m_surface (surface) { } class GLES2ImageApi : public ImageApi, private glu::CallLogWrapper { public: class GLES2Action : public Action { public: bool invoke (ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; virtual bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const = 0; }; class Create : public GLES2Action { public: Create (MovePtr<ImageSource> imgSource) : m_imgSource(imgSource) {} string getRequiredExtension (void) const { return m_imgSource->getRequiredExtension(); } bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; glw::GLenum getEffectiveFormat (void) const { return m_imgSource->getEffectiveFormat(); } private: UniquePtr<ImageSource> m_imgSource; }; class Render : public GLES2Action { public: string getRequiredExtension (void) const { return "GL_OES_EGL_image"; } }; class RenderTexture2D : public Render { public: bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; }; class RenderTextureCubemap : public Render { public: bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; }; class RenderReadPixelsRenderbuffer : public Render { public: bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; }; class RenderDepthbuffer : public Render { public: bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; }; class RenderStencilbuffer : public Render { public: bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; }; class RenderTryAll : public Render { public: bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; }; class Modify : public GLES2Action { public: string getRequiredExtension (void) const { return "GL_OES_EGL_image"; } }; class ModifyTexSubImage : public Modify { public: ModifyTexSubImage (GLenum format, GLenum type) : m_format(format), m_type(type) {} bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; GLenum getFormat (void) const { return m_format; } GLenum getType (void) const { return m_type; } private: GLenum m_format; GLenum m_type; }; class ModifyRenderbuffer : public Modify { public: bool invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const; protected: virtual void initializeRbo (GLES2ImageApi& api, GLuint rbo, tcu::Texture2D& ref) const = 0; }; class ModifyRenderbufferClearColor : public ModifyRenderbuffer { public: ModifyRenderbufferClearColor (tcu::Vec4 color) : m_color(color) {} protected: void initializeRbo (GLES2ImageApi& api, GLuint rbo, tcu::Texture2D& ref) const; tcu::Vec4 m_color; }; class ModifyRenderbufferClearDepth : public ModifyRenderbuffer { public: ModifyRenderbufferClearDepth (GLfloat depth) : m_depth(depth) {} protected: void initializeRbo (GLES2ImageApi& api, GLuint rbo, tcu::Texture2D& ref) const; GLfloat m_depth; }; class ModifyRenderbufferClearStencil : public ModifyRenderbuffer { public: ModifyRenderbufferClearStencil (GLint stencil) : m_stencil(stencil) {} protected: void initializeRbo (GLES2ImageApi& api, GLuint rbo, tcu::Texture2D& ref) const; GLint m_stencil; }; GLES2ImageApi (const Library& egl, const glw::Functions& gl, int contextId, tcu::TestLog& log, EGLDisplay display, EGLSurface surface, EGLConfig config); ~GLES2ImageApi (void); private: EGLContext m_context; const glw::Functions& m_gl; MovePtr<UniqueImage> createImage (const ImageSource& source, const ClientBuffer& buffer) const; }; GLES2ImageApi::GLES2ImageApi (const Library& egl, const glw::Functions& gl, int contextId, tcu::TestLog& log, EGLDisplay display, EGLSurface surface, EGLConfig config) : ImageApi (egl, contextId, display, surface) , glu::CallLogWrapper (gl, log) , m_context (DE_NULL) , m_gl (gl) { const EGLint attriblist[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; EGLint configId = -1; EGLU_CHECK_CALL(m_egl, getConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId)); getLog() << tcu::TestLog::Message << "Creating gles2 context with config id: " << configId << " context: " << m_contextId << tcu::TestLog::EndMessage; egl.bindAPI(EGL_OPENGL_ES_API); m_context = m_egl.createContext(m_display, config, EGL_NO_CONTEXT, attriblist); EGLU_CHECK_MSG(m_egl, "Failed to create GLES2 context"); egl.makeCurrent(display, m_surface, m_surface, m_context); EGLU_CHECK_MSG(m_egl, "Failed to make context current"); } GLES2ImageApi::~GLES2ImageApi (void) { m_egl.makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); m_egl.destroyContext(m_display, m_context); } bool GLES2ImageApi::GLES2Action::invoke (ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const { GLES2ImageApi& gles2Api = dynamic_cast<GLES2ImageApi&>(api); gles2Api.m_egl.makeCurrent(gles2Api.m_display, gles2Api.m_surface, gles2Api.m_surface, gles2Api.m_context); return invokeGLES2(gles2Api, image, ref); } bool GLES2ImageApi::Create::invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& image, tcu::Texture2D& ref) const { de::UniquePtr<ClientBuffer> buffer (m_imgSource->createBuffer(api.m_gl, &ref)); image = api.createImage(*m_imgSource, *buffer); return true; } MovePtr<UniqueImage> GLES2ImageApi::createImage (const ImageSource& source, const ClientBuffer& buffer) const { const EGLImageKHR image = source.createImage(m_egl, m_display, m_context, buffer.get()); return MovePtr<UniqueImage>(new UniqueImage(m_egl, m_display, image)); } static void imageTargetTexture2D (const Library& egl, const glw::Functions& gl, GLeglImageOES img) { gl.eglImageTargetTexture2DOES(GL_TEXTURE_2D, img); { const GLenum error = gl.getError(); if (error == GL_INVALID_OPERATION) TCU_THROW(NotSupportedError, "Creating texture2D from EGLImage type not supported"); GLU_EXPECT_NO_ERROR(error, "glEGLImageTargetTexture2DOES()"); EGLU_CHECK_MSG(egl, "glEGLImageTargetTexture2DOES()"); } } static void imageTargetRenderbuffer (const Library& egl, const glw::Functions& gl, GLeglImageOES img) { gl.eglImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, img); { const GLenum error = gl.getError(); if (error == GL_INVALID_OPERATION) TCU_THROW(NotSupportedError, "Creating renderbuffer from EGLImage type not supported"); GLU_EXPECT_NO_ERROR(error, "glEGLImageTargetRenderbufferStorageOES()"); EGLU_CHECK_MSG(egl, "glEGLImageTargetRenderbufferStorageOES()"); } } static void framebufferRenderbuffer (const glw::Functions& gl, GLenum attachment, GLuint rbo) { GLU_CHECK_GLW_CALL(gl, framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbo)); TCU_CHECK_AND_THROW(NotSupportedError, gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, ("EGLImage as " + string(glu::getFramebufferAttachmentName(attachment)) + " not supported").c_str()); } static const float squareTriangleCoords[] = { -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0 }; bool GLES2ImageApi::RenderTexture2D::invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& img, tcu::Texture2D& reference) const { const glw::Functions& gl = api.m_gl; tcu::TestLog& log = api.getLog(); Texture srcTex (gl); // Branch only taken in TryAll case if (reference.getFormat().order == tcu::TextureFormat::DS || reference.getFormat().order == tcu::TextureFormat::D) throw IllegalRendererException(); // Skip, GLES2 does not support sampling depth textures if (reference.getFormat().order == tcu::TextureFormat::S) throw IllegalRendererException(); // Skip, GLES2 does not support sampling stencil textures gl.clearColor(0.0, 0.0, 0.0, 0.0); gl.viewport(0, 0, reference.getWidth(), reference.getHeight()); gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); gl.disable(GL_DEPTH_TEST); log << tcu::TestLog::Message << "Rendering EGLImage as GL_TEXTURE_2D in context: " << api.m_contextId << tcu::TestLog::EndMessage; TCU_CHECK(**img != EGL_NO_IMAGE_KHR); GLU_CHECK_GLW_CALL(gl, bindTexture(GL_TEXTURE_2D, *srcTex)); imageTargetTexture2D(api.m_egl, gl, **img); GLU_CHECK_GLW_CALL(gl, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); GLU_CHECK_GLW_CALL(gl, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); GLU_CHECK_GLW_CALL(gl, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GLU_CHECK_GLW_CALL(gl, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); const char* const vertexShader = "attribute highp vec2 a_coord;\n" "varying mediump vec2 v_texCoord;\n" "void main(void) {\n" "\tv_texCoord = vec2((a_coord.x + 1.0) * 0.5, (a_coord.y + 1.0) * 0.5);\n" "\tgl_Position = vec4(a_coord, -0.1, 1.0);\n" "}\n"; const char* const fragmentShader = "varying mediump vec2 v_texCoord;\n" "uniform sampler2D u_sampler;\n" "void main(void) {\n" "\tmediump vec4 texColor = texture2D(u_sampler, v_texCoord);\n" "\tgl_FragColor = vec4(texColor);\n" "}"; Program program(gl, vertexShader, fragmentShader); TCU_CHECK(program.isOk()); GLuint glProgram = program.getProgram(); GLU_CHECK_GLW_CALL(gl, useProgram(glProgram)); GLuint coordLoc = gl.getAttribLocation(glProgram, "a_coord"); TCU_CHECK_MSG((int)coordLoc != -1, "Couldn't find attribute a_coord"); GLuint samplerLoc = gl.getUniformLocation(glProgram, "u_sampler"); TCU_CHECK_MSG((int)samplerLoc != (int)-1, "Couldn't find uniform u_sampler"); GLU_CHECK_GLW_CALL(gl, bindTexture(GL_TEXTURE_2D, *srcTex)); GLU_CHECK_GLW_CALL(gl, uniform1i(samplerLoc, 0)); GLU_CHECK_GLW_CALL(gl, enableVertexAttribArray(coordLoc)); GLU_CHECK_GLW_CALL(gl, vertexAttribPointer(coordLoc, 2, GL_FLOAT, GL_FALSE, 0, squareTriangleCoords)); GLU_CHECK_GLW_CALL(gl, drawArrays(GL_TRIANGLES, 0, 6)); GLU_CHECK_GLW_CALL(gl, disableVertexAttribArray(coordLoc)); GLU_CHECK_GLW_CALL(gl, bindTexture(GL_TEXTURE_2D, 0)); tcu::Surface refSurface (reference.getWidth(), reference.getHeight()); tcu::Surface screen (reference.getWidth(), reference.getHeight()); GLU_CHECK_GLW_CALL(gl, readPixels(0, 0, screen.getWidth(), screen.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, screen.getAccess().getDataPtr())); tcu::copy(refSurface.getAccess(), reference.getLevel(0)); float threshold = 0.05f; bool match = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refSurface, screen, threshold, tcu::COMPARE_LOG_RESULT); return match; } bool GLES2ImageApi::RenderDepthbuffer::invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& img, tcu::Texture2D& reference) const { const glw::Functions& gl = api.m_gl; tcu::TestLog& log = api.getLog(); Framebuffer framebuffer (gl); Renderbuffer renderbufferColor (gl); Renderbuffer renderbufferDepth (gl); const tcu::RGBA compareThreshold (32, 32, 32, 32); // layer colors are far apart, large thresholds are ok // Branch only taken in TryAll case if (reference.getFormat().order != tcu::TextureFormat::DS && reference.getFormat().order != tcu::TextureFormat::D) throw IllegalRendererException(); // Skip, interpreting non-depth data as depth data is not meaningful log << tcu::TestLog::Message << "Rendering with depth buffer" << tcu::TestLog::EndMessage; GLU_CHECK_GLW_CALL(gl, bindFramebuffer(GL_FRAMEBUFFER, *framebuffer)); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, *renderbufferColor)); GLU_CHECK_GLW_CALL(gl, renderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, reference.getWidth(), reference.getHeight())); framebufferRenderbuffer(gl, GL_COLOR_ATTACHMENT0, *renderbufferColor); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, *renderbufferDepth)); imageTargetRenderbuffer(api.m_egl, gl, **img); framebufferRenderbuffer(gl, GL_DEPTH_ATTACHMENT, *renderbufferDepth); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, 0)); GLU_CHECK_GLW_CALL(gl, viewport(0, 0, reference.getWidth(), reference.getHeight())); // Render const char* vertexShader = "attribute highp vec2 a_coord;\n" "uniform highp float u_depth;\n" "void main(void) {\n" "\tgl_Position = vec4(a_coord, u_depth, 1.0);\n" "}\n"; const char* fragmentShader = "uniform mediump vec4 u_color;\n" "void main(void) {\n" "\tgl_FragColor = u_color;\n" "}"; Program program(gl, vertexShader, fragmentShader); TCU_CHECK(program.isOk()); GLuint glProgram = program.getProgram(); GLU_CHECK_GLW_CALL(gl, useProgram(glProgram)); GLuint coordLoc = gl.getAttribLocation(glProgram, "a_coord"); TCU_CHECK_MSG((int)coordLoc != -1, "Couldn't find attribute a_coord"); GLuint colorLoc = gl.getUniformLocation(glProgram, "u_color"); TCU_CHECK_MSG((int)colorLoc != (int)-1, "Couldn't find uniform u_color"); GLuint depthLoc = gl.getUniformLocation(glProgram, "u_depth"); TCU_CHECK_MSG((int)depthLoc != (int)-1, "Couldn't find uniform u_depth"); GLU_CHECK_GLW_CALL(gl, clearColor(0.5f, 1.0f, 0.5f, 1.0f)); GLU_CHECK_GLW_CALL(gl, clear(GL_COLOR_BUFFER_BIT)); tcu::Vec4 depthLevelColors[] = { tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), tcu::Vec4(1.0f, 0.0f, 1.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(0.5f, 0.0f, 0.0f, 1.0f), tcu::Vec4(0.0f, 0.5f, 0.0f, 1.0f), tcu::Vec4(0.5f, 0.5f, 0.0f, 1.0f) }; GLU_CHECK_GLW_CALL(gl, enableVertexAttribArray(coordLoc)); GLU_CHECK_GLW_CALL(gl, vertexAttribPointer(coordLoc, 2, GL_FLOAT, GL_FALSE, 0, squareTriangleCoords)); GLU_CHECK_GLW_CALL(gl, enable(GL_DEPTH_TEST)); GLU_CHECK_GLW_CALL(gl, depthFunc(GL_LESS)); GLU_CHECK_GLW_CALL(gl, depthMask(GL_FALSE)); for (int level = 0; level < DE_LENGTH_OF_ARRAY(depthLevelColors); level++) { const tcu::Vec4 color = depthLevelColors[level]; const float clipDepth = ((float)(level + 1) * 0.1f) * 2.0f - 1.0f; // depth in clip coords GLU_CHECK_GLW_CALL(gl, uniform4f(colorLoc, color.x(), color.y(), color.z(), color.w())); GLU_CHECK_GLW_CALL(gl, uniform1f(depthLoc, clipDepth)); GLU_CHECK_GLW_CALL(gl, drawArrays(GL_TRIANGLES, 0, 6)); } GLU_CHECK_GLW_CALL(gl, depthMask(GL_TRUE)); GLU_CHECK_GLW_CALL(gl, disable(GL_DEPTH_TEST)); GLU_CHECK_GLW_CALL(gl, disableVertexAttribArray(coordLoc)); const ConstPixelBufferAccess& refAccess = reference.getLevel(0); tcu::Surface screen (reference.getWidth(), reference.getHeight()); tcu::Surface referenceScreen (reference.getWidth(), reference.getHeight()); gl.readPixels(0, 0, screen.getWidth(), screen.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, screen.getAccess().getDataPtr()); for (int y = 0; y < reference.getHeight(); y++) { for (int x = 0; x < reference.getWidth(); x++) { tcu::Vec4 result = tcu::Vec4(0.5f, 1.0f, 0.5f, 1.0f); for (int level = 0; level < DE_LENGTH_OF_ARRAY(depthLevelColors); level++) { if ((float)(level + 1) * 0.1f < refAccess.getPixDepth(x, y)) result = depthLevelColors[level]; } referenceScreen.getAccess().setPixel(result, x, y); } } GLU_CHECK_GLW_CALL(gl, bindFramebuffer(GL_FRAMEBUFFER, 0)); GLU_CHECK_GLW_CALL(gl, finish()); return tcu::pixelThresholdCompare(log, "Depth buffer rendering result", "Result from rendering with depth buffer", referenceScreen, screen, compareThreshold, tcu::COMPARE_LOG_RESULT); } bool GLES2ImageApi::RenderStencilbuffer::invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& img, tcu::Texture2D& reference) const { // Branch only taken in TryAll case if (reference.getFormat().order != tcu::TextureFormat::DS && reference.getFormat().order != tcu::TextureFormat::S) throw IllegalRendererException(); // Skip, interpreting non-stencil data as stencil data is not meaningful const glw::Functions& gl = api.m_gl; tcu::TestLog& log = api.getLog(); Framebuffer framebuffer (gl); Renderbuffer renderbufferColor (gl); Renderbuffer renderbufferStencil (gl); const tcu::RGBA compareThreshold (32, 32, 32, 32); // layer colors are far apart, large thresholds are ok const deUint32 numStencilBits = tcu::getTextureFormatBitDepth(tcu::getEffectiveDepthStencilTextureFormat(reference.getLevel(0).getFormat(), tcu::Sampler::MODE_STENCIL)).x(); const deUint32 maxStencil = deBitMask32(0, numStencilBits); log << tcu::TestLog::Message << "Rendering with stencil buffer" << tcu::TestLog::EndMessage; GLU_CHECK_GLW_CALL(gl, bindFramebuffer(GL_FRAMEBUFFER, *framebuffer)); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, *renderbufferColor)); GLU_CHECK_GLW_CALL(gl, renderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, reference.getWidth(), reference.getHeight())); framebufferRenderbuffer(gl, GL_COLOR_ATTACHMENT0, *renderbufferColor); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, *renderbufferStencil)); imageTargetRenderbuffer(api.m_egl, gl, **img); framebufferRenderbuffer(gl, GL_STENCIL_ATTACHMENT, *renderbufferStencil); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, 0)); GLU_CHECK_GLW_CALL(gl, viewport(0, 0, reference.getWidth(), reference.getHeight())); // Render const char* vertexShader = "attribute highp vec2 a_coord;\n" "void main(void) {\n" "\tgl_Position = vec4(a_coord, 0.0, 1.0);\n" "}\n"; const char* fragmentShader = "uniform mediump vec4 u_color;\n" "void main(void) {\n" "\tgl_FragColor = u_color;\n" "}"; Program program(gl, vertexShader, fragmentShader); TCU_CHECK(program.isOk()); GLuint glProgram = program.getProgram(); GLU_CHECK_GLW_CALL(gl, useProgram(glProgram)); GLuint coordLoc = gl.getAttribLocation(glProgram, "a_coord"); TCU_CHECK_MSG((int)coordLoc != -1, "Couldn't find attribute a_coord"); GLuint colorLoc = gl.getUniformLocation(glProgram, "u_color"); TCU_CHECK_MSG((int)colorLoc != (int)-1, "Couldn't find uniform u_color"); GLU_CHECK_GLW_CALL(gl, clearColor(0.5f, 1.0f, 0.5f, 1.0f)); GLU_CHECK_GLW_CALL(gl, clear(GL_COLOR_BUFFER_BIT)); tcu::Vec4 stencilLevelColors[] = { tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), tcu::Vec4(1.0f, 0.0f, 1.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(0.5f, 0.0f, 0.0f, 1.0f), tcu::Vec4(0.0f, 0.5f, 0.0f, 1.0f), tcu::Vec4(0.5f, 0.5f, 0.0f, 1.0f) }; GLU_CHECK_GLW_CALL(gl, enableVertexAttribArray(coordLoc)); GLU_CHECK_GLW_CALL(gl, vertexAttribPointer(coordLoc, 2, GL_FLOAT, GL_FALSE, 0, squareTriangleCoords)); GLU_CHECK_GLW_CALL(gl, enable(GL_STENCIL_TEST)); GLU_CHECK_GLW_CALL(gl, stencilOp(GL_KEEP, GL_KEEP, GL_KEEP)); for (int level = 0; level < DE_LENGTH_OF_ARRAY(stencilLevelColors); level++) { const tcu::Vec4 color = stencilLevelColors[level]; const int stencil = (int)(((float)(level + 1) * 0.1f) * (float)maxStencil); GLU_CHECK_GLW_CALL(gl, stencilFunc(GL_LESS, stencil, 0xFFFFFFFFu)); GLU_CHECK_GLW_CALL(gl, uniform4f(colorLoc, color.x(), color.y(), color.z(), color.w())); GLU_CHECK_GLW_CALL(gl, drawArrays(GL_TRIANGLES, 0, 6)); } GLU_CHECK_GLW_CALL(gl, disable(GL_STENCIL_TEST)); GLU_CHECK_GLW_CALL(gl, disableVertexAttribArray(coordLoc)); const ConstPixelBufferAccess& refAccess = reference.getLevel(0); tcu::Surface screen (reference.getWidth(), reference.getHeight()); tcu::Surface referenceScreen (reference.getWidth(), reference.getHeight()); gl.readPixels(0, 0, screen.getWidth(), screen.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, screen.getAccess().getDataPtr()); for (int y = 0; y < reference.getHeight(); y++) for (int x = 0; x < reference.getWidth(); x++) { tcu::Vec4 result = tcu::Vec4(0.5f, 1.0f, 0.5f, 1.0f); for (int level = 0; level < DE_LENGTH_OF_ARRAY(stencilLevelColors); level++) { const int levelStencil = (int)(((float)(level + 1) * 0.1f) * (float)maxStencil); if (levelStencil < refAccess.getPixStencil(x, y)) result = stencilLevelColors[level]; } referenceScreen.getAccess().setPixel(result, x, y); } GLU_CHECK_GLW_CALL(gl, bindFramebuffer(GL_FRAMEBUFFER, 0)); GLU_CHECK_GLW_CALL(gl, finish()); return tcu::pixelThresholdCompare(log, "StencilResult", "Result from rendering with stencil buffer", referenceScreen, screen, compareThreshold, tcu::COMPARE_LOG_RESULT); } bool GLES2ImageApi::RenderReadPixelsRenderbuffer::invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& img, tcu::Texture2D& reference) const { const glw::Functions& gl = api.m_gl; const tcu::IVec4 bitDepth = tcu::getTextureFormatMantissaBitDepth(reference.getFormat()); const tcu::IVec4 threshold (2 * (tcu::IVec4(1) << (tcu::IVec4(8) - bitDepth))); const tcu::RGBA threshold8 ((deUint8)(de::clamp(threshold[0], 0, 255)), (deUint8)(de::clamp(threshold[1], 0, 255)), (deUint8)(de::clamp(threshold[2], 0, 255)), (deUint8)(de::clamp(threshold[3], 0, 255))); tcu::TestLog& log = api.getLog(); Framebuffer framebuffer (gl); Renderbuffer renderbuffer (gl); tcu::Surface screen (reference.getWidth(), reference.getHeight()); tcu::Surface refSurface (reference.getWidth(), reference.getHeight()); // Branch only taken in TryAll case if (reference.getFormat().order == tcu::TextureFormat::DS || reference.getFormat().order == tcu::TextureFormat::D) throw IllegalRendererException(); // Skip, GLES2 does not support ReadPixels for depth attachments if (reference.getFormat().order == tcu::TextureFormat::S) throw IllegalRendererException(); // Skip, GLES2 does not support ReadPixels for stencil attachments log << tcu::TestLog::Message << "Reading with ReadPixels from renderbuffer" << tcu::TestLog::EndMessage; GLU_CHECK_GLW_CALL(gl, bindFramebuffer(GL_FRAMEBUFFER, *framebuffer)); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, *renderbuffer)); imageTargetRenderbuffer(api.m_egl, gl, **img); framebufferRenderbuffer(gl, GL_COLOR_ATTACHMENT0, *renderbuffer); GLU_CHECK_GLW_CALL(gl, viewport(0, 0, reference.getWidth(), reference.getHeight())); gl.readPixels(0, 0, screen.getWidth(), screen.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, screen.getAccess().getDataPtr()); GLU_CHECK_GLW_CALL(gl, bindFramebuffer(GL_FRAMEBUFFER, 0)); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, 0)); GLU_CHECK_GLW_CALL(gl, finish()); tcu::copy(refSurface.getAccess(), reference.getLevel(0)); return tcu::pixelThresholdCompare(log, "Renderbuffer read", "Result from reading renderbuffer", refSurface, screen, threshold8, tcu::COMPARE_LOG_RESULT); } bool GLES2ImageApi::RenderTryAll::invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& img, tcu::Texture2D& reference) const { bool foundSupported = false; tcu::TestLog& log = api.getLog(); GLES2ImageApi::RenderTexture2D renderTex2D; GLES2ImageApi::RenderReadPixelsRenderbuffer renderReadPixels; GLES2ImageApi::RenderDepthbuffer renderDepth; GLES2ImageApi::RenderStencilbuffer renderStencil; Action* actions[] = { &renderTex2D, &renderReadPixels, &renderDepth, &renderStencil }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(actions); ++ndx) { try { if (!actions[ndx]->invoke(api, img, reference)) return false; foundSupported = true; } catch (const tcu::NotSupportedError& error) { log << tcu::TestLog::Message << error.what() << tcu::TestLog::EndMessage; } catch (const IllegalRendererException&) { // not valid renderer } } if (!foundSupported) throw tcu::NotSupportedError("Rendering not supported", "", __FILE__, __LINE__); return true; } bool GLES2ImageApi::ModifyTexSubImage::invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& img, tcu::Texture2D& reference) const { const glw::Functions& gl = api.m_gl; tcu::TestLog& log = api.getLog(); glu::Texture srcTex (gl); const int xOffset = 8; const int yOffset = 16; const int xSize = de::clamp(16, 0, reference.getWidth() - xOffset); const int ySize = de::clamp(16, 0, reference.getHeight() - yOffset); tcu::Texture2D src (glu::mapGLTransferFormat(m_format, m_type), xSize, ySize); log << tcu::TestLog::Message << "Modifying EGLImage with gl.texSubImage2D" << tcu::TestLog::EndMessage; src.allocLevel(0); tcu::fillWithComponentGradients(src.getLevel(0), tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); GLU_CHECK_GLW_CALL(gl, bindTexture(GL_TEXTURE_2D, *srcTex)); imageTargetTexture2D(api.m_egl, gl, **img); GLU_CHECK_GLW_CALL(gl, texSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, src.getWidth(), src.getHeight(), m_format, m_type, src.getLevel(0).getDataPtr())); GLU_CHECK_GLW_CALL(gl, bindTexture(GL_TEXTURE_2D, 0)); GLU_CHECK_GLW_CALL(gl, finish()); tcu::copy(tcu::getSubregion(reference.getLevel(0), xOffset, yOffset, 0, xSize, ySize, 1), src.getLevel(0)); return true; } bool GLES2ImageApi::ModifyRenderbuffer::invokeGLES2 (GLES2ImageApi& api, MovePtr<UniqueImage>& img, tcu::Texture2D& reference) const { const glw::Functions& gl = api.m_gl; tcu::TestLog& log = api.getLog(); glu::Framebuffer framebuffer (gl); glu::Renderbuffer renderbuffer (gl); log << tcu::TestLog::Message << "Modifying EGLImage with glClear to renderbuffer" << tcu::TestLog::EndMessage; GLU_CHECK_GLW_CALL(gl, bindFramebuffer(GL_FRAMEBUFFER, *framebuffer)); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, *renderbuffer)); imageTargetRenderbuffer(api.m_egl, gl, **img); initializeRbo(api, *renderbuffer, reference); GLU_CHECK_GLW_CALL(gl, bindFramebuffer(GL_FRAMEBUFFER, 0)); GLU_CHECK_GLW_CALL(gl, bindRenderbuffer(GL_RENDERBUFFER, 0)); GLU_CHECK_GLW_CALL(gl, finish()); return true; } void GLES2ImageApi::ModifyRenderbufferClearColor::initializeRbo (GLES2ImageApi& api, GLuint renderbuffer, tcu::Texture2D& reference) const { const glw::Functions& gl = api.m_gl; framebufferRenderbuffer(gl, GL_COLOR_ATTACHMENT0, renderbuffer); GLU_CHECK_GLW_CALL(gl, viewport(0, 0, reference.getWidth(), reference.getHeight())); GLU_CHECK_GLW_CALL(gl, clearColor(m_color.x(), m_color.y(), m_color.z(), m_color.w())); GLU_CHECK_GLW_CALL(gl, clear(GL_COLOR_BUFFER_BIT)); tcu::clear(reference.getLevel(0), m_color); } void GLES2ImageApi::ModifyRenderbufferClearDepth::initializeRbo (GLES2ImageApi& api, GLuint renderbuffer, tcu::Texture2D& reference) const { const glw::Functions& gl = api.m_gl; framebufferRenderbuffer(gl, GL_DEPTH_ATTACHMENT, renderbuffer); GLU_CHECK_GLW_CALL(gl, viewport(0, 0, reference.getWidth(), reference.getHeight())); GLU_CHECK_GLW_CALL(gl, clearDepthf(m_depth)); GLU_CHECK_GLW_CALL(gl, clear(GL_DEPTH_BUFFER_BIT)); tcu::clearDepth(reference.getLevel(0), m_depth); } void GLES2ImageApi::ModifyRenderbufferClearStencil::initializeRbo (GLES2ImageApi& api, GLuint renderbuffer, tcu::Texture2D& reference) const { const glw::Functions& gl = api.m_gl; framebufferRenderbuffer(gl, GL_STENCIL_ATTACHMENT, renderbuffer); GLU_CHECK_GLW_CALL(gl, viewport(0, 0, reference.getWidth(), reference.getHeight())); GLU_CHECK_GLW_CALL(gl, clearStencil(m_stencil)); GLU_CHECK_GLW_CALL(gl, clear(GL_STENCIL_BUFFER_BIT)); tcu::clearStencil(reference.getLevel(0), m_stencil); } class ImageFormatCase : public TestCase, private glu::CallLogWrapper { public: ImageFormatCase (EglTestContext& eglTestCtx, const TestSpec& spec); ~ImageFormatCase (void); void init (void); void deinit (void); IterateResult iterate (void); void checkExtensions (void); private: EGLConfig getConfig (void); const TestSpec m_spec; vector<ImageApi*> m_apiContexts; EGLDisplay m_display; eglu::NativeWindow* m_window; EGLSurface m_surface; EGLConfig m_config; int m_curIter; MovePtr<UniqueImage>m_img; tcu::Texture2D m_refImg; glw::Functions m_gl; }; EGLConfig ImageFormatCase::getConfig (void) { const EGLint attribList[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 8, EGL_NONE }; return eglu::chooseSingleConfig(m_eglTestCtx.getLibrary(), m_display, attribList); } ImageFormatCase::ImageFormatCase (EglTestContext& eglTestCtx, const TestSpec& spec) : TestCase (eglTestCtx, spec.name.c_str(), spec.desc.c_str()) , glu::CallLogWrapper (m_gl, eglTestCtx.getTestContext().getLog()) , m_spec (spec) , m_display (EGL_NO_DISPLAY) , m_window (DE_NULL) , m_surface (EGL_NO_SURFACE) , m_config (0) , m_curIter (0) , m_refImg (tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), 1, 1) { } ImageFormatCase::~ImageFormatCase (void) { deinit(); } void ImageFormatCase::checkExtensions (void) { const Library& egl = m_eglTestCtx.getLibrary(); const EGLDisplay dpy = m_display; set<string> exts; const vector<string> glExts = de::splitString((const char*) m_gl.getString(GL_EXTENSIONS)); const vector<string> eglExts = eglu::getDisplayExtensions(egl, dpy); exts.insert(glExts.begin(), glExts.end()); exts.insert(eglExts.begin(), eglExts.end()); if (eglu::getVersion(egl, dpy) >= eglu::Version(1, 5)) { // EGL 1.5 has built-in support for EGLImage and GL sources exts.insert("EGL_KHR_image_base"); exts.insert("EGL_KHR_gl_texture_2D_image"); exts.insert("EGL_KHR_gl_texture_cubemap_image"); exts.insert("EGL_KHR_gl_renderbuffer_image"); } if (!de::contains(exts, "EGL_KHR_image_base") && !de::contains(exts, "EGL_KHR_image")) { getLog() << tcu::TestLog::Message << "EGL version is under 1.5 and neither EGL_KHR_image nor EGL_KHR_image_base is supported." << "One should be supported." << tcu::TestLog::EndMessage; TCU_THROW(NotSupportedError, "Extension not supported: EGL_KHR_image_base"); } for (int operationNdx = 0; operationNdx < (int)m_spec.operations.size(); operationNdx++) { const TestSpec::Operation& op = m_spec.operations[operationNdx]; const string ext = op.action->getRequiredExtension(); if (!de::contains(exts, ext)) TCU_THROW_EXPR(NotSupportedError, "Extension not supported", ext.c_str()); } } void ImageFormatCase::init (void) { const Library& egl = m_eglTestCtx.getLibrary(); const eglu::NativeWindowFactory& windowFactory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine()); try { m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay()); m_config = getConfig(); m_window = windowFactory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine()))); m_surface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_display, m_config, DE_NULL); { const char* extensions[] = { "GL_OES_EGL_image" }; m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0), DE_LENGTH_OF_ARRAY(extensions), &extensions[0]); } for (int contextNdx = 0; contextNdx < (int)m_spec.contexts.size(); contextNdx++) { ImageApi* api = DE_NULL; switch (m_spec.contexts[contextNdx]) { case TestSpec::API_GLES2: { api = new GLES2ImageApi(egl, m_gl, contextNdx, getLog(), m_display, m_surface, m_config); break; } default: DE_ASSERT(false); break; } m_apiContexts.push_back(api); } checkExtensions(); } catch (...) { deinit(); throw; } } void ImageFormatCase::deinit (void) { const Library& egl = m_eglTestCtx.getLibrary(); for (int contexNdx = 0 ; contexNdx < (int)m_apiContexts.size(); contexNdx++) delete m_apiContexts[contexNdx]; m_apiContexts.clear(); if (m_surface != EGL_NO_SURFACE) { egl.destroySurface(m_display, m_surface); m_surface = EGL_NO_SURFACE; } delete m_window; m_window = DE_NULL; if (m_display != EGL_NO_DISPLAY) { egl.terminate(m_display); m_display = EGL_NO_DISPLAY; } } TestCase::IterateResult ImageFormatCase::iterate (void) { const TestSpec::Operation& op = m_spec.operations[m_curIter++]; ImageApi& api = *m_apiContexts[op.apiIndex]; const bool isOk = op.action->invoke(api, m_img, m_refImg); if (isOk && m_curIter < (int)m_spec.operations.size()) return CONTINUE; else if (isOk) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); return STOP; } struct LabeledAction { string label; MovePtr<Action> action; }; // A simple vector mockup that we need because MovePtr isn't copy-constructible. struct LabeledActions { LabeledActions (void) : m_numActions(0){} LabeledAction& operator[] (int ndx) { DE_ASSERT(0 <= ndx && ndx < m_numActions); return m_actions[ndx]; } void add (const string& label, MovePtr<Action> action); int size (void) const { return m_numActions; } private: LabeledAction m_actions[32]; int m_numActions; }; void LabeledActions::add (const string& label, MovePtr<Action> action) { DE_ASSERT(m_numActions < DE_LENGTH_OF_ARRAY(m_actions)); m_actions[m_numActions].label = label; m_actions[m_numActions].action = action; ++m_numActions; } class ImageTests : public TestCaseGroup { protected: ImageTests (EglTestContext& eglTestCtx, const string& name, const string& desc) : TestCaseGroup(eglTestCtx, name.c_str(), desc.c_str()) {} void addCreateTexture (const string& name, EGLenum source, GLenum internalFormat, GLenum format, GLenum type); void addCreateRenderbuffer (const string& name, GLenum format); void addCreateAndroidNative (const string& name, GLenum format); void addCreateTexture2DActions (const string& prefix); void addCreateTextureCubemapActions (const string& suffix, GLenum internalFormat, GLenum format, GLenum type); void addCreateRenderbufferActions (void); void addCreateAndroidNativeActions (void); LabeledActions m_createActions; }; void ImageTests::addCreateTexture (const string& name, EGLenum source, GLenum internalFormat, GLenum format, GLenum type) { m_createActions.add(name, MovePtr<Action>(new GLES2ImageApi::Create(createTextureImageSource(source, internalFormat, format, type)))); } void ImageTests::addCreateRenderbuffer (const string& name, GLenum format) { m_createActions.add(name, MovePtr<Action>(new GLES2ImageApi::Create(createRenderbufferImageSource(format)))); } void ImageTests::addCreateAndroidNative (const string& name, GLenum format) { m_createActions.add(name, MovePtr<Action>(new GLES2ImageApi::Create(createAndroidNativeImageSource(format)))); } void ImageTests::addCreateTexture2DActions (const string& prefix) { addCreateTexture(prefix + "rgb8", EGL_GL_TEXTURE_2D_KHR, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE); addCreateTexture(prefix + "rgb565", EGL_GL_TEXTURE_2D_KHR, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5); addCreateTexture(prefix + "rgba8", EGL_GL_TEXTURE_2D_KHR, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE); addCreateTexture(prefix + "rgb5_a1", EGL_GL_TEXTURE_2D_KHR, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1); addCreateTexture(prefix + "rgba4", EGL_GL_TEXTURE_2D_KHR, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4); } void ImageTests::addCreateTextureCubemapActions (const string& suffix, GLenum internalFormat, GLenum format, GLenum type) { addCreateTexture("cubemap_positive_x" + suffix, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR, internalFormat, format, type); addCreateTexture("cubemap_positive_y" + suffix, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR, internalFormat, format, type); addCreateTexture("cubemap_positive_z" + suffix, EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR, internalFormat, format, type); addCreateTexture("cubemap_negative_x" + suffix, EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR, internalFormat, format, type); addCreateTexture("cubemap_negative_y" + suffix, EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR, internalFormat, format, type); addCreateTexture("cubemap_negative_z" + suffix, EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR, internalFormat, format, type); } void ImageTests::addCreateRenderbufferActions (void) { addCreateRenderbuffer("renderbuffer_rgba4", GL_RGBA4); addCreateRenderbuffer("renderbuffer_rgb5_a1", GL_RGB5_A1); addCreateRenderbuffer("renderbuffer_rgb565", GL_RGB565); addCreateRenderbuffer("renderbuffer_depth16", GL_DEPTH_COMPONENT16); addCreateRenderbuffer("renderbuffer_stencil", GL_STENCIL_INDEX8); } void ImageTests::addCreateAndroidNativeActions (void) { addCreateAndroidNative("android_native_rgb565", GL_RGB565); addCreateAndroidNative("android_native_rgb8", GL_RGB8); addCreateAndroidNative("android_native_rgba4", GL_RGBA4); addCreateAndroidNative("android_native_rgb5_a1", GL_RGB5_A1); addCreateAndroidNative("android_native_rgba8", GL_RGBA8); } class RenderTests : public ImageTests { protected: RenderTests (EglTestContext& eglTestCtx, const string& name, const string& desc) : ImageTests (eglTestCtx, name, desc) {} void addRenderActions (void); LabeledActions m_renderActions; }; void RenderTests::addRenderActions (void) { m_renderActions.add("texture", MovePtr<Action>(new GLES2ImageApi::RenderTexture2D())); m_renderActions.add("read_pixels", MovePtr<Action>(new GLES2ImageApi::RenderReadPixelsRenderbuffer())); m_renderActions.add("depth_buffer", MovePtr<Action>(new GLES2ImageApi::RenderDepthbuffer())); m_renderActions.add("stencil_buffer", MovePtr<Action>(new GLES2ImageApi::RenderStencilbuffer())); } class SimpleCreationTests : public RenderTests { public: SimpleCreationTests (EglTestContext& eglTestCtx, const string& name, const string& desc) : RenderTests(eglTestCtx, name, desc) {} void init (void); }; bool isDepthFormat (GLenum format) { switch (format) { case GL_RGB: case GL_RGB8: case GL_RGB565: case GL_RGBA: case GL_RGBA4: case GL_RGBA8: case GL_RGB5_A1: return false; case GL_DEPTH_COMPONENT16: return true; case GL_STENCIL_INDEX8: return false; default: DE_ASSERT(false); return false; } } bool isStencilFormat (GLenum format) { switch (format) { case GL_RGB: case GL_RGB8: case GL_RGB565: case GL_RGBA: case GL_RGBA4: case GL_RGBA8: case GL_RGB5_A1: return false; case GL_DEPTH_COMPONENT16: return false; case GL_STENCIL_INDEX8: return true; default: DE_ASSERT(false); return false; } } bool isCompatibleCreateAndRenderActions (const Action& create, const Action& render) { if (const GLES2ImageApi::Create* gles2Create = dynamic_cast<const GLES2ImageApi::Create*>(&create)) { const GLenum createFormat = gles2Create->getEffectiveFormat(); if (dynamic_cast<const GLES2ImageApi::RenderTexture2D*>(&render)) { // GLES2 does not have depth or stencil textures if (isDepthFormat(createFormat) || isStencilFormat(createFormat)) return false; } if (dynamic_cast<const GLES2ImageApi::RenderReadPixelsRenderbuffer*>(&render)) { // GLES2 does not support readPixels for depth or stencil if (isDepthFormat(createFormat) || isStencilFormat(createFormat)) return false; } if (dynamic_cast<const GLES2ImageApi::RenderDepthbuffer*>(&render)) { // Copying non-depth data to depth renderbuffer and expecting meaningful // results just doesn't make any sense. if (!isDepthFormat(createFormat)) return false; } if (dynamic_cast<const GLES2ImageApi::RenderStencilbuffer*>(&render)) { // Copying non-stencil data to stencil renderbuffer and expecting meaningful // results just doesn't make any sense. if (!isStencilFormat(createFormat)) return false; } return true; } else DE_ASSERT(false); return false; } void SimpleCreationTests::init (void) { addCreateTexture2DActions("texture_"); addCreateTextureCubemapActions("_rgba", GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE); addCreateTextureCubemapActions("_rgb", GL_RGB, GL_RGB, GL_UNSIGNED_BYTE); addCreateRenderbufferActions(); addCreateAndroidNativeActions(); addRenderActions(); for (int createNdx = 0; createNdx < m_createActions.size(); createNdx++) { const LabeledAction& createAction = m_createActions[createNdx]; for (int renderNdx = 0; renderNdx < m_renderActions.size(); renderNdx++) { const LabeledAction& renderAction = m_renderActions[renderNdx]; TestSpec spec; if (!isCompatibleCreateAndRenderActions(*createAction.action, *renderAction.action)) continue; spec.name = std::string("gles2_") + createAction.label + "_" + renderAction.label; spec.desc = spec.name; spec.contexts.push_back(TestSpec::API_GLES2); spec.operations.push_back(TestSpec::Operation(0, *createAction.action)); spec.operations.push_back(TestSpec::Operation(0, *renderAction.action)); addChild(new ImageFormatCase(m_eglTestCtx, spec)); } } } TestCaseGroup* createSimpleCreationTests (EglTestContext& eglTestCtx, const string& name, const string& desc) { return new SimpleCreationTests(eglTestCtx, name, desc); } bool isCompatibleFormats (GLenum createFormat, GLenum modifyFormat, GLenum modifyType) { switch (modifyFormat) { case GL_RGB: switch (modifyType) { case GL_UNSIGNED_BYTE: return createFormat == GL_RGB || createFormat == GL_RGB8 || createFormat == GL_RGB565 || createFormat == GL_SRGB8; case GL_BYTE: return createFormat == GL_RGB8_SNORM; case GL_UNSIGNED_SHORT_5_6_5: return createFormat == GL_RGB || createFormat == GL_RGB565; case GL_UNSIGNED_INT_10F_11F_11F_REV: return createFormat == GL_R11F_G11F_B10F; case GL_UNSIGNED_INT_5_9_9_9_REV: return createFormat == GL_RGB9_E5; case GL_HALF_FLOAT: return createFormat == GL_RGB16F || createFormat == GL_R11F_G11F_B10F || createFormat == GL_RGB9_E5; case GL_FLOAT: return createFormat == GL_RGB16F || createFormat == GL_RGB32F || createFormat == GL_R11F_G11F_B10F || createFormat == GL_RGB9_E5; default: DE_FATAL("Unknown modify type"); return false; } case GL_RGBA: switch (modifyType) { case GL_UNSIGNED_BYTE: return createFormat == GL_RGBA8 || createFormat == GL_RGB5_A1 || createFormat == GL_RGBA4 || createFormat == GL_SRGB8_ALPHA8 || createFormat == GL_RGBA; case GL_UNSIGNED_SHORT_4_4_4_4: return createFormat == GL_RGBA4 || createFormat == GL_RGBA; case GL_UNSIGNED_SHORT_5_5_5_1: return createFormat == GL_RGB5_A1 || createFormat == GL_RGBA; case GL_UNSIGNED_INT_2_10_10_10_REV: return createFormat == GL_RGB10_A2 || createFormat == GL_RGB5_A1; case GL_HALF_FLOAT: return createFormat == GL_RGBA16F; case GL_FLOAT: return createFormat == GL_RGBA16F || createFormat == GL_RGBA32F; default: DE_FATAL("Unknown modify type"); return false; }; default: DE_FATAL("Unknown modify format"); return false; } } bool isCompatibleCreateAndModifyActions (const Action& create, const Action& modify) { if (const GLES2ImageApi::Create* gles2Create = dynamic_cast<const GLES2ImageApi::Create*>(&create)) { const GLenum createFormat = gles2Create->getEffectiveFormat(); if (const GLES2ImageApi::ModifyTexSubImage* gles2TexSubImageModify = dynamic_cast<const GLES2ImageApi::ModifyTexSubImage*>(&modify)) { const GLenum modifyFormat = gles2TexSubImageModify->getFormat(); const GLenum modifyType = gles2TexSubImageModify->getType(); return isCompatibleFormats(createFormat, modifyFormat, modifyType); } if (dynamic_cast<const GLES2ImageApi::ModifyRenderbufferClearColor*>(&modify)) { // reintepreting color as non-color is not meaningful if (isDepthFormat(createFormat) || isStencilFormat(createFormat)) return false; } if (dynamic_cast<const GLES2ImageApi::ModifyRenderbufferClearDepth*>(&modify)) { // reintepreting depth as non-depth is not meaningful if (!isDepthFormat(createFormat)) return false; } if (dynamic_cast<const GLES2ImageApi::ModifyRenderbufferClearStencil*>(&modify)) { // reintepreting stencil as non-stencil is not meaningful if (!isStencilFormat(createFormat)) return false; } return true; } else DE_ASSERT(false); return false; } class MultiContextRenderTests : public RenderTests { public: MultiContextRenderTests (EglTestContext& eglTestCtx, const string& name, const string& desc); void init (void); void addClearActions (void); private: LabeledActions m_clearActions; }; MultiContextRenderTests::MultiContextRenderTests (EglTestContext& eglTestCtx, const string& name, const string& desc) : RenderTests (eglTestCtx, name, desc) { } void MultiContextRenderTests::addClearActions (void) { m_clearActions.add("renderbuffer_clear_color", MovePtr<Action>(new GLES2ImageApi::ModifyRenderbufferClearColor(tcu::Vec4(0.8f, 0.2f, 0.9f, 1.0f)))); m_clearActions.add("renderbuffer_clear_depth", MovePtr<Action>(new GLES2ImageApi::ModifyRenderbufferClearDepth(0.75f))); m_clearActions.add("renderbuffer_clear_stencil", MovePtr<Action>(new GLES2ImageApi::ModifyRenderbufferClearStencil(97))); } void MultiContextRenderTests::init (void) { addCreateTexture2DActions("texture_"); addCreateTextureCubemapActions("_rgba8", GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE); addCreateTextureCubemapActions("_rgb8", GL_RGB, GL_RGB, GL_UNSIGNED_BYTE); addCreateRenderbufferActions(); addCreateAndroidNativeActions(); addRenderActions(); addClearActions(); for (int createNdx = 0; createNdx < m_createActions.size(); createNdx++) for (int renderNdx = 0; renderNdx < m_renderActions.size(); renderNdx++) for (int clearNdx = 0; clearNdx < m_clearActions.size(); clearNdx++) { const LabeledAction& createAction = m_createActions[createNdx]; const LabeledAction& renderAction = m_renderActions[renderNdx]; const LabeledAction& clearAction = m_clearActions[clearNdx]; TestSpec spec; if (!isCompatibleCreateAndRenderActions(*createAction.action, *renderAction.action)) continue; if (!isCompatibleCreateAndModifyActions(*createAction.action, *clearAction.action)) continue; spec.name = std::string("gles2_") + createAction.label + "_" + renderAction.label; spec.desc = spec.name; spec.contexts.push_back(TestSpec::API_GLES2); spec.contexts.push_back(TestSpec::API_GLES2); spec.operations.push_back(TestSpec::Operation(0, *createAction.action)); spec.operations.push_back(TestSpec::Operation(0, *renderAction.action)); spec.operations.push_back(TestSpec::Operation(0, *clearAction.action)); spec.operations.push_back(TestSpec::Operation(1, *createAction.action)); spec.operations.push_back(TestSpec::Operation(0, *renderAction.action)); spec.operations.push_back(TestSpec::Operation(1, *renderAction.action)); addChild(new ImageFormatCase(m_eglTestCtx, spec)); } } TestCaseGroup* createMultiContextRenderTests (EglTestContext& eglTestCtx, const string& name, const string& desc) { return new MultiContextRenderTests(eglTestCtx, name, desc); } class ModifyTests : public ImageTests { public: ModifyTests (EglTestContext& eglTestCtx, const string& name, const string& desc) : ImageTests(eglTestCtx, name, desc) {} void init (void); protected: void addModifyActions(void); LabeledActions m_modifyActions; GLES2ImageApi::RenderTryAll m_renderAction; }; void ModifyTests::addModifyActions (void) { m_modifyActions.add("tex_subimage_rgb8", MovePtr<Action>(new GLES2ImageApi::ModifyTexSubImage(GL_RGB, GL_UNSIGNED_BYTE))); m_modifyActions.add("tex_subimage_rgb565", MovePtr<Action>(new GLES2ImageApi::ModifyTexSubImage(GL_RGB, GL_UNSIGNED_SHORT_5_6_5))); m_modifyActions.add("tex_subimage_rgba8", MovePtr<Action>(new GLES2ImageApi::ModifyTexSubImage(GL_RGBA, GL_UNSIGNED_BYTE))); m_modifyActions.add("tex_subimage_rgb5_a1", MovePtr<Action>(new GLES2ImageApi::ModifyTexSubImage(GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1))); m_modifyActions.add("tex_subimage_rgba4", MovePtr<Action>(new GLES2ImageApi::ModifyTexSubImage(GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4))); m_modifyActions.add("renderbuffer_clear_color", MovePtr<Action>(new GLES2ImageApi::ModifyRenderbufferClearColor(tcu::Vec4(0.3f, 0.5f, 0.3f, 1.0f)))); m_modifyActions.add("renderbuffer_clear_depth", MovePtr<Action>(new GLES2ImageApi::ModifyRenderbufferClearDepth(0.7f))); m_modifyActions.add("renderbuffer_clear_stencil", MovePtr<Action>(new GLES2ImageApi::ModifyRenderbufferClearStencil(78))); } void ModifyTests::init (void) { addCreateTexture2DActions("tex_"); addCreateRenderbufferActions(); addCreateAndroidNativeActions(); addModifyActions(); for (int createNdx = 0; createNdx < m_createActions.size(); createNdx++) { LabeledAction& createAction = m_createActions[createNdx]; for (int modifyNdx = 0; modifyNdx < m_modifyActions.size(); modifyNdx++) { LabeledAction& modifyAction = m_modifyActions[modifyNdx]; if (!isCompatibleCreateAndModifyActions(*createAction.action, *modifyAction.action)) continue; TestSpec spec; spec.name = createAction.label + "_" + modifyAction.label; spec.desc = "gles2_tex_sub_image"; spec.contexts.push_back(TestSpec::API_GLES2); spec.operations.push_back(TestSpec::Operation(0, *createAction.action)); spec.operations.push_back(TestSpec::Operation(0, m_renderAction)); spec.operations.push_back(TestSpec::Operation(0, *modifyAction.action)); spec.operations.push_back(TestSpec::Operation(0, m_renderAction)); addChild(new ImageFormatCase(m_eglTestCtx, spec)); } } } TestCaseGroup* createModifyTests (EglTestContext& eglTestCtx, const string& name, const string& desc) { return new ModifyTests(eglTestCtx, name, desc); } } // Image } // egl } // deqp