| // Copyright 2017 The SwiftShader Authors. All Rights Reserved. | |
| // | |
| // 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. | |
| // OpenGL ES unit tests that provide coverage for functionality not tested by | |
| // the dEQP test suite. Also used as a smoke test. | |
| #include "gtest/gtest.h" | |
| #include "gmock/gmock.h" | |
| #include <EGL/egl.h> | |
| #include <GLES2/gl2.h> | |
| #include <GLES2/gl2ext.h> | |
| #include <GLES3/gl3.h> | |
| #include <GL/glcorearb.h> | |
| #include <GL/glext.h> | |
| #if defined(_WIN32) | |
| #include <Windows.h> | |
| #endif | |
| #include <string.h> | |
| #include <cstdint> | |
| #define EXPECT_GLENUM_EQ(expected, actual) EXPECT_EQ(static_cast<GLenum>(expected), static_cast<GLenum>(actual)) | |
| #define EXPECT_NO_GL_ERROR() EXPECT_GLENUM_EQ(GL_NO_ERROR, glGetError()) | |
| #define EXPECT_NO_EGL_ERROR() EXPECT_EQ(EGL_SUCCESS, eglGetError()) | |
| class SwiftShaderTest : public testing::Test | |
| { | |
| protected: | |
| void SetUp() override | |
| { | |
| #if defined(_WIN32) && !defined(STANDALONE) | |
| // The DLLs are delay loaded (see BUILD.gn), so we can load | |
| // the correct ones from Chrome's swiftshader subdirectory. | |
| HMODULE libEGL = LoadLibraryA("swiftshader\\libEGL.dll"); | |
| EXPECT_NE((HMODULE)NULL, libEGL); | |
| HMODULE libGLESv2 = LoadLibraryA("swiftshader\\libGLESv2.dll"); | |
| EXPECT_NE((HMODULE)NULL, libGLESv2); | |
| #endif | |
| } | |
| void expectFramebufferColor(const unsigned char referenceColor[4], GLint x = 0, GLint y = 0) | |
| { | |
| unsigned char color[4] = { 0 }; | |
| glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(color[0], referenceColor[0]); | |
| EXPECT_EQ(color[1], referenceColor[1]); | |
| EXPECT_EQ(color[2], referenceColor[2]); | |
| EXPECT_EQ(color[3], referenceColor[3]); | |
| } | |
| void expectFramebufferColor(const float referenceColor[4], GLint x = 0, GLint y = 0) | |
| { | |
| float color[4] = { 0 }; | |
| glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, &color); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(color[0], referenceColor[0]); | |
| EXPECT_EQ(color[1], referenceColor[1]); | |
| EXPECT_EQ(color[2], referenceColor[2]); | |
| EXPECT_EQ(color[3], referenceColor[3]); | |
| } | |
| void Initialize(int version, bool withChecks) | |
| { | |
| EXPECT_NO_EGL_ERROR(); | |
| display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| if(withChecks) | |
| { | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_NE(EGL_NO_DISPLAY, display); | |
| eglQueryString(display, EGL_VENDOR); | |
| EXPECT_EQ(EGL_NOT_INITIALIZED, eglGetError()); | |
| } | |
| EGLint major; | |
| EGLint minor; | |
| EGLBoolean initialized = eglInitialize(display, &major, &minor); | |
| if(withChecks) | |
| { | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, initialized); | |
| EXPECT_EQ(1, major); | |
| EXPECT_EQ(4, minor); | |
| const char *eglVendor = eglQueryString(display, EGL_VENDOR); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_STREQ("Google Inc.", eglVendor); | |
| const char *eglVersion = eglQueryString(display, EGL_VERSION); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_THAT(eglVersion, testing::HasSubstr("1.4 SwiftShader ")); | |
| } | |
| eglBindAPI(EGL_OPENGL_ES_API); | |
| EXPECT_NO_EGL_ERROR(); | |
| const EGLint configAttributes[] = | |
| { | |
| EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, | |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |
| EGL_ALPHA_SIZE, 8, | |
| EGL_NONE | |
| }; | |
| EGLint num_config = -1; | |
| EGLBoolean success = eglChooseConfig(display, configAttributes, &config, 1, &num_config); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(num_config, 1); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, success); | |
| if(withChecks) | |
| { | |
| EGLint conformant = 0; | |
| eglGetConfigAttrib(display, config, EGL_CONFORMANT, &conformant); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_TRUE(conformant & EGL_OPENGL_ES2_BIT); | |
| EGLint renderableType = 0; | |
| eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_TRUE(renderableType & EGL_OPENGL_ES2_BIT); | |
| EGLint surfaceType = 0; | |
| eglGetConfigAttrib(display, config, EGL_SURFACE_TYPE, &surfaceType); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_TRUE(surfaceType & EGL_WINDOW_BIT); | |
| } | |
| EGLint surfaceAttributes[] = | |
| { | |
| EGL_WIDTH, 1920, | |
| EGL_HEIGHT, 1080, | |
| EGL_NONE | |
| }; | |
| surface = eglCreatePbufferSurface(display, config, surfaceAttributes); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_NE(EGL_NO_SURFACE, surface); | |
| EGLint contextAttributes[] = | |
| { | |
| EGL_CONTEXT_CLIENT_VERSION, version, | |
| EGL_NONE | |
| }; | |
| context = eglCreateContext(display, config, NULL, contextAttributes); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_NE(EGL_NO_CONTEXT, context); | |
| success = eglMakeCurrent(display, surface, surface, context); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, success); | |
| if(withChecks) | |
| { | |
| EGLDisplay currentDisplay = eglGetCurrentDisplay(); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(display, currentDisplay); | |
| EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(surface, currentDrawSurface); | |
| EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(surface, currentReadSurface); | |
| EGLContext currentContext = eglGetCurrentContext(); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(context, currentContext); | |
| } | |
| EXPECT_NO_GL_ERROR(); | |
| } | |
| void Uninitialize() | |
| { | |
| EXPECT_NO_GL_ERROR(); | |
| EGLBoolean success = eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, success); | |
| EGLDisplay currentDisplay = eglGetCurrentDisplay(); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(EGL_NO_DISPLAY, currentDisplay); | |
| EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(EGL_NO_SURFACE, currentDrawSurface); | |
| EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(EGL_NO_SURFACE, currentReadSurface); | |
| EGLContext currentContext = eglGetCurrentContext(); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ(EGL_NO_CONTEXT, currentContext); | |
| success = eglDestroyContext(display, context); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, success); | |
| success = eglDestroySurface(display, surface); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, success); | |
| success = eglTerminate(display); | |
| EXPECT_NO_EGL_ERROR(); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, success); | |
| } | |
| struct ProgramHandles | |
| { | |
| GLuint program; | |
| GLuint vertexShader; | |
| GLuint fragmentShader; | |
| }; | |
| GLuint MakeShader(const std::string &source, GLenum shaderType) | |
| { | |
| GLuint shader = glCreateShader(shaderType); | |
| const char *c_source[1] = { source.c_str() }; | |
| glShaderSource(shader, 1, c_source, nullptr); | |
| glCompileShader(shader); | |
| EXPECT_NO_GL_ERROR(); | |
| GLchar buf[1024]; | |
| GLint compileStatus = 0; | |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); | |
| glGetShaderInfoLog(shader, sizeof(buf), nullptr, buf); | |
| EXPECT_EQ(compileStatus, GL_TRUE) << "Compile status: " << std::endl << buf; | |
| return shader; | |
| } | |
| GLuint MakeProgram(GLuint vs, GLuint fs) | |
| { | |
| GLuint program; | |
| program = glCreateProgram(); | |
| EXPECT_NO_GL_ERROR(); | |
| glAttachShader(program, vs); | |
| glAttachShader(program, fs); | |
| EXPECT_NO_GL_ERROR(); | |
| return program; | |
| } | |
| void LinkProgram(GLuint program) | |
| { | |
| GLchar buf[1024]; | |
| glLinkProgram(program); | |
| GLint linkStatus = 0; | |
| glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); | |
| glGetProgramInfoLog(program, sizeof(buf), nullptr, buf); | |
| EXPECT_NE(linkStatus, 0) << "Link status: " << std::endl << buf; | |
| EXPECT_NO_GL_ERROR(); | |
| } | |
| ProgramHandles createProgram(const std::string& vs, const std::string& fs) | |
| { | |
| ProgramHandles ph; | |
| ph.vertexShader = MakeShader(vs, GL_VERTEX_SHADER); | |
| ph.fragmentShader = MakeShader(fs, GL_FRAGMENT_SHADER); | |
| ph.program = MakeProgram(ph.vertexShader, ph.fragmentShader); | |
| LinkProgram(ph.program); | |
| return ph; | |
| } | |
| void deleteProgram(const ProgramHandles& ph) | |
| { | |
| glDeleteShader(ph.fragmentShader); | |
| glDeleteShader(ph.vertexShader); | |
| glDeleteProgram(ph.program); | |
| EXPECT_NO_GL_ERROR(); | |
| } | |
| void drawQuad(GLuint program, const char* textureName = nullptr) | |
| { | |
| GLint prevProgram = 0; | |
| glGetIntegerv(GL_CURRENT_PROGRAM, &prevProgram); | |
| glUseProgram(program); | |
| EXPECT_NO_GL_ERROR(); | |
| GLint posLoc = glGetAttribLocation(program, "position"); | |
| EXPECT_NO_GL_ERROR(); | |
| if(textureName) | |
| { | |
| GLint location = glGetUniformLocation(program, textureName); | |
| ASSERT_NE(-1, location); | |
| glUniform1i(location, 0); | |
| } | |
| float vertices[18] = { -1.0f, 1.0f, 0.5f, | |
| -1.0f, -1.0f, 0.5f, | |
| 1.0f, -1.0f, 0.5f, | |
| -1.0f, 1.0f, 0.5f, | |
| 1.0f, -1.0f, 0.5f, | |
| 1.0f, 1.0f, 0.5f }; | |
| glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, vertices); | |
| glEnableVertexAttribArray(posLoc); | |
| glDrawArrays(GL_TRIANGLES, 0, 6); | |
| EXPECT_NO_GL_ERROR(); | |
| glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, nullptr); | |
| glDisableVertexAttribArray(posLoc); | |
| glUseProgram(prevProgram); | |
| EXPECT_NO_GL_ERROR(); | |
| } | |
| std::string replace(std::string str, const std::string& substr, const std::string& replacement) | |
| { | |
| size_t pos = 0; | |
| while((pos = str.find(substr, pos)) != std::string::npos) { | |
| str.replace(pos, substr.length(), replacement); | |
| pos += replacement.length(); | |
| } | |
| return str; | |
| } | |
| void checkCompiles(std::string v, std::string f) | |
| { | |
| Initialize(3, false); | |
| std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| out float unfoldable; | |
| $INSERT | |
| void main() | |
| { | |
| unfoldable = position.x; | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| gl_Position.x += F(unfoldable);\ | |
| })"; | |
| std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| in float unfoldable; | |
| out vec4 fragColor; | |
| $INSERT | |
| void main() | |
| { | |
| fragColor = vec4(1.0, 1.0, 1.0, 1.0); | |
| fragColor.x += F(unfoldable); | |
| })"; | |
| vs = replace(vs, "$INSERT", (v.length() > 0) ? v : "float F(float ignored) { return 0.0; }"); | |
| fs = replace(fs, "$INSERT", (f.length() > 0) ? f : "float F(float ignored) { return 0.0; }"); | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| glUseProgram(ph.program); | |
| drawQuad(ph.program); | |
| deleteProgram(ph); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| void checkCompiles(std::string s) | |
| { | |
| checkCompiles(s, ""); | |
| checkCompiles("", s); | |
| } | |
| std::string checkCompileFails(std::string source, GLenum glShaderType) | |
| { | |
| Initialize(3, false); | |
| GLint compileStatus = 0; | |
| const char *c_source[1] = { source.c_str() }; | |
| GLuint glShader = glCreateShader(glShaderType); | |
| glShaderSource(glShader, 1, c_source, nullptr); | |
| glCompileShader(glShader); | |
| EXPECT_NO_GL_ERROR(); | |
| std::string log; | |
| char *buf; | |
| GLsizei length = 0; | |
| GLsizei written = 0; | |
| glGetShaderiv(glShader, GL_COMPILE_STATUS, &compileStatus); | |
| EXPECT_EQ(compileStatus, GL_FALSE); | |
| glGetShaderiv(glShader, GL_INFO_LOG_LENGTH, &length); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_NE(length, 0); | |
| buf = new char[length]; | |
| glGetShaderInfoLog(glShader, length, &written, buf); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(length, written + 1); | |
| log.assign(buf, length); | |
| delete[] buf; | |
| glDeleteShader(glShader); | |
| Uninitialize(); | |
| return log; | |
| } | |
| void checkCompileFails(std::string s) | |
| { | |
| std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| out float unfoldable; | |
| $INSERT | |
| void main() | |
| { | |
| unfoldable = position.x; | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| gl_Position.x += F(unfoldable); | |
| })"; | |
| std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| in float unfoldable; | |
| out vec4 fragColor; | |
| $INSERT | |
| void main() | |
| { | |
| fragColor = vec4(1.0, 1.0, 1.0, 1.0); | |
| fragColor.x += F(unfoldable); | |
| })"; | |
| vs = replace(vs, "$INSERT", s); | |
| fs = replace(fs, "$INSERT", s); | |
| checkCompileFails(vs, GL_VERTEX_SHADER); | |
| checkCompileFails(fs, GL_FRAGMENT_SHADER); | |
| } | |
| EGLDisplay getDisplay() const { return display; } | |
| EGLConfig getConfig() const { return config; } | |
| EGLSurface getSurface() const { return surface; } | |
| EGLContext getContext() const { return context; } | |
| private: | |
| EGLDisplay display; | |
| EGLConfig config; | |
| EGLSurface surface; | |
| EGLContext context; | |
| }; | |
| TEST_F(SwiftShaderTest, Initalization) | |
| { | |
| Initialize(2, true); | |
| const GLubyte *glVendor = glGetString(GL_VENDOR); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_STREQ("Google Inc.", (const char*)glVendor); | |
| const GLubyte *glRenderer = glGetString(GL_RENDERER); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_STREQ("Google SwiftShader", (const char*)glRenderer); | |
| // SwiftShader return an OpenGL ES 3.0 context when a 2.0 context is requested, as allowed by the spec. | |
| const GLubyte *glVersion = glGetString(GL_VERSION); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_THAT((const char*)glVersion, testing::HasSubstr("OpenGL ES 3.0 SwiftShader ")); | |
| Uninitialize(); | |
| } | |
| // Test attempting to clear an incomplete framebuffer | |
| TEST_F(SwiftShaderTest, ClearIncomplete) | |
| { | |
| Initialize(3, false); | |
| GLfloat zero_float = 0; | |
| GLuint renderbuffer; | |
| glGenRenderbuffers(1, &renderbuffer); | |
| GLuint framebuffer; | |
| glGenFramebuffers(1, &framebuffer); | |
| glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | |
| EXPECT_NO_GL_ERROR(); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_R8I, 43, 27); | |
| EXPECT_NO_GL_ERROR(); | |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); | |
| EXPECT_NO_GL_ERROR(); | |
| glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer); | |
| EXPECT_NO_GL_ERROR(); | |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); | |
| EXPECT_NO_GL_ERROR(); | |
| glClearBufferfv(GL_DEPTH, 0, &zero_float); | |
| EXPECT_GLENUM_EQ(GL_INVALID_FRAMEBUFFER_OPERATION, glGetError()); | |
| Uninitialize(); | |
| } | |
| // Test unrolling of a loop | |
| TEST_F(SwiftShaderTest, UnrollLoop) | |
| { | |
| Initialize(3, false); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| const std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| out vec4 color; | |
| void main() | |
| { | |
| for(int i = 0; i < 4; i++) | |
| { | |
| color[i] = (i % 2 == 0) ? 0.0 : 1.0; | |
| } | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| in vec4 color; | |
| out vec4 fragColor; | |
| void main() | |
| { | |
| fragColor = color; | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| // Expect the info log to contain "unrolled". This is not a spec requirement. | |
| GLsizei length = 0; | |
| glGetShaderiv(ph.vertexShader, GL_INFO_LOG_LENGTH, &length); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_NE(length, 0); | |
| char *log = new char[length]; | |
| GLsizei written = 0; | |
| glGetShaderInfoLog(ph.vertexShader, length, &written, log); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(length, written + 1); | |
| EXPECT_NE(strstr(log, "unrolled"), nullptr); | |
| delete[] log; | |
| glUseProgram(ph.program); | |
| glClearColor(0.0, 0.0, 0.0, 0.0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| drawQuad(ph.program); | |
| deleteProgram(ph); | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test non-canonical or non-deterministic loops do not get unrolled | |
| TEST_F(SwiftShaderTest, DynamicLoop) | |
| { | |
| Initialize(3, false); | |
| const std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| out vec4 color; | |
| void main() | |
| { | |
| for(int i = 0; i < 4; ) | |
| { | |
| color[i] = (i % 2 == 0) ? 0.0 : 1.0; | |
| i++; | |
| } | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| in vec4 color; | |
| out vec4 fragColor; | |
| void main() | |
| { | |
| vec4 temp; | |
| for(int i = 0; i < 4; i++) | |
| { | |
| if(color.x < 0.0) return; | |
| temp[i] = color[i]; | |
| } | |
| fragColor = vec4(temp[0], temp[1], temp[2], temp[3]); | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| // Expect the info logs to be empty. This is not a spec requirement. | |
| GLsizei length = 0; | |
| glGetShaderiv(ph.vertexShader, GL_INFO_LOG_LENGTH, &length); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(length, 0); | |
| glGetShaderiv(ph.fragmentShader, GL_INFO_LOG_LENGTH, &length); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(length, 0); | |
| glUseProgram(ph.program); | |
| glClearColor(0.0, 0.0, 0.0, 0.0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| drawQuad(ph.program); | |
| deleteProgram(ph); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test dynamic indexing | |
| TEST_F(SwiftShaderTest, DynamicIndexing) | |
| { | |
| Initialize(3, false); | |
| const std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| out float color[4]; | |
| void main() | |
| { | |
| for(int i = 0; i < 4; ) | |
| { | |
| int j = (gl_VertexID + i) % 4; | |
| color[j] = (j % 2 == 0) ? 0.0 : 1.0; | |
| i++; | |
| } | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| in float color[4]; | |
| out vec4 fragColor; | |
| void main() | |
| { | |
| float temp[4]; | |
| for(int i = 0; i < 4; ) | |
| { | |
| temp[i] = color[i]; | |
| i++; | |
| } | |
| fragColor = vec4(temp[0], temp[1], temp[2], temp[3]); | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| glUseProgram(ph.program); | |
| glClearColor(0.0, 0.0, 0.0, 0.0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| drawQuad(ph.program); | |
| deleteProgram(ph); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test vertex attribute location linking | |
| TEST_F(SwiftShaderTest, AttributeLocation) | |
| { | |
| Initialize(3, false); | |
| const std::string vs = | |
| R"(#version 300 es | |
| layout(location = 0) in vec4 a0; // Explicitly bound in GLSL | |
| layout(location = 2) in vec4 a2; // Explicitly bound in GLSL | |
| in vec4 a5; // Bound to location 5 by API | |
| in mat2 a3; // Implicit location | |
| in vec4 a1; // Implicit location | |
| in vec4 a6; // Implicit location | |
| out vec4 color; | |
| void main() | |
| { | |
| vec4 a34 = vec4(a3[0], a3[1]); | |
| gl_Position = a0; | |
| color = (a2 == vec4(1.0, 2.0, 3.0, 4.0) && | |
| a34 == vec4(5.0, 6.0, 7.0, 8.0) && | |
| a5 == vec4(9.0, 10.0, 11.0, 12.0) && | |
| a1 == vec4(13.0, 14.0, 15.0, 16.0) && | |
| a6 == vec4(17.0, 18.0, 19.0, 20.0)) ? | |
| vec4(0.0, 1.0, 0.0, 1.0) : | |
| vec4(1.0, 0.0, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| in vec4 color; | |
| out vec4 fragColor; | |
| void main() | |
| { | |
| fragColor = color; | |
| })"; | |
| ProgramHandles ph; | |
| ph.vertexShader = MakeShader(vs, GL_VERTEX_SHADER); | |
| ph.fragmentShader = MakeShader(fs, GL_FRAGMENT_SHADER); | |
| ph.program = glCreateProgram(); | |
| EXPECT_NO_GL_ERROR(); | |
| // Not assigned a layout location in GLSL. Bind it explicitly with the API. | |
| glBindAttribLocation(ph.program, 5, "a5"); | |
| EXPECT_NO_GL_ERROR(); | |
| // Should not override GLSL layout location qualifier | |
| glBindAttribLocation(ph.program, 8, "a2"); | |
| EXPECT_NO_GL_ERROR(); | |
| glAttachShader(ph.program, ph.vertexShader); | |
| glAttachShader(ph.program, ph.fragmentShader); | |
| glLinkProgram(ph.program); | |
| EXPECT_NO_GL_ERROR(); | |
| // Changes after linking should have no effect | |
| glBindAttribLocation(ph.program, 0, "a1"); | |
| glBindAttribLocation(ph.program, 6, "a2"); | |
| glBindAttribLocation(ph.program, 2, "a6"); | |
| GLint linkStatus = 0; | |
| glGetProgramiv(ph.program, GL_LINK_STATUS, &linkStatus); | |
| EXPECT_NE(linkStatus, 0); | |
| EXPECT_NO_GL_ERROR(); | |
| float vertices[6][3] = { { -1.0f, 1.0f, 0.5f }, | |
| { -1.0f, -1.0f, 0.5f }, | |
| { 1.0f, -1.0f, 0.5f }, | |
| { -1.0f, 1.0f, 0.5f }, | |
| { 1.0f, -1.0f, 0.5f }, | |
| { 1.0f, 1.0f, 0.5f } }; | |
| float attributes[5][4] = { { 1.0f, 2.0f, 3.0f, 4.0f }, | |
| { 5.0f, 6.0f, 7.0f, 8.0f }, | |
| { 9.0f, 10.0f, 11.0f, 12.0f }, | |
| { 13.0f, 14.0f, 15.0f, 16.0f }, | |
| { 17.0f, 18.0f, 19.0f, 20.0f } }; | |
| GLint a0 = glGetAttribLocation(ph.program, "a0"); | |
| EXPECT_EQ(a0, 0); | |
| glVertexAttribPointer(a0, 3, GL_FLOAT, GL_FALSE, 0, vertices); | |
| glEnableVertexAttribArray(a0); | |
| EXPECT_NO_GL_ERROR(); | |
| GLint a2 = glGetAttribLocation(ph.program, "a2"); | |
| EXPECT_EQ(a2, 2); | |
| glVertexAttribPointer(a2, 4, GL_FLOAT, GL_FALSE, 0, attributes[0]); | |
| glVertexAttribDivisor(a2, 1); | |
| glEnableVertexAttribArray(a2); | |
| EXPECT_NO_GL_ERROR(); | |
| GLint a3 = glGetAttribLocation(ph.program, "a3"); | |
| EXPECT_EQ(a3, 3); // Note: implementation specific | |
| glVertexAttribPointer(a3 + 0, 2, GL_FLOAT, GL_FALSE, 0, &attributes[1][0]); | |
| glVertexAttribPointer(a3 + 1, 2, GL_FLOAT, GL_FALSE, 0, &attributes[1][2]); | |
| glVertexAttribDivisor(a3 + 0, 1); | |
| glVertexAttribDivisor(a3 + 1, 1); | |
| glEnableVertexAttribArray(a3 + 0); | |
| glEnableVertexAttribArray(a3 + 1); | |
| EXPECT_NO_GL_ERROR(); | |
| GLint a5 = glGetAttribLocation(ph.program, "a5"); | |
| EXPECT_EQ(a5, 5); | |
| glVertexAttribPointer(a5, 4, GL_FLOAT, GL_FALSE, 0, attributes[2]); | |
| glVertexAttribDivisor(a5, 1); | |
| glEnableVertexAttribArray(a5); | |
| EXPECT_NO_GL_ERROR(); | |
| GLint a1 = glGetAttribLocation(ph.program, "a1"); | |
| EXPECT_EQ(a1, 1); // Note: implementation specific | |
| glVertexAttribPointer(a1, 4, GL_FLOAT, GL_FALSE, 0, attributes[3]); | |
| glVertexAttribDivisor(a1, 1); | |
| glEnableVertexAttribArray(a1); | |
| EXPECT_NO_GL_ERROR(); | |
| GLint a6 = glGetAttribLocation(ph.program, "a6"); | |
| EXPECT_EQ(a6, 6); // Note: implementation specific | |
| glVertexAttribPointer(a6, 4, GL_FLOAT, GL_FALSE, 0, attributes[4]); | |
| glVertexAttribDivisor(a6, 1); | |
| glEnableVertexAttribArray(a6); | |
| EXPECT_NO_GL_ERROR(); | |
| glUseProgram(ph.program); | |
| glDrawArrays(GL_TRIANGLES, 0, 6); | |
| EXPECT_NO_GL_ERROR(); | |
| deleteProgram(ph); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test negative layout locations | |
| TEST_F(SwiftShaderTest, NegativeLocation) | |
| { | |
| const std::string vs = | |
| R"(#version 300 es | |
| layout(location = 0x86868686u) in vec4 a0; // Explicitly bound in GLSL | |
| layout(location = 0x96969696u) in vec4 a2; // Explicitly bound in GLSL | |
| in vec4 a5; // Bound to location 5 by API | |
| in mat2 a3; // Implicit location | |
| in vec4 a1; // Implicit location | |
| in vec4 a6; // Implicit location | |
| out vec4 color; | |
| float F(float f) | |
| { | |
| vec4 a34 = vec4(a3[0], a3[1]);\n" | |
| gl_Position = a0;\n" | |
| color = (a2 == vec4(1.0, 2.0, 3.0, 4.0) && | |
| a34 == vec4(5.0, 6.0, 7.0, 8.0) && | |
| a5 == vec4(9.0, 10.0, 11.0, 12.0) && | |
| a1 == vec4(13.0, 14.0, 15.0, 16.0) && | |
| a6 == vec4(17.0, 18.0, 19.0, 20.0)) ? | |
| vec4(0.0, 1.0, 0.0, 1.0) : | |
| vec4(1.0, 0.0, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| in vec4 color; | |
| layout(location = 0xA6A6A6A6u) out vec4 fragColor; | |
| float F main() | |
| { | |
| fragColor = color; | |
| })"; | |
| { | |
| std::string log = checkCompileFails(vs, GL_VERTEX_SHADER); | |
| EXPECT_NE(strstr(log.c_str(), "out of range: location must be non-negative"), nullptr); | |
| } | |
| { | |
| std::string log = checkCompileFails(fs, GL_FRAGMENT_SHADER); | |
| EXPECT_NE(strstr(log.c_str(), "out of range: location must be non-negative"), nullptr); | |
| } | |
| } | |
| // Tests clearing of a texture with 'dirty' content. | |
| TEST_F(SwiftShaderTest, ClearDirtyTexture) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_2D, tex); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_R11F_G11F_B10F, 256, 256, 0, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, nullptr); | |
| EXPECT_NO_GL_ERROR(); | |
| GLuint fbo = 1; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); | |
| float dirty_color[3] = { 128 / 255.0f, 64 / 255.0f, 192 / 255.0f }; | |
| GLint dirty_x = 8; | |
| GLint dirty_y = 12; | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, dirty_x, dirty_y, 1, 1, GL_RGB, GL_FLOAT, dirty_color); | |
| const float clear_color[4] = { 1.0f, 32.0f, 0.5f, 1.0f }; | |
| glClearColor(clear_color[0], clear_color[1], clear_color[2], 1.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| expectFramebufferColor(clear_color, dirty_x, dirty_y); | |
| Uninitialize(); | |
| } | |
| // Tests copying between textures of different floating-point formats using a framebuffer object. | |
| TEST_F(SwiftShaderTest, CopyTexImage) | |
| { | |
| Initialize(3, false); | |
| GLuint tex1 = 1; | |
| float green[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; | |
| glBindTexture(GL_TEXTURE_2D, tex1); | |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 16, 16); | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 5, 10, 1, 1, GL_RGBA, GL_FLOAT, &green); | |
| EXPECT_NO_GL_ERROR(); | |
| GLuint fbo = 1; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex1, 0); | |
| EXPECT_NO_GL_ERROR(); | |
| GLuint tex2 = 2; | |
| glBindTexture(GL_TEXTURE_2D, tex2); | |
| glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 6, 8, 8, 0); | |
| EXPECT_NO_GL_ERROR(); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex2, 0); | |
| expectFramebufferColor(green, 3, 4); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Tests reading of half-float textures. | |
| TEST_F(SwiftShaderTest, ReadHalfFloat) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_2D, tex); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 256, 256, 0, GL_RGB, GL_HALF_FLOAT, nullptr); | |
| EXPECT_NO_GL_ERROR(); | |
| GLuint fbo = 1; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); | |
| const float clear_color[4] = { 1.0f, 32.0f, 0.5f, 1.0f }; | |
| glClearColor(clear_color[0], clear_color[1], clear_color[2], 1.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| uint16_t pixel[3] = { 0x1234, 0x3F80, 0xAAAA }; | |
| GLint x = 6; | |
| GLint y = 3; | |
| glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 1, 1, GL_RGB, GL_HALF_FLOAT, pixel); | |
| // This relies on GL_HALF_FLOAT being a valid type for read-back, | |
| // which isn't guaranteed by the spec but is supported by SwiftShader. | |
| uint16_t read_color[3] = { 0, 0, 0 }; | |
| glReadPixels(x, y, 1, 1, GL_RGB, GL_HALF_FLOAT, &read_color); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(read_color[0], pixel[0]); | |
| EXPECT_EQ(read_color[1], pixel[1]); | |
| EXPECT_EQ(read_color[2], pixel[2]); | |
| Uninitialize(); | |
| } | |
| // Tests construction of a structure containing a single matrix | |
| TEST_F(SwiftShaderTest, MatrixInStruct) | |
| { | |
| Initialize(2, false); | |
| const std::string fs = | |
| R"(#version 100 | |
| precision mediump float; | |
| struct S | |
| { | |
| mat2 rotation; | |
| }; | |
| void main(void) | |
| { | |
| float angle = 1.0; | |
| S(mat2(1.0, angle, 1.0, 1.0)); | |
| })"; | |
| MakeShader(fs, GL_FRAGMENT_SHADER); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test sampling from a sampler in a struct as a function argument | |
| TEST_F(SwiftShaderTest, SamplerArrayInStructArrayAsFunctionArg) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_2D, tex); | |
| EXPECT_NO_GL_ERROR(); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, green); | |
| EXPECT_NO_GL_ERROR(); | |
| const std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| void main() | |
| { | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| struct SamplerStruct{ sampler2D tex[2]; }; | |
| vec4 doSample(in SamplerStruct s[2]) | |
| { | |
| return texture(s[1].tex[1], vec2(0.0)); | |
| } | |
| uniform SamplerStruct samplerStruct[2]; | |
| out vec4 fragColor; | |
| void main() | |
| { | |
| fragColor = doSample(samplerStruct); | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| glUseProgram(ph.program); | |
| GLint location = glGetUniformLocation(ph.program, "samplerStruct[1].tex[1]"); | |
| ASSERT_NE(-1, location); | |
| glUniform1i(location, 0); | |
| glClearColor(0.0, 0.0, 0.0, 0.0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| drawQuad(ph.program, "samplerStruct[1].tex[1]"); | |
| deleteProgram(ph); | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test sampling from a sampler in a struct as a function argument | |
| TEST_F(SwiftShaderTest, AtanCornerCases) | |
| { | |
| Initialize(3, false); | |
| const std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| void main() | |
| { | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| const float kPI = 3.14159265358979323846; | |
| uniform float positive_value; | |
| uniform float negative_value; | |
| out vec4 fragColor; | |
| void main() | |
| { | |
| // Should yield vec4(0, pi, pi/2, -pi/2) | |
| vec4 result = atan(vec4(0.0, 0.0, positive_value, negative_value), | |
| vec4(positive_value, negative_value, 0.0, 0.0)); | |
| fragColor = (result / vec4(kPI)) + vec4(0.5, -0.5, 0.0, 1.0) + vec4(0.5 / 255.0); | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| glUseProgram(ph.program); | |
| GLint positive_value = glGetUniformLocation(ph.program, "positive_value"); | |
| ASSERT_NE(-1, positive_value); | |
| GLint negative_value = glGetUniformLocation(ph.program, "negative_value"); | |
| ASSERT_NE(-1, negative_value); | |
| glUniform1f(positive_value, 1.0); | |
| glUniform1f(negative_value, -1.0); | |
| glClearColor(0.0, 0.0, 0.0, 0.0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| drawQuad(ph.program, nullptr); | |
| deleteProgram(ph); | |
| unsigned char grey[4] = { 128, 128, 128, 128 }; | |
| expectFramebufferColor(grey); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| TEST_F(SwiftShaderTest, TransformFeedback_DrawArraysInstanced) | |
| { | |
| Initialize(3, false); | |
| std::string fs = | |
| R"(#version 300 es | |
| in mediump vec2 vary; | |
| out mediump vec4 color; | |
| void main() | |
| { | |
| color = vec4(vary, 0.0, 1.0); | |
| })"; | |
| std::string vs = | |
| R"(#version 300 es | |
| layout(location=0) in mediump vec2 pos; | |
| out mediump vec2 vary; | |
| void main() | |
| { | |
| vary = pos; | |
| gl_Position = vec4(pos, 0.0, 1.0); | |
| })"; | |
| GLuint vert = MakeShader(vs, GL_VERTEX_SHADER); | |
| GLuint frag = MakeShader(fs, GL_FRAGMENT_SHADER); | |
| GLuint program = MakeProgram(vert, frag); | |
| LinkProgram(program); | |
| glBeginTransformFeedback(GL_POINTS); | |
| glDrawArraysInstanced(GL_POINTS, 0, 1, 1); | |
| Uninitialize(); | |
| } | |
| TEST_F(SwiftShaderTest, TransformFeedback_BadViewport) | |
| { | |
| Initialize(3, false); | |
| GLuint tfBuffer; | |
| glGenBuffers(1, &tfBuffer); | |
| glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tfBuffer); | |
| glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 1 << 12, nullptr, GL_STATIC_DRAW); | |
| std::string vsSource = | |
| R"(#version 300 es | |
| in vec4 a_position; | |
| void main() | |
| { | |
| gl_Position = a_position; | |
| })"; | |
| std::string fsSource = | |
| R"(#version 300 es | |
| precision highp float; | |
| out vec4 my_FragColor; | |
| void main() | |
| { | |
| my_FragColor = vec4(1.0, 0.0, 0.0, 1.0); | |
| })"; | |
| const char *varyings[] = { "gl_Position" }; | |
| GLuint vs = MakeShader(vsSource, GL_VERTEX_SHADER); | |
| GLuint fs = MakeShader(fsSource, GL_FRAGMENT_SHADER); | |
| GLuint program = MakeProgram(vs, fs); | |
| glTransformFeedbackVaryings(program, 1, | |
| &varyings[0], GL_INTERLEAVED_ATTRIBS); | |
| LinkProgram(program); | |
| glUseProgram(program); | |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer); | |
| glBeginTransformFeedback(GL_TRIANGLES); | |
| GLuint primitivesWrittenQuery = 0; | |
| glGenQueries(1, &primitivesWrittenQuery); | |
| glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, primitivesWrittenQuery); | |
| glViewport(0, 10000000, 300, 300); | |
| GLint positionLocation = glGetAttribLocation(program, "a_position"); | |
| GLfloat quadVertices[] = { | |
| -1.0f, 1.0f, 0.5f, | |
| -1.0f, -1.0f, 0.5f, | |
| 1.0f, -1.0f, 0.5f, | |
| -1.0f, 1.0f, 0.5f, | |
| 1.0f, -1.0f, 0.5f, | |
| 1.0f, 1.0f, 0.5f, | |
| }; | |
| glBindBuffer(GL_ARRAY_BUFFER, 0); | |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, &quadVertices[0]); | |
| glDrawArrays(GL_TRIANGLES, 0, 6); | |
| glDisableVertexAttribArray(positionLocation); | |
| glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr); | |
| glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); | |
| glEndTransformFeedback(); | |
| GLuint primitivesWritten = 0; | |
| glGetQueryObjectuiv(primitivesWrittenQuery, GL_QUERY_RESULT_EXT, &primitivesWritten); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(2u, primitivesWritten); | |
| Uninitialize(); | |
| } | |
| // Test conditions that should result in a GL_OUT_OF_MEMORY and not crash | |
| TEST_F(SwiftShaderTest, OutOfMemory) | |
| { | |
| // Image sizes are assumed to fit in a 32-bit signed integer by the renderer, | |
| // so test that we can't create a 2+ GiB image. | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_3D, tex); | |
| const int width = 0xC2; | |
| const int height = 0x541; | |
| const int depth = 0x404; | |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA32F, width, height, depth, 0, GL_RGBA, GL_FLOAT, nullptr); | |
| EXPECT_GLENUM_EQ(GL_OUT_OF_MEMORY, glGetError()); | |
| // The spec states that the GL is in an undefined state when GL_OUT_OF_MEMORY | |
| // is returned, and the context must be recreated before attempting more rendering. | |
| Uninitialize(); | |
| } | |
| } | |
| TEST_F(SwiftShaderTest, ViewportBounds) | |
| { | |
| auto doRenderWithViewportSettings = [&](GLint x, GLint y, GLsizei w, GLsizei h) | |
| { | |
| Initialize(3, false); | |
| std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| out float unfoldable; | |
| void main() | |
| { | |
| unfoldable = position.x; | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| std::string fs = | |
| R"(#version 300 es | |
| precision mediump float; | |
| in float unfoldable; | |
| out vec4 fragColor; | |
| void main() | |
| { | |
| fragColor = vec4(1.0, 1.0, 1.0, 1.0); | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| glUseProgram(ph.program); | |
| glViewport(x, y, w, h); | |
| drawQuad(ph.program); | |
| EXPECT_NO_GL_ERROR(); | |
| deleteProgram(ph); | |
| Uninitialize(); | |
| }; | |
| GLsizei w = 100; | |
| GLsizei h = 100; | |
| GLint minPos = -2000; | |
| doRenderWithViewportSettings(0, 0, 0, 0); | |
| doRenderWithViewportSettings(0, 0, w, h); | |
| // Negative positions | |
| doRenderWithViewportSettings(minPos, 0, w, h); | |
| doRenderWithViewportSettings(0, minPos, w, h); | |
| doRenderWithViewportSettings(minPos, minPos, w, h); | |
| } | |
| // Test using TexImage2D to define a rectangle texture | |
| TEST_F(SwiftShaderTest, TextureRectangle_TexImage2D) | |
| { | |
| Initialize(2, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| // Defining level 0 is allowed | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EXPECT_NO_GL_ERROR(); | |
| // Defining level other than 0 is not allowed | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| GLint maxSize = 0; | |
| glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &maxSize); | |
| // Defining a texture of the max size is allowed | |
| { | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, maxSize, maxSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| GLenum error = glGetError(); | |
| ASSERT_TRUE(error == GL_NO_ERROR || error == GL_OUT_OF_MEMORY); | |
| } | |
| // Defining a texture larger than the max size is disallowed | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, maxSize + 1, maxSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, maxSize, maxSize + 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| Uninitialize(); | |
| } | |
| // Test using CompressedTexImage2D cannot be used on a retangle texture | |
| TEST_F(SwiftShaderTest, TextureRectangle_CompressedTexImage2DDisallowed) | |
| { | |
| Initialize(2, false); | |
| const char data[128] = { 0 }; | |
| // Control case: 2D texture | |
| { | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_2D, tex); | |
| glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 16, 16, 0, 128, data); | |
| EXPECT_NO_GL_ERROR(); | |
| } | |
| // Rectangle textures cannot be compressed | |
| { | |
| GLuint tex = 2; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| glCompressedTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 16, 16, 0, 128, data); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| } | |
| Uninitialize(); | |
| } | |
| // Test using TexStorage2D to define a rectangle texture (ES3) | |
| TEST_F(SwiftShaderTest, TextureRectangle_TexStorage2D) | |
| { | |
| Initialize(3, false); | |
| // Defining one level is allowed | |
| { | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8UI, 16, 16); | |
| EXPECT_NO_GL_ERROR(); | |
| } | |
| // Having more than one level is not allowed | |
| { | |
| GLuint tex = 2; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| // Use 5 levels because the EXT_texture_storage extension requires a mip chain all the way | |
| // to a 1x1 mip. | |
| glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 5, GL_RGBA8UI, 16, 16); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| } | |
| GLint maxSize = 0; | |
| glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &maxSize); | |
| // Defining a texture of the max size is allowed but still allow for OOM | |
| { | |
| GLuint tex = 3; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8UI, maxSize, maxSize); | |
| GLenum error = glGetError(); | |
| ASSERT_TRUE(error == GL_NO_ERROR || error == GL_OUT_OF_MEMORY); | |
| } | |
| // Defining a texture larger than the max size is disallowed | |
| { | |
| GLuint tex = 4; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8UI, maxSize + 1, maxSize); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8UI, maxSize, maxSize + 1); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| } | |
| // Compressed formats are disallowed | |
| GLuint tex = 5; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 16, 16); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| Uninitialize(); | |
| } | |
| // Test validation of disallowed texture parameters | |
| TEST_F(SwiftShaderTest, TextureRectangle_TexParameterRestriction) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| // Only wrap mode CLAMP_TO_EDGE is supported | |
| // Wrap S | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| EXPECT_NO_GL_ERROR(); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_REPEAT); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| // Wrap T | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| EXPECT_NO_GL_ERROR(); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_REPEAT); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| // Min filter has to be nearest or linear | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| EXPECT_NO_GL_ERROR(); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| EXPECT_NO_GL_ERROR(); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| // Base level has to be 0 | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_BASE_LEVEL, 0); | |
| EXPECT_NO_GL_ERROR(); | |
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_BASE_LEVEL, 1); | |
| EXPECT_GLENUM_EQ(GL_INVALID_OPERATION, glGetError()); | |
| Uninitialize(); | |
| } | |
| // Test validation of "level" in FramebufferTexture2D | |
| TEST_F(SwiftShaderTest, TextureRectangle_FramebufferTexture2DLevel) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| EXPECT_NO_GL_ERROR(); | |
| GLuint fbo = 1; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| // Using level 0 of a rectangle texture is valid. | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0); | |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); | |
| EXPECT_NO_GL_ERROR(); | |
| // Setting level != 0 is invalid | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 1); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| Uninitialize(); | |
| } | |
| // Test sampling from a rectangle texture | |
| TEST_F(SwiftShaderTest, TextureRectangle_SamplingFromRectangle) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| EXPECT_NO_GL_ERROR(); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, green); | |
| EXPECT_NO_GL_ERROR(); | |
| const std::string vs = | |
| R"(attribute vec4 position; | |
| void main() | |
| { | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#extension GL_ARB_texture_rectangle : require | |
| precision mediump float; | |
| uniform sampler2DRect tex; | |
| void main() | |
| { | |
| gl_FragColor = texture2DRect(tex, vec2(0, 0)); | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| glUseProgram(ph.program); | |
| GLint location = glGetUniformLocation(ph.program, "tex"); | |
| ASSERT_NE(-1, location); | |
| glUniform1i(location, 0); | |
| glClearColor(0.0, 0.0, 0.0, 0.0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| drawQuad(ph.program, "tex"); | |
| deleteProgram(ph); | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test sampling from a rectangle texture | |
| TEST_F(SwiftShaderTest, TextureRectangle_SamplingFromRectangleESSL3) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| EXPECT_NO_GL_ERROR(); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, green); | |
| EXPECT_NO_GL_ERROR(); | |
| const std::string vs = | |
| R"(#version 300 es | |
| in vec4 position; | |
| void main() | |
| { | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#version 300 es | |
| #extension GL_ARB_texture_rectangle : require | |
| precision mediump float; | |
| uniform sampler2DRect tex; | |
| out vec4 fragColor; | |
| void main() | |
| { | |
| fragColor = texture(tex, vec2(0, 0)); | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| glUseProgram(ph.program); | |
| GLint location = glGetUniformLocation(ph.program, "tex"); | |
| ASSERT_NE(-1, location); | |
| glUniform1i(location, 0); | |
| glClearColor(0.0, 0.0, 0.0, 0.0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| drawQuad(ph.program, "tex"); | |
| deleteProgram(ph); | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test attaching a rectangle texture and rendering to it. | |
| TEST_F(SwiftShaderTest, TextureRectangle_RenderToRectangle) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| unsigned char black[4] = { 0, 0, 0, 255 }; | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, black); | |
| GLuint fbo = 1; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0); | |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); | |
| EXPECT_NO_GL_ERROR(); | |
| // Clearing a texture is just as good as checking we can render to it, right? | |
| glClearColor(0.0, 1.0, 0.0, 1.0); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| TEST_F(SwiftShaderTest, TextureRectangle_DefaultSamplerParameters) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| GLint minFilter = 0; | |
| glGetTexParameteriv(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, &minFilter); | |
| EXPECT_GLENUM_EQ(GL_LINEAR, minFilter); | |
| GLint wrapS = 0; | |
| glGetTexParameteriv(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, &wrapS); | |
| EXPECT_GLENUM_EQ(GL_CLAMP_TO_EDGE, wrapS); | |
| GLint wrapT = 0; | |
| glGetTexParameteriv(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, &wrapT); | |
| EXPECT_GLENUM_EQ(GL_CLAMP_TO_EDGE, wrapT); | |
| Uninitialize(); | |
| } | |
| // Test glCopyTexImage with rectangle textures (ES3) | |
| TEST_F(SwiftShaderTest, TextureRectangle_CopyTexImage) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| glClearColor(0, 1, 0, 1); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| // Error case: level != 0 | |
| glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8, 0, 0, 1, 1, 0); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| // level = 0 works and defines the texture. | |
| glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, 0, 0, 1, 1, 0); | |
| EXPECT_NO_GL_ERROR(); | |
| GLuint fbo = 1; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| // Test glCopyTexSubImage with rectangle textures (ES3) | |
| TEST_F(SwiftShaderTest, TextureRectangle_CopyTexSubImage) | |
| { | |
| Initialize(3, false); | |
| GLuint tex = 1; | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); | |
| unsigned char black[4] = { 0, 0, 0, 255 }; | |
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, black); | |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| glClearColor(0, 1, 0, 1); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| // Error case: level != 0 | |
| glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 1, 0, 0, 0, 0, 1, 1); | |
| EXPECT_GLENUM_EQ(GL_INVALID_VALUE, glGetError()); | |
| // level = 0 works and defines the texture. | |
| glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, 0, 0, 1, 1); | |
| EXPECT_NO_GL_ERROR(); | |
| GLuint fbo = 1; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0); | |
| unsigned char green[4] = { 0, 255, 0, 255 }; | |
| expectFramebufferColor(green); | |
| EXPECT_NO_GL_ERROR(); | |
| Uninitialize(); | |
| } | |
| TEST_F(SwiftShaderTest, BlitTest) | |
| { | |
| Initialize(3, false); | |
| GLuint fbos[] = {0, 0}; | |
| glGenFramebuffers(2, fbos); | |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); | |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); | |
| GLuint textures[] = {0, 0}; | |
| glGenTextures(2, textures); | |
| glBindTexture(GL_TEXTURE_2D, textures[0]); | |
| unsigned char red[4][4] = { | |
| {255, 0, 0, 255}, | |
| {255, 0, 0, 255}, | |
| {255, 0, 0, 255}, | |
| {255, 0, 0, 255} | |
| }; | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, red); | |
| EXPECT_NO_GL_ERROR(); | |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0); | |
| EXPECT_NO_GL_ERROR(); | |
| glBindTexture(GL_TEXTURE_2D, textures[1]); | |
| unsigned char black[4][4] = { | |
| {0, 0, 0, 255}, | |
| {0, 0, 0, 255}, | |
| {0, 0, 0, 255}, | |
| {0, 0, 0, 255} | |
| }; | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, black); | |
| EXPECT_NO_GL_ERROR(); | |
| glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0); | |
| // Test that glBlitFramebuffer works as expected for the normal case. | |
| glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_EQ(red[0][1], black[0][1]); | |
| // Check that glBlitFramebuffer doesn't crash with ugly input. | |
| const int big = (int) 2e9; | |
| const int small = 200; | |
| const int neg_small = -small; | |
| const int neg_big = -big; | |
| int max = 0x7fffffff; | |
| int data[][8] = { | |
| // sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 | |
| {0, 0, 0, 0, 0, 0, 0, 0}, | |
| {-1, -1, -1, -1, -1, -1, -1, -1}, | |
| {1, 1, 1, 1, 1, 1, 1, 1}, | |
| {-1, -1, 1, 1, -1, -1, 1, 1}, | |
| {0, 0, 127, (int) 2e9, 10, 10, 200, 200}, | |
| {-2, -2, 127, 2147483470, 10, 10, 200, 200}, | |
| {big, small, small, big, big, big, small, small}, | |
| {neg_small, small, neg_small, neg_small, neg_small, big, small}, | |
| {big, big-1, big-2, big-3, big-4, big-5, big-6, big-7}, | |
| {big, neg_big, neg_big, big, small, big, 0, neg_small}, | |
| {323479648, 21931, 1769809195, 32733, 0, 0, -161640504, 32766}, | |
| {0, 0, max, max, 0, 0, 8, 8}, | |
| {0, 0, 8, 8, 0, 0, max, max}, | |
| {0, 0, max, max, 0, 0, max, max}, | |
| {-1, -1, max, max, 0, 0, 8, 8}, | |
| {0, 0, 8, 8, -1, -1, max, max}, | |
| {-1, -1, max, max, -1, -1, max, max}, | |
| {-max-1, -max-1, max, max, -max-1, -max-1, max, max} | |
| }; | |
| for (int i = 0; i < (int) (sizeof(data)/sizeof(data[0])); i++) | |
| { | |
| glBlitFramebuffer( | |
| data[i][0], data[i][1], data[i][2], data[i][3], | |
| data[i][4], data[i][5], data[i][6], data[i][7], | |
| GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
| // Ignore error state, just make sure that we don't crash on these inputs. | |
| } | |
| // Clear the error state before uninitializing test. | |
| glGetError(); | |
| glDeleteFramebuffers(2, fbos); | |
| glDeleteTextures(2, textures); | |
| Uninitialize(); | |
| } | |
| TEST_F(SwiftShaderTest, InvalidEnum_TexImage2D) | |
| { | |
| Initialize(3, false); | |
| const GLenum invalidTarget = GL_TEXTURE_3D; | |
| glTexImage2D(invalidTarget, 0, GL_R11F_G11F_B10F, 256, 256, 0, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, nullptr); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| float pixels[3] = { 0.0f, 0.0f, 0.0f }; | |
| glTexSubImage2D(invalidTarget, 0, 0, 0, 1, 1, GL_RGB, GL_FLOAT, pixels); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| glCopyTexImage2D(invalidTarget, 0, GL_RGB, 2, 6, 8, 8, 0); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| glCopyTexSubImage2D(invalidTarget, 0, 0, 0, 0, 0, 1, 1); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| const char data[128] = { 0 }; | |
| glCompressedTexImage2D(invalidTarget, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 16, 16, 0, 128, data); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| glCompressedTexSubImage2D(invalidTarget, 0, 0, 0, 0, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 0, 0); | |
| EXPECT_GLENUM_EQ(GL_INVALID_ENUM, glGetError()); | |
| Uninitialize(); | |
| } | |
| TEST_F(SwiftShaderTest, CompilerLimits_DeepNestedIfs) | |
| { | |
| std::string body = "return 1.0;"; | |
| for (int i = 0; i < 16; i++) | |
| { | |
| body = " if (f > " + std::to_string(i * 0.1f) + ") {\n" + body + "}\n"; | |
| } | |
| checkCompiles( | |
| "float F(float f) {\n" + body + " return 0.0f;\n}\n" | |
| ); | |
| } | |
| TEST_F(SwiftShaderTest, CompilerLimits_DeepNestedSwitches) | |
| { | |
| std::string body = "return 1.0;"; | |
| for (int i = 0; i < 16; i++) | |
| { | |
| body = " switch (int(f)) {\n case 1:\n f *= 2.0;\n" + body + "}\n"; | |
| } | |
| checkCompiles("float F(float f) {\n" + body + " return 0.0f;\n}\n"); | |
| } | |
| TEST_F(SwiftShaderTest, CompilerLimits_DeepNestedLoops) | |
| { | |
| std::string loops = "f = f + f * 2.0;"; | |
| for (int i = 0; i < 16; i++) | |
| { | |
| auto it = "l" + std::to_string(i); | |
| loops = " for (int " + it + " = 0; " + it + " < i; " + it + "++) {\n" + loops + "}\n"; | |
| } | |
| checkCompiles( | |
| "float F(float f) {\n" | |
| " int i = (f > 0.0) ? 1 : 0;\n" + loops + | |
| " return f;\n" | |
| "}\n" | |
| ); | |
| } | |
| TEST_F(SwiftShaderTest, CompilerLimits_DeepNestedCalls) | |
| { | |
| std::string funcs = "float E(float f) { return f * 2.0f; }\n"; | |
| std::string last = "E"; | |
| for (int i = 0; i < 16; i++) | |
| { | |
| std::string f = "C" + std::to_string(i); | |
| funcs += "float " + f + "(float f) { return " + last + "(f) + 1.0f; }\n"; | |
| last = f; | |
| } | |
| checkCompiles(funcs + | |
| "float F(float f) { return " + last + "(f); }\n" | |
| ); | |
| } | |
| TEST_F(SwiftShaderTest, CompilerLimits_ManyCallSites) | |
| { | |
| std::string calls; | |
| for (int i = 0; i < 256; i++) | |
| { | |
| calls += " f += C(f);\n"; | |
| } | |
| checkCompiles( | |
| "float C(float f) { return f * 2.0f; }\n" | |
| "float F(float f) {\n" + calls + " return f;\n}\n" | |
| ); | |
| } | |
| TEST_F(SwiftShaderTest, CompilerLimits_DeepNestedCallsInUnusedFunction) | |
| { | |
| std::string funcs = "float E(float f) { return f * 2.0f; }\n"; | |
| std::string last = "E"; | |
| for (int i = 0; i < 16; i++) | |
| { | |
| std::string f = "C" + std::to_string(i); | |
| funcs += "float " + f + "(float f) { return " + last + "(f) + 1.0f; }\n"; | |
| last = f; | |
| } | |
| checkCompiles(funcs + | |
| "float F(float f) { return f; }\n" | |
| ); | |
| } | |
| // Test that the compiler correctly handles functions being stripped. | |
| // The frontend will strip the Dead functions, but may keep the their function | |
| // labels reserved. This produces labels that are greater than the number of | |
| // live functions. | |
| TEST_F(SwiftShaderTest, CompilerLimits_SparseLabels) | |
| { | |
| checkCompiles( | |
| R"(void Dead1() {} | |
| void Dead2() {} | |
| void Dead3() {} | |
| void Dead4() {} | |
| void Dead5() { Dead1(); Dead2(); Dead3(); Dead4(); } | |
| float F(float f) { for(int i = 0; i < -1; ++i) { Dead5(); } return f; })" | |
| ); | |
| } | |
| // Test that the compiler doesn't compile arrays larger than | |
| // GL_MAX_{VERTEX/FRAGMENT}_UNIFORM_VECTOR. | |
| TEST_F(SwiftShaderTest, CompilerLimits_ArraySize) | |
| { | |
| checkCompileFails( | |
| R"(uniform float u_var[100000000]; | |
| float F(float f) { return u_var[2]; })"); | |
| checkCompileFails( | |
| R"(struct structType { mediump sampler2D m0; mediump samplerCube m1; }; | |
| uniform structType u_var[100000000]; | |
| float F(float f) { return texture(u_var[2].m1, vec3(0.0)), vec4(0.26, 1.72, 0.60, 0.12).x; })"); | |
| } | |
| // Test that the compiler rejects negations of things that can't be negated. | |
| TEST_F(SwiftShaderTest, BadNegation) | |
| { | |
| checkCompileFails( | |
| R"(uniform samplerCube m; | |
| float F (float f) { vec4 ret = texture(-m, vec3(f)); return ret.x; })" | |
| ); | |
| checkCompileFails( | |
| R"(uniform sampler2D m[9]; | |
| vec4 G (sampler2D X[9]) { return texture(X[0], vec2(0.0f)); } | |
| float F (float f) { vec4 ret = G(-m); return ret.x; })" | |
| ); | |
| checkCompileFails( | |
| R"(struct structType { int a; float b; }; | |
| uniform structType m; | |
| float F (float f) { structType n = -m; return f; })" | |
| ); | |
| checkCompileFails( | |
| R"(struct structType { int a; float b; }; | |
| uniform structType m[4]; | |
| float F (float f) { structType n[4] = -m; return f; })" | |
| ); | |
| checkCompileFails( | |
| R"(uniform float m[4]; | |
| float G (float f[4]) { return f[0]; } | |
| float F (float f) { return G(-m); })" | |
| ); | |
| } | |
| #ifndef EGL_ANGLE_iosurface_client_buffer | |
| #define EGL_ANGLE_iosurface_client_buffer 1 | |
| #define EGL_IOSURFACE_ANGLE 0x3454 | |
| #define EGL_IOSURFACE_PLANE_ANGLE 0x345A | |
| #define EGL_TEXTURE_RECTANGLE_ANGLE 0x345B | |
| #define EGL_TEXTURE_TYPE_ANGLE 0x345C | |
| #define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D | |
| #endif /* EGL_ANGLE_iosurface_client_buffer */ | |
| #if defined(__APPLE__) | |
| #include <CoreFoundation/CoreFoundation.h> | |
| #include <IOSurface/IOSurface.h> | |
| namespace | |
| { | |
| void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value) | |
| { | |
| CFNumberRef number = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value); | |
| CFDictionaryAddValue(dictionary, key, number); | |
| CFRelease(number); | |
| } | |
| } // anonymous namespace | |
| class EGLClientBufferWrapper | |
| { | |
| public: | |
| EGLClientBufferWrapper(int width = 1, int height = 1) | |
| { | |
| // Create a 1 by 1 BGRA8888 IOSurface | |
| ioSurface = nullptr; | |
| CFMutableDictionaryRef dict = CFDictionaryCreateMutable( | |
| kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
| AddIntegerValue(dict, kIOSurfaceWidth, width); | |
| AddIntegerValue(dict, kIOSurfaceHeight, height); | |
| AddIntegerValue(dict, kIOSurfacePixelFormat, 'BGRA'); | |
| AddIntegerValue(dict, kIOSurfaceBytesPerElement, 4); | |
| ioSurface = IOSurfaceCreate(dict); | |
| CFRelease(dict); | |
| EXPECT_NE(nullptr, ioSurface); | |
| } | |
| ~EGLClientBufferWrapper() | |
| { | |
| IOSurfaceUnlock(ioSurface, kIOSurfaceLockReadOnly, nullptr); | |
| CFRelease(ioSurface); | |
| } | |
| EGLClientBuffer getClientBuffer() const | |
| { | |
| return ioSurface; | |
| } | |
| const unsigned char* lockColor() | |
| { | |
| IOSurfaceLock(ioSurface, kIOSurfaceLockReadOnly, nullptr); | |
| return reinterpret_cast<const unsigned char*>(IOSurfaceGetBaseAddress(ioSurface)); | |
| } | |
| void unlockColor() | |
| { | |
| IOSurfaceUnlock(ioSurface, kIOSurfaceLockReadOnly, nullptr); | |
| } | |
| void writeColor(void* data, size_t dataSize) | |
| { | |
| // Write the data to the IOSurface | |
| IOSurfaceLock(ioSurface, 0, nullptr); | |
| memcpy(IOSurfaceGetBaseAddress(ioSurface), data, dataSize); | |
| IOSurfaceUnlock(ioSurface, 0, nullptr); | |
| } | |
| private: | |
| IOSurfaceRef ioSurface; | |
| }; | |
| #else // __APPLE__ | |
| class EGLClientBufferWrapper | |
| { | |
| public: | |
| EGLClientBufferWrapper(int width = 1, int height = 1) | |
| { | |
| clientBuffer = new unsigned char[4 * width * height]; | |
| } | |
| ~EGLClientBufferWrapper() | |
| { | |
| delete[] clientBuffer; | |
| } | |
| EGLClientBuffer getClientBuffer() const | |
| { | |
| return clientBuffer; | |
| } | |
| const unsigned char* lockColor() | |
| { | |
| return clientBuffer; | |
| } | |
| void unlockColor() | |
| { | |
| } | |
| void writeColor(void* data, size_t dataSize) | |
| { | |
| memcpy(clientBuffer, data, dataSize); | |
| } | |
| private: | |
| unsigned char* clientBuffer; | |
| }; | |
| #endif | |
| class IOSurfaceClientBufferTest : public SwiftShaderTest | |
| { | |
| protected: | |
| EGLSurface createIOSurfacePbuffer(EGLClientBuffer buffer, EGLint width, EGLint height, EGLint plane, GLenum internalFormat, GLenum type) const | |
| { | |
| // Make a PBuffer from it using the EGL_ANGLE_iosurface_client_buffer extension | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, width, | |
| EGL_HEIGHT, height, | |
| EGL_IOSURFACE_PLANE_ANGLE, plane, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, (EGLint)internalFormat, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, (EGLint)type, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, buffer, getConfig(), attribs); | |
| EXPECT_NE(EGL_NO_SURFACE, pbuffer); | |
| return pbuffer; | |
| } | |
| void bindIOSurfaceToTexture(EGLClientBuffer buffer, EGLint width, EGLint height, EGLint plane, GLenum internalFormat, GLenum type, EGLSurface *pbuffer, GLuint *texture) const | |
| { | |
| *pbuffer = createIOSurfacePbuffer(buffer, width, height, plane, internalFormat, type); | |
| // Bind the pbuffer | |
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, *texture); | |
| EGLBoolean result = eglBindTexImage(getDisplay(), *pbuffer, EGL_BACK_BUFFER); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, result); | |
| EXPECT_NO_EGL_ERROR(); | |
| } | |
| void doClear(GLenum internalFormat, bool clearToZero) | |
| { | |
| if(internalFormat == GL_R16UI) | |
| { | |
| GLuint color = clearToZero ? 0 : 257; | |
| glClearBufferuiv(GL_COLOR, 0, &color); | |
| EXPECT_NO_GL_ERROR(); | |
| } | |
| else | |
| { | |
| glClearColor(clearToZero ? 0.0f : 1.0f / 255.0f, | |
| clearToZero ? 0.0f : 2.0f / 255.0f, | |
| clearToZero ? 0.0f : 3.0f / 255.0f, | |
| clearToZero ? 0.0f : 4.0f / 255.0f); | |
| EXPECT_NO_GL_ERROR(); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| EXPECT_NO_GL_ERROR(); | |
| } | |
| } | |
| void doClearTest(EGLClientBufferWrapper& clientBufferWrapper, GLenum internalFormat, GLenum type, void *data, size_t dataSize) | |
| { | |
| ASSERT_TRUE(dataSize <= 4); | |
| // Bind the IOSurface to a texture and clear it. | |
| GLuint texture = 1; | |
| EGLSurface pbuffer; | |
| bindIOSurfaceToTexture(clientBufferWrapper.getClientBuffer(), 1, 1, 0, internalFormat, type, &pbuffer, &texture); | |
| // glClear the pbuffer | |
| GLuint fbo = 2; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| EXPECT_NO_GL_ERROR(); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, texture, 0); | |
| EXPECT_NO_GL_ERROR(); | |
| EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE); | |
| EXPECT_NO_GL_ERROR(); | |
| doClear(internalFormat, false); | |
| // Unbind pbuffer and check content. | |
| EGLBoolean result = eglReleaseTexImage(getDisplay(), pbuffer, EGL_BACK_BUFFER); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, result); | |
| EXPECT_NO_EGL_ERROR(); | |
| const unsigned char* color = clientBufferWrapper.lockColor(); | |
| for(size_t i = 0; i < dataSize; ++i) | |
| { | |
| EXPECT_EQ(color[i], reinterpret_cast<unsigned char*>(data)[i]); | |
| } | |
| result = eglDestroySurface(getDisplay(), pbuffer); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, result); | |
| EXPECT_NO_EGL_ERROR(); | |
| } | |
| void doSampleTest(EGLClientBufferWrapper& clientBufferWrapper, GLenum internalFormat, GLenum type, void *data, size_t dataSize) | |
| { | |
| ASSERT_TRUE(dataSize <= 4); | |
| clientBufferWrapper.writeColor(data, dataSize); | |
| // Bind the IOSurface to a texture and clear it. | |
| GLuint texture = 1; | |
| EGLSurface pbuffer; | |
| bindIOSurfaceToTexture(clientBufferWrapper.getClientBuffer(), 1, 1, 0, internalFormat, type, &pbuffer, &texture); | |
| doClear(internalFormat, true); | |
| // Create program and draw quad using it | |
| const std::string vs = | |
| R"(attribute vec4 position; | |
| void main() | |
| { | |
| gl_Position = vec4(position.xy, 0.0, 1.0); | |
| })"; | |
| const std::string fs = | |
| R"(#extension GL_ARB_texture_rectangle : require | |
| precision mediump float; | |
| uniform sampler2DRect tex; | |
| void main() | |
| { | |
| gl_FragColor = texture2DRect(tex, vec2(0, 0)); | |
| })"; | |
| const ProgramHandles ph = createProgram(vs, fs); | |
| drawQuad(ph.program, "tex"); | |
| deleteProgram(ph); | |
| EXPECT_NO_GL_ERROR(); | |
| // Unbind pbuffer and check content. | |
| EGLBoolean result = eglReleaseTexImage(getDisplay(), pbuffer, EGL_BACK_BUFFER); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, result); | |
| EXPECT_NO_EGL_ERROR(); | |
| const unsigned char* color = clientBufferWrapper.lockColor(); | |
| for(size_t i = 0; i < dataSize; ++i) | |
| { | |
| EXPECT_EQ(color[i], reinterpret_cast<unsigned char*>(data)[i]); | |
| } | |
| clientBufferWrapper.unlockColor(); | |
| } | |
| }; | |
| // Tests for the EGL_ANGLE_iosurface_client_buffer extension | |
| TEST_F(IOSurfaceClientBufferTest, RenderToBGRA8888IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| unsigned char data[4] = { 3, 2, 1, 4 }; | |
| doClearTest(clientBufferWrapper, GL_BGRA_EXT, GL_UNSIGNED_BYTE, data, 4); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test reading from BGRA8888 IOSurfaces | |
| TEST_F(IOSurfaceClientBufferTest, ReadFromBGRA8888IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| unsigned char data[4] = { 3, 2, 1, 4 }; | |
| doSampleTest(clientBufferWrapper, GL_BGRA_EXT, GL_UNSIGNED_BYTE, data, 4); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test using RGBX8888 IOSurfaces for rendering | |
| TEST_F(IOSurfaceClientBufferTest, RenderToRGBX8888IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| unsigned char data[3] = { 1, 2, 3 }; | |
| doClearTest(clientBufferWrapper, GL_RGB, GL_UNSIGNED_BYTE, data, 3); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test reading from RGBX8888 IOSurfaces | |
| TEST_F(IOSurfaceClientBufferTest, ReadFromRGBX8888IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| unsigned char data[3] = { 1, 2, 3 }; | |
| doSampleTest(clientBufferWrapper, GL_RGB, GL_UNSIGNED_BYTE, data, 3); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test using RG88 IOSurfaces for rendering | |
| TEST_F(IOSurfaceClientBufferTest, RenderToRG88IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| unsigned char data[2] = { 1, 2 }; | |
| doClearTest(clientBufferWrapper, GL_RG, GL_UNSIGNED_BYTE, data, 2); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test reading from RG88 IOSurfaces | |
| TEST_F(IOSurfaceClientBufferTest, ReadFromRG88IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| unsigned char data[2] = { 1, 2 }; | |
| doSampleTest(clientBufferWrapper, GL_RG, GL_UNSIGNED_BYTE, data, 2); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test using R8 IOSurfaces for rendering | |
| TEST_F(IOSurfaceClientBufferTest, RenderToR8IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| unsigned char data[1] = { 1 }; | |
| doClearTest(clientBufferWrapper, GL_RED, GL_UNSIGNED_BYTE, data, 1); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test reading from R8 IOSurfaces | |
| TEST_F(IOSurfaceClientBufferTest, ReadFromR8IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| unsigned char data[1] = { 1 }; | |
| doSampleTest(clientBufferWrapper, GL_RED, GL_UNSIGNED_BYTE, data, 1); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test using R16 IOSurfaces for rendering | |
| TEST_F(IOSurfaceClientBufferTest, RenderToR16IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| uint16_t data[1] = { 257 }; | |
| doClearTest(clientBufferWrapper, GL_R16UI, GL_UNSIGNED_SHORT, data, 2); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test reading from R8 IOSurfaces | |
| TEST_F(IOSurfaceClientBufferTest, ReadFromR16IOSurface) | |
| { | |
| Initialize(3, false); | |
| { // EGLClientBufferWrapper scope | |
| EGLClientBufferWrapper clientBufferWrapper; | |
| uint16_t data[1] = { 257 }; | |
| doSampleTest(clientBufferWrapper, GL_R16UI, GL_UNSIGNED_SHORT, data, 1); | |
| } // end of EGLClientBufferWrapper scope | |
| Uninitialize(); | |
| } | |
| // Test the validation errors for missing attributes for eglCreatePbufferFromClientBuffer with | |
| // IOSurface | |
| TEST_F(IOSurfaceClientBufferTest, NegativeValidationMissingAttributes) | |
| { | |
| Initialize(3, false); | |
| { | |
| EGLClientBufferWrapper clientBufferWrapper(10, 10); | |
| // Success case | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_NE(EGL_NO_SURFACE, pbuffer); | |
| EGLBoolean result = eglDestroySurface(getDisplay(), pbuffer); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, result); | |
| EXPECT_NO_EGL_ERROR(); | |
| } | |
| // Missing EGL_WIDTH | |
| { | |
| const EGLint attribs[] = { | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError()); | |
| } | |
| // Missing EGL_HEIGHT | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError()); | |
| } | |
| // Missing EGL_IOSURFACE_PLANE_ANGLE | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError()); | |
| } | |
| // Missing EGL_TEXTURE_TARGET - EGL_BAD_MATCH from the base spec of | |
| // eglCreatePbufferFromClientBuffer | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_MATCH, eglGetError()); | |
| } | |
| // Missing EGL_TEXTURE_INTERNAL_FORMAT_ANGLE | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError()); | |
| } | |
| // Missing EGL_TEXTURE_FORMAT - EGL_BAD_MATCH from the base spec of | |
| // eglCreatePbufferFromClientBuffer | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_MATCH, eglGetError()); | |
| } | |
| // Missing EGL_TEXTURE_TYPE_ANGLE | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError()); | |
| } | |
| } | |
| Uninitialize(); | |
| } | |
| // Test the validation errors for bad parameters for eglCreatePbufferFromClientBuffer with IOSurface | |
| TEST_F(IOSurfaceClientBufferTest, NegativeValidationBadAttributes) | |
| { | |
| Initialize(3, false); | |
| { | |
| EGLClientBufferWrapper clientBufferWrapper(10, 10); | |
| // Success case | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_NE(EGL_NO_SURFACE, pbuffer); | |
| EGLBoolean result = eglDestroySurface(getDisplay(), pbuffer); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, result); | |
| EXPECT_NO_EGL_ERROR(); | |
| } | |
| // EGL_TEXTURE_FORMAT must be EGL_TEXTURE_RGBA | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| // EGL_WIDTH must be at least 1 | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 0, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| // EGL_HEIGHT must be at least 1 | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 0, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| #if defined(__APPLE__) | |
| // EGL_WIDTH must be at most the width of the IOSurface | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 11, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| // EGL_HEIGHT must be at most the height of the IOSurface | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 11, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| // EGL_IOSURFACE_PLANE_ANGLE must less than the number of planes of the IOSurface | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 1, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| #endif | |
| // EGL_TEXTURE_FORMAT must be at EGL_TEXTURE_RECTANGLE_ANGLE | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| // EGL_IOSURFACE_PLANE_ANGLE must be at least 0 | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, -1, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| // The internal format / type most be listed in the table | |
| { | |
| const EGLint attribs[] = { | |
| EGL_WIDTH, 10, | |
| EGL_HEIGHT, 10, | |
| EGL_IOSURFACE_PLANE_ANGLE, 0, | |
| EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, | |
| EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RGBA, | |
| EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, | |
| EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, | |
| EGL_NONE, EGL_NONE, | |
| }; | |
| EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs); | |
| EXPECT_EQ(EGL_NO_SURFACE, pbuffer); | |
| EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError()); | |
| } | |
| } | |
| Uninitialize(); | |
| } | |
| // Test IOSurface pbuffers can be made current | |
| TEST_F(IOSurfaceClientBufferTest, MakeCurrentAllowed) | |
| { | |
| Initialize(3, false); | |
| { | |
| EGLClientBufferWrapper clientBufferWrapper(10, 10); | |
| EGLSurface pbuffer = createIOSurfacePbuffer(clientBufferWrapper.getClientBuffer(), 10, 10, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE); | |
| EGLBoolean result = eglMakeCurrent(getDisplay(), pbuffer, pbuffer, getContext()); | |
| EXPECT_EQ((EGLBoolean)EGL_TRUE, result); | |
| EXPECT_NO_EGL_ERROR(); | |
| } | |
| Uninitialize(); | |
| } | |