|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <GLES2/gl2.h> | 
|  | #include <GLES2/gl2ext.h> | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include "gpu/command_buffer/tests/gl_manager.h" | 
|  | #include "gpu/command_buffer/tests/gl_test_utils.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | #define SHADER(Src) #Src | 
|  |  | 
|  | namespace gpu { | 
|  |  | 
|  | class DepthTextureTest : public testing::Test { | 
|  | protected: | 
|  | static const GLsizei kResolution = 64; | 
|  | void SetUp() override { | 
|  | GLManager::Options options; | 
|  | options.size = gfx::Size(kResolution, kResolution); | 
|  | gl_.Initialize(options); | 
|  | } | 
|  |  | 
|  | void TearDown() override { gl_.Destroy(); } | 
|  |  | 
|  | GLuint SetupUnitQuad(GLint position_location); | 
|  |  | 
|  | GLManager gl_; | 
|  | }; | 
|  |  | 
|  | GLuint DepthTextureTest::SetupUnitQuad(GLint position_location) { | 
|  | GLuint vbo = 0; | 
|  | glGenBuffers(1, &vbo); | 
|  | glBindBuffer(GL_ARRAY_BUFFER, vbo); | 
|  | static float vertices[] = { | 
|  | 1.0f,  1.0f,  1.0f, | 
|  | -1.0f,  1.0f,  0.0f, | 
|  | -1.0f, -1.0f, -1.0f, | 
|  | 1.0f,  1.0f,  1.0f, | 
|  | -1.0f, -1.0f, -1.0f, | 
|  | 1.0f, -1.0f,  0.0f, | 
|  | }; | 
|  | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); | 
|  | glEnableVertexAttribArray(position_location); | 
|  | glVertexAttribPointer(position_location, 3, GL_FLOAT, GL_FALSE, 0, 0); | 
|  |  | 
|  | return vbo; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | struct FormatType { | 
|  | GLenum format; | 
|  | GLenum type; | 
|  | }; | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | TEST_F(DepthTextureTest, RenderTo) { | 
|  | if (!GLTestHelper::HasExtension("GL_CHROMIUM_depth_texture")) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool have_depth_stencil = GLTestHelper::HasExtension( | 
|  | "GL_OES_packed_depth_stencil"); | 
|  |  | 
|  | static const char* v_shader_str = SHADER( | 
|  | attribute vec4 v_position; | 
|  | void main() | 
|  | { | 
|  | gl_Position = v_position; | 
|  | } | 
|  | ); | 
|  | static const char* f_shader_str = SHADER( | 
|  | precision mediump float; | 
|  | uniform sampler2D u_texture; | 
|  | uniform vec2 u_resolution; | 
|  | void main() | 
|  | { | 
|  | vec2 texcoord = gl_FragCoord.xy / u_resolution; | 
|  | gl_FragColor = texture2D(u_texture, texcoord); | 
|  | } | 
|  | ); | 
|  |  | 
|  | GLuint program = GLTestHelper::LoadProgram(v_shader_str, f_shader_str); | 
|  |  | 
|  | GLint position_loc = glGetAttribLocation(program, "v_position"); | 
|  | GLint resolution_loc = glGetUniformLocation(program, "u_resolution"); | 
|  |  | 
|  | SetupUnitQuad(position_loc); | 
|  |  | 
|  | // Depth test needs to be on for the depth buffer to be updated. | 
|  | glEnable(GL_DEPTH_TEST); | 
|  |  | 
|  | // create an fbo | 
|  | GLuint fbo = 0; | 
|  | glGenFramebuffers(1, &fbo); | 
|  | glBindFramebuffer(GL_FRAMEBUFFER, fbo); | 
|  |  | 
|  | // create a depth texture. | 
|  | GLuint color_texture = 0; | 
|  | GLuint depth_texture = 0; | 
|  |  | 
|  | glGenTextures(1, &color_texture); | 
|  | glBindTexture(GL_TEXTURE_2D, color_texture); | 
|  | glTexImage2D( | 
|  | GL_TEXTURE_2D, 0, GL_RGBA, kResolution, kResolution, | 
|  | 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | 
|  | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 
|  | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 
|  | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 
|  | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 
|  | glFramebufferTexture2D( | 
|  | GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_texture, 0); | 
|  |  | 
|  | glGenTextures(1, &depth_texture); | 
|  | glBindTexture(GL_TEXTURE_2D, depth_texture); | 
|  | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 
|  | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 
|  | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 
|  | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 
|  | glFramebufferTexture2D( | 
|  | GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); | 
|  |  | 
|  | glUseProgram(program); | 
|  | glUniform2f(resolution_loc, kResolution, kResolution); | 
|  |  | 
|  | static const FormatType format_types[] = { | 
|  | { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT }, | 
|  | { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT }, | 
|  | { GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES }, | 
|  | }; | 
|  | for (size_t ii = 0; ii < arraysize(format_types); ++ii) { | 
|  | const FormatType& format_type = format_types[ii]; | 
|  | GLenum format = format_type.format; | 
|  | GLenum type = format_type.type; | 
|  |  | 
|  | if (format == GL_DEPTH_STENCIL_OES && !have_depth_stencil) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | glBindTexture(GL_TEXTURE_2D, depth_texture); | 
|  | glTexImage2D( | 
|  | GL_TEXTURE_2D, 0, format, kResolution, kResolution, | 
|  | 0, format, type, NULL); | 
|  |  | 
|  | glBindFramebuffer(GL_FRAMEBUFFER, fbo); | 
|  | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); | 
|  | EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), status) | 
|  | << "iteration: " << ii; | 
|  | if (status != GL_FRAMEBUFFER_COMPLETE) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (!GLTestHelper::CheckGLError("no errors after setup", __LINE__)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | glClearColor(1.0f, 1.0f, 1.0f, 1.0f); | 
|  | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
|  |  | 
|  | // Disconnect the texture so we'll render with the default texture. | 
|  | glBindTexture(GL_TEXTURE_2D, 0); | 
|  |  | 
|  | // Render to the fbo. | 
|  | glDrawArrays(GL_TRIANGLES, 0, 6); | 
|  |  | 
|  | if (!GLTestHelper::CheckGLError("no errors after depth draw", __LINE__)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Render with the depth texture. | 
|  | glBindFramebuffer(GL_FRAMEBUFFER, 0); | 
|  | glBindTexture(GL_TEXTURE_2D, depth_texture); | 
|  | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
|  | glDrawArrays(GL_TRIANGLES, 0, 6); | 
|  |  | 
|  | if (!GLTestHelper::CheckGLError("no errors after texture draw", __LINE__)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | uint8_t actual_pixels[kResolution * kResolution * 4] = { | 
|  | 0, | 
|  | }; | 
|  | glReadPixels( | 
|  | 0, 0, kResolution, kResolution, GL_RGBA, GL_UNSIGNED_BYTE, | 
|  | actual_pixels); | 
|  |  | 
|  | if (!GLTestHelper::CheckGLError("no errors after readpixels", __LINE__)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Check that each pixel's red value is less than the previous pixel in | 
|  | // either direction. Basically verify we have a gradient. No assumption is | 
|  | // made about the other channels green, blue and alpha since, according to | 
|  | // the GL_CHROMIUM_depth_texture spec, they have undefined values for | 
|  | // depth textures. | 
|  | int bad_count = 0;  // used to not spam the log with too many messages. | 
|  | for (GLint yy = 0; bad_count < 16 && yy < kResolution; ++yy) { | 
|  | for (GLint xx = 0; bad_count < 16 && xx < kResolution; ++xx) { | 
|  | const uint8_t* actual = &actual_pixels[(yy * kResolution + xx) * 4]; | 
|  | const uint8_t* left = actual - 4; | 
|  | const uint8_t* down = actual - kResolution * 4; | 
|  |  | 
|  | // NOTE: Qualcomm on Nexus 4 the right most column has the same | 
|  | // values as the next to right most column. (bad interpolator?) | 
|  | if (xx > 0 && xx < kResolution - 1) { | 
|  | EXPECT_GT(actual[0], left[0]) | 
|  | << "pixel at " << xx << ", " << yy | 
|  | << " actual[0] =" << static_cast<unsigned>(actual[0]) | 
|  | << " left[0] =" << static_cast<unsigned>(left[0]) | 
|  | << " actual =" << reinterpret_cast<const void*>(actual) | 
|  | << " left =" << reinterpret_cast<const void*>(left); | 
|  | bad_count += (actual[0] > left[0] ? 0 : 1); | 
|  | } | 
|  |  | 
|  | if (yy > 0 && yy < kResolution - 1) { | 
|  | EXPECT_GT(actual[0], down[0]) << "pixel at " << xx << ", " << yy; | 
|  | bad_count += (actual[0] > down[0] ? 0 : 1); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check that bottom left corner is vastly different thatn top right. | 
|  | EXPECT_GT( | 
|  | actual_pixels[(kResolution * kResolution - 1) * 4] - actual_pixels[0], | 
|  | 0xC0); | 
|  |  | 
|  | GLTestHelper::CheckGLError("no errors after everything", __LINE__); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace gpu | 
|  |  | 
|  |  | 
|  |  | 
|  |  |