Skip to content

Commit

Permalink
gpu: Allow fences to check whether a flush has occurred
Browse files Browse the repository at this point in the history
This skips waiting on a fence that was created but never committed.

BUG=352419

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@258122 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
sievers@chromium.org committed Mar 19, 2014
1 parent 971b08c commit e893bc8
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 27 deletions.
13 changes: 8 additions & 5 deletions gpu/command_buffer/service/mailbox_manager_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#include "gpu/command_buffer/service/mailbox_synchronizer.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_context_stub.h"
#include "ui/gl/gl_mock.h"
#include "ui/gl/gl_surface_stub.h"

namespace gpu {
namespace gles2 {
Expand Down Expand Up @@ -188,6 +190,9 @@ class MailboxManagerSyncTest : public MailboxManagerTest {
manager2_ = new MailboxManager;
gl_.reset(new ::testing::StrictMock< ::gfx::MockGLInterface>());
::gfx::MockGLInterface::SetGLInterface(gl_.get());
context_ = new gfx::GLContextStub();
surface_ = new gfx::GLSurfaceStub();
context_->MakeCurrent(surface_);
}

Texture* DefineTexture() {
Expand Down Expand Up @@ -249,12 +254,15 @@ class MailboxManagerSyncTest : public MailboxManagerTest {
virtual void TearDown() {
MailboxManagerTest::TearDown();
MailboxSynchronizer::Terminate();
context_->ReleaseCurrent(NULL);
::gfx::MockGLInterface::SetGLInterface(NULL);
gl_.reset();
}

scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_;
scoped_refptr<MailboxManager> manager2_;
scoped_refptr<gfx::GLContext> context_;
scoped_refptr<gfx::GLSurface> surface_;

private:
DISALLOW_COPY_AND_ASSIGN(MailboxManagerSyncTest);
Expand Down Expand Up @@ -283,7 +291,6 @@ TEST_F(MailboxManagerSyncTest, ProduceSyncDestroy) {
EXPECT_EQ(texture, manager_->ConsumeTexture(GL_TEXTURE_2D, name));

// Synchronize
EXPECT_CALL(*gl_, Flush()).Times(1);
manager_->PushTextureUpdates();
manager2_->PullTextureUpdates();

Expand All @@ -305,7 +312,6 @@ TEST_F(MailboxManagerSyncTest, ProduceConsumeResize) {
EXPECT_EQ(texture, manager_->ConsumeTexture(GL_TEXTURE_2D, name));

// Synchronize
EXPECT_CALL(*gl_, Flush()).Times(1);
manager_->PushTextureUpdates();
manager2_->PullTextureUpdates();

Expand Down Expand Up @@ -334,7 +340,6 @@ TEST_F(MailboxManagerSyncTest, ProduceConsumeResize) {
EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) == NULL);

// Synchronize again
EXPECT_CALL(*gl_, Flush()).Times(1);
manager_->PushTextureUpdates();
SetupUpdateTexParamExpectations(
kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
Expand Down Expand Up @@ -396,7 +401,6 @@ TEST_F(MailboxManagerSyncTest, ProduceConsumeBidirectional) {
manager2_->ProduceTexture(GL_TEXTURE_2D, name2, texture2);

// Make visible.
EXPECT_CALL(*gl_, Flush()).Times(2);
manager_->PushTextureUpdates();
manager2_->PushTextureUpdates();

Expand Down Expand Up @@ -435,7 +439,6 @@ TEST_F(MailboxManagerSyncTest, ProduceConsumeBidirectional) {
Mock::VerifyAndClearExpectations(gl_.get());

// Synchronize in both directions
EXPECT_CALL(*gl_, Flush()).Times(2);
manager_->PushTextureUpdates();
manager2_->PushTextureUpdates();
// manager1 should see the change to texture2 mag_filter being applied.
Expand Down
2 changes: 0 additions & 2 deletions gpu/command_buffer/service/mailbox_synchronizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,6 @@ void MailboxSynchronizer::PushTextureUpdates(MailboxManager* manager) {
textures_.insert(std::make_pair(texture, TextureVersion(group)));
}
}
// Make sure all write fences are flushed.
glFlush();
}

void MailboxSynchronizer::UpdateTextureLocked(Texture* texture,
Expand Down
8 changes: 2 additions & 6 deletions gpu/command_buffer/service/texture_definition.cc
Original file line number Diff line number Diff line change
Expand Up @@ -278,12 +278,8 @@ void NativeImageBuffer::DidRead(gfx::GLImage* client) {

void NativeImageBuffer::DidWrite(gfx::GLImage* client) {
base::AutoLock lock(lock_);
// TODO(sievers): crbug.com/352419
// This is super-risky. We need to somehow find out about when the current
// context gets flushed, so that we will only ever wait on the write fence
// (esp. from another context) if it was flushed and is guaranteed to clear.
// On the other hand, proactively flushing here is not feasible in terms
// of perf when there are multiple draw calls per frame.
// Sharing semantics require the client to flush in order to make changes
// visible to other clients.
write_fence_.reset(gfx::GLFence::CreateWithoutFlush());
write_client_ = client;
for (std::list<ClientInfo>::iterator it = client_infos_.begin();
Expand Down
27 changes: 27 additions & 0 deletions ui/gl/gl_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ base::LazyInstance<base::ThreadLocalPointer<GLContext> >::Leaky
current_real_context_ = LAZY_INSTANCE_INITIALIZER;
} // namespace

GLContext::FlushEvent::FlushEvent() {
}

GLContext::FlushEvent::~FlushEvent() {
}

void GLContext::FlushEvent::Signal() {
flag_.Set();
}

bool GLContext::FlushEvent::IsSignaled() {
return flag_.IsSet();
}

GLContext::GLContext(GLShareGroup* share_group) : share_group_(share_group) {
if (!share_group_.get())
share_group_ = new GLShareGroup;
Expand All @@ -40,6 +54,13 @@ GLContext::~GLContext() {
}
}

scoped_refptr<GLContext::FlushEvent> GLContext::SignalFlush() {
DCHECK(IsCurrent(NULL));
scoped_refptr<FlushEvent> flush_event = new FlushEvent();
flush_events_.push_back(flush_event);
return flush_event;
}

bool GLContext::GetTotalGpuMemory(size_t* bytes) {
DCHECK(bytes);
*bytes = 0;
Expand Down Expand Up @@ -172,6 +193,12 @@ void GLContext::SetRealGLApi() {
SetGLToRealGLApi();
}

void GLContext::OnFlush() {
for (size_t n = 0; n < flush_events_.size(); n++)
flush_events_[n]->Signal();
flush_events_.clear();
}

GLContextReal::GLContextReal(GLShareGroup* share_group)
: GLContext(share_group) {}

Expand Down
26 changes: 26 additions & 0 deletions ui/gl/gl_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
#define UI_GL_GL_CONTEXT_H_

#include <string>
#include <vector>

#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/cancellation_flag.h"
#include "ui/gl/gl_share_group.h"
#include "ui/gl/gl_state_restorer.h"
#include "ui/gl/gpu_preference.h"
Expand All @@ -32,6 +34,25 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> {
virtual bool Initialize(
GLSurface* compatible_surface, GpuPreference gpu_preference) = 0;

class FlushEvent : public base::RefCountedThreadSafe<FlushEvent> {
public:
bool IsSignaled();

private:
friend class base::RefCountedThreadSafe<FlushEvent>;
friend class GLContext;
FlushEvent();
virtual ~FlushEvent();
void Signal();

base::CancellationFlag flag_;
};

// Needs to be called with this context current. It will return a FlushEvent
// that is initially unsignaled, but will transition to signaled after the
// next glFlush() or glFinish() occurs in this context.
scoped_refptr<FlushEvent> SignalFlush();

// Destroys the GL context.
virtual void Destroy() = 0;

Expand Down Expand Up @@ -114,6 +135,9 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> {
// Returns the GL renderer string. The context must be current.
virtual std::string GetGLRenderer();

// Called when glFlush()/glFinish() is called with this context current.
void OnFlush();

protected:
virtual ~GLContext();

Expand All @@ -140,6 +164,8 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> {
scoped_ptr<GLStateRestorer> state_restorer_;
scoped_ptr<GLVersionInfo> version_info_;

std::vector<scoped_refptr<FlushEvent> > flush_events_;

DISALLOW_COPY_AND_ASSIGN(GLContext);
};

Expand Down
59 changes: 47 additions & 12 deletions ui/gl/gl_fence.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,27 @@ class GLFenceNVFence: public gfx::GLFence {
// We will arbitrarily return TRUE for consistency.
glGenFencesNV(1, &fence_);
glSetFenceNV(fence_, GL_ALL_COMPLETED_NV);
if (flush)
if (flush) {
glFlush();
} else {
flush_event_ = gfx::GLContext::GetCurrent()->SignalFlush();
}
}

virtual bool HasCompleted() OVERRIDE {
return !!glTestFenceNV(fence_);
}

virtual void ClientWait() OVERRIDE {
glFinishFenceNV(fence_);
if (!flush_event_ || flush_event_->IsSignaled()) {
glFinishFenceNV(fence_);
} else {
LOG(ERROR) << "Trying to wait for uncommitted fence. Skipping...";
}
}

virtual void ServerWait() OVERRIDE {
glFinishFenceNV(fence_);
ClientWait();
}

private:
Expand All @@ -47,14 +54,18 @@ class GLFenceNVFence: public gfx::GLFence {
}

GLuint fence_;
scoped_refptr<gfx::GLContext::FlushEvent> flush_event_;
};

class GLFenceARBSync: public gfx::GLFence {
public:
GLFenceARBSync(bool flush) {
sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
if (flush)
if (flush) {
glFlush();
} else {
flush_event_ = gfx::GLContext::GetCurrent()->SignalFlush();
}
}

virtual bool HasCompleted() OVERRIDE {
Expand All @@ -69,11 +80,19 @@ class GLFenceARBSync: public gfx::GLFence {
}

virtual void ClientWait() OVERRIDE {
glClientWaitSync(sync_, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
if (!flush_event_ || flush_event_->IsSignaled()) {
glClientWaitSync(sync_, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
} else {
LOG(ERROR) << "Trying to wait for uncommitted fence. Skipping...";
}
}

virtual void ServerWait() OVERRIDE {
glWaitSync(sync_, 0, GL_TIMEOUT_IGNORED);
if (!flush_event_ || flush_event_->IsSignaled()) {
glWaitSync(sync_, 0, GL_TIMEOUT_IGNORED);
} else {
LOG(ERROR) << "Trying to wait for uncommitted fence. Skipping...";
}
}

private:
Expand All @@ -82,6 +101,7 @@ class GLFenceARBSync: public gfx::GLFence {
}

GLsync sync_;
scoped_refptr<gfx::GLContext::FlushEvent> flush_event_;
};

#if !defined(OS_MACOSX)
Expand All @@ -90,8 +110,11 @@ class EGLFenceSync : public gfx::GLFence {
EGLFenceSync(bool flush) {
display_ = eglGetCurrentDisplay();
sync_ = eglCreateSyncKHR(display_, EGL_SYNC_FENCE_KHR, NULL);
if (flush)
if (flush) {
glFlush();
} else {
flush_event_ = gfx::GLContext::GetCurrent()->SignalFlush();
}
}

virtual bool HasCompleted() OVERRIDE {
Expand All @@ -102,14 +125,22 @@ class EGLFenceSync : public gfx::GLFence {
}

virtual void ClientWait() OVERRIDE {
EGLint flags = 0;
EGLTimeKHR time = EGL_FOREVER_KHR;
eglClientWaitSyncKHR(display_, sync_, flags, time);
if (!flush_event_ || flush_event_->IsSignaled()) {
EGLint flags = 0;
EGLTimeKHR time = EGL_FOREVER_KHR;
eglClientWaitSyncKHR(display_, sync_, flags, time);
} else {
LOG(ERROR) << "Trying to wait for uncommitted fence. Skipping...";
}
}

virtual void ServerWait() OVERRIDE {
EGLint flags = 0;
eglWaitSyncKHR(display_, sync_, flags);
if (!flush_event_ || flush_event_->IsSignaled()) {
EGLint flags = 0;
eglWaitSyncKHR(display_, sync_, flags);
} else {
LOG(ERROR) << "Trying to wait for uncommitted fence. Skipping...";
}
}


Expand All @@ -120,11 +151,15 @@ class EGLFenceSync : public gfx::GLFence {

EGLSyncKHR sync_;
EGLDisplay display_;
scoped_refptr<gfx::GLContext::FlushEvent> flush_event_;
};
#endif // !OS_MACOSX

// static
gfx::GLFence* CreateFence(bool flush) {
DCHECK(gfx::GLContext::GetCurrent())
<< "Trying to create fence with no context";

#if !defined(OS_MACOSX)
if (gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync)
return new EGLFenceSync(flush);
Expand Down
7 changes: 6 additions & 1 deletion ui/gl/gl_fence.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ class GL_EXPORT GLFence {
virtual ~GLFence();

static GLFence* Create();

// Creates a fence that is not guaranteed to signal until the current context
// is flushed. Use with caution.
// is flushed. It is illegal to call Client/ServerWait() on a fence without
// having explicitly called glFlush() or glFinish() in the originating
// context.
static GLFence* CreateWithoutFlush();

virtual bool HasCompleted() = 0;
virtual void ClientWait() = 0;

// Will block the server if supported, but might fall back to blocking the
// client.
virtual void ServerWait() = 0;
Expand Down
Loading

0 comments on commit e893bc8

Please sign in to comment.