diff options
Diffstat (limited to 'content/common/gpu/client/gl_helper_unittests.cc')
-rw-r--r-- | content/common/gpu/client/gl_helper_unittests.cc | 301 |
1 files changed, 296 insertions, 5 deletions
diff --git a/content/common/gpu/client/gl_helper_unittests.cc b/content/common/gpu/client/gl_helper_unittests.cc index 21a77f7..4e53258 100644 --- a/content/common/gpu/client/gl_helper_unittests.cc +++ b/content/common/gpu/client/gl_helper_unittests.cc @@ -12,14 +12,18 @@ #include <GLES2/gl2extchromium.h> #include "base/at_exit.h" +#include "base/bind.h" #include "base/command_line.h" #include "base/file_util.h" +#include "base/message_loop.h" +#include "base/run_loop.h" #include "base/stringprintf.h" #include "base/time.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/client/gl_helper_scaling.h" #include "content/public/test/unittest_test_suite.h" #include "content/test/content_test_suite.h" +#include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkTypes.h" @@ -59,7 +63,6 @@ class GLHelperTest : public testing::Test { webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl:: CreateOffscreenContext(attributes)); context_->makeContextCurrent(); - helper_.reset(new content::GLHelper(context_.get())); helper_scaling_.reset(new content::GLHelperScaling( context_.get(), @@ -156,6 +159,9 @@ class GLHelperTest : public testing::Test { ret.append("bicubic 1/2"); xy_matters = true; break; + case GLHelperScaling::SHADER_PLANAR: + ret.append("planar"); + break; } if (xy_matters) { @@ -233,6 +239,10 @@ class GLHelperTest : public testing::Test { // Codify valid scale operations. switch (scaler_stages[i].shader) { + case GLHelperScaling::SHADER_PLANAR: + EXPECT_TRUE(false) << "Invalid shader."; + break; + case GLHelperScaling::SHADER_BILINEAR: if (quality != content::GLHelper::SCALER_QUALITY_FAST) { x_samples = 1; @@ -503,19 +513,19 @@ class GLHelperTest : public testing::Test { for (int x = 0; x < xsize; ++x) { for (int y = 0; y < ysize; ++y) { switch (test_pattern) { - case 0: // Smooth test pattern + case 0: // Smooth test pattern SetChannel(&input_pixels, x, y, 0, x * 10); SetChannel(&input_pixels, x, y, 1, y * 10); SetChannel(&input_pixels, x, y, 2, (x + y) * 10); SetChannel(&input_pixels, x, y, 3, 255); break; - case 1: // Small blocks + case 1: // Small blocks SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0); SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0); SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0); SetChannel(&input_pixels, x, y, 3, 255); break; - case 2: // Medium blocks + case 2: // Medium blocks SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50); SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50); SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5); @@ -647,6 +657,249 @@ class GLHelperTest : public testing::Test { EXPECT_EQ(PrintStages(stages), description); } + // Note: Left/Right means Top/Bottom when used for Y dimension. + enum Margin { + MarginLeft, + MarginMiddle, + MarginRight, + MarginInvalid, + }; + + static Margin NextMargin(Margin m) { + switch (m) { + case MarginLeft: + return MarginMiddle; + case MarginMiddle: + return MarginRight; + case MarginRight: + return MarginInvalid; + default: + return MarginInvalid; + } + } + + int compute_margin(int insize, int outsize, Margin m) { + int available = outsize - insize; + switch (m) { + default: + EXPECT_TRUE(false) << "This should not happen."; + return 0; + case MarginLeft: + return 0; + case MarginMiddle: + return (available / 2) & ~1; + case MarginRight: + return available; + } + } + + // Convert 0.0 - 1.0 to 0 - 255 + int float_to_byte(float v) { + int ret = static_cast<int>(floorf(v * 255.0f + 0.5f)); + if (ret < 0) { + return 0; + } + if (ret > 255) { + return 255; + } + return ret; + } + + static void callcallback(const base::Callback<void()>& callback, + bool result) { + callback.Run(); + } + + void PrintPlane(unsigned char *plane, int xsize, int stride, int ysize) { + for (int y = 0; y < ysize; y++) { + for (int x = 0; x < xsize ; x++) { + printf("%3d, ", plane[y * stride + x]); + } + printf(" (%p)\n", plane + y * stride); + } + } + + // Compare two planes make sure that each component of each pixel + // is no more than |maxdiff| apart. + void ComparePlane(unsigned char* truth, + unsigned char* other, + int maxdiff, + int xsize, + int stride, + int ysize, + SkBitmap* source, + std::string message) { + for (int x = 0; x < xsize; x++) { + for (int y = 0; y < ysize; y++) { + int a = other[y * stride + x]; + int b = truth[y * stride + x]; + EXPECT_NEAR(a, b, maxdiff) + << " x=" << x + << " y=" << y + << " " << message; + if (std::abs(a - b) > maxdiff) { + printf("-------expected--------\n"); + PrintPlane(truth, xsize, stride, ysize); + printf("-------actual--------\n"); + PrintPlane(other, xsize, stride, ysize); + if (source) { + printf("-------before yuv conversion: red--------\n"); + PrintChannel(source, 0); + printf("-------before yuv conversion: green------\n"); + PrintChannel(source, 1); + printf("-------before yuv conversion: blue-------\n"); + PrintChannel(source, 2); + } + return; + } + } + } + } + + // YUV readback test. Create a test pattern, convert to YUV + // with reference implementation and compare to what gl_helper + // returns. + void TestYUVReadback(int xsize, + int ysize, + int output_xsize, + int output_ysize, + int xmargin, + int ymargin, + int test_pattern) { + WebGLId src_texture = context_->createTexture(); + SkBitmap input_pixels; + input_pixels.setConfig(SkBitmap::kARGB_8888_Config, xsize, ysize); + input_pixels.allocPixels(); + SkAutoLockPixels lock(input_pixels); + + for (int x = 0; x < xsize; ++x) { + for (int y = 0; y < ysize; ++y) { + switch (test_pattern) { + case 0: // Smooth test pattern + SetChannel(&input_pixels, x, y, 0, x * 10); + SetChannel(&input_pixels, x, y, 1, y * 10); + SetChannel(&input_pixels, x, y, 2, (x + y) * 10); + SetChannel(&input_pixels, x, y, 3, 255); + break; + case 1: // Small blocks + SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 3, 255); + break; + case 2: // Medium blocks + SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50); + SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50); + SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5); + SetChannel(&input_pixels, x, y, 3, 255); + break; + } + } + } + + context_->bindTexture(GL_TEXTURE_2D, src_texture); + context_->texImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + xsize, + ysize, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + input_pixels.getPixels()); + + std::string message = base::StringPrintf("input size: %dx%d " + "output size: %dx%d " + "margin: %dx%d " + "pattern: %d", + xsize, ysize, + output_xsize, output_ysize, + xmargin, ymargin, + test_pattern); + scoped_ptr<ReadbackYUVInterface> yuv_reader( + helper_->CreateReadbackPipelineYUV( + content::GLHelper::SCALER_QUALITY_GOOD, + gfx::Size(xsize, ysize), + gfx::Rect(0, 0, xsize, ysize), + gfx::Size(output_xsize, output_ysize), + gfx::Rect(xmargin, ymargin, xsize, ysize), + false)); + + scoped_refptr<media::VideoFrame> output_frame = + media::VideoFrame::CreateFrame( + media::VideoFrame::YV12, + gfx::Size(output_xsize, output_ysize), + gfx::Rect(0, 0, output_xsize, output_ysize), + gfx::Size(output_xsize, output_ysize), + base::TimeDelta::FromSeconds(0)); + scoped_refptr<media::VideoFrame> truth_frame = + media::VideoFrame::CreateFrame( + media::VideoFrame::YV12, + gfx::Size(output_xsize, output_ysize), + gfx::Rect(0, 0, output_xsize, output_ysize), + gfx::Size(output_xsize, output_ysize), + base::TimeDelta::FromSeconds(0)); + + base::RunLoop run_loop; + yuv_reader->ReadbackYUV( + src_texture, + output_frame.get(), + base::Bind(&callcallback, run_loop.QuitClosure())); + run_loop.Run(); + + unsigned char* Y = truth_frame->data(media::VideoFrame::kYPlane); + unsigned char* U = truth_frame->data(media::VideoFrame::kUPlane); + unsigned char* V = truth_frame->data(media::VideoFrame::kVPlane); + int32 y_stride = truth_frame->stride(media::VideoFrame::kYPlane); + int32 u_stride = truth_frame->stride(media::VideoFrame::kUPlane); + int32 v_stride = truth_frame->stride(media::VideoFrame::kVPlane); + memset(Y, 0x00, y_stride * output_ysize); + memset(U, 0x80, u_stride * output_ysize / 2); + memset(V, 0x80, v_stride * output_ysize / 2); + + for (int y = 0; y < ysize; y++) { + for (int x = 0; x < xsize; x++) { + Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte( + ChannelAsFloat(&input_pixels, x, y, 0) * 0.257 + + ChannelAsFloat(&input_pixels, x, y, 1) * 0.504 + + ChannelAsFloat(&input_pixels, x, y, 2) * 0.098 + + 0.0625); + } + } + + for (int y = 0; y < ysize / 2; y++) { + for (int x = 0; x < xsize / 2; x++) { + U[(y + ymargin / 2) * u_stride + x + xmargin / 2] = + float_to_byte( + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * -0.148 + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.291 + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * 0.439 + + 0.5); + V[(y + ymargin / 2) * v_stride + x + xmargin / 2] = + float_to_byte( + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * 0.439 + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.368 + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * -0.071 + + 0.5); + } + } + + ComparePlane(Y, output_frame->data(media::VideoFrame::kYPlane), 2, + output_xsize, y_stride, output_ysize, + &input_pixels, + message + " Y plane"); + ComparePlane(U, output_frame->data(media::VideoFrame::kUPlane), 2, + output_xsize / 2, u_stride, output_ysize / 2, + &input_pixels, + message + " U plane"); + ComparePlane(V, output_frame->data(media::VideoFrame::kVPlane), 2, + output_xsize / 2, v_stride, output_ysize / 2, + &input_pixels, + message + " V plane"); + + context_->deleteTexture(src_texture); + } + void TestAddOps(int src, int dst, bool scale_x, @@ -844,6 +1097,42 @@ class GLHelperTest : public testing::Test { std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; }; +TEST_F(GLHelperTest, YUVReadbackTest) { + int sizes[] = { 2, 4, 14 }; + for (unsigned int x = 0; x < arraysize(sizes); x++) { + for (unsigned int y = 0; y < arraysize(sizes); y++) { + for (unsigned int ox = x; ox < arraysize(sizes); ox++) { + for (unsigned int oy = y; oy < arraysize(sizes); oy++) { + // If output is a subsection of the destination frame, (letterbox) + // then try different variations of where the subsection goes. + for (Margin xm = x < ox ? MarginLeft : MarginRight; + xm <= MarginRight; + xm = NextMargin(xm)) { + for (Margin ym = y < oy ? MarginLeft : MarginRight; + ym <= MarginRight; + ym = NextMargin(ym)) { + for (int pattern = 0; pattern < 3; pattern++) { + TestYUVReadback( + sizes[x], + sizes[y], + sizes[ox], + sizes[oy], + compute_margin(sizes[x], sizes[ox], xm), + compute_margin(sizes[y], sizes[oy], ym), + pattern); + if (HasFailure()) { + return; + } + } + } + } + } + } + } + } +} + + // Per pixel tests, all sizes are small so that we can print // out the generated bitmaps. TEST_F(GLHelperTest, ScaleTest) { @@ -947,5 +1236,7 @@ int main(int argc, char** argv) { #endif gfx::GLSurface::InitializeOneOff(); - return content::UnitTestTestSuite(suite).Run(); + content::UnitTestTestSuite runner(suite); + base::MessageLoop message_loop; + return runner.Run(); } |