Commit f26763fc authored by Yuri Wiitala's avatar Yuri Wiitala Committed by Commit Bot

GLScaler: Build ScalerStage chains and add pixel tests.

Adds the implementation that determines which shader programs to use,
and in what order, to execute scaling and format conversion. Also, adds
pixel tests to check all the various features provided by GLScaler (via
its Parameters).

Bug: 870036
Change-Id: I7cb1d64d6df79a3a6221c2ab6cdf4b504b26c5b1
Reviewed-on: https://chromium-review.googlesource.com/c/1298737
Commit-Queue: Yuri Wiitala <miu@chromium.org>
Reviewed-by: default avatarXiangjun Zhang <xjz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603762}
parent fc94bd1e
......@@ -221,6 +221,7 @@ viz_source_set("unit_tests") {
"frame_sinks/delay_based_time_source_unittest.cc",
"gl_helper_unittest.cc",
"gl_scaler_overscan_pixeltest.cc",
"gl_scaler_pixeltest.cc",
"gl_scaler_shader_pixeltest.cc",
"gl_scaler_test_util.cc",
"gl_scaler_test_util.h",
......
This diff is collapsed.
......@@ -9,6 +9,7 @@
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <tuple>
#include <utility>
......@@ -260,10 +261,12 @@ class VIZ_COMMON_EXPORT GLScaler : public ContextLostObserver {
private:
friend class GLScalerOverscanPixelTest;
friend class GLScalerShaderPixelTest;
friend VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream&,
const GLScaler&);
using GLES2Interface = gpu::gles2::GLES2Interface;
enum Axis { HORIZONTAL, VERTICAL };
enum Axis { HORIZONTAL = 0, VERTICAL = 1 };
// The shaders used by each stage in the scaling pipeline.
enum class Shader : int8_t {
......@@ -370,7 +373,11 @@ class VIZ_COMMON_EXPORT GLScaler : public ContextLostObserver {
void set_input_stage(std::unique_ptr<ScalerStage> stage) {
input_stage_ = std::move(stage);
}
std::unique_ptr<ScalerStage> take_input_stage() {
return std::move(input_stage_);
}
ShaderProgram* shader_program() const { return program_; }
void set_shader_program(ShaderProgram* program) { program_ = program; }
bool is_flipped_source() const { return is_flipped_source_; }
......@@ -428,6 +435,32 @@ class VIZ_COMMON_EXPORT GLScaler : public ContextLostObserver {
const gfx::ColorTransform* color_transform,
const GLenum swizzle[2]);
// Create a scaling chain using the bilinear shaders.
static std::unique_ptr<ScalerStage> CreateAGoodScalingChain(
gpu::gles2::GLES2Interface* gl,
const gfx::Vector2d& scale_from,
const gfx::Vector2d& scale_to);
// Create a scaling chain using the bicubic shaders.
static std::unique_ptr<ScalerStage> CreateTheBestScalingChain(
gpu::gles2::GLES2Interface* gl,
const gfx::Vector2d& scale_from,
const gfx::Vector2d& scale_to);
// Modifies |chain| by appending an export stage, to rearrange the image data
// according to the requested |export_format|. In some cases, this will delete
// the final stage in |chain| before appending the export stage.
static std::unique_ptr<ScalerStage> MaybeAppendExportStage(
gpu::gles2::GLES2Interface* gl,
std::unique_ptr<ScalerStage> chain,
Parameters::ExportFormat export_format);
// Returns the other of the two axes.
static Axis TheOtherAxis(Axis axis);
// Returns the name of the |shader| in string form, for logging purposes.
static const char* GetShaderName(Shader shader);
// Returns true if the given |gl| context mentions all of |names| in its
// extensions string.
static bool AreAllGLExtensionsPresent(gpu::gles2::GLES2Interface* gl,
......@@ -463,9 +496,16 @@ class VIZ_COMMON_EXPORT GLScaler : public ContextLostObserver {
// The chain of ScalerStages.
std::unique_ptr<ScalerStage> chain_;
// The color space in which the scaling stages operate.
gfx::ColorSpace scaling_color_space_;
DISALLOW_COPY_AND_ASSIGN(GLScaler);
};
// For logging.
VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
const GLScaler& scaler);
} // namespace viz
#endif // COMPONENTS_VIZ_COMMON_GL_SCALER_H_
This diff is collapsed.
......@@ -346,7 +346,7 @@ class GLScalerShaderPixelTest
// Android seems to have texture sampling and/or readback accuracy issues
// with these programs that are not at all seen on any of the desktop
// platforms. Also, versions before Marshmallow seem to have a much larger
// accuracy issue with a few of the programs. Thus, use higher thresholds,
// accuracy issues with a few of the programs. Thus, use higher thresholds,
// assuming that the programs are correct if they can pass a much lower
// threshold on other platforms.
if (base::android::BuildInfo::GetInstance()->sdk_int() <
......
......@@ -256,6 +256,65 @@ SkBitmap GLScalerTestUtil::CreatePackedPlanarBitmap(const SkBitmap& source,
return result;
}
// static
void GLScalerTestUtil::UnpackPlanarBitmap(const SkBitmap& plane,
int channel,
SkBitmap* out) {
// The heuristic below auto-adapts to subsampled plane sizes. However, there
// are two cricital requirements: 1) |plane| cannot be empty; 2) |plane| must
// have a size that cleanly unpacks to |out|'s size.
CHECK_GT(plane.width(), 0);
CHECK_GT(plane.height(), 0);
const int col_sampling_ratio = out->width() / plane.width();
CHECK_EQ(out->width() % plane.width(), 0);
CHECK_GT(col_sampling_ratio, 0);
const int row_sampling_ratio = out->height() / plane.height();
CHECK_EQ(out->height() % plane.height(), 0);
CHECK_GT(row_sampling_ratio, 0);
const int ch_sampling_ratio = col_sampling_ratio / 4;
CHECK_GT(ch_sampling_ratio, 0);
// These determine which single byte in each of |out|'s uint32_t-valued pixels
// will be modified.
constexpr int kShiftForChannel[4] = {kRedShift, kGreenShift, kBlueShift,
kAlphaShift};
const int output_shift = kShiftForChannel[channel];
const uint32_t output_retain_mask = ~(UINT32_C(0xff) << output_shift);
// Iterate over the pixels of |out|, sampling each of the 4 components of each
// of |plane|'s pixels.
for (int y = 0; y < out->height(); ++y) {
const uint32_t* const src = plane.getAddr32(0, y / row_sampling_ratio);
uint32_t* const dst = out->getAddr32(0, y);
for (int x = 0; x < out->width(); ++x) {
// Zero-out the existing byte (e.g., if channel==1, then "RGBA" → "R0BA").
dst[x] &= output_retain_mask;
// From |src|, grab one of "XYZW". Then, copy it to the target byte in
// |dst| (e.g., if x_src_ch=3, then grab "W" from |src|, and |dst| changes
// from "R0BA" to "RWBA").
const int x_src = x / col_sampling_ratio;
const int x_src_ch = (x / ch_sampling_ratio) % 4;
dst[x] |= ((src[x_src] >> kShiftForChannel[x_src_ch]) & 0xff)
<< output_shift;
}
}
}
// static
SkBitmap GLScalerTestUtil::CreateVerticallyFlippedBitmap(
const SkBitmap& source) {
SkBitmap bitmap;
bitmap.allocPixels(source.info());
CHECK_EQ(bitmap.rowBytes(), source.rowBytes());
for (int y = 0; y < bitmap.height(); ++y) {
const int src_y = bitmap.height() - y - 1;
memcpy(bitmap.getAddr32(0, y), source.getAddr32(0, src_y),
bitmap.rowBytes());
}
return bitmap;
}
// The area and color of the bars in a 1920x1080 HD SMPTE color bars test image
// (https://commons.wikimedia.org/wiki/File:SMPTE_Color_Bars_16x9.svg). The gray
// linear gradient bar is defined as half solid 0-level black and half solid
......
......@@ -98,6 +98,17 @@ class GLScalerTestUtil {
// considered a reference implementation of that.
static SkBitmap CreatePackedPlanarBitmap(const SkBitmap& source, int channel);
// Performs the inverse operation to CreatePackedPlanarBitmap(). This takes
// all of the data in |plane| and uses it to populate a single color channel
// of all the pixels of |out|. The |plane| can be a full-size or half-size
// (subsampled) plane.
static void UnpackPlanarBitmap(const SkBitmap& plane,
int channel,
SkBitmap* out);
// Returns the |source| bitmap, but with its content vertically flipped.
static SkBitmap CreateVerticallyFlippedBitmap(const SkBitmap& source);
// The area and color of the bars in a 1920x1080 HD SMPTE color bars test
// image (https://commons.wikimedia.org/wiki/File:SMPTE_Color_Bars_16x9.svg).
// The gray linear gradient bar is defined as half solid 0-level black and
......
......@@ -4,6 +4,8 @@
#include "components/viz/common/gl_scaler.h"
#include "cc/test/pixel_test.h"
#include "components/viz/common/gl_scaler_test_util.h"
#include "components/viz/common/gpu/context_provider.h"
#include "gpu/GLES2/gl2chromium.h"
#include "gpu/GLES2/gl2extchromium.h"
......@@ -79,7 +81,12 @@ class MockContextProvider : public ContextProvider {
}
};
class GLScalerTest : public testing::Test {};
class GLScalerTest : public cc::PixelTest {
protected:
void SetUp() final { cc::PixelTest::SetUpGLWithoutRenderer(false); }
void TearDown() final { cc::PixelTest::TearDown(); }
};
TEST_F(GLScalerTest, AddAndRemovesSelfAsContextLossObserver) {
NiceMock<MockContextProvider> provider;
......@@ -93,7 +100,7 @@ TEST_F(GLScalerTest, AddAndRemovesSelfAsContextLossObserver) {
GLScaler scaler(base::WrapRefCounted(&provider));
}
TEST_F(GLScalerTest, CleansUpWhenContextIsLost) {
TEST_F(GLScalerTest, RemovesObserverWhenContextIsLost) {
NiceMock<MockContextProvider> provider;
ContextLostObserver* registered_observer = nullptr;
Sequence s;
......@@ -108,9 +115,29 @@ TEST_F(GLScalerTest, CleansUpWhenContextIsLost) {
Mock::VerifyAndClearExpectations(&provider);
}
TEST_F(GLScalerTest, StopsScalingWhenContextIsLost) {
GLScaler scaler(context_provider());
// Configure the scaler with default parameters (1:1 scale ratio).
ASSERT_TRUE(scaler.Configure(GLScaler::Parameters()));
// Call Scale() and expect it to return true to indicate the operation
// succeeded.
GLScalerTestTextureHelper helper(context_provider()->ContextGL());
constexpr gfx::Size kSomeSize = gfx::Size(32, 32);
const GLuint src_texture = helper.CreateTexture(kSomeSize);
const GLuint dest_texture = helper.CreateTexture(kSomeSize);
EXPECT_TRUE(scaler.Scale(src_texture, kSomeSize, gfx::Vector2d(),
dest_texture, gfx::Rect(kSomeSize)));
// After the context is lost, another call to Scale() should return false.
static_cast<ContextLostObserver&>(scaler).OnContextLost();
EXPECT_FALSE(scaler.Scale(src_texture, kSomeSize, gfx::Vector2d(),
dest_texture, gfx::Rect(kSomeSize)));
}
TEST_F(GLScalerTest, Configure_RequiresValidScalingVectors) {
NiceMock<MockContextProvider> provider;
GLScaler scaler(base::WrapRefCounted(&provider));
GLScaler scaler(context_provider());
GLScaler::Parameters params;
EXPECT_TRUE(scaler.Configure(params));
......@@ -123,8 +150,7 @@ TEST_F(GLScalerTest, Configure_RequiresValidScalingVectors) {
}
TEST_F(GLScalerTest, Configure_ResolvesUnspecifiedColorSpaces) {
NiceMock<MockContextProvider> provider;
GLScaler scaler(base::WrapRefCounted(&provider));
GLScaler scaler(context_provider());
// Neither source nor output space specified: Both should resolve to sRGB.
GLScaler::Parameters params;
......@@ -151,8 +177,7 @@ TEST_F(GLScalerTest, Configure_ResolvesUnspecifiedColorSpaces) {
}
TEST_F(GLScalerTest, Configure_RequiresValidSwizzles) {
NiceMock<MockContextProvider> provider;
GLScaler scaler(base::WrapRefCounted(&provider));
GLScaler scaler(context_provider());
GLScaler::Parameters params;
// Test that all valid combinations work.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment