Commit 043a6152 authored by achaulk's avatar achaulk Committed by Commit bot

Partial swap implementation for surfaceless

BUG=404089

Review URL: https://codereview.chromium.org/571623003

Cr-Commit-Position: refs/heads/master@{#296198}
parent 34994f79
......@@ -4,9 +4,13 @@
#include "content/browser/compositor/buffer_queue.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "content/common/gpu/client/context_provider_command_buffer.h"
#include "content/common/gpu/client/gl_helper.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkRegion.h"
namespace content {
......@@ -46,8 +50,45 @@ void BufferQueue::BindFramebuffer() {
}
}
void BufferQueue::SwapBuffers() {
in_flight_surfaces_.push(current_surface_);
void BufferQueue::CopyBufferDamage(int texture,
int source_texture,
const gfx::Rect& new_damage,
const gfx::Rect& old_damage) {
ImageTransportFactory::GetInstance()->GetGLHelper()->CopySubBufferDamage(
texture,
source_texture,
SkRegion(SkIRect::MakeXYWH(new_damage.x(),
new_damage.y(),
new_damage.width(),
new_damage.height())),
SkRegion(SkIRect::MakeXYWH(old_damage.x(),
old_damage.y(),
old_damage.width(),
old_damage.height())));
}
void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) {
for (size_t i = 0; i < available_surfaces_.size(); i++)
available_surfaces_[i].damage.Union(damage);
for (std::deque<AllocatedSurface>::iterator it =
in_flight_surfaces_.begin();
it != in_flight_surfaces_.end();
++it)
it->damage.Union(damage);
}
void BufferQueue::SwapBuffers(const gfx::Rect& damage) {
if (damage != gfx::Rect(size_)) {
// We must have a frame available to copy from.
DCHECK(!in_flight_surfaces_.empty());
CopyBufferDamage(current_surface_.texture,
in_flight_surfaces_.back().texture,
damage,
current_surface_.damage);
}
UpdateBufferDamage(damage);
current_surface_.damage = gfx::Rect();
in_flight_surfaces_.push_back(current_surface_);
current_surface_.texture = 0;
current_surface_.image = 0;
}
......@@ -70,7 +111,7 @@ void BufferQueue::Reshape(const gfx::Size& size, float scale_factor) {
void BufferQueue::PageFlipComplete() {
if (in_flight_surfaces_.size() > 1) {
available_surfaces_.push_back(in_flight_surfaces_.front());
in_flight_surfaces_.pop();
in_flight_surfaces_.pop_front();
}
}
......@@ -78,7 +119,7 @@ void BufferQueue::FreeAllSurfaces() {
FreeSurface(&current_surface_);
while (!in_flight_surfaces_.empty()) {
FreeSurface(&in_flight_surfaces_.front());
in_flight_surfaces_.pop();
in_flight_surfaces_.pop_front();
}
for (size_t i = 0; i < available_surfaces_.size(); i++)
FreeSurface(&available_surfaces_[i]);
......@@ -100,9 +141,9 @@ void BufferQueue::FreeSurface(AllocatedSurface* surface) {
BufferQueue::AllocatedSurface BufferQueue::GetNextSurface() {
if (!available_surfaces_.empty()) {
AllocatedSurface id = available_surfaces_.back();
AllocatedSurface surface = available_surfaces_.back();
available_surfaces_.pop_back();
return id;
return surface;
}
unsigned int texture = 0;
......@@ -114,15 +155,20 @@ BufferQueue::AllocatedSurface BufferQueue::GetNextSurface() {
// We don't want to allow anything more than triple buffering.
DCHECK_LT(allocated_count_, 4U);
unsigned int id = context_provider_->ContextGL()->CreateImageCHROMIUM(
unsigned int id = gl->CreateImageCHROMIUM(
size_.width(),
size_.height(),
internalformat_,
GL_IMAGE_SCANOUT_CHROMIUM);
if (!id) {
LOG(ERROR) << "Failed to allocate backing CreateImageCHROMIUM surface";
gl->DeleteTextures(1, &texture);
return AllocatedSurface();
}
allocated_count_++;
gl->BindTexture(GL_TEXTURE_2D, texture);
gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, id);
return AllocatedSurface(texture, id);
return AllocatedSurface(texture, id, gfx::Rect(size_));
}
} // namespace content
......@@ -9,7 +9,9 @@
#include <vector>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "content/common/content_export.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
namespace cc {
......@@ -26,12 +28,12 @@ class CONTENT_EXPORT BufferQueue {
public:
BufferQueue(scoped_refptr<cc::ContextProvider> context_provider,
unsigned int internalformat);
~BufferQueue();
virtual ~BufferQueue();
bool Initialize();
void BindFramebuffer();
void SwapBuffers();
void SwapBuffers(const gfx::Rect& damage);
void PageFlipComplete();
void Reshape(const gfx::Size& size, float scale_factor);
......@@ -39,18 +41,31 @@ class CONTENT_EXPORT BufferQueue {
private:
friend class BufferQueueTest;
struct AllocatedSurface {
AllocatedSurface() : texture(0), image(0) {}
AllocatedSurface(unsigned int texture, unsigned int image)
: texture(texture), image(image) {}
AllocatedSurface(unsigned int texture,
unsigned int image,
const gfx::Rect& rect)
: texture(texture), image(image), damage(rect) {}
unsigned int texture;
unsigned int image;
gfx::Rect damage; // This is the damage for this frame from the previous.
};
void FreeAllSurfaces();
void FreeSurface(AllocatedSurface* surface);
// Copy everything that is in |copy_rect|, except for what is in
// |exclude_rect| from |source_texture| to |texture|.
virtual void CopyBufferDamage(int texture,
int source_texture,
const gfx::Rect& new_damage,
const gfx::Rect& old_damage);
void UpdateBufferDamage(const gfx::Rect& damage);
// Return a surface, available to be drawn into.
AllocatedSurface GetNextSurface();
......@@ -61,7 +76,7 @@ class CONTENT_EXPORT BufferQueue {
unsigned int internalformat_;
AllocatedSurface current_surface_; // This surface is currently bound.
std::vector<AllocatedSurface> available_surfaces_; // These are free for use.
std::queue<AllocatedSurface> in_flight_surfaces_;
std::deque<AllocatedSurface> in_flight_surfaces_;
DISALLOW_COPY_AND_ASSIGN(BufferQueue);
};
......
......@@ -19,24 +19,43 @@ using ::testing::Ne;
using ::testing::Return;
namespace content {
class MockBufferQueue : public BufferQueue {
public:
MockBufferQueue(scoped_refptr<cc::ContextProvider> context_provider,
unsigned int internalformat)
: BufferQueue(context_provider, internalformat) {}
MOCK_METHOD4(CopyBufferDamage,
void(int, int, const gfx::Rect&, const gfx::Rect&));
};
class BufferQueueTest : public ::testing::Test {
public:
BufferQueueTest() : doublebuffering_(true), first_frame_(true) {}
virtual void SetUp() OVERRIDE {
scoped_refptr<cc::TestContextProvider> context_provider =
cc::TestContextProvider::Create(cc::TestWebGraphicsContext3D::Create());
context_provider->BindToCurrentThread();
output_surface_.reset(new BufferQueue(context_provider, GL_RGBA8_OES));
output_surface_.reset(new MockBufferQueue(context_provider, GL_RGBA8_OES));
output_surface_->Initialize();
}
unsigned current_surface() { return output_surface_->current_surface_.image; }
const std::vector<BufferQueue::AllocatedSurface>& available_surfaces() {
return output_surface_->available_surfaces_;
}
const std::queue<BufferQueue::AllocatedSurface>& in_flight_surfaces() {
const std::deque<BufferQueue::AllocatedSurface>& in_flight_surfaces() {
return output_surface_->in_flight_surfaces_;
}
const BufferQueue::AllocatedSurface& last_frame() {
return output_surface_->in_flight_surfaces_.back();
}
const BufferQueue::AllocatedSurface& next_frame() {
return output_surface_->available_surfaces_.back();
}
const gfx::Size size() { return output_surface_->size_; }
int CountBuffers() {
int n = available_surfaces().size() + in_flight_surfaces().size();
if (current_surface())
......@@ -50,13 +69,29 @@ class BufferQueueTest : public ::testing::Test {
EXPECT_TRUE(InsertUnique(&buffers, current_surface()));
for (size_t i = 0; i < available_surfaces().size(); i++)
EXPECT_TRUE(InsertUnique(&buffers, available_surfaces()[i].image));
std::queue<BufferQueue::AllocatedSurface> copy = in_flight_surfaces();
while (!copy.empty()) {
EXPECT_TRUE(InsertUnique(&buffers, copy.front().image));
copy.pop();
}
for (std::deque<BufferQueue::AllocatedSurface>::const_iterator it =
in_flight_surfaces().begin();
it != in_flight_surfaces().end();
++it)
EXPECT_TRUE(InsertUnique(&buffers, it->image));
}
void SwapBuffers() {
output_surface_->SwapBuffers(gfx::Rect(output_surface_->size_));
}
void SendDamagedFrame(const gfx::Rect& damage) {
// We don't care about the GL-level implementation here, just how it uses
// damage rects.
output_surface_->BindFramebuffer();
output_surface_->SwapBuffers(damage);
if (doublebuffering_ || !first_frame_)
output_surface_->PageFlipComplete();
first_frame_ = false;
}
void SendFullFrame() { SendDamagedFrame(gfx::Rect(output_surface_->size_)); }
protected:
bool InsertUnique(std::set<unsigned>* set, unsigned value) {
if (!value)
......@@ -67,10 +102,17 @@ class BufferQueueTest : public ::testing::Test {
return true;
}
scoped_ptr<BufferQueue> output_surface_;
scoped_ptr<MockBufferQueue> output_surface_;
bool doublebuffering_;
bool first_frame_;
};
namespace {
const gfx::Size screen_size = gfx::Size(30, 30);
const gfx::Rect screen_rect = gfx::Rect(screen_size);
const gfx::Rect small_damage = gfx::Rect(gfx::Size(10, 10));
const gfx::Rect large_damage = gfx::Rect(gfx::Size(20, 20));
const gfx::Rect overlapping_damage = gfx::Rect(gfx::Size(5, 20));
class MockedContext : public cc::TestWebGraphicsContext3D {
public:
......@@ -133,6 +175,51 @@ TEST(BufferQueueStandaloneTest, FboBinding) {
output_surface->BindFramebuffer();
}
TEST_F(BufferQueueTest, PartialSwapReuse) {
// Check that
output_surface_->Reshape(screen_size, 1.0f);
ASSERT_TRUE(doublebuffering_);
EXPECT_CALL(*output_surface_,
CopyBufferDamage(_, _, small_damage, screen_rect)).Times(1);
EXPECT_CALL(*output_surface_,
CopyBufferDamage(_, _, small_damage, small_damage)).Times(1);
EXPECT_CALL(*output_surface_,
CopyBufferDamage(_, _, large_damage, small_damage)).Times(1);
SendFullFrame();
SendDamagedFrame(small_damage);
SendDamagedFrame(small_damage);
SendDamagedFrame(large_damage);
// Verify that the damage has propagated.
EXPECT_EQ(next_frame().damage, large_damage);
}
TEST_F(BufferQueueTest, PartialSwapFullFrame) {
output_surface_->Reshape(screen_size, 1.0f);
ASSERT_TRUE(doublebuffering_);
EXPECT_CALL(*output_surface_,
CopyBufferDamage(_, _, small_damage, screen_rect)).Times(1);
SendFullFrame();
SendDamagedFrame(small_damage);
SendFullFrame();
SendFullFrame();
EXPECT_EQ(next_frame().damage, screen_rect);
}
TEST_F(BufferQueueTest, PartialSwapOverlapping) {
output_surface_->Reshape(screen_size, 1.0f);
ASSERT_TRUE(doublebuffering_);
EXPECT_CALL(*output_surface_,
CopyBufferDamage(_, _, small_damage, screen_rect)).Times(1);
EXPECT_CALL(*output_surface_,
CopyBufferDamage(_, _, overlapping_damage, small_damage))
.Times(1);
SendFullFrame();
SendDamagedFrame(small_damage);
SendDamagedFrame(overlapping_damage);
EXPECT_EQ(next_frame().damage, overlapping_damage);
}
TEST_F(BufferQueueTest, MultipleBindCalls) {
// Check that multiple bind calls do not create or change surfaces.
output_surface_->BindFramebuffer();
......@@ -149,7 +236,7 @@ TEST_F(BufferQueueTest, CheckDoubleBuffering) {
output_surface_->BindFramebuffer();
EXPECT_EQ(1, CountBuffers());
EXPECT_NE(0U, current_surface());
output_surface_->SwapBuffers();
SwapBuffers();
EXPECT_EQ(1U, in_flight_surfaces().size());
output_surface_->PageFlipComplete();
EXPECT_EQ(1U, in_flight_surfaces().size());
......@@ -158,7 +245,7 @@ TEST_F(BufferQueueTest, CheckDoubleBuffering) {
CheckUnique();
EXPECT_NE(0U, current_surface());
EXPECT_EQ(1U, in_flight_surfaces().size());
output_surface_->SwapBuffers();
SwapBuffers();
CheckUnique();
EXPECT_EQ(2U, in_flight_surfaces().size());
output_surface_->PageFlipComplete();
......@@ -176,10 +263,10 @@ TEST_F(BufferQueueTest, CheckTripleBuffering) {
// This bit is the same sequence tested in the doublebuffering case.
output_surface_->BindFramebuffer();
output_surface_->SwapBuffers();
SwapBuffers();
output_surface_->PageFlipComplete();
output_surface_->BindFramebuffer();
output_surface_->SwapBuffers();
SwapBuffers();
EXPECT_EQ(2, CountBuffers());
CheckUnique();
......
......@@ -38,11 +38,13 @@ void GpuSurfacelessBrowserCompositorOutputSurface::SwapBuffers(
cc::CompositorFrame* frame) {
DCHECK(output_surface_);
GLuint texture = output_surface_->current_texture_id();
output_surface_->SwapBuffers(frame->gl_frame_data->sub_buffer_rect);
const gfx::Size& size = frame->gl_frame_data->size;
context_provider_->ContextGL()->ScheduleOverlayPlaneCHROMIUM(
0,
GL_OVERLAY_TRANSFORM_NONE_CHROMIUM,
output_surface_->current_texture_id(),
texture,
0,
0,
size.width(),
......@@ -51,7 +53,6 @@ void GpuSurfacelessBrowserCompositorOutputSurface::SwapBuffers(
0,
1.0f,
1.0f);
output_surface_->SwapBuffers();
GpuBrowserCompositorOutputSurface::SwapBuffers(frame);
}
......
......@@ -138,6 +138,12 @@ class GL_EXPORT GLSurfaceOzoneSurfaceless : public SurfacelessEGL {
virtual VSyncProvider* GetVSyncProvider() OVERRIDE {
return vsync_provider_.get();
}
virtual bool SupportsPostSubBuffer() OVERRIDE { return true; }
virtual bool PostSubBuffer(int x, int y, int width, int height) OVERRIDE {
// The actual sub buffer handling is handled at higher layers.
SwapBuffers();
return true;
}
private:
virtual ~GLSurfaceOzoneSurfaceless() {
......
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