| Index: content/browser/compositor/yuv_readback_unittest.cc
|
| diff --git a/content/browser/compositor/yuv_readback_unittest.cc b/content/browser/compositor/yuv_readback_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..176b3bf45ab7481ce2d061e7c750c1c2c1b55dc4
|
| --- /dev/null
|
| +++ b/content/browser/compositor/yuv_readback_unittest.cc
|
| @@ -0,0 +1,554 @@
|
| +// Copyright 2014 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 "base/json/json_reader.h"
|
| +#include "base/memory/ref_counted_memory.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/test/launcher/unit_test_launcher.h"
|
| +#include "base/test/test_suite.h"
|
| +#include "content/browser/compositor/gl_helper.h"
|
| +#include "gpu/command_buffer/client/gl_in_process_context.h"
|
| +#include "gpu/command_buffer/client/gles2_implementation.h"
|
| +#include "media/base/video_frame.h"
|
| +#include "media/base/video_util.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "third_party/skia/include/core/SkBitmap.h"
|
| +#include "ui/gl/gl_implementation.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +int kYUVReadbackSizes[] = {2, 4, 14};
|
| +}
|
| +
|
| +class YUVReadbackTest : public testing::Test {
|
| + protected:
|
| + void SetUp() override {
|
| + gpu::gles2::ContextCreationAttribHelper attributes;
|
| + attributes.alpha_size = 8;
|
| + attributes.depth_size = 24;
|
| + attributes.red_size = 8;
|
| + attributes.green_size = 8;
|
| + attributes.blue_size = 8;
|
| + attributes.stencil_size = 8;
|
| + attributes.samples = 4;
|
| + attributes.sample_buffers = 1;
|
| + attributes.bind_generates_resource = false;
|
| +
|
| + context_.reset(gpu::GLInProcessContext::Create(
|
| + nullptr, /* service */
|
| + nullptr, /* surface */
|
| + true, /* offscreen */
|
| + gfx::kNullAcceleratedWidget, /* window */
|
| + gfx::Size(1, 1), /* size */
|
| + nullptr, /* share_context */
|
| + attributes, gfx::PreferDiscreteGpu,
|
| + ::gpu::GLInProcessContextSharedMemoryLimits(),
|
| + nullptr, /* gpu_memory_buffer_manager */
|
| + nullptr /* image_factory */));
|
| + gl_ = context_->GetImplementation();
|
| + gpu::ContextSupport* support = context_->GetImplementation();
|
| +
|
| + helper_.reset(new content::GLHelper(gl_, support));
|
| + }
|
| +
|
| + void TearDown() override {
|
| + helper_.reset(NULL);
|
| + context_.reset(NULL);
|
| + }
|
| +
|
| + void StartTracing(const std::string& filter) {
|
| + base::trace_event::TraceLog::GetInstance()->SetEnabled(
|
| + base::trace_event::TraceConfig(filter,
|
| + base::trace_event::RECORD_UNTIL_FULL),
|
| + base::trace_event::TraceLog::RECORDING_MODE);
|
| + }
|
| +
|
| + static void TraceDataCB(
|
| + const base::Callback<void()>& callback,
|
| + std::string* output,
|
| + const scoped_refptr<base::RefCountedString>& json_events_str,
|
| + bool has_more_events) {
|
| + if (output->size() > 1 && !json_events_str->data().empty()) {
|
| + output->append(",");
|
| + }
|
| + output->append(json_events_str->data());
|
| + if (!has_more_events) {
|
| + callback.Run();
|
| + }
|
| + }
|
| +
|
| + // End tracing, return tracing data in a simple map
|
| + // of event name->counts.
|
| + void EndTracing(std::map<std::string, int>* event_counts) {
|
| + std::string json_data = "[";
|
| + base::trace_event::TraceLog::GetInstance()->SetDisabled();
|
| + base::RunLoop run_loop;
|
| + base::trace_event::TraceLog::GetInstance()->Flush(
|
| + base::Bind(&YUVReadbackTest::TraceDataCB, run_loop.QuitClosure(),
|
| + base::Unretained(&json_data)));
|
| + run_loop.Run();
|
| + json_data.append("]");
|
| +
|
| + std::string error_msg;
|
| + std::unique_ptr<base::Value> trace_data =
|
| + base::JSONReader::ReadAndReturnError(json_data, 0, NULL, &error_msg);
|
| + CHECK(trace_data) << "JSON parsing failed (" << error_msg
|
| + << ") JSON data:" << std::endl
|
| + << json_data;
|
| +
|
| + base::ListValue* list;
|
| + CHECK(trace_data->GetAsList(&list));
|
| + for (size_t i = 0; i < list->GetSize(); i++) {
|
| + base::Value* item = NULL;
|
| + if (list->Get(i, &item)) {
|
| + base::DictionaryValue* dict;
|
| + CHECK(item->GetAsDictionary(&dict));
|
| + std::string name;
|
| + CHECK(dict->GetString("name", &name));
|
| + std::string trace_type;
|
| + CHECK(dict->GetString("ph", &trace_type));
|
| + // Count all except END traces, as they come in BEGIN/END pairs.
|
| + if (trace_type != "E" && trace_type != "e")
|
| + (*event_counts)[name]++;
|
| + VLOG(1) << "trace name: " << name;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Look up a single channel value. Works for 4-channel and single channel
|
| + // bitmaps. Clamp x/y.
|
| + int Channel(SkBitmap* pixels, int x, int y, int c) {
|
| + if (pixels->bytesPerPixel() == 4) {
|
| + uint32_t* data =
|
| + pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)),
|
| + std::max(0, std::min(y, pixels->height() - 1)));
|
| + return (*data) >> (c * 8) & 0xff;
|
| + } else {
|
| + DCHECK_EQ(pixels->bytesPerPixel(), 1);
|
| + DCHECK_EQ(c, 0);
|
| + return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)),
|
| + std::max(0, std::min(y, pixels->height() - 1)));
|
| + }
|
| + }
|
| +
|
| + // Set a single channel value. Works for 4-channel and single channel
|
| + // bitmaps. Clamp x/y.
|
| + void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) {
|
| + DCHECK_GE(x, 0);
|
| + DCHECK_GE(y, 0);
|
| + DCHECK_LT(x, pixels->width());
|
| + DCHECK_LT(y, pixels->height());
|
| + if (pixels->bytesPerPixel() == 4) {
|
| + uint32_t* data = pixels->getAddr32(x, y);
|
| + v = std::max(0, std::min(v, 255));
|
| + *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
|
| + } else {
|
| + DCHECK_EQ(pixels->bytesPerPixel(), 1);
|
| + DCHECK_EQ(c, 0);
|
| + uint8_t* data = pixels->getAddr8(x, y);
|
| + v = std::max(0, std::min(v, 255));
|
| + *data = v;
|
| + }
|
| + }
|
| +
|
| + // Print all the R, G, B or A values from an SkBitmap in a
|
| + // human-readable format.
|
| + void PrintChannel(SkBitmap* pixels, int c) {
|
| + for (int y = 0; y < pixels->height(); y++) {
|
| + std::string formatted;
|
| + for (int x = 0; x < pixels->width(); x++) {
|
| + formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c)));
|
| + }
|
| + LOG(ERROR) << formatted;
|
| + }
|
| + }
|
| +
|
| + // Get a single R, G, B or A value as a float.
|
| + float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) {
|
| + return Channel(pixels, x, y, c) / 255.0;
|
| + }
|
| +
|
| + // Works like a GL_LINEAR lookup on an SkBitmap.
|
| + float Bilinear(SkBitmap* pixels, float x, float y, int c) {
|
| + x -= 0.5;
|
| + y -= 0.5;
|
| + int base_x = static_cast<int>(floorf(x));
|
| + int base_y = static_cast<int>(floorf(y));
|
| + x -= base_x;
|
| + y -= base_y;
|
| + return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) +
|
| + ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) +
|
| + ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y +
|
| + ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y);
|
| + }
|
| +
|
| + void FlipSKBitmap(SkBitmap* bitmap) {
|
| + int bpp = bitmap->bytesPerPixel();
|
| + DCHECK(bpp == 4 || bpp == 1);
|
| + int top_line = 0;
|
| + int bottom_line = bitmap->height() - 1;
|
| + while (top_line < bottom_line) {
|
| + for (int x = 0; x < bitmap->width(); x++) {
|
| + bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line),
|
| + *bitmap->getAddr32(x, bottom_line))
|
| + : std::swap(*bitmap->getAddr8(x, top_line),
|
| + *bitmap->getAddr8(x, bottom_line));
|
| + }
|
| + top_line++;
|
| + bottom_line--;
|
| + }
|
| + }
|
| +
|
| + // 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++) {
|
| + std::string formatted;
|
| + for (int x = 0; x < xsize; x++) {
|
| + formatted.append(base::StringPrintf("%3d, ", plane[y * stride + x]));
|
| + }
|
| + LOG(ERROR) << formatted << " (" << (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,
|
| + int truth_stride,
|
| + unsigned char* other,
|
| + int other_stride,
|
| + int maxdiff,
|
| + int xsize,
|
| + 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 * other_stride + x];
|
| + int b = truth[y * truth_stride + x];
|
| + EXPECT_NEAR(a, b, maxdiff) << " x=" << x << " y=" << y << " "
|
| + << message;
|
| + if (std::abs(a - b) > maxdiff) {
|
| + LOG(ERROR) << "-------expected--------";
|
| + PrintPlane(truth, xsize, truth_stride, ysize);
|
| + LOG(ERROR) << "-------actual--------";
|
| + PrintPlane(other, xsize, other_stride, ysize);
|
| + if (source) {
|
| + LOG(ERROR) << "-------before yuv conversion: red--------";
|
| + PrintChannel(source, 0);
|
| + LOG(ERROR) << "-------before yuv conversion: green------";
|
| + PrintChannel(source, 1);
|
| + LOG(ERROR) << "-------before yuv conversion: blue-------";
|
| + 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,
|
| + bool flip,
|
| + bool use_mrt,
|
| + content::GLHelper::ScalerQuality quality) {
|
| + GLuint src_texture;
|
| + gl_->GenTextures(1, &src_texture);
|
| + SkBitmap input_pixels;
|
| + input_pixels.allocN32Pixels(xsize, ysize);
|
| +
|
| + 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;
|
| + }
|
| + }
|
| + }
|
| +
|
| + gl_->BindTexture(GL_TEXTURE_2D, src_texture);
|
| + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsize, ysize, 0, GL_RGBA,
|
| + GL_UNSIGNED_BYTE, input_pixels.getPixels());
|
| +
|
| + gpu::Mailbox mailbox;
|
| + gl_->GenMailboxCHROMIUM(mailbox.name);
|
| + EXPECT_FALSE(mailbox.IsZero());
|
| + gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
|
| + const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM();
|
| + gl_->ShallowFlushCHROMIUM();
|
| +
|
| + gpu::SyncToken sync_token;
|
| + gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
|
| +
|
| + std::string message = base::StringPrintf(
|
| + "input size: %dx%d "
|
| + "output size: %dx%d "
|
| + "margin: %dx%d "
|
| + "pattern: %d %s %s",
|
| + xsize, ysize, output_xsize, output_ysize, xmargin, ymargin,
|
| + test_pattern, flip ? "flip" : "noflip", flip ? "mrt" : "nomrt");
|
| + std::unique_ptr<ReadbackYUVInterface> yuv_reader(
|
| + helper_->CreateReadbackPipelineYUV(
|
| + quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize),
|
| + gfx::Size(xsize, ysize), flip, use_mrt));
|
| +
|
| + scoped_refptr<media::VideoFrame> output_frame =
|
| + media::VideoFrame::CreateFrame(
|
| + media::PIXEL_FORMAT_YV12,
|
| + // The coded size of the output frame is rounded up to the next
|
| + // 16-byte boundary. This tests that the readback is being
|
| + // positioned inside the frame's visible region, and not dependent
|
| + // on its coded size.
|
| + gfx::Size((output_xsize + 15) & ~15, (output_ysize + 15) & ~15),
|
| + 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::PIXEL_FORMAT_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(mailbox, sync_token, output_frame->visible_rect(),
|
| + output_frame->stride(media::VideoFrame::kYPlane),
|
| + output_frame->data(media::VideoFrame::kYPlane),
|
| + output_frame->stride(media::VideoFrame::kUPlane),
|
| + output_frame->data(media::VideoFrame::kUPlane),
|
| + output_frame->stride(media::VideoFrame::kVPlane),
|
| + output_frame->data(media::VideoFrame::kVPlane),
|
| + gfx::Point(xmargin, ymargin),
|
| + base::Bind(&callcallback, run_loop.QuitClosure()));
|
| +
|
| + const gfx::Rect paste_rect(gfx::Point(xmargin, ymargin),
|
| + gfx::Size(xsize, ysize));
|
| + media::LetterboxYUV(output_frame.get(), paste_rect);
|
| + run_loop.Run();
|
| +
|
| + if (flip) {
|
| + FlipSKBitmap(&input_pixels);
|
| + }
|
| +
|
| + unsigned char* Y = truth_frame->visible_data(media::VideoFrame::kYPlane);
|
| + unsigned char* U = truth_frame->visible_data(media::VideoFrame::kUPlane);
|
| + unsigned char* V = truth_frame->visible_data(media::VideoFrame::kVPlane);
|
| + int32_t y_stride = truth_frame->stride(media::VideoFrame::kYPlane);
|
| + int32_t u_stride = truth_frame->stride(media::VideoFrame::kUPlane);
|
| + int32_t 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);
|
| +
|
| + const float kRGBtoYColorWeights[] = {0.257f, 0.504f, 0.098f, 0.0625f};
|
| + const float kRGBtoUColorWeights[] = {-0.148f, -0.291f, 0.439f, 0.5f};
|
| + const float kRGBtoVColorWeights[] = {0.439f, -0.368f, -0.071f, 0.5f};
|
| +
|
| + 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) * kRGBtoYColorWeights[0] +
|
| + ChannelAsFloat(&input_pixels, x, y, 1) * kRGBtoYColorWeights[1] +
|
| + ChannelAsFloat(&input_pixels, x, y, 2) * kRGBtoYColorWeights[2] +
|
| + kRGBtoYColorWeights[3]);
|
| + }
|
| + }
|
| +
|
| + 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) *
|
| + kRGBtoUColorWeights[0] +
|
| + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) *
|
| + kRGBtoUColorWeights[1] +
|
| + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) *
|
| + kRGBtoUColorWeights[2] +
|
| + kRGBtoUColorWeights[3]);
|
| + V[(y + ymargin / 2) * v_stride + x + xmargin / 2] =
|
| + float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) *
|
| + kRGBtoVColorWeights[0] +
|
| + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) *
|
| + kRGBtoVColorWeights[1] +
|
| + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) *
|
| + kRGBtoVColorWeights[2] +
|
| + kRGBtoVColorWeights[3]);
|
| + }
|
| + }
|
| +
|
| + ComparePlane(
|
| + Y, y_stride, output_frame->visible_data(media::VideoFrame::kYPlane),
|
| + output_frame->stride(media::VideoFrame::kYPlane), 2, output_xsize,
|
| + output_ysize, &input_pixels, message + " Y plane");
|
| + ComparePlane(
|
| + U, u_stride, output_frame->visible_data(media::VideoFrame::kUPlane),
|
| + output_frame->stride(media::VideoFrame::kUPlane), 2, output_xsize / 2,
|
| + output_ysize / 2, &input_pixels, message + " U plane");
|
| + ComparePlane(
|
| + V, v_stride, output_frame->visible_data(media::VideoFrame::kVPlane),
|
| + output_frame->stride(media::VideoFrame::kVPlane), 2, output_xsize / 2,
|
| + output_ysize / 2, &input_pixels, message + " V plane");
|
| +
|
| + gl_->DeleteTextures(1, &src_texture);
|
| + }
|
| +
|
| + std::unique_ptr<gpu::GLInProcessContext> context_;
|
| + gpu::gles2::GLES2Interface* gl_;
|
| + std::unique_ptr<content::GLHelper> helper_;
|
| + gfx::DisableNullDrawGLBindings enable_pixel_output_;
|
| +};
|
| +
|
| +TEST_F(YUVReadbackTest, YUVReadbackOptTest) {
|
| + // This test uses the gpu.service/gpu_decoder tracing events to detect how
|
| + // many scaling passes are actually performed by the YUV readback pipeline.
|
| + StartTracing(TRACE_DISABLED_BY_DEFAULT(
|
| + "gpu.service") "," TRACE_DISABLED_BY_DEFAULT("gpu_decoder"));
|
| +
|
| + TestYUVReadback(800, 400, 800, 400, 0, 0, 1, false, true,
|
| + content::GLHelper::SCALER_QUALITY_FAST);
|
| +
|
| + std::map<std::string, int> event_counts;
|
| + EndTracing(&event_counts);
|
| + int draw_buffer_calls = event_counts["kDrawBuffersEXTImmediate"];
|
| + int draw_arrays_calls = event_counts["kDrawArrays"];
|
| + VLOG(1) << "Draw buffer calls: " << draw_buffer_calls;
|
| + VLOG(1) << "DrawArrays calls: " << draw_arrays_calls;
|
| +
|
| + if (draw_buffer_calls) {
|
| + // When using MRT, the YUV readback code should only
|
| + // execute two draw arrays, and scaling should be integrated
|
| + // into those two calls since we are using the FAST scalign
|
| + // quality.
|
| + EXPECT_EQ(2, draw_arrays_calls);
|
| + } else {
|
| + // When not using MRT, there are three passes for the YUV,
|
| + // and one for the scaling.
|
| + EXPECT_EQ(4, draw_arrays_calls);
|
| + }
|
| +}
|
| +
|
| +class YUVReadbackPixelTest
|
| + : public YUVReadbackTest,
|
| + public ::testing::WithParamInterface<
|
| + std::tr1::tuple<bool, bool, unsigned int, unsigned int>> {};
|
| +
|
| +TEST_P(YUVReadbackPixelTest, Test) {
|
| + bool flip = std::tr1::get<0>(GetParam());
|
| + bool use_mrt = std::tr1::get<1>(GetParam());
|
| + unsigned int x = std::tr1::get<2>(GetParam());
|
| + unsigned int y = std::tr1::get<3>(GetParam());
|
| +
|
| + for (unsigned int ox = x; ox < arraysize(kYUVReadbackSizes); ox++) {
|
| + for (unsigned int oy = y; oy < arraysize(kYUVReadbackSizes); 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(
|
| + kYUVReadbackSizes[x], kYUVReadbackSizes[y],
|
| + kYUVReadbackSizes[ox], kYUVReadbackSizes[oy],
|
| + compute_margin(kYUVReadbackSizes[x], kYUVReadbackSizes[ox], xm),
|
| + compute_margin(kYUVReadbackSizes[y], kYUVReadbackSizes[oy], ym),
|
| + pattern, flip, use_mrt, content::GLHelper::SCALER_QUALITY_GOOD);
|
| + if (HasFailure()) {
|
| + return;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +// First argument is intentionally empty.
|
| +INSTANTIATE_TEST_CASE_P(
|
| + ,
|
| + YUVReadbackPixelTest,
|
| + ::testing::Combine(
|
| + ::testing::Bool(),
|
| + ::testing::Bool(),
|
| + ::testing::Range<unsigned int>(0, arraysize(kYUVReadbackSizes)),
|
| + ::testing::Range<unsigned int>(0, arraysize(kYUVReadbackSizes))));
|
| +
|
| +} // namespace content
|
|
|