| // Copyright 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 "cc/output/gl_renderer.h" |
| |
| #include <set> |
| |
| #include "cc/base/math_util.h" |
| #include "cc/debug/test_web_graphics_context_3d.h" |
| #include "cc/output/compositor_frame_metadata.h" |
| #include "cc/resources/prioritized_resource_manager.h" |
| #include "cc/resources/resource_provider.h" |
| #include "cc/resources/sync_point_helper.h" |
| #include "cc/test/fake_impl_proxy.h" |
| #include "cc/test/fake_layer_tree_host_impl.h" |
| #include "cc/test/fake_output_surface.h" |
| #include "cc/test/fake_output_surface_client.h" |
| #include "cc/test/mock_quad_culler.h" |
| #include "cc/test/pixel_test.h" |
| #include "cc/test/render_pass_test_common.h" |
| #include "cc/test/render_pass_test_utils.h" |
| #include "gpu/GLES2/gl2extchromium.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/khronos/GLES2/gl2.h" |
| #include "third_party/skia/include/core/SkImageFilter.h" |
| #include "third_party/skia/include/core/SkMatrix.h" |
| #include "third_party/skia/include/effects/SkColorFilterImageFilter.h" |
| #include "third_party/skia/include/effects/SkColorMatrixFilter.h" |
| #include "ui/gfx/transform.h" |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::Args; |
| using testing::AtLeast; |
| using testing::ElementsAre; |
| using testing::Expectation; |
| using testing::InSequence; |
| using testing::Mock; |
| using testing::Return; |
| using testing::StrictMock; |
| using WebKit::WebGLId; |
| using WebKit::WebString; |
| using WebKit::WGC3Dbitfield; |
| using WebKit::WGC3Dboolean; |
| using WebKit::WGC3Dchar; |
| using WebKit::WGC3Denum; |
| using WebKit::WGC3Dfloat; |
| using WebKit::WGC3Dint; |
| using WebKit::WGC3Dintptr; |
| using WebKit::WGC3Dsizei; |
| using WebKit::WGC3Dsizeiptr; |
| using WebKit::WGC3Duint; |
| |
| namespace cc { |
| |
| #define EXPECT_PROGRAM_VALID(program_binding) \ |
| do { \ |
| EXPECT_TRUE(program_binding->program()); \ |
| EXPECT_TRUE(program_binding->initialized()); \ |
| } while (false) |
| |
| // Explicitly named to be a friend in GLRenderer for shader access. |
| class GLRendererShaderPixelTest : public GLRendererPixelTest { |
| public: |
| void TestShaders() { |
| ASSERT_FALSE(renderer()->IsContextLost()); |
| EXPECT_PROGRAM_VALID(renderer()->GetTileCheckerboardProgram()); |
| EXPECT_PROGRAM_VALID(renderer()->GetDebugBorderProgram()); |
| EXPECT_PROGRAM_VALID(renderer()->GetSolidColorProgram()); |
| EXPECT_PROGRAM_VALID(renderer()->GetSolidColorProgramAA()); |
| TestShadersWithTexCoordPrecision(TexCoordPrecisionMedium); |
| TestShadersWithTexCoordPrecision(TexCoordPrecisionHigh); |
| ASSERT_FALSE(renderer()->IsContextLost()); |
| } |
| |
| void TestShadersWithTexCoordPrecision(TexCoordPrecision precision) { |
| EXPECT_PROGRAM_VALID(renderer()->GetTileProgram(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetTileProgramOpaque(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetTileProgramAA(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetTileProgramSwizzle(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetTileProgramSwizzleOpaque(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetTileProgramSwizzleAA(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetRenderPassProgram(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetRenderPassProgramAA(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgram(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgramAA(precision)); |
| EXPECT_PROGRAM_VALID( |
| renderer()->GetRenderPassColorMatrixProgram(precision)); |
| EXPECT_PROGRAM_VALID( |
| renderer()->GetRenderPassMaskColorMatrixProgramAA(precision)); |
| EXPECT_PROGRAM_VALID( |
| renderer()->GetRenderPassColorMatrixProgramAA(precision)); |
| EXPECT_PROGRAM_VALID( |
| renderer()->GetRenderPassMaskColorMatrixProgram(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetTextureProgram(precision)); |
| EXPECT_PROGRAM_VALID( |
| renderer()->GetNonPremultipliedTextureProgram(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetTextureBackgroundProgram(precision)); |
| EXPECT_PROGRAM_VALID( |
| renderer()->GetNonPremultipliedTextureBackgroundProgram(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetTextureIOSurfaceProgram(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetVideoYUVProgram(precision)); |
| EXPECT_PROGRAM_VALID(renderer()->GetVideoYUVAProgram(precision)); |
| // This is unlikely to be ever true in tests due to usage of osmesa. |
| if (renderer()->Capabilities().using_egl_image) |
| EXPECT_PROGRAM_VALID(renderer()->GetVideoStreamTextureProgram(precision)); |
| else |
| EXPECT_FALSE(renderer()->GetVideoStreamTextureProgram(precision)); |
| } |
| }; |
| |
| namespace { |
| |
| #if !defined(OS_ANDROID) |
| TEST_F(GLRendererShaderPixelTest, AllShadersCompile) { TestShaders(); } |
| #endif |
| |
| class FrameCountingContext : public TestWebGraphicsContext3D { |
| public: |
| FrameCountingContext() |
| : frame_(0) { |
| test_capabilities_.set_visibility = true; |
| test_capabilities_.discard_backbuffer = true; |
| } |
| |
| // WebGraphicsContext3D methods. |
| |
| // This method would normally do a glSwapBuffers under the hood. |
| virtual void prepareTexture() { frame_++; } |
| |
| // Methods added for test. |
| int frame_count() { return frame_; } |
| |
| private: |
| int frame_; |
| }; |
| |
| class FakeRendererClient : public RendererClient { |
| public: |
| FakeRendererClient() |
| : host_impl_(&proxy_), |
| set_full_root_layer_damage_count_(0), |
| root_layer_(LayerImpl::Create(host_impl_.active_tree(), 1)), |
| viewport_(gfx::Rect(0, 0, 1, 1)), |
| clip_(gfx::Rect(0, 0, 1, 1)) { |
| root_layer_->CreateRenderSurface(); |
| RenderPass::Id render_pass_id = |
| root_layer_->render_surface()->RenderPassId(); |
| scoped_ptr<RenderPass> root_render_pass = RenderPass::Create(); |
| root_render_pass->SetNew( |
| render_pass_id, gfx::Rect(), gfx::Rect(), gfx::Transform()); |
| render_passes_in_draw_order_.push_back(root_render_pass.Pass()); |
| } |
| |
| // RendererClient methods. |
| virtual gfx::Rect DeviceViewport() const OVERRIDE { return viewport_; } |
| virtual gfx::Rect DeviceClip() const OVERRIDE { return clip_; } |
| virtual void SetFullRootLayerDamage() OVERRIDE { |
| set_full_root_layer_damage_count_++; |
| } |
| virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE { |
| return CompositorFrameMetadata(); |
| } |
| |
| // Methods added for test. |
| int set_full_root_layer_damage_count() const { |
| return set_full_root_layer_damage_count_; |
| } |
| void set_viewport(gfx::Rect viewport) { viewport_ = viewport; } |
| void set_clip(gfx::Rect clip) { clip_ = clip; } |
| |
| RenderPass* root_render_pass() { return render_passes_in_draw_order_.back(); } |
| RenderPassList* render_passes_in_draw_order() { |
| return &render_passes_in_draw_order_; |
| } |
| |
| private: |
| FakeImplProxy proxy_; |
| FakeLayerTreeHostImpl host_impl_; |
| int set_full_root_layer_damage_count_; |
| scoped_ptr<LayerImpl> root_layer_; |
| RenderPassList render_passes_in_draw_order_; |
| gfx::Rect viewport_; |
| gfx::Rect clip_; |
| }; |
| |
| class FakeRendererGL : public GLRenderer { |
| public: |
| FakeRendererGL(RendererClient* client, |
| const LayerTreeSettings* settings, |
| OutputSurface* output_surface, |
| ResourceProvider* resource_provider) |
| : GLRenderer(client, |
| settings, |
| output_surface, |
| resource_provider, |
| NULL, |
| 0) {} |
| |
| // GLRenderer methods. |
| |
| // Changing visibility to public. |
| using GLRenderer::Initialize; |
| using GLRenderer::IsBackbufferDiscarded; |
| using GLRenderer::DoDrawQuad; |
| using GLRenderer::BeginDrawingFrame; |
| using GLRenderer::FinishDrawingQuadList; |
| using GLRenderer::stencil_enabled; |
| }; |
| |
| class GLRendererTest : public testing::Test { |
| protected: |
| GLRendererTest() { |
| scoped_ptr<FrameCountingContext> context3d(new FrameCountingContext); |
| context3d_ = context3d.get(); |
| |
| output_surface_ = FakeOutputSurface::Create3d( |
| context3d.PassAs<TestWebGraphicsContext3D>()).Pass(); |
| CHECK(output_surface_->BindToClient(&output_surface_client_)); |
| |
| resource_provider_ = |
| ResourceProvider::Create(output_surface_.get(), 0, false).Pass(); |
| renderer_ = make_scoped_ptr(new FakeRendererGL(&renderer_client_, |
| &settings_, |
| output_surface_.get(), |
| resource_provider_.get())); |
| } |
| |
| virtual void SetUp() { renderer_->Initialize(); } |
| |
| void SwapBuffers() { renderer_->SwapBuffers(); } |
| |
| LayerTreeSettings settings_; |
| FrameCountingContext* context3d_; |
| FakeOutputSurfaceClient output_surface_client_; |
| scoped_ptr<FakeOutputSurface> output_surface_; |
| FakeRendererClient renderer_client_; |
| scoped_ptr<ResourceProvider> resource_provider_; |
| scoped_ptr<FakeRendererGL> renderer_; |
| }; |
| |
| // Closing the namespace here so that GLRendererShaderTest can take advantage |
| // of the friend relationship with GLRenderer and all of the mock classes |
| // declared above it. |
| } // namespace |
| |
| |
| // Gives unique shader ids and unique program ids for tests that need them. |
| class ShaderCreatorMockGraphicsContext : public TestWebGraphicsContext3D { |
| public: |
| ShaderCreatorMockGraphicsContext() |
| : next_program_id_number_(10000), |
| next_shader_id_number_(1) {} |
| |
| bool hasShader(WebGLId shader) { |
| return shader_set_.find(shader) != shader_set_.end(); |
| } |
| |
| bool hasProgram(WebGLId program) { |
| return program_set_.find(program) != program_set_.end(); |
| } |
| |
| virtual WebGLId createProgram() { |
| unsigned program = next_program_id_number_; |
| program_set_.insert(program); |
| next_program_id_number_++; |
| return program; |
| } |
| |
| virtual void deleteProgram(WebGLId program) { |
| ASSERT_TRUE(hasProgram(program)); |
| program_set_.erase(program); |
| } |
| |
| virtual void useProgram(WebGLId program) { |
| if (!program) |
| return; |
| ASSERT_TRUE(hasProgram(program)); |
| } |
| |
| virtual WebKit::WebGLId createShader(WebKit::WGC3Denum) { |
| unsigned shader = next_shader_id_number_; |
| shader_set_.insert(shader); |
| next_shader_id_number_++; |
| return shader; |
| } |
| |
| virtual void deleteShader(WebKit::WebGLId shader) { |
| ASSERT_TRUE(hasShader(shader)); |
| shader_set_.erase(shader); |
| } |
| |
| virtual void attachShader(WebGLId program, WebGLId shader) { |
| ASSERT_TRUE(hasProgram(program)); |
| ASSERT_TRUE(hasShader(shader)); |
| } |
| |
| protected: |
| unsigned next_program_id_number_; |
| unsigned next_shader_id_number_; |
| std::set<unsigned> program_set_; |
| std::set<unsigned> shader_set_; |
| }; |
| |
| class GLRendererShaderTest : public testing::Test { |
| protected: |
| GLRendererShaderTest() { |
| output_surface_ = FakeOutputSurface::Create3d( |
| scoped_ptr<TestWebGraphicsContext3D>( |
| new ShaderCreatorMockGraphicsContext())).Pass(); |
| CHECK(output_surface_->BindToClient(&output_surface_client_)); |
| |
| resource_provider_ = ResourceProvider::Create( |
| output_surface_.get(), 0, false).Pass(); |
| renderer_.reset(new FakeRendererGL(&renderer_client_, |
| &settings_, |
| output_surface_.get(), |
| resource_provider_.get())); |
| renderer_->Initialize(); |
| } |
| |
| void TestRenderPassProgram() { |
| EXPECT_PROGRAM_VALID(renderer_->render_pass_program_); |
| EXPECT_EQ(renderer_->render_pass_program_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| void TestRenderPassColorMatrixProgram() { |
| EXPECT_PROGRAM_VALID(renderer_->render_pass_color_matrix_program_); |
| EXPECT_EQ(renderer_->render_pass_color_matrix_program_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| void TestRenderPassMaskProgram() { |
| EXPECT_PROGRAM_VALID(renderer_->render_pass_mask_program_); |
| EXPECT_EQ(renderer_->render_pass_mask_program_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| void TestRenderPassMaskColorMatrixProgram() { |
| EXPECT_PROGRAM_VALID(renderer_->render_pass_mask_color_matrix_program_); |
| EXPECT_EQ(renderer_->render_pass_mask_color_matrix_program_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| void TestRenderPassProgramAA() { |
| EXPECT_PROGRAM_VALID(renderer_->render_pass_program_aa_); |
| EXPECT_EQ(renderer_->render_pass_program_aa_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| void TestRenderPassColorMatrixProgramAA() { |
| EXPECT_PROGRAM_VALID(renderer_->render_pass_color_matrix_program_aa_); |
| EXPECT_EQ(renderer_->render_pass_color_matrix_program_aa_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| void TestRenderPassMaskProgramAA() { |
| EXPECT_PROGRAM_VALID(renderer_->render_pass_mask_program_aa_); |
| EXPECT_EQ(renderer_->render_pass_mask_program_aa_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| void TestRenderPassMaskColorMatrixProgramAA() { |
| EXPECT_PROGRAM_VALID(renderer_->render_pass_mask_color_matrix_program_aa_); |
| EXPECT_EQ(renderer_->render_pass_mask_color_matrix_program_aa_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| void TestSolidColorProgramAA() { |
| EXPECT_PROGRAM_VALID(renderer_->solid_color_program_aa_); |
| EXPECT_EQ(renderer_->solid_color_program_aa_->program(), |
| renderer_->program_shadow_); |
| } |
| |
| LayerTreeSettings settings_; |
| FakeOutputSurfaceClient output_surface_client_; |
| scoped_ptr<FakeOutputSurface> output_surface_; |
| FakeRendererClient renderer_client_; |
| scoped_ptr<ResourceProvider> resource_provider_; |
| scoped_ptr<FakeRendererGL> renderer_; |
| }; |
| |
| namespace { |
| |
| // Test GLRenderer discardBackbuffer functionality: |
| // Suggest recreating framebuffer when one already exists. |
| // Expected: it does nothing. |
| TEST_F(GLRendererTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing) { |
| renderer_->SetDiscardBackBufferWhenNotVisible(false); |
| EXPECT_EQ(0, renderer_client_.set_full_root_layer_damage_count()); |
| EXPECT_FALSE(renderer_->IsBackbufferDiscarded()); |
| |
| SwapBuffers(); |
| EXPECT_EQ(1, context3d_->frame_count()); |
| } |
| |
| // Test GLRenderer DiscardBackbuffer functionality: |
| // Suggest discarding framebuffer when one exists and the renderer is not |
| // visible. |
| // Expected: it is discarded and damage tracker is reset. |
| TEST_F( |
| GLRendererTest, |
| SuggestBackbufferNoShouldDiscardBackbufferAndDamageRootLayerIfNotVisible) { |
| renderer_->SetVisible(false); |
| renderer_->SetDiscardBackBufferWhenNotVisible(true); |
| EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); |
| EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); |
| } |
| |
| // Test GLRenderer DiscardBackbuffer functionality: |
| // Suggest discarding framebuffer when one exists and the renderer is visible. |
| // Expected: the allocation is ignored. |
| TEST_F(GLRendererTest, SuggestBackbufferNoDoNothingWhenVisible) { |
| renderer_->SetVisible(true); |
| renderer_->SetDiscardBackBufferWhenNotVisible(true); |
| EXPECT_EQ(0, renderer_client_.set_full_root_layer_damage_count()); |
| EXPECT_FALSE(renderer_->IsBackbufferDiscarded()); |
| } |
| |
| // Test GLRenderer DiscardBackbuffer functionality: |
| // Suggest discarding framebuffer when one does not exist. |
| // Expected: it does nothing. |
| TEST_F(GLRendererTest, SuggestBackbufferNoWhenItDoesntExistShouldDoNothing) { |
| renderer_->SetVisible(false); |
| renderer_->SetDiscardBackBufferWhenNotVisible(true); |
| EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); |
| EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); |
| |
| renderer_->SetDiscardBackBufferWhenNotVisible(true); |
| EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); |
| EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); |
| } |
| |
| // Test GLRenderer DiscardBackbuffer functionality: |
| // Begin drawing a frame while a framebuffer is discarded. |
| // Expected: will recreate framebuffer. |
| TEST_F(GLRendererTest, DiscardedBackbufferIsRecreatedForScopeDuration) { |
| renderer_->SetVisible(false); |
| renderer_->SetDiscardBackBufferWhenNotVisible(true); |
| EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); |
| EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); |
| |
| renderer_->SetVisible(true); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_FALSE(renderer_->IsBackbufferDiscarded()); |
| |
| SwapBuffers(); |
| EXPECT_EQ(1, context3d_->frame_count()); |
| } |
| |
| TEST_F(GLRendererTest, FramebufferDiscardedAfterReadbackWhenNotVisible) { |
| renderer_->SetVisible(false); |
| renderer_->SetDiscardBackBufferWhenNotVisible(true); |
| EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); |
| EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); |
| |
| char pixels[4]; |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_FALSE(renderer_->IsBackbufferDiscarded()); |
| |
| renderer_->GetFramebufferPixels(pixels, gfx::Rect(0, 0, 1, 1)); |
| EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); |
| EXPECT_EQ(2, renderer_client_.set_full_root_layer_damage_count()); |
| } |
| |
| TEST_F(GLRendererTest, ExternalStencil) { |
| EXPECT_FALSE(renderer_->stencil_enabled()); |
| |
| output_surface_->set_has_external_stencil_test(true); |
| renderer_client_.root_render_pass()->has_transparent_background = false; |
| |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_TRUE(renderer_->stencil_enabled()); |
| } |
| |
| class ForbidSynchronousCallContext : public TestWebGraphicsContext3D { |
| public: |
| ForbidSynchronousCallContext() {} |
| |
| virtual bool getActiveAttrib(WebGLId program, |
| WGC3Duint index, |
| ActiveInfo& info) { |
| ADD_FAILURE(); |
| return false; |
| } |
| virtual bool getActiveUniform(WebGLId program, |
| WGC3Duint index, |
| ActiveInfo& info) { |
| ADD_FAILURE(); |
| return false; |
| } |
| virtual void getAttachedShaders(WebGLId program, |
| WGC3Dsizei max_count, |
| WGC3Dsizei* count, |
| WebGLId* shaders) { |
| ADD_FAILURE(); |
| } |
| virtual WGC3Dint getAttribLocation(WebGLId program, const WGC3Dchar* name) { |
| ADD_FAILURE(); |
| return 0; |
| } |
| virtual void getBooleanv(WGC3Denum pname, WGC3Dboolean* value) { |
| ADD_FAILURE(); |
| } |
| virtual void getBufferParameteriv(WGC3Denum target, |
| WGC3Denum pname, |
| WGC3Dint* value) { |
| ADD_FAILURE(); |
| } |
| virtual Attributes getContextAttributes() { |
| ADD_FAILURE(); |
| return attributes_; |
| } |
| virtual WGC3Denum getError() { |
| ADD_FAILURE(); |
| return 0; |
| } |
| virtual void getFloatv(WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); } |
| virtual void getFramebufferAttachmentParameteriv(WGC3Denum target, |
| WGC3Denum attachment, |
| WGC3Denum pname, |
| WGC3Dint* value) { |
| ADD_FAILURE(); |
| } |
| virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value) { |
| if (pname == GL_MAX_TEXTURE_SIZE) { |
| // MAX_TEXTURE_SIZE is cached client side, so it's OK to query. |
| *value = 1024; |
| } else { |
| ADD_FAILURE(); |
| } |
| } |
| |
| // We allow querying the shader compilation and program link status in debug |
| // mode, but not release. |
| virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value) { |
| #ifndef NDEBUG |
| *value = 1; |
| #else |
| ADD_FAILURE(); |
| #endif |
| } |
| |
| virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) { |
| #ifndef NDEBUG |
| *value = 1; |
| #else |
| ADD_FAILURE(); |
| #endif |
| } |
| |
| virtual WebString getString(WGC3Denum name) { |
| ADD_FAILURE() << name; |
| return WebString(); |
| } |
| |
| virtual WebString getProgramInfoLog(WebGLId program) { |
| ADD_FAILURE(); |
| return WebString(); |
| } |
| virtual void getRenderbufferParameteriv(WGC3Denum target, |
| WGC3Denum pname, |
| WGC3Dint* value) { |
| ADD_FAILURE(); |
| } |
| |
| virtual WebString getShaderInfoLog(WebGLId shader) { |
| ADD_FAILURE(); |
| return WebString(); |
| } |
| virtual void getShaderPrecisionFormat(WGC3Denum shadertype, |
| WGC3Denum precisiontype, |
| WGC3Dint* range, |
| WGC3Dint* precision) { |
| ADD_FAILURE(); |
| } |
| virtual WebString getShaderSource(WebGLId shader) { |
| ADD_FAILURE(); |
| return WebString(); |
| } |
| virtual void getTexParameterfv(WGC3Denum target, |
| WGC3Denum pname, |
| WGC3Dfloat* value) { |
| ADD_FAILURE(); |
| } |
| virtual void getTexParameteriv(WGC3Denum target, |
| WGC3Denum pname, |
| WGC3Dint* value) { |
| ADD_FAILURE(); |
| } |
| virtual void getUniformfv(WebGLId program, |
| WGC3Dint location, |
| WGC3Dfloat* value) { |
| ADD_FAILURE(); |
| } |
| virtual void getUniformiv(WebGLId program, |
| WGC3Dint location, |
| WGC3Dint* value) { |
| ADD_FAILURE(); |
| } |
| virtual WGC3Dint getUniformLocation(WebGLId program, const WGC3Dchar* name) { |
| ADD_FAILURE(); |
| return 0; |
| } |
| virtual void getVertexAttribfv(WGC3Duint index, |
| WGC3Denum pname, |
| WGC3Dfloat* value) { |
| ADD_FAILURE(); |
| } |
| virtual void getVertexAttribiv(WGC3Duint index, |
| WGC3Denum pname, |
| WGC3Dint* value) { |
| ADD_FAILURE(); |
| } |
| virtual WGC3Dsizeiptr getVertexAttribOffset(WGC3Duint index, |
| WGC3Denum pname) { |
| ADD_FAILURE(); |
| return 0; |
| } |
| }; |
| |
| // This test isn't using the same fixture as GLRendererTest, and you can't mix |
| // TEST() and TEST_F() with the same name, Hence LRC2. |
| TEST(GLRendererTest2, InitializationDoesNotMakeSynchronousCalls) { |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( |
| scoped_ptr<TestWebGraphicsContext3D>(new ForbidSynchronousCallContext))); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| |
| EXPECT_TRUE(renderer.Initialize()); |
| } |
| |
| class LoseContextOnFirstGetContext : public TestWebGraphicsContext3D { |
| public: |
| LoseContextOnFirstGetContext() : context_lost_(false) {} |
| |
| virtual bool makeContextCurrent() OVERRIDE { return !context_lost_; } |
| |
| virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value) |
| OVERRIDE { |
| context_lost_ = true; |
| *value = 0; |
| } |
| |
| virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) |
| OVERRIDE { |
| context_lost_ = true; |
| *value = 0; |
| } |
| |
| virtual WGC3Denum getGraphicsResetStatusARB() OVERRIDE { |
| return context_lost_ ? 1 : 0; |
| } |
| |
| private: |
| bool context_lost_; |
| }; |
| |
| TEST(GLRendererTest2, InitializationWithQuicklyLostContextDoesNotAssert) { |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( |
| scoped_ptr<TestWebGraphicsContext3D>(new LoseContextOnFirstGetContext))); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| |
| renderer.Initialize(); |
| } |
| |
| class ClearCountingContext : public TestWebGraphicsContext3D { |
| public: |
| ClearCountingContext() { |
| test_capabilities_.discard_framebuffer = true; |
| } |
| |
| MOCK_METHOD3(discardFramebufferEXT, |
| void(WGC3Denum target, |
| WGC3Dsizei numAttachments, |
| const WGC3Denum* attachments)); |
| MOCK_METHOD1(clear, void(WGC3Dbitfield mask)); |
| }; |
| |
| TEST(GLRendererTest2, OpaqueBackground) { |
| scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext); |
| ClearCountingContext* context = context_owned.get(); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( |
| context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| |
| renderer_client.root_render_pass()->has_transparent_background = false; |
| |
| EXPECT_TRUE(renderer.Initialize()); |
| |
| // On DEBUG builds, render passes with opaque background clear to blue to |
| // easily see regions that were not drawn on the screen. |
| EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, _, _)) |
| .With(Args<2, 1>(ElementsAre(GL_COLOR_EXT))) |
| .Times(1); |
| #ifdef NDEBUG |
| EXPECT_CALL(*context, clear(_)).Times(0); |
| #else |
| EXPECT_CALL(*context, clear(_)).Times(1); |
| #endif |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| Mock::VerifyAndClearExpectations(context); |
| } |
| |
| TEST(GLRendererTest2, TransparentBackground) { |
| scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext); |
| ClearCountingContext* context = context_owned.get(); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( |
| context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| |
| renderer_client.root_render_pass()->has_transparent_background = true; |
| |
| EXPECT_TRUE(renderer.Initialize()); |
| |
| EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, 1, _)) |
| .Times(1); |
| EXPECT_CALL(*context, clear(_)).Times(1); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| |
| Mock::VerifyAndClearExpectations(context); |
| } |
| |
| TEST(GLRendererTest2, OffscreenOutputSurface) { |
| scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext); |
| ClearCountingContext* context = context_owned.get(); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::CreateOffscreen( |
| context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| |
| EXPECT_TRUE(renderer.Initialize()); |
| |
| EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, _, _)) |
| .With(Args<2, 1>(ElementsAre(GL_COLOR_ATTACHMENT0))) |
| .Times(1); |
| EXPECT_CALL(*context, clear(_)).Times(AnyNumber()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| Mock::VerifyAndClearExpectations(context); |
| } |
| |
| class VisibilityChangeIsLastCallTrackingContext |
| : public TestWebGraphicsContext3D { |
| public: |
| VisibilityChangeIsLastCallTrackingContext() |
| : last_call_was_set_visibility_(false) { |
| test_capabilities_.set_visibility = true; |
| test_capabilities_.discard_backbuffer = true; |
| } |
| |
| // WebGraphicsContext3D methods. |
| virtual void setVisibilityCHROMIUM(bool visible) { |
| DCHECK(last_call_was_set_visibility_ == false); |
| last_call_was_set_visibility_ = true; |
| } |
| virtual void flush() { |
| last_call_was_set_visibility_ = false; |
| } |
| virtual void deleteTexture(WebGLId) { |
| last_call_was_set_visibility_ = false; |
| } |
| virtual void deleteFramebuffer(WebGLId) { |
| last_call_was_set_visibility_ = false; |
| } |
| virtual void deleteQueryEXT(WebGLId) { |
| last_call_was_set_visibility_ = false; |
| } |
| virtual void deleteRenderbuffer(WebGLId) { |
| last_call_was_set_visibility_ = false; |
| } |
| virtual void discardBackbufferCHROMIUM() { |
| last_call_was_set_visibility_ = false; |
| } |
| virtual void ensureBackbufferCHROMIUM() { |
| last_call_was_set_visibility_ = false; |
| } |
| |
| // Methods added for test. |
| bool last_call_was_set_visibility() const { |
| return last_call_was_set_visibility_; |
| } |
| |
| private: |
| bool last_call_was_set_visibility_; |
| }; |
| |
| TEST(GLRendererTest2, VisibilityChangeIsLastCall) { |
| scoped_ptr<VisibilityChangeIsLastCallTrackingContext> context_owned( |
| new VisibilityChangeIsLastCallTrackingContext); |
| VisibilityChangeIsLastCallTrackingContext* context = context_owned.get(); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( |
| context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| |
| EXPECT_TRUE(renderer.Initialize()); |
| |
| // Ensure that the call to setVisibilityCHROMIUM is the last call issue to the |
| // GPU process, after glFlush is called, and after the RendererClient's |
| // SetManagedMemoryPolicy is called. Plumb this tracking between both the |
| // RenderClient and the Context by giving them both a pointer to a variable on |
| // the stack. |
| renderer.SetVisible(true); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| renderer.SetVisible(false); |
| EXPECT_TRUE(context->last_call_was_set_visibility()); |
| } |
| |
| class TextureStateTrackingContext : public TestWebGraphicsContext3D { |
| public: |
| TextureStateTrackingContext() |
| : active_texture_(GL_INVALID_ENUM) { |
| test_capabilities_.egl_image_external = true; |
| } |
| |
| MOCK_METHOD3(texParameteri, |
| void(WGC3Denum target, WGC3Denum pname, WGC3Dint param)); |
| MOCK_METHOD4(drawElements, |
| void(WGC3Denum mode, |
| WGC3Dsizei count, |
| WGC3Denum type, |
| WGC3Dintptr offset)); |
| |
| virtual void activeTexture(WGC3Denum texture) { |
| EXPECT_NE(texture, active_texture_); |
| active_texture_ = texture; |
| } |
| |
| WGC3Denum active_texture() const { return active_texture_; } |
| |
| private: |
| WGC3Denum active_texture_; |
| }; |
| |
| TEST(GLRendererTest2, ActiveTextureState) { |
| scoped_ptr<TextureStateTrackingContext> context_owned( |
| new TextureStateTrackingContext); |
| TextureStateTrackingContext* context = context_owned.get(); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( |
| context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| |
| // During initialization we are allowed to set any texture parameters. |
| EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber()); |
| EXPECT_TRUE(renderer.Initialize()); |
| |
| cc::RenderPass::Id id(1, 1); |
| scoped_ptr<TestRenderPass> pass = TestRenderPass::Create(); |
| pass->SetNew(id, |
| gfx::Rect(0, 0, 100, 100), |
| gfx::Rect(0, 0, 100, 100), |
| gfx::Transform()); |
| pass->AppendOneOfEveryQuadType(resource_provider.get(), RenderPass::Id(2, 1)); |
| |
| // Set up expected texture filter state transitions that match the quads |
| // created in AppendOneOfEveryQuadType(). |
| Mock::VerifyAndClearExpectations(context); |
| { |
| InSequence sequence; |
| |
| // yuv_quad is drawn with the default linear filter. |
| EXPECT_CALL(*context, drawElements(_, _, _, _)); |
| |
| // tile_quad is drawn with GL_NEAREST because it is not transformed or |
| // scaled. |
| EXPECT_CALL( |
| *context, |
| texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); |
| EXPECT_CALL( |
| *context, |
| texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); |
| EXPECT_CALL(*context, drawElements(_, _, _, _)); |
| |
| // transformed_tile_quad uses GL_LINEAR. |
| EXPECT_CALL(*context, drawElements(_, _, _, _)); |
| |
| // scaled_tile_quad also uses GL_LINEAR. |
| EXPECT_CALL(*context, drawElements(_, _, _, _)); |
| |
| // The remaining quads also use GL_LINEAR because nearest neighbor |
| // filtering is currently only used with tile quads. |
| EXPECT_CALL(*context, drawElements(_, _, _, _)).Times(6); |
| } |
| |
| cc::DirectRenderer::DrawingFrame drawing_frame; |
| renderer.BeginDrawingFrame(&drawing_frame); |
| EXPECT_EQ(static_cast<unsigned>(GL_TEXTURE0), context->active_texture()); |
| |
| for (cc::QuadList::BackToFrontIterator |
| it = pass->quad_list.BackToFrontBegin(); |
| it != pass->quad_list.BackToFrontEnd(); |
| ++it) { |
| renderer.DoDrawQuad(&drawing_frame, *it); |
| } |
| renderer.FinishDrawingQuadList(); |
| EXPECT_EQ(static_cast<unsigned>(GL_TEXTURE0), context->active_texture()); |
| Mock::VerifyAndClearExpectations(context); |
| } |
| |
| class NoClearRootRenderPassMockContext : public TestWebGraphicsContext3D { |
| public: |
| MOCK_METHOD1(clear, void(WGC3Dbitfield mask)); |
| MOCK_METHOD4(drawElements, |
| void(WGC3Denum mode, |
| WGC3Dsizei count, |
| WGC3Denum type, |
| WGC3Dintptr offset)); |
| }; |
| |
| TEST(GLRendererTest2, ShouldClearRootRenderPass) { |
| scoped_ptr<NoClearRootRenderPassMockContext> mock_context_owned( |
| new NoClearRootRenderPassMockContext); |
| NoClearRootRenderPassMockContext* mock_context = mock_context_owned.get(); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( |
| mock_context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| settings.should_clear_root_render_pass = false; |
| |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| EXPECT_TRUE(renderer.Initialize()); |
| |
| gfx::Rect viewport_rect(renderer_client.DeviceViewport()); |
| ScopedPtrVector<RenderPass>& render_passes = |
| *renderer_client.render_passes_in_draw_order(); |
| render_passes.clear(); |
| |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| |
| RenderPass::Id child_pass_id(2, 0); |
| TestRenderPass* child_pass = AddRenderPass( |
| &render_passes, child_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(child_pass, viewport_rect, SK_ColorBLUE); |
| |
| AddRenderPassQuad(root_pass, child_pass); |
| |
| #ifdef NDEBUG |
| GLint clear_bits = GL_COLOR_BUFFER_BIT; |
| #else |
| GLint clear_bits = GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; |
| #endif |
| |
| // First render pass is not the root one, clearing should happen. |
| EXPECT_CALL(*mock_context, clear(clear_bits)).Times(AtLeast(1)); |
| |
| Expectation first_render_pass = |
| EXPECT_CALL(*mock_context, drawElements(_, _, _, _)).Times(1); |
| |
| // The second render pass is the root one, clearing should be prevented. |
| EXPECT_CALL(*mock_context, clear(clear_bits)).Times(0) |
| .After(first_render_pass); |
| |
| EXPECT_CALL(*mock_context, drawElements(_, _, _, _)).Times(AnyNumber()) |
| .After(first_render_pass); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| |
| // In multiple render passes all but the root pass should clear the |
| // framebuffer. |
| Mock::VerifyAndClearExpectations(&mock_context); |
| } |
| |
| class ScissorTestOnClearCheckingContext : public TestWebGraphicsContext3D { |
| public: |
| ScissorTestOnClearCheckingContext() : scissor_enabled_(false) {} |
| |
| virtual void clear(WGC3Dbitfield) { EXPECT_FALSE(scissor_enabled_); } |
| |
| virtual void enable(WGC3Denum cap) { |
| if (cap == GL_SCISSOR_TEST) |
| scissor_enabled_ = true; |
| } |
| |
| virtual void disable(WGC3Denum cap) { |
| if (cap == GL_SCISSOR_TEST) |
| scissor_enabled_ = false; |
| } |
| |
| private: |
| bool scissor_enabled_; |
| }; |
| |
| TEST(GLRendererTest2, ScissorTestWhenClearing) { |
| scoped_ptr<ScissorTestOnClearCheckingContext> context_owned( |
| new ScissorTestOnClearCheckingContext); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( |
| context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| EXPECT_TRUE(renderer.Initialize()); |
| EXPECT_FALSE(renderer.Capabilities().using_partial_swap); |
| |
| gfx::Rect viewport_rect(renderer_client.DeviceViewport()); |
| ScopedPtrVector<RenderPass>& render_passes = |
| *renderer_client.render_passes_in_draw_order(); |
| render_passes.clear(); |
| |
| gfx::Rect grand_child_rect(25, 25); |
| RenderPass::Id grand_child_pass_id(3, 0); |
| TestRenderPass* grand_child_pass = AddRenderPass( |
| &render_passes, grand_child_pass_id, grand_child_rect, gfx::Transform()); |
| AddClippedQuad(grand_child_pass, grand_child_rect, SK_ColorYELLOW); |
| |
| gfx::Rect child_rect(50, 50); |
| RenderPass::Id child_pass_id(2, 0); |
| TestRenderPass* child_pass = AddRenderPass( |
| &render_passes, child_pass_id, child_rect, gfx::Transform()); |
| AddQuad(child_pass, child_rect, SK_ColorBLUE); |
| |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| |
| AddRenderPassQuad(root_pass, child_pass); |
| AddRenderPassQuad(child_pass, grand_child_pass); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| } |
| |
| class DiscardCheckingContext : public TestWebGraphicsContext3D { |
| public: |
| DiscardCheckingContext() : discarded_(0) { |
| set_have_post_sub_buffer(true); |
| set_have_discard_framebuffer(true); |
| } |
| |
| virtual void discardFramebufferEXT(WGC3Denum target, |
| WGC3Dsizei numAttachments, |
| const WGC3Denum* attachments) { |
| ++discarded_; |
| } |
| |
| int discarded() const { return discarded_; } |
| void reset() { discarded_ = 0; } |
| |
| private: |
| int discarded_; |
| }; |
| |
| class NonReshapableOutputSurface : public FakeOutputSurface { |
| public: |
| explicit NonReshapableOutputSurface( |
| scoped_ptr<TestWebGraphicsContext3D> context3d) |
| : FakeOutputSurface(TestContextProvider::Create(context3d.Pass()), |
| false) { |
| surface_size_ = gfx::Size(500, 500); |
| } |
| virtual void Reshape(gfx::Size size, float scale_factor) OVERRIDE {} |
| void set_fixed_size(gfx::Size size) { surface_size_ = size; } |
| }; |
| |
| TEST(GLRendererTest2, NoDiscardOnPartialUpdates) { |
| scoped_ptr<DiscardCheckingContext> context_owned(new DiscardCheckingContext); |
| DiscardCheckingContext* context = context_owned.get(); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<NonReshapableOutputSurface> output_surface( |
| new NonReshapableOutputSurface( |
| context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| output_surface->set_fixed_size(gfx::Size(100, 100)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| settings.partial_swap_enabled = true; |
| FakeRendererClient renderer_client; |
| renderer_client.set_viewport(gfx::Rect(0, 0, 100, 100)); |
| renderer_client.set_clip(gfx::Rect(0, 0, 100, 100)); |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| EXPECT_TRUE(renderer.Initialize()); |
| EXPECT_TRUE(renderer.Capabilities().using_partial_swap); |
| |
| gfx::Rect viewport_rect(renderer_client.DeviceViewport()); |
| ScopedPtrVector<RenderPass>& render_passes = |
| *renderer_client.render_passes_in_draw_order(); |
| render_passes.clear(); |
| |
| { |
| // Partial frame, should not discard. |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| root_pass->damage_rect = gfx::RectF(2.f, 2.f, 3.f, 3.f); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_EQ(0, context->discarded()); |
| context->reset(); |
| } |
| { |
| // Full frame, should discard. |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| root_pass->damage_rect = gfx::RectF(root_pass->output_rect); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_EQ(1, context->discarded()); |
| context->reset(); |
| } |
| { |
| // Partial frame, disallow partial swap, should discard. |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| root_pass->damage_rect = gfx::RectF(2.f, 2.f, 3.f, 3.f); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, false); |
| EXPECT_EQ(1, context->discarded()); |
| context->reset(); |
| } |
| { |
| // Full frame, external scissor is set, should not discard. |
| output_surface->set_has_external_stencil_test(true); |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| root_pass->damage_rect = gfx::RectF(root_pass->output_rect); |
| root_pass->has_transparent_background = false; |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_EQ(0, context->discarded()); |
| context->reset(); |
| output_surface->set_has_external_stencil_test(false); |
| } |
| { |
| // Full frame, clipped, should not discard. |
| renderer_client.set_clip(gfx::Rect(10, 10, 10, 10)); |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| root_pass->damage_rect = gfx::RectF(root_pass->output_rect); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_EQ(0, context->discarded()); |
| context->reset(); |
| } |
| { |
| // Full frame, doesn't cover the surface, should not discard. |
| renderer_client.set_viewport(gfx::Rect(10, 10, 10, 10)); |
| viewport_rect = renderer_client.DeviceViewport(); |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| root_pass->damage_rect = gfx::RectF(root_pass->output_rect); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_EQ(0, context->discarded()); |
| context->reset(); |
| } |
| { |
| // Full frame, doesn't cover the surface (no offset), should not discard. |
| renderer_client.set_viewport(gfx::Rect(0, 0, 50, 50)); |
| renderer_client.set_clip(gfx::Rect(0, 0, 100, 100)); |
| viewport_rect = renderer_client.DeviceViewport(); |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(root_pass, viewport_rect, SK_ColorGREEN); |
| root_pass->damage_rect = gfx::RectF(root_pass->output_rect); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| EXPECT_EQ(0, context->discarded()); |
| context->reset(); |
| } |
| } |
| |
| class FlippedScissorAndViewportContext : public TestWebGraphicsContext3D { |
| public: |
| FlippedScissorAndViewportContext() |
| : did_call_viewport_(false), did_call_scissor_(false) {} |
| virtual ~FlippedScissorAndViewportContext() { |
| EXPECT_TRUE(did_call_viewport_); |
| EXPECT_TRUE(did_call_scissor_); |
| } |
| |
| virtual void viewport(GLint x, GLint y, GLsizei width, GLsizei height) { |
| EXPECT_EQ(10, x); |
| EXPECT_EQ(390, y); |
| EXPECT_EQ(100, width); |
| EXPECT_EQ(100, height); |
| did_call_viewport_ = true; |
| } |
| |
| virtual void scissor(GLint x, GLint y, GLsizei width, GLsizei height) { |
| EXPECT_EQ(30, x); |
| EXPECT_EQ(450, y); |
| EXPECT_EQ(20, width); |
| EXPECT_EQ(20, height); |
| did_call_scissor_ = true; |
| } |
| |
| private: |
| bool did_call_viewport_; |
| bool did_call_scissor_; |
| }; |
| |
| TEST(GLRendererTest2, ScissorAndViewportWithinNonreshapableSurface) { |
| // In Android WebView, the OutputSurface is unable to respect reshape() calls |
| // and maintains a fixed size. This test verifies that glViewport and |
| // glScissor's Y coordinate is flipped correctly in this environment, and that |
| // the glViewport can be at a nonzero origin within the surface. |
| scoped_ptr<FlippedScissorAndViewportContext> context_owned( |
| new FlippedScissorAndViewportContext); |
| |
| FakeOutputSurfaceClient output_surface_client; |
| scoped_ptr<OutputSurface> output_surface(new NonReshapableOutputSurface( |
| context_owned.PassAs<TestWebGraphicsContext3D>())); |
| CHECK(output_surface->BindToClient(&output_surface_client)); |
| |
| scoped_ptr<ResourceProvider> resource_provider( |
| ResourceProvider::Create(output_surface.get(), 0, false)); |
| |
| LayerTreeSettings settings; |
| FakeRendererClient renderer_client; |
| renderer_client.set_viewport(gfx::Rect(10, 10, 100, 100)); |
| renderer_client.set_clip(gfx::Rect(10, 10, 100, 100)); |
| FakeRendererGL renderer(&renderer_client, |
| &settings, |
| output_surface.get(), |
| resource_provider.get()); |
| EXPECT_TRUE(renderer.Initialize()); |
| EXPECT_FALSE(renderer.Capabilities().using_partial_swap); |
| |
| gfx::Rect viewport_rect(renderer_client.DeviceViewport().size()); |
| gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20); |
| ScopedPtrVector<RenderPass>& render_passes = |
| *renderer_client.render_passes_in_draw_order(); |
| render_passes.clear(); |
| |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass = AddRenderPass( |
| &render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN); |
| |
| renderer.DecideRenderPassAllocationsForFrame( |
| *renderer_client.render_passes_in_draw_order()); |
| renderer.DrawFrame( |
| renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); |
| } |
| |
| TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { |
| gfx::Rect viewport_rect(renderer_client_.DeviceViewport()); |
| ScopedPtrVector<RenderPass>* render_passes = |
| renderer_client_.render_passes_in_draw_order(); |
| |
| gfx::Rect child_rect(50, 50); |
| RenderPass::Id child_pass_id(2, 0); |
| TestRenderPass* child_pass; |
| |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass; |
| |
| cc::ResourceProvider::ResourceId mask = |
| resource_provider_->CreateResource(gfx::Size(20, 12), |
| GL_CLAMP_TO_EDGE, |
| ResourceProvider::TextureUsageAny, |
| resource_provider_->best_texture_format()); |
| resource_provider_->AllocateForTesting(mask); |
| |
| SkScalar matrix[20]; |
| float amount = 0.5f; |
| matrix[0] = 0.213f + 0.787f * amount; |
| matrix[1] = 0.715f - 0.715f * amount; |
| matrix[2] = 1.f - (matrix[0] + matrix[1]); |
| matrix[3] = matrix[4] = 0; |
| matrix[5] = 0.213f - 0.213f * amount; |
| matrix[6] = 0.715f + 0.285f * amount; |
| matrix[7] = 1.f - (matrix[5] + matrix[6]); |
| matrix[8] = matrix[9] = 0; |
| matrix[10] = 0.213f - 0.213f * amount; |
| matrix[11] = 0.715f - 0.715f * amount; |
| matrix[12] = 1.f - (matrix[10] + matrix[11]); |
| matrix[13] = matrix[14] = 0; |
| matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0; |
| matrix[18] = 1; |
| skia::RefPtr<SkColorFilter> color_filter( |
| skia::AdoptRef(new SkColorMatrixFilter(matrix))); |
| skia::RefPtr<SkImageFilter> filter = skia::AdoptRef( |
| SkColorFilterImageFilter::Create(color_filter.get(), NULL)); |
| FilterOperations filters; |
| filters.Append(FilterOperation::CreateReferenceFilter(filter)); |
| |
| gfx::Transform transform_causing_aa; |
| transform_causing_aa.Rotate(20.0); |
| |
| // RenderPassProgram |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass( |
| render_passes, child_pass_id, child_rect, gfx::Transform()); |
| |
| root_pass = AddRenderPass( |
| render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| |
| AddRenderPassQuad(root_pass, |
| child_pass, |
| 0, |
| FilterOperations(), |
| gfx::Transform()); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| TestRenderPassProgram(); |
| |
| // RenderPassColorMatrixProgram |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass( |
| render_passes, child_pass_id, child_rect, transform_causing_aa); |
| |
| root_pass = AddRenderPass( |
| render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| |
| AddRenderPassQuad(root_pass, child_pass, 0, filters, gfx::Transform()); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| TestRenderPassColorMatrixProgram(); |
| |
| // RenderPassMaskProgram |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass( |
| render_passes, child_pass_id, child_rect, gfx::Transform()); |
| |
| root_pass = AddRenderPass( |
| render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| |
| AddRenderPassQuad(root_pass, |
| child_pass, |
| mask, |
| FilterOperations(), |
| gfx::Transform()); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| TestRenderPassMaskProgram(); |
| |
| // RenderPassMaskColorMatrixProgram |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass( |
| render_passes, child_pass_id, child_rect, gfx::Transform()); |
| |
| root_pass = AddRenderPass( |
| render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| |
| AddRenderPassQuad(root_pass, child_pass, mask, filters, gfx::Transform()); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| TestRenderPassMaskColorMatrixProgram(); |
| |
| // RenderPassProgramAA |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass( |
| render_passes, child_pass_id, child_rect, transform_causing_aa); |
| |
| root_pass = AddRenderPass( |
| render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| |
| AddRenderPassQuad(root_pass, |
| child_pass, |
| 0, |
| FilterOperations(), |
| transform_causing_aa); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| TestRenderPassProgramAA(); |
| |
| // RenderPassColorMatrixProgramAA |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass( |
| render_passes, child_pass_id, child_rect, transform_causing_aa); |
| |
| root_pass = AddRenderPass( |
| render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| |
| AddRenderPassQuad(root_pass, child_pass, 0, filters, transform_causing_aa); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| TestRenderPassColorMatrixProgramAA(); |
| |
| // RenderPassMaskProgramAA |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass(render_passes, child_pass_id, child_rect, |
| transform_causing_aa); |
| |
| root_pass = AddRenderPass(render_passes, root_pass_id, viewport_rect, |
| gfx::Transform()); |
| |
| AddRenderPassQuad(root_pass, child_pass, mask, FilterOperations(), |
| transform_causing_aa); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| TestRenderPassMaskProgramAA(); |
| |
| // RenderPassMaskColorMatrixProgramAA |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass(render_passes, child_pass_id, child_rect, |
| transform_causing_aa); |
| |
| root_pass = AddRenderPass(render_passes, root_pass_id, viewport_rect, |
| transform_causing_aa); |
| |
| AddRenderPassQuad(root_pass, child_pass, mask, filters, transform_causing_aa); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| TestRenderPassMaskColorMatrixProgramAA(); |
| } |
| |
| // At this time, the AA code path cannot be taken if the surface's rect would |
| // project incorrectly by the given transform, because of w<0 clipping. |
| TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) { |
| gfx::Rect child_rect(50, 50); |
| RenderPass::Id child_pass_id(2, 0); |
| TestRenderPass* child_pass; |
| |
| gfx::Rect viewport_rect(renderer_client_.DeviceViewport()); |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass; |
| |
| gfx::Transform transform_preventing_aa; |
| transform_preventing_aa.ApplyPerspectiveDepth(40.0); |
| transform_preventing_aa.RotateAboutYAxis(-20.0); |
| transform_preventing_aa.Scale(30.0, 1.0); |
| |
| // Verify that the test transform and test rect actually do cause the clipped |
| // flag to trigger. Otherwise we are not testing the intended scenario. |
| bool clipped = false; |
| MathUtil::MapQuad(transform_preventing_aa, |
| gfx::QuadF(child_rect), |
| &clipped); |
| ASSERT_TRUE(clipped); |
| |
| // Set up the render pass quad to be drawn |
| ScopedPtrVector<RenderPass>* render_passes = |
| renderer_client_.render_passes_in_draw_order(); |
| |
| render_passes->clear(); |
| |
| child_pass = AddRenderPass( |
| render_passes, child_pass_id, child_rect, transform_preventing_aa); |
| |
| root_pass = AddRenderPass( |
| render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| |
| AddRenderPassQuad(root_pass, |
| child_pass, |
| 0, |
| FilterOperations(), |
| transform_preventing_aa); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| |
| // If use_aa incorrectly ignores clipping, it will use the |
| // RenderPassProgramAA shader instead of the RenderPassProgram. |
| TestRenderPassProgram(); |
| } |
| |
| TEST_F(GLRendererShaderTest, DrawSolidColorShader) { |
| gfx::Rect viewport_rect(renderer_client_.DeviceViewport()); |
| ScopedPtrVector<RenderPass>* render_passes = |
| renderer_client_.render_passes_in_draw_order(); |
| |
| RenderPass::Id root_pass_id(1, 0); |
| TestRenderPass* root_pass; |
| |
| gfx::Transform pixel_aligned_transform_causing_aa; |
| pixel_aligned_transform_causing_aa.Translate(25.5f, 25.5f); |
| pixel_aligned_transform_causing_aa.Scale(0.5f, 0.5f); |
| |
| render_passes->clear(); |
| |
| root_pass = AddRenderPass( |
| render_passes, root_pass_id, viewport_rect, gfx::Transform()); |
| AddTransformedQuad(root_pass, |
| viewport_rect, |
| SK_ColorYELLOW, |
| pixel_aligned_transform_causing_aa); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *renderer_client_.render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); |
| |
| TestSolidColorProgramAA(); |
| } |
| |
| class OutputSurfaceMockContext : public TestWebGraphicsContext3D { |
| public: |
| OutputSurfaceMockContext() { |
| test_capabilities_.discard_backbuffer = true; |
| test_capabilities_.post_sub_buffer = true; |
| } |
| |
| // Specifically override methods even if they are unused (used in conjunction |
| // with StrictMock). We need to make sure that GLRenderer does not issue |
| // framebuffer-related GL calls directly. Instead these are supposed to go |
| // through the OutputSurface abstraction. |
| MOCK_METHOD0(ensureBackbufferCHROMIUM, void()); |
| MOCK_METHOD0(discardBackbufferCHROMIUM, void()); |
| MOCK_METHOD2(bindFramebuffer, void(WGC3Denum target, WebGLId framebuffer)); |
| MOCK_METHOD0(prepareTexture, void()); |
| MOCK_METHOD3(reshapeWithScaleFactor, |
| void(int width, int height, float scale_factor)); |
| MOCK_METHOD4(drawElements, |
| void(WGC3Denum mode, |
| WGC3Dsizei count, |
| WGC3Denum type, |
| WGC3Dintptr offset)); |
| }; |
| |
| class MockOutputSurface : public OutputSurface { |
| public: |
| MockOutputSurface() |
| : OutputSurface(TestContextProvider::Create( |
| scoped_ptr<TestWebGraphicsContext3D>( |
| new StrictMock<OutputSurfaceMockContext>))) { |
| surface_size_ = gfx::Size(100, 100); |
| } |
| virtual ~MockOutputSurface() {} |
| |
| MOCK_METHOD0(EnsureBackbuffer, void()); |
| MOCK_METHOD0(DiscardBackbuffer, void()); |
| MOCK_METHOD2(Reshape, void(gfx::Size size, float scale_factor)); |
| MOCK_METHOD0(BindFramebuffer, void()); |
| MOCK_METHOD1(SwapBuffers, void(CompositorFrame* frame)); |
| }; |
| |
| class MockOutputSurfaceTest : public testing::Test, public FakeRendererClient { |
| protected: |
| virtual void SetUp() { |
| FakeOutputSurfaceClient output_surface_client_; |
| CHECK(output_surface_.BindToClient(&output_surface_client_)); |
| |
| resource_provider_ = |
| ResourceProvider::Create(&output_surface_, 0, false).Pass(); |
| |
| renderer_.reset(new FakeRendererGL( |
| this, &settings_, &output_surface_, resource_provider_.get())); |
| EXPECT_TRUE(renderer_->Initialize()); |
| } |
| |
| void SwapBuffers() { renderer_->SwapBuffers(); } |
| |
| void DrawFrame(float device_scale_factor) { |
| gfx::Rect viewport_rect(DeviceViewport()); |
| ScopedPtrVector<RenderPass>* render_passes = render_passes_in_draw_order(); |
| render_passes->clear(); |
| |
| RenderPass::Id render_pass_id(1, 0); |
| TestRenderPass* render_pass = AddRenderPass( |
| render_passes, render_pass_id, viewport_rect, gfx::Transform()); |
| AddQuad(render_pass, viewport_rect, SK_ColorGREEN); |
| |
| EXPECT_CALL(output_surface_, EnsureBackbuffer()).WillRepeatedly(Return()); |
| |
| EXPECT_CALL(output_surface_, |
| Reshape(DeviceViewport().size(), device_scale_factor)).Times(1); |
| |
| EXPECT_CALL(output_surface_, BindFramebuffer()).Times(1); |
| |
| EXPECT_CALL(*Context(), drawElements(_, _, _, _)).Times(1); |
| |
| renderer_->DecideRenderPassAllocationsForFrame( |
| *render_passes_in_draw_order()); |
| renderer_->DrawFrame( |
| render_passes_in_draw_order(), NULL, device_scale_factor, true); |
| } |
| |
| OutputSurfaceMockContext* Context() { |
| return static_cast<OutputSurfaceMockContext*>( |
| output_surface_.context_provider()->Context3d()); |
| } |
| |
| LayerTreeSettings settings_; |
| FakeOutputSurfaceClient output_surface_client_; |
| StrictMock<MockOutputSurface> output_surface_; |
| scoped_ptr<ResourceProvider> resource_provider_; |
| scoped_ptr<FakeRendererGL> renderer_; |
| }; |
| |
| TEST_F(MockOutputSurfaceTest, DrawFrameAndSwap) { |
| DrawFrame(1.f); |
| |
| EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); |
| renderer_->SwapBuffers(); |
| } |
| |
| TEST_F(MockOutputSurfaceTest, DrawFrameAndResizeAndSwap) { |
| DrawFrame(1.f); |
| EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); |
| renderer_->SwapBuffers(); |
| |
| set_viewport(gfx::Rect(0, 0, 2, 2)); |
| renderer_->ViewportChanged(); |
| |
| DrawFrame(2.f); |
| EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); |
| renderer_->SwapBuffers(); |
| |
| DrawFrame(2.f); |
| EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); |
| renderer_->SwapBuffers(); |
| |
| set_viewport(gfx::Rect(0, 0, 1, 1)); |
| renderer_->ViewportChanged(); |
| |
| DrawFrame(1.f); |
| EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); |
| renderer_->SwapBuffers(); |
| } |
| |
| class GLRendererTestSyncPoint : public GLRendererPixelTest { |
| protected: |
| static void SyncPointCallback(int* callback_count) { |
| ++(*callback_count); |
| base::MessageLoop::current()->QuitWhenIdle(); |
| } |
| |
| static void OtherCallback(int* callback_count) { |
| ++(*callback_count); |
| base::MessageLoop::current()->QuitWhenIdle(); |
| } |
| }; |
| |
| #if !defined(OS_ANDROID) |
| TEST_F(GLRendererTestSyncPoint, SignalSyncPointOnLostContext) { |
| int sync_point_callback_count = 0; |
| int other_callback_count = 0; |
| unsigned sync_point = |
| output_surface_->context_provider()->Context3d()->insertSyncPoint(); |
| |
| output_surface_->context_provider()->Context3d()->loseContextCHROMIUM( |
| GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); |
| |
| SyncPointHelper::SignalSyncPoint( |
| output_surface_->context_provider()->Context3d(), |
| sync_point, |
| base::Bind(&SyncPointCallback, &sync_point_callback_count)); |
| EXPECT_EQ(0, sync_point_callback_count); |
| EXPECT_EQ(0, other_callback_count); |
| |
| // Make the sync point happen. |
| output_surface_->context_provider()->Context3d()->finish(); |
| // Post a task after the sync point. |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&OtherCallback, &other_callback_count)); |
| |
| base::MessageLoop::current()->Run(); |
| |
| // The sync point shouldn't have happened since the context was lost. |
| EXPECT_EQ(0, sync_point_callback_count); |
| EXPECT_EQ(1, other_callback_count); |
| } |
| |
| TEST_F(GLRendererTestSyncPoint, SignalSyncPoint) { |
| int sync_point_callback_count = 0; |
| int other_callback_count = 0; |
| unsigned sync_point = |
| output_surface_->context_provider()->Context3d()->insertSyncPoint(); |
| |
| SyncPointHelper::SignalSyncPoint( |
| output_surface_->context_provider()->Context3d(), |
| sync_point, |
| base::Bind(&SyncPointCallback, &sync_point_callback_count)); |
| EXPECT_EQ(0, sync_point_callback_count); |
| EXPECT_EQ(0, other_callback_count); |
| |
| // Make the sync point happen. |
| output_surface_->context_provider()->Context3d()->finish(); |
| // Post a task after the sync point. |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&OtherCallback, &other_callback_count)); |
| |
| base::MessageLoop::current()->Run(); |
| |
| // The sync point should have happened. |
| EXPECT_EQ(1, sync_point_callback_count); |
| EXPECT_EQ(1, other_callback_count); |
| } |
| #endif // OS_ANDROID |
| |
| } // namespace |
| } // namespace cc |