| // 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 | |