summaryrefslogtreecommitdiffstats
path: root/gpu
diff options
context:
space:
mode:
authorapatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-25 22:08:35 +0000
committerapatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-25 22:08:35 +0000
commit6217d397bf501ec1ec5b7270d493006badd4d87a (patch)
treefce6c8e9c15b79bab29a9535ce9929dd9d53735b /gpu
parent21dedcc12d21ccb288244249522d77bc074c501e (diff)
downloadchromium_src-6217d397bf501ec1ec5b7270d493006badd4d87a.zip
chromium_src-6217d397bf501ec1ec5b7270d493006badd4d87a.tar.gz
chromium_src-6217d397bf501ec1ec5b7270d493006badd4d87a.tar.bz2
Calling OpenGL from the renderer process
- Added ability for renderer processes to render to a real window (Windows only so far). - Added ability to create offscreen frame buffer objects that can be resized later. - OpenGL context can have a "parent" context that can access its last swapped back buffer through a texture ID. - Moved code to establish GPU channel from RenderWidget to RenderThread. - Changed way service size command buffer object lifetimes are managed. TEST=trybot and visual verification that OpenGL can clear the browser window to magenta. BUG=none Review URL: http://codereview.chromium.org/1136006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42679 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu')
-rwxr-xr-xgpu/command_buffer/build_gles2_cmd_buffer.py10
-rw-r--r--gpu/command_buffer/client/cmd_buffer_helper_test.cc7
-rw-r--r--gpu/command_buffer/client/fenced_allocator_test.cc7
-rw-r--r--gpu/command_buffer/client/gles2_demo.cc10
-rw-r--r--gpu/command_buffer/client/gles2_implementation.h2
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc727
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.h12
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_autogen.h6
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_mock.h6
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc2
-rw-r--r--gpu/command_buffer/service/gpu_processor.cc15
-rw-r--r--gpu/command_buffer/service/gpu_processor.h39
-rw-r--r--gpu/command_buffer/service/gpu_processor_linux.cc25
-rw-r--r--gpu/command_buffer/service/gpu_processor_mac.cc23
-rw-r--r--gpu/command_buffer/service/gpu_processor_unittest.cc10
-rw-r--r--gpu/command_buffer/service/gpu_processor_win.cc20
-rw-r--r--gpu/command_buffer/service/texture_manager.cc9
-rw-r--r--gpu/command_buffer/service/texture_manager.h2
-rw-r--r--gpu/demos/framework/window.cc9
-rw-r--r--gpu/gpu.gyp1
20 files changed, 820 insertions, 122 deletions
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index 16f084b..6d11c08 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1178,6 +1178,11 @@ _FUNCTION_INFO = {
'cmd_args':
'GLuint shader, const char* data',
},
+ 'SwapBuffers': {
+ 'type': 'Custom',
+ 'impl_func': False,
+ 'unit_test': False,
+ },
'TexImage2D': {'type': 'Manual', 'immediate': True},
'TexParameterf': {'decoder_func': 'DoTexParameterf'},
'TexParameteri': {'decoder_func': 'DoTexParameteri'},
@@ -1222,11 +1227,6 @@ _FUNCTION_INFO = {
'cmd_args': 'GLuint indx, GLint size, GLenum type, GLboolean normalized, '
'GLsizei stride, GLuint offset',
},
- 'SwapBuffers': {
- 'impl_func': False,
- 'decoder_func': 'DoSwapBuffers',
- 'unit_test': False,
- },
}
diff --git a/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
index ffb8137..d754f5b 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper_test.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
@@ -51,12 +51,12 @@ class CommandBufferHelperTest : public testing::Test {
0,
api_mock_.get());
- scoped_refptr<GPUProcessor> gpu_processor(new GPUProcessor(
+ gpu_processor_.reset(new GPUProcessor(
command_buffer_.get(), NULL, parser_, 1));
command_buffer_->SetPutOffsetChangeCallback(NewCallback(
- gpu_processor.get(), &GPUProcessor::ProcessCommands));
+ gpu_processor_.get(), &GPUProcessor::ProcessCommands));
- api_mock_->set_engine(gpu_processor.get());
+ api_mock_->set_engine(gpu_processor_.get());
helper_.reset(new CommandBufferHelper(command_buffer_.get()));
helper_->Initialize();
@@ -132,6 +132,7 @@ class CommandBufferHelperTest : public testing::Test {
MessageLoop message_loop_;
scoped_ptr<AsyncAPIMock> api_mock_;
scoped_ptr<CommandBufferService> command_buffer_;
+ scoped_ptr<GPUProcessor> gpu_processor_;
CommandParser* parser_;
scoped_ptr<CommandBufferHelper> helper_;
Sequence sequence_;
diff --git a/gpu/command_buffer/client/fenced_allocator_test.cc b/gpu/command_buffer/client/fenced_allocator_test.cc
index c47f355..8dc1b780 100644
--- a/gpu/command_buffer/client/fenced_allocator_test.cc
+++ b/gpu/command_buffer/client/fenced_allocator_test.cc
@@ -52,12 +52,12 @@ class BaseFencedAllocatorTest : public testing::Test {
0,
api_mock_.get());
- scoped_refptr<GPUProcessor> gpu_processor(new GPUProcessor(
+ gpu_processor_.reset(new GPUProcessor(
command_buffer_.get(), NULL, parser_, INT_MAX));
command_buffer_->SetPutOffsetChangeCallback(NewCallback(
- gpu_processor.get(), &GPUProcessor::ProcessCommands));
+ gpu_processor_.get(), &GPUProcessor::ProcessCommands));
- api_mock_->set_engine(gpu_processor.get());
+ api_mock_->set_engine(gpu_processor_.get());
helper_.reset(new CommandBufferHelper(command_buffer_.get()));
helper_->Initialize();
@@ -76,6 +76,7 @@ class BaseFencedAllocatorTest : public testing::Test {
MessageLoop message_loop_;
scoped_ptr<AsyncAPIMock> api_mock_;
scoped_ptr<CommandBufferService> command_buffer_;
+ scoped_ptr<GPUProcessor> gpu_processor_;
CommandParser* parser_;
scoped_ptr<CommandBufferHelper> helper_;
};
diff --git a/gpu/command_buffer/client/gles2_demo.cc b/gpu/command_buffer/client/gles2_demo.cc
index 41c42b1..6035042 100644
--- a/gpu/command_buffer/client/gles2_demo.cc
+++ b/gpu/command_buffer/client/gles2_demo.cc
@@ -54,14 +54,16 @@ bool GLES2Demo::Setup(void* hwnd, int32 size) {
if (!command_buffer->Initialize(size))
return NULL;
- scoped_refptr<GPUProcessor> gpu_processor(
- new GPUProcessor(command_buffer.get()));
- if (!gpu_processor->Initialize(reinterpret_cast<HWND>(hwnd))) {
+ GPUProcessor* gpu_processor = new GPUProcessor(command_buffer.get());
+ if (!gpu_processor->Initialize(reinterpret_cast<HWND>(hwnd),
+ NULL,
+ gfx::Size(),
+ 0)) {
return NULL;
}
command_buffer->SetPutOffsetChangeCallback(
- NewCallback(gpu_processor.get(), &GPUProcessor::ProcessCommands));
+ NewCallback(gpu_processor, &GPUProcessor::ProcessCommands));
GLES2CmdHelper* helper = new GLES2CmdHelper(command_buffer.get());
if (!helper->Initialize()) {
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h
index c1ee9c6..fd98686 100644
--- a/gpu/command_buffer/client/gles2_implementation.h
+++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -43,13 +43,13 @@ class GLES2Implementation {
// this file instead of having to edit some template or the code generator.
#include "../client/gles2_implementation_autogen.h"
- private:
// Makes a set of Ids for glGen___ functions.
void MakeIds(GLsizei n, GLuint* ids);
// Frees a set of Ids for glDelete___ functions.
void FreeIds(GLsizei n, const GLuint* ids);
+ private:
// Gets the shared memory id for the result buffer.
uint32 result_shm_id() const {
return transfer_buffer_id_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 512a819..23c3423 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -14,6 +14,7 @@
#include "base/callback.h"
#include "base/linked_ptr.h"
#include "base/scoped_ptr.h"
+#include "base/weak_ptr.h"
#define GLES2_GPU_SERVICE 1
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
@@ -36,9 +37,15 @@
#include "app/surface/accelerated_surface_mac.h"
#endif
+#if !defined(GL_DEPTH24_STENCIL8)
+#define GL_DEPTH24_STENCIL8 0x88F0
+#endif
+
namespace gpu {
namespace gles2 {
+class GLES2DecoderImpl;
+
// Check that certain assumptions the code makes are true. There are places in
// the code where shared memory is passed direclty to GL. Example, glUniformiv,
// glShaderSource. The command buffer code assumes GLint and GLsizei (and maybe
@@ -107,6 +114,144 @@ const CommandInfo g_command_info[] = {
#undef GLES2_CMD_OP
};
+// This class prevents any GL errors that occur when it is in scope from
+// being reported to the client.
+class ScopedGLErrorSuppressor {
+ public:
+ explicit ScopedGLErrorSuppressor(GLES2DecoderImpl* decoder);
+ ~ScopedGLErrorSuppressor();
+ private:
+ GLES2DecoderImpl* decoder_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedGLErrorSuppressor);
+};
+
+// Temporarily changes a decoder's bound 2D texture and restore it when this
+// object goes out of scope. Also temporarily switches to using active texture
+// unit zero in case the client has changed that to something invalid.
+class ScopedTexture2DBinder {
+ public:
+ ScopedTexture2DBinder(GLES2DecoderImpl* decoder, GLuint id);
+ ~ScopedTexture2DBinder();
+
+ private:
+ GLES2DecoderImpl* decoder_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedTexture2DBinder);
+};
+
+// Temporarily changes a decoder's bound render buffer and restore it when this
+// object goes out of scope.
+class ScopedRenderBufferBinder {
+ public:
+ ScopedRenderBufferBinder(GLES2DecoderImpl* decoder, GLuint id);
+ ~ScopedRenderBufferBinder();
+
+ private:
+ GLES2DecoderImpl* decoder_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedRenderBufferBinder);
+};
+
+// Temporarily changes a decoder's bound frame buffer and restore it when this
+// object goes out of scope.
+class ScopedFrameBufferBinder {
+ public:
+ ScopedFrameBufferBinder(GLES2DecoderImpl* decoder, GLuint id);
+ ~ScopedFrameBufferBinder();
+
+ private:
+ GLES2DecoderImpl* decoder_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedFrameBufferBinder);
+};
+
+// Encapsulates an OpenGL texture.
+class Texture {
+ public:
+ explicit Texture(GLES2DecoderImpl* decoder);
+ ~Texture();
+
+ // Create a new render texture.
+ void Create();
+
+ // Set the initial size and format of a render texture or resize it.
+ bool AllocateStorage(const gfx::Size& size);
+
+ // Copy the contents of the currently bound frame buffer.
+ void Copy(const gfx::Size& size);
+
+ // Destroy the render texture. This must be explicitly called before
+ // destroying this object.
+ void Destroy();
+
+ GLuint id() const {
+ return id_;
+ }
+
+ private:
+ GLES2DecoderImpl* decoder_;
+ GLuint id_;
+ DISALLOW_COPY_AND_ASSIGN(Texture);
+};
+
+// Encapsulates an OpenGL render buffer of any format.
+class RenderBuffer {
+ public:
+ explicit RenderBuffer(GLES2DecoderImpl* decoder);
+ ~RenderBuffer();
+
+ // Create a new render buffer.
+ void Create();
+
+ // Set the initial size and format of a render buffer or resize it.
+ bool AllocateStorage(const gfx::Size& size, GLenum format);
+
+ // Destroy the render buffer. This must be explicitly called before destroying
+ // this object.
+ void Destroy();
+
+ GLuint id() const {
+ return id_;
+ }
+
+ private:
+ GLES2DecoderImpl* decoder_;
+ GLuint id_;
+ DISALLOW_COPY_AND_ASSIGN(RenderBuffer);
+};
+
+// Encapsulates an OpenGL frame buffer.
+class FrameBuffer {
+ public:
+ explicit FrameBuffer(GLES2DecoderImpl* decoder);
+ ~FrameBuffer();
+
+ // Create a new frame buffer.
+ void Create();
+
+ // Attach a color render buffer to a frame buffer.
+ void AttachRenderTexture(Texture* texture);
+
+ // Attach a depth stencil render buffer to a frame buffer. Note that
+ // this unbinds any currently bound frame buffer.
+ void AttachDepthStencilRenderBuffer(RenderBuffer* render_buffer);
+
+ // Clear the given attached buffers.
+ void Clear(GLbitfield buffers);
+
+ // Destroy the frame buffer. This must be explicitly called before destroying
+ // this object.
+ void Destroy();
+
+ // See glCheckFramebufferStatusEXT.
+ GLenum CheckStatus();
+
+ GLuint id() const {
+ return id_;
+ }
+
+ private:
+ GLES2DecoderImpl* decoder_;
+ GLuint id_;
+ DISALLOW_COPY_AND_ASSIGN(FrameBuffer);
+};
// } // anonymous namespace.
GLES2Decoder::GLES2Decoder(ContextGroup* group)
@@ -129,7 +274,8 @@ GLES2Decoder::~GLES2Decoder() {
// This class implements GLES2Decoder so we don't have to expose all the GLES2
// cmd stuff to outside this class.
-class GLES2DecoderImpl : public GLES2Decoder {
+class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>,
+ public GLES2Decoder {
public:
explicit GLES2DecoderImpl(ContextGroup* group);
@@ -210,8 +356,11 @@ class GLES2DecoderImpl : public GLES2Decoder {
virtual const char* GetCommandName(unsigned int command_id) const;
// Overridden from GLES2Decoder.
- virtual bool Initialize();
+ virtual bool Initialize(GLES2Decoder* parent,
+ const gfx::Size& size,
+ uint32 parent_client_texture_id);
virtual void Destroy();
+ virtual void ResizeOffscreenFrameBuffer(const gfx::Size& size);
virtual bool MakeCurrent();
virtual uint32 GetServiceIdForTesting(uint32 client_id);
virtual GLES2Util* GetGLES2Util() { return &util_; }
@@ -239,6 +388,13 @@ class GLES2DecoderImpl : public GLES2Decoder {
virtual void SetSwapBuffersCallback(Callback0::Type* callback);
private:
+ friend class ScopedGLErrorSuppressor;
+ friend class ScopedTexture2DBinder;
+ friend class ScopedFrameBufferBinder;
+ friend class ScopedRenderBufferBinder;
+ friend class RenderBuffer;
+ friend class FrameBuffer;
+
// State associated with each texture unit.
struct TextureUnit {
TextureUnit() : bind_target(GL_TEXTURE_2D) { }
@@ -309,6 +465,8 @@ class GLES2DecoderImpl : public GLES2Decoder {
static bool InitGlew();
void DestroyPlatformSpecific();
+ bool UpdateOffscreenFrameBufferSize();
+
// Template to help call glGenXXX functions.
template <void gl_gen_function(GLES2DecoderImpl*, GLsizei, GLuint*)>
bool GenGLObjects(GLsizei n, const GLuint* client_ids) {
@@ -343,8 +501,8 @@ class GLES2DecoderImpl : public GLES2Decoder {
GLsizei n, const GLuint* client_ids, GLuint* service_ids);
// Creates a TextureInfo for the given texture.
- void CreateTextureInfo(GLuint texture) {
- texture_manager()->CreateTextureInfo(texture);
+ TextureManager::TextureInfo* CreateTextureInfo(GLuint texture) {
+ return texture_manager()->CreateTextureInfo(texture);
}
// Gets the texture info for the given texture. Returns NULL if none exists.
@@ -561,9 +719,6 @@ class GLES2DecoderImpl : public GLES2Decoder {
void DoRenderbufferStorage(
GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
- // Swaps the buffers (copies/renders to the current window).
- void DoSwapBuffers();
-
// Wrappers for glTexParameter functions.
void DoTexParameterf(GLenum target, GLenum pname, GLfloat param);
void DoTexParameteri(GLenum target, GLenum pname, GLint param);
@@ -590,6 +745,10 @@ class GLES2DecoderImpl : public GLES2Decoder {
// command.
void CopyRealGLErrorsToWrapper();
+ // Clear all real GL errors. This is to prevent the client from seeing any
+ // errors caused by GL calls that it was not responsible for issuing.
+ void ClearRealGLErrors();
+
// Checks if the current program and vertex attributes are valid for drawing.
bool IsDrawValid(GLuint max_vertex_accessed);
@@ -654,6 +813,17 @@ class GLES2DecoderImpl : public GLES2Decoder {
#undef GLES2_CMD_OP
+ // A parent decoder can access this decoders saved offscreen frame buffer.
+ // The parent pointer is reset if the parent is destroyed.
+ base::WeakPtr<GLES2DecoderImpl> parent_;
+
+ // Width and height to which an offscreen frame buffer should be resized on
+ // the next call to SwapBuffers.
+ gfx::Size pending_size_;
+
+ // Width and height of a decoder that renders to an offscreen frame buffer.
+ gfx::Size current_size_;
+
// Current GL error bits.
uint32 error_bits_;
@@ -714,11 +884,252 @@ class GLES2DecoderImpl : public GLES2Decoder {
bool anti_aliased_;
+ // The offscreen frame buffer that the client renders to.
+ scoped_ptr<FrameBuffer> offscreen_target_frame_buffer_;
+ scoped_ptr<Texture> offscreen_target_color_texture_;
+ scoped_ptr<RenderBuffer> offscreen_target_depth_stencil_render_buffer_;
+
+ // The copy that is saved when SwapBuffers is called.
+ scoped_ptr<Texture> offscreen_saved_color_texture_;
+
+ // A frame buffer used for rendering to render textures and render buffers
+ // without concern about any state the client might have changed on the frame
+ // buffers it has access to.
+ scoped_ptr<FrameBuffer> temporary_frame_buffer_;
+
scoped_ptr<Callback0::Type> swap_buffers_callback_;
DISALLOW_COPY_AND_ASSIGN(GLES2DecoderImpl);
};
+ScopedGLErrorSuppressor::ScopedGLErrorSuppressor(GLES2DecoderImpl* decoder)
+ : decoder_(decoder) {
+ decoder_->CopyRealGLErrorsToWrapper();
+}
+
+ScopedGLErrorSuppressor::~ScopedGLErrorSuppressor() {
+ decoder_->ClearRealGLErrors();
+}
+
+ScopedTexture2DBinder::ScopedTexture2DBinder(GLES2DecoderImpl* decoder,
+ GLuint id)
+ : decoder_(decoder) {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+
+ // TODO(apatrick): Check if there are any other states that need to be reset
+ // before binding a new texture.
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, id);
+}
+
+ScopedTexture2DBinder::~ScopedTexture2DBinder() {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ GLES2DecoderImpl::TextureUnit& info = decoder_->texture_units_[0];
+ GLuint last_id;
+ if (info.bound_texture_2d)
+ last_id = info.bound_texture_2d->texture_id();
+ else
+ last_id = 0;
+
+ glBindTexture(GL_TEXTURE_2D, last_id);
+ glActiveTexture(GL_TEXTURE0 + decoder_->active_texture_unit_);
+}
+
+ScopedRenderBufferBinder::ScopedRenderBufferBinder(GLES2DecoderImpl* decoder,
+ GLuint id)
+ : decoder_(decoder) {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ glBindRenderbufferEXT(GL_RENDERBUFFER, id);
+}
+
+ScopedRenderBufferBinder::~ScopedRenderBufferBinder() {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ glBindRenderbufferEXT(GL_RENDERBUFFER, decoder_->bound_renderbuffer_);
+}
+
+ScopedFrameBufferBinder::ScopedFrameBufferBinder(GLES2DecoderImpl* decoder,
+ GLuint id)
+ : decoder_(decoder) {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ glBindFramebufferEXT(GL_FRAMEBUFFER, id);
+}
+
+ScopedFrameBufferBinder::~ScopedFrameBufferBinder() {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ if (decoder_->bound_framebuffer_ == 0 &&
+ decoder_->offscreen_target_frame_buffer_.get()) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER,
+ decoder_->offscreen_target_frame_buffer_->id());
+ } else {
+ glBindFramebufferEXT(GL_FRAMEBUFFER, decoder_->bound_framebuffer_);
+ }
+}
+
+Texture::Texture(GLES2DecoderImpl* decoder)
+ : decoder_(decoder),
+ id_(0) {
+}
+
+Texture::~Texture() {
+ // This does not destroy the render texture because that would require that
+ // the associated GL context was current. Just check that it was explicitly
+ // destroyed.
+ DCHECK_EQ(id_, 0u);
+}
+
+void Texture::Create() {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ Destroy();
+ glGenTextures(1, &id_);
+}
+
+bool Texture::AllocateStorage(const gfx::Size& size) {
+ DCHECK_NE(id_, 0u);
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ ScopedTexture2DBinder binder(decoder_, id_);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
+
+ glTexImage2D(GL_TEXTURE_2D,
+ 0, // mip level
+ GL_RGBA,
+ size.width(),
+ size.height(),
+ 0, // border
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ NULL);
+
+ return glGetError() == GL_NO_ERROR;
+}
+
+void Texture::Copy(const gfx::Size& size) {
+ DCHECK_NE(id_, 0u);
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ ScopedTexture2DBinder binder(decoder_, id_);
+ glCopyTexImage2D(GL_TEXTURE_2D,
+ 0, // level
+ GL_RGBA,
+ 0, 0,
+ size.width(),
+ size.height(),
+ 0); // border
+}
+
+void Texture::Destroy() {
+ if (id_ != 0) {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ glDeleteTextures(1, &id_);
+ id_ = 0;
+ }
+}
+
+RenderBuffer::RenderBuffer(GLES2DecoderImpl* decoder)
+ : decoder_(decoder),
+ id_(0) {
+}
+
+RenderBuffer::~RenderBuffer() {
+ // This does not destroy the render buffer because that would require that
+ // the associated GL context was current. Just check that it was explicitly
+ // destroyed.
+ DCHECK_EQ(id_, 0u);
+}
+
+void RenderBuffer::Create() {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ Destroy();
+ glGenRenderbuffersEXT(1, &id_);
+}
+
+bool RenderBuffer::AllocateStorage(const gfx::Size& size, GLenum format) {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ ScopedRenderBufferBinder binder(decoder_, id_);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER,
+ format,
+ size.width(),
+ size.height());
+ return glGetError() == GL_NO_ERROR;
+}
+
+void RenderBuffer::Destroy() {
+ if (id_ != 0) {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ glDeleteRenderbuffersEXT(1, &id_);
+ id_ = 0;
+ }
+}
+
+FrameBuffer::FrameBuffer(GLES2DecoderImpl* decoder)
+ : decoder_(decoder),
+ id_(0) {
+}
+
+FrameBuffer::~FrameBuffer() {
+ // This does not destroy the frame buffer because that would require that
+ // the associated GL context was current. Just check that it was explicitly
+ // destroyed.
+ DCHECK_EQ(id_, 0u);
+}
+
+void FrameBuffer::Create() {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ Destroy();
+ glGenFramebuffersEXT(1, &id_);
+}
+
+void FrameBuffer::AttachRenderTexture(Texture* texture) {
+ DCHECK_NE(id_, 0u);
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ ScopedFrameBufferBinder binder(decoder_, id_);
+ GLuint attach_id = texture ? texture->id() : 0;
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ attach_id,
+ 0);
+}
+
+void FrameBuffer::AttachDepthStencilRenderBuffer(RenderBuffer* render_buffer) {
+ DCHECK_NE(id_, 0u);
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ ScopedFrameBufferBinder binder(decoder_, id_);
+ GLuint attach_id = render_buffer ? render_buffer->id() : 0;
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER,
+ attach_id);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER,
+ attach_id);
+}
+
+void FrameBuffer::Clear(GLbitfield buffers) {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ ScopedFrameBufferBinder binder(decoder_, id_);
+ glClear(buffers);
+}
+
+void FrameBuffer::Destroy() {
+ if (id_ != 0) {
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ glDeleteFramebuffersEXT(1, &id_);
+ id_ = 0;
+ }
+}
+
+GLenum FrameBuffer::CheckStatus() {
+ DCHECK_NE(id_, 0u);
+ ScopedGLErrorSuppressor suppressor(decoder_);
+ ScopedFrameBufferBinder binder(decoder_, id_);
+ return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
+}
+
GLES2Decoder* GLES2Decoder::Create(ContextGroup* group) {
return new GLES2DecoderImpl(group);
}
@@ -750,11 +1161,21 @@ GLES2DecoderImpl::GLES2DecoderImpl(ContextGroup* group)
anti_aliased_(false) {
}
-bool GLES2DecoderImpl::Initialize() {
+bool GLES2DecoderImpl::Initialize(GLES2Decoder* parent,
+ const gfx::Size& size,
+ uint32 parent_client_texture_id) {
+ // Keep only a weak pointer to the parent so we don't unmap its client
+ // frame buffer is after it has been destroyed.
+ if (parent)
+ parent_ = static_cast<GLES2DecoderImpl*>(parent)->AsWeakPtr();
+
+ pending_size_ = size;
+
if (!InitPlatformSpecific()) {
Destroy();
return false;
}
+
if (!MakeCurrent()) {
Destroy();
return false;
@@ -806,6 +1227,52 @@ bool GLES2DecoderImpl::Initialize() {
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
CHECK_GL_ERROR();
+ if (size.width() > 0 && size.height() > 0) {
+ // Create the target frame buffer. This is the one that the client renders
+ // directly to.
+ offscreen_target_frame_buffer_.reset(new FrameBuffer(this));
+ offscreen_target_frame_buffer_->Create();
+ offscreen_target_color_texture_.reset(new Texture(this));
+ offscreen_target_color_texture_->Create();
+ offscreen_target_depth_stencil_render_buffer_.reset(
+ new RenderBuffer(this));
+ offscreen_target_depth_stencil_render_buffer_->Create();
+
+ // Create the saved offscreen texture. The target frame buffer is copied
+ // here when SwapBuffers is called.
+ offscreen_saved_color_texture_.reset(new Texture(this));
+ offscreen_saved_color_texture_->Create();
+
+ // Create the temporary frame buffer, used to operate on render textures
+ // without concern for state the client might have changed on the frame
+ // buffers it has access to, like the clear color and the color mask.
+ temporary_frame_buffer_.reset(new FrameBuffer(this));
+ temporary_frame_buffer_->Create();
+
+ // Map the ID of the saved offscreen texture into the parent so that
+ // it can reference it.
+ if (parent_) {
+ GLuint service_id = offscreen_saved_color_texture_->id();
+ parent_->id_manager()->AddMapping(parent_client_texture_id,
+ service_id);
+ TextureManager::TextureInfo* info =
+ parent_->CreateTextureInfo(service_id);
+ parent_->texture_manager()->SetInfoTarget(info, GL_TEXTURE_2D);
+ }
+
+ // Allocate the render buffers at their initial size and check the status
+ // of the frame buffers is okay.
+ if (!UpdateOffscreenFrameBufferSize()) {
+ DLOG(ERROR) << "Could not allocate offscreen buffer storage.";
+ Destroy();
+ return false;
+ }
+
+ // Bind to the new default frame buffer (the offscreen target frame buffer).
+ // This should now be associated with ID zero.
+ DoBindFramebuffer(GL_FRAMEBUFFER, 0);
+ }
+
return true;
}
@@ -919,7 +1386,7 @@ bool GLES2DecoderImpl::InitializeOneOff(bool anti_aliased) {
// GL context was successfully created and applied to the window's DC.
// Startup GLEW, the GL extensions wrangler.
if (InitGlew()) {
- DLOG(INFO) << "Initialized GLEW " << ::glewGetString(GLEW_VERSION);
+ DLOG(INFO) << "Initialized GLEW " << glewGetString(GLEW_VERSION);
} else {
::wglMakeCurrent(intermediate_dc, NULL);
::wglDeleteContext(gl_context);
@@ -1124,6 +1591,7 @@ void GLES2DecoderImpl::UnregisterObjects(
}
bool GLES2DecoderImpl::InitPlatformSpecific() {
+ bool offscreen = pending_size_.width() > 0 && pending_size_.height() > 0;
#if defined(UNIT_TEST)
#elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2)
#elif defined(OS_WIN)
@@ -1132,22 +1600,13 @@ bool GLES2DecoderImpl::InitPlatformSpecific() {
if (!success)
return false;
- if (hwnd()) {
- // The GL context will render to this window.
- gl_device_context_ = ::GetDC(hwnd());
-
- if (!::SetPixelFormat(gl_device_context_,
- pixel_format_,
- &kPixelFormatDescriptor)) {
- DLOG(ERROR) << "Unable to set the pixel format for GL context.";
- DestroyPlatformSpecific();
- return false;
- }
- } else {
+ if (offscreen) {
// Create a device context compatible with the primary display.
HDC display_device_context = ::CreateDC(L"DISPLAY", NULL, NULL, NULL);
- // Create a 1 x 1 pbuffer suitable for use with the device.
+ // Create a 1 x 1 pbuffer suitable for use with the device. This is just
+ // a stepping stone towards creating a frame buffer object. It doesn't
+ // matter what size it is.
const int kNoAttributes[] = { 0 };
pbuffer_ = ::wglCreatePbufferARB(display_device_context,
pixel_format_,
@@ -1166,6 +1625,17 @@ bool GLES2DecoderImpl::InitPlatformSpecific() {
DestroyPlatformSpecific();
return false;
}
+ } else {
+ // The GL context will render to this window.
+ gl_device_context_ = ::GetDC(hwnd());
+
+ if (!::SetPixelFormat(gl_device_context_,
+ pixel_format_,
+ &kPixelFormatDescriptor)) {
+ DLOG(ERROR) << "Unable to set the pixel format for GL context.";
+ DestroyPlatformSpecific();
+ return false;
+ }
}
gl_context_ = ::wglCreateContext(gl_device_context_);
@@ -1174,11 +1644,32 @@ bool GLES2DecoderImpl::InitPlatformSpecific() {
DestroyPlatformSpecific();
return false;
}
+
+ if (parent_) {
+ if (!wglShareLists(parent_->gl_context_, gl_context_)) {
+ DLOG(ERROR) << "Could not share GL contexts.";
+ DestroyPlatformSpecific();
+ return false;
+ }
+ }
+
#elif defined(OS_LINUX)
+ // TODO(apatrick): offscreen rendering not yet supported on this platform.
+ DCHECK(!offscreen);
+
+ // TODO(apatrick): parent contexts not yet supported on this platform.
+ DCHECK(!parent_);
+
DCHECK(window());
if (!window()->Initialize())
return false;
#elif defined(OS_MACOSX)
+ // TODO(apatrick): offscreen rendering not yet supported on this platform.
+ DCHECK(!offscreen);
+
+ // TODO(apatrick): parent contexts not yet supported on this platform.
+ DCHECK(!parent_);
+
return surface_.Initialize();
#endif
@@ -1192,7 +1683,7 @@ bool GLES2DecoderImpl::InitGlew() {
GLenum glew_error = glewInit();
if (glew_error != GLEW_OK) {
DLOG(ERROR) << "Unable to initialise GLEW : "
- << ::glewGetErrorString(glew_error);
+ << glewGetErrorString(glew_error);
return false;
}
@@ -1265,6 +1756,85 @@ void GLES2DecoderImpl::DestroyPlatformSpecific() {
#endif
}
+bool GLES2DecoderImpl::UpdateOffscreenFrameBufferSize() {
+ if (current_size_ != pending_size_)
+ return true;
+
+ // Reallocate the offscreen target buffers.
+ if (!offscreen_target_color_texture_->AllocateStorage(pending_size_)) {
+ return false;
+ }
+
+ if (!offscreen_target_depth_stencil_render_buffer_->AllocateStorage(
+ pending_size_, GL_DEPTH24_STENCIL8)) {
+ return false;
+ }
+
+ // Attach the offscreen target buffers to the temporary frame buffer
+ // so they can be cleared using that frame buffer's clear parameters (all
+ // zero, no color mask, etc).
+ temporary_frame_buffer_->AttachRenderTexture(
+ offscreen_target_color_texture_.get());
+ temporary_frame_buffer_->AttachDepthStencilRenderBuffer(
+ offscreen_target_depth_stencil_render_buffer_.get());
+ if (temporary_frame_buffer_->CheckStatus() !=
+ GL_FRAMEBUFFER_COMPLETE) {
+ return false;
+ }
+
+ // Clear the offscreen target buffers to all zero (using the saved frame
+ // buffer they are temporarily attached to).
+ temporary_frame_buffer_->Clear(
+ GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ // Detach the offscreen target buffer.
+ temporary_frame_buffer_->AttachRenderTexture(NULL);
+ temporary_frame_buffer_->AttachDepthStencilRenderBuffer(NULL);
+
+ // Attach the offscreen target buffers to the proper frame buffer.
+ offscreen_target_frame_buffer_->AttachRenderTexture(
+ offscreen_target_color_texture_.get());
+ offscreen_target_frame_buffer_->AttachDepthStencilRenderBuffer(
+ offscreen_target_depth_stencil_render_buffer_.get());
+ if (offscreen_target_frame_buffer_->CheckStatus() !=
+ GL_FRAMEBUFFER_COMPLETE) {
+ return false;
+ }
+
+ // Create the saved offscreen color texture.
+ offscreen_saved_color_texture_->AllocateStorage(pending_size_);
+
+ // Clear the offscreen saved color texture by copying the cleared target
+ // frame buffer into it.
+ {
+ ScopedFrameBufferBinder binder(this, offscreen_target_frame_buffer_->id());
+ offscreen_saved_color_texture_->Copy(pending_size_);
+ }
+
+ // Update the info about the offscreen saved color texture in the parent.
+ // The reference to the parent is a weak pointer and will become null if the
+ // parent is later destroyed.
+ if (parent_) {
+ GLuint service_id = offscreen_saved_color_texture_->id();
+
+ TextureManager::TextureInfo* info =
+ parent_->texture_manager()->GetTextureInfo(service_id);
+ DCHECK(info);
+
+ info->SetLevelInfo(GL_TEXTURE_2D,
+ 0, // level
+ GL_RGBA,
+ pending_size_.width(), pending_size_.height(),
+ 1, // depth
+ 0, // border
+ GL_RGBA,
+ GL_UNSIGNED_BYTE);
+ }
+
+ current_size_ = pending_size_;
+ return true;
+}
+
#if defined(OS_MACOSX)
uint64 GLES2DecoderImpl::SetWindowSizeForIOSurface(int32 width, int32 height) {
@@ -1300,6 +1870,37 @@ void GLES2DecoderImpl::SetSwapBuffersCallback(Callback0::Type* callback) {
}
void GLES2DecoderImpl::Destroy() {
+ MakeCurrent();
+
+ // Remove the saved frame buffer mapping from the parent decoder. The
+ // parent pointer is a weak pointer so it will be null if the parent has
+ // already been destroyed.
+ if (parent_) {
+ // First check the texture has been mapped into the parent. This might not
+ // be the case if initialization failed midway through.
+ GLuint service_id = offscreen_saved_color_texture_->id();
+ GLuint client_id;
+ if (parent_->id_manager()->GetClientId(service_id, &client_id)) {
+ parent_->texture_manager()->RemoveTextureInfo(service_id);
+ parent_->id_manager()->RemoveMapping(client_id, service_id);
+ }
+ }
+
+ if (offscreen_target_frame_buffer_.get())
+ offscreen_target_frame_buffer_->Destroy();
+
+ if (offscreen_target_color_texture_.get())
+ offscreen_target_color_texture_->Destroy();
+
+ if (offscreen_target_depth_stencil_render_buffer_.get())
+ offscreen_target_depth_stencil_render_buffer_->Destroy();
+
+ if (temporary_frame_buffer_.get())
+ temporary_frame_buffer_->Destroy();
+
+ if (offscreen_saved_color_texture_.get())
+ offscreen_saved_color_texture_->Destroy();
+
#if defined(UNIT_TEST)
#elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2)
#elif defined(OS_LINUX)
@@ -1312,6 +1913,13 @@ void GLES2DecoderImpl::Destroy() {
DestroyPlatformSpecific();
}
+void GLES2DecoderImpl::ResizeOffscreenFrameBuffer(const gfx::Size& size) {
+ // We can't resize the render buffers immediately because there might be a
+ // partial frame rendered into them and we don't want the tail end of that
+ // rendered into the reallocated storage. Defer until the next SwapBuffers.
+ pending_size_ = size;
+}
+
const char* GLES2DecoderImpl::GetCommandName(unsigned int command_id) const {
if (command_id > kStartPoint && command_id < kNumCommands) {
return gles2::GetCommandName(static_cast<CommandId>(command_id));
@@ -1452,6 +2060,12 @@ void GLES2DecoderImpl::DoBindBuffer(GLenum target, GLuint buffer) {
void GLES2DecoderImpl::DoBindFramebuffer(GLenum target, GLuint framebuffer) {
bound_framebuffer_ = framebuffer;
+
+ // When rendering to an offscreen frame buffer, instead of unbinding from
+ // the current frame buffer, bind to the offscreen target frame buffer.
+ if (framebuffer == 0 && offscreen_target_frame_buffer_.get())
+ framebuffer = offscreen_target_frame_buffer_->id();
+
glBindFramebufferEXT(target, framebuffer);
}
@@ -1685,24 +2299,6 @@ void GLES2DecoderImpl::DoLinkProgram(GLuint program) {
}
};
-void GLES2DecoderImpl::DoSwapBuffers() {
-#if defined(UNIT_TEST)
-#elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2)
-#elif defined(OS_WIN)
- ::SwapBuffers(gl_device_context_);
-#elif defined(OS_LINUX)
- DCHECK(window());
- window()->SwapBuffers();
-#elif defined(OS_MACOSX)
- // TODO(kbr): Need to property hook up and track the OpenGL state and hook
- // up the notion of the currently bound FBO.
- surface_.SwapBuffers();
-#endif
- if (swap_buffers_callback_.get()) {
- swap_buffers_callback_->Run();
- }
-}
-
void GLES2DecoderImpl::DoTexParameterf(
GLenum target, GLenum pname, GLfloat param) {
TextureManager::TextureInfo* info = GetTextureInfoForTarget(target);
@@ -1812,6 +2408,13 @@ void GLES2DecoderImpl::CopyRealGLErrorsToWrapper() {
}
}
+void GLES2DecoderImpl::ClearRealGLErrors() {
+ GLenum error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ NOTREACHED() << "GL error " << error << " was unhandled.";
+ }
+}
+
bool GLES2DecoderImpl::VertexAttribInfo::CanAccess(GLuint index) {
if (!enabled_) {
return true;
@@ -2981,6 +3584,48 @@ error::Error GLES2DecoderImpl::HandleGetActiveAttrib(
return error::kNoError;
}
+error::Error GLES2DecoderImpl::HandleSwapBuffers(
+ uint32 immediate_data_size, const gles2::SwapBuffers& c) {
+ // Check a client created frame buffer is not bound. TODO(apatrick):
+ // this error is overkill. It will require that the client recreate the
+ // context to continue.
+ if (bound_framebuffer_ != 0)
+ return error::kLostContext;
+
+ // If offscreen then don't actually SwapBuffers to the display. Just copy
+ // the rendered frame to another frame buffer.
+ if (offscreen_target_frame_buffer_.get()) {
+ ScopedGLErrorSuppressor suppressor(this);
+
+ // First check to see if a deferred offscreen render buffer resize is
+ // pending.
+ if (!UpdateOffscreenFrameBufferSize())
+ return error::kLostContext;
+
+ ScopedFrameBufferBinder binder(this, offscreen_target_frame_buffer_->id());
+ offscreen_saved_color_texture_->Copy(current_size_);
+ } else {
+#if defined(UNIT_TEST)
+#elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2)
+#elif defined(OS_WIN)
+ ::SwapBuffers(gl_device_context_);
+#elif defined(OS_LINUX)
+ DCHECK(window());
+ window()->SwapBuffers();
+#elif defined(OS_MACOSX)
+ // TODO(kbr): Need to property hook up and track the OpenGL state and hook
+ // up the notion of the currently bound FBO.
+ surface_.SwapBuffers();
+#endif
+ }
+
+ if (swap_buffers_callback_.get()) {
+ swap_buffers_callback_->Run();
+ }
+
+ return error::kNoError;
+}
+
// Include the auto-generated part of this file. We split this because it means
// we can easily edit the non-auto generated parts right here in this file
// instead of having to edit some template or the code generator.
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index 26c6239..58e1193 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -15,6 +15,8 @@
#if defined(OS_MACOSX)
#include "app/surface/transport_dib.h"
#endif
+
+#include "gfx/size.h"
#include "gpu/command_buffer/service/common_decoder.h"
@@ -71,14 +73,20 @@ class GLES2Decoder : public CommonDecoder {
Callback1<TransportDIB::Id>::Type* deallocator) = 0;
#endif
- // Initializes the graphics context.
+ // Initializes the graphics context. Can create an offscreen
+ // decoder with a frame buffer that can be referenced from the parent.
// Returns:
// true if successful.
- virtual bool Initialize() = 0;
+ virtual bool Initialize(GLES2Decoder* parent,
+ const gfx::Size& size,
+ uint32 parent_texture_id) = 0;
// Destroys the graphics context.
virtual void Destroy() = 0;
+ // Resize an offscreen frame buffer.
+ virtual void ResizeOffscreenFrameBuffer(const gfx::Size& size) = 0;
+
// Make this decoder's GL context current.
virtual bool MakeCurrent() = 0;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index cb534a4..7507d95 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -2842,11 +2842,5 @@ error::Error GLES2DecoderImpl::HandleViewport(
return error::kNoError;
}
-error::Error GLES2DecoderImpl::HandleSwapBuffers(
- uint32 immediate_data_size, const gles2::SwapBuffers& c) {
- DoSwapBuffers();
- return error::kNoError;
-}
-
#endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index 2ef2d2d..f94eb7d 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -7,6 +7,7 @@
#ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_MOCK_H_
#define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_MOCK_H_
+#include "gfx/size.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "base/callback.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -28,8 +29,11 @@ class MockGLES2Decoder : public GLES2Decoder {
#if defined(OS_MACOSX)
MOCK_METHOD2(SetWindowSize, uint64(int32 width, int32 height));
#endif
- MOCK_METHOD0(Initialize, bool());
+ MOCK_METHOD3(Initialize, bool(GLES2Decoder* parent,
+ const gfx::Size& size,
+ uint32 parent_texture_id));
MOCK_METHOD0(Destroy, void());
+ MOCK_METHOD1(ResizeOffscreenFrameBuffer, void(const gfx::Size& size));
MOCK_METHOD0(MakeCurrent, bool());
MOCK_METHOD1(GetServiceIdForTesting, uint32(uint32 client_id));
MOCK_METHOD0(GetGLES2Util, GLES2Util*());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 0bb32c6..10acd1b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -92,7 +92,7 @@ void GLES2DecoderTestBase::SetUp() {
shared_memory_id_ = kSharedMemoryId;
decoder_.reset(GLES2Decoder::Create(&group_));
- decoder_->Initialize();
+ decoder_->Initialize(NULL, gfx::Size(), 0);
decoder_->set_engine(engine_.get());
EXPECT_CALL(*gl_, GenBuffersARB(_, _))
diff --git a/gpu/command_buffer/service/gpu_processor.cc b/gpu/command_buffer/service/gpu_processor.cc
index 7a748e7..d08069c 100644
--- a/gpu/command_buffer/service/gpu_processor.cc
+++ b/gpu/command_buffer/service/gpu_processor.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/callback.h"
+#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "gpu/command_buffer/service/gpu_processor.h"
@@ -12,7 +13,8 @@ namespace gpu {
GPUProcessor::GPUProcessor(CommandBuffer* command_buffer)
: command_buffer_(command_buffer),
- commands_per_update_(100) {
+ commands_per_update_(100),
+ method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
DCHECK(command_buffer);
decoder_.reset(gles2::GLES2Decoder::Create(&group_));
decoder_->set_engine(this);
@@ -23,13 +25,15 @@ GPUProcessor::GPUProcessor(CommandBuffer* command_buffer,
CommandParser* parser,
int commands_per_update)
: command_buffer_(command_buffer),
- commands_per_update_(commands_per_update) {
+ commands_per_update_(commands_per_update),
+ method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
DCHECK(command_buffer);
decoder_.reset(decoder);
parser_.reset(parser);
}
GPUProcessor::~GPUProcessor() {
+ Destroy();
}
void GPUProcessor::ProcessCommands() {
@@ -59,7 +63,8 @@ void GPUProcessor::ProcessCommands() {
if (!parser_->IsEmpty()) {
MessageLoop::current()->PostTask(
- FROM_HERE, NewRunnableMethod(this, &GPUProcessor::ProcessCommands));
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(&GPUProcessor::ProcessCommands));
}
}
@@ -83,6 +88,10 @@ int32 GPUProcessor::GetGetOffset() {
return parser_->get();
}
+void GPUProcessor::ResizeOffscreenFrameBuffer(const gfx::Size& size) {
+ decoder_->ResizeOffscreenFrameBuffer(size);
+}
+
#if defined(OS_MACOSX)
uint64 GPUProcessor::SetWindowSizeForIOSurface(int32 width, int32 height) {
return decoder_->SetWindowSizeForIOSurface(width, height);
diff --git a/gpu/command_buffer/service/gpu_processor.h b/gpu/command_buffer/service/gpu_processor.h
index d34e35d..d61bd60 100644
--- a/gpu/command_buffer/service/gpu_processor.h
+++ b/gpu/command_buffer/service/gpu_processor.h
@@ -9,7 +9,9 @@
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/shared_memory.h"
+#include "base/task.h"
#include "gfx/native_widget_types.h"
+#include "gfx/size.h"
#include "gpu/command_buffer/common/command_buffer.h"
#include "gpu/command_buffer/service/cmd_buffer_engine.h"
#include "gpu/command_buffer/service/cmd_parser.h"
@@ -20,8 +22,7 @@ namespace gpu {
// This class processes commands in a command buffer. It is event driven and
// posts tasks to the current message loop to do additional work.
-class GPUProcessor : public base::RefCounted<GPUProcessor>,
- public CommandBufferEngine {
+class GPUProcessor : public CommandBufferEngine {
public:
explicit GPUProcessor(CommandBuffer* command_buffer);
@@ -31,7 +32,10 @@ class GPUProcessor : public base::RefCounted<GPUProcessor>,
CommandParser* parser,
int commands_per_update);
- virtual bool Initialize(gfx::PluginWindowHandle hwnd);
+ virtual bool Initialize(gfx::PluginWindowHandle hwnd,
+ GPUProcessor* parent,
+ const gfx::Size& size,
+ uint32 parent_texture_id);
virtual ~GPUProcessor();
@@ -45,6 +49,9 @@ class GPUProcessor : public base::RefCounted<GPUProcessor>,
virtual bool SetGetOffset(int32 offset);
virtual int32 GetGetOffset();
+ // Asynchronously resizes an offscreen frame buffer.
+ void ResizeOffscreenFrameBuffer(const gfx::Size& size);
+
#if defined(OS_MACOSX)
// Needed only on Mac OS X, which does not render into an on-screen
// window and therefore requires the backing store to be resized
@@ -71,37 +78,15 @@ class GPUProcessor : public base::RefCounted<GPUProcessor>,
// through the ProcessCommands callback.
CommandBuffer* command_buffer_;
- scoped_ptr< ::base::SharedMemory> mapped_ring_buffer_;
int commands_per_update_;
gles2::ContextGroup group_;
scoped_ptr<gles2::GLES2Decoder> decoder_;
scoped_ptr<CommandParser> parser_;
+
+ ScopedRunnableMethodFactory<GPUProcessor> method_factory_;
};
} // namespace gpu
-// Callbacks to the GPUProcessor hold a reference count.
-template <typename Method>
-class CallbackStorage<gpu::GPUProcessor, Method> {
- public:
- CallbackStorage(gpu::GPUProcessor* obj, Method method)
- : obj_(obj),
- meth_(method) {
- DCHECK(obj_);
- obj_->AddRef();
- }
-
- ~CallbackStorage() {
- obj_->Release();
- }
-
- protected:
- gpu::GPUProcessor* obj_;
- Method meth_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CallbackStorage);
-};
-
#endif // GPU_COMMAND_BUFFER_SERVICE_GPU_PROCESSOR_H_
diff --git a/gpu/command_buffer/service/gpu_processor_linux.cc b/gpu/command_buffer/service/gpu_processor_linux.cc
index 205cceb..d430933 100644
--- a/gpu/command_buffer/service/gpu_processor_linux.cc
+++ b/gpu/command_buffer/service/gpu_processor_linux.cc
@@ -10,7 +10,10 @@ using ::base::SharedMemory;
namespace gpu {
-bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) {
+bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle,
+ GPUProcessor* parent,
+ const gfx::Size& size,
+ uint32 parent_texture_id) {
DCHECK(handle);
// Cannot reinitialize.
@@ -34,17 +37,27 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) {
// Initialize GAPI immediately if the window handle is valid.
XWindowWrapper *window = new XWindowWrapper(GDK_DISPLAY(), handle);
decoder_->set_window_wrapper(window);
- return decoder_->Initialize();
-}
+ gles2::GLES2Decoder* parent_decoder = parent ? parent->decoder_.get() : NULL;
+ if (!decoder_->Initialize(parent_decoder,
+ size,
+ parent_texture_id)) {
+ Destroy();
+ return false;
+ }
+
+ return true;}
void GPUProcessor::Destroy() {
- // Destroy GAPI if window handle has not already become invalid.
- XWindowWrapper *window = decoder_->window();
- if (window) {
+ // Destroy decoder if initialized.
+ if (decoder_.get()) {
+ XWindowWrapper *window = decoder_->window();
decoder_->Destroy();
decoder_->set_window_wrapper(NULL);
delete window;
+ decoder_.reset();
}
+
+ parser_.reset();
}
} // namespace gpu
diff --git a/gpu/command_buffer/service/gpu_processor_mac.cc b/gpu/command_buffer/service/gpu_processor_mac.cc
index a36560c..ef13fc6 100644
--- a/gpu/command_buffer/service/gpu_processor_mac.cc
+++ b/gpu/command_buffer/service/gpu_processor_mac.cc
@@ -8,7 +8,10 @@ using ::base::SharedMemory;
namespace gpu {
-bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) {
+bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle,
+ GPUProcessor* parent,
+ const gfx::Size& size,
+ uint32 parent_texture_id) {
// At this level we do not need the PluginWindowHandle. It is only
// needed at the CommandBufferStub level to identify which GPU
// plugin instance is creating a new backing store in response to a
@@ -29,11 +32,25 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) {
}
// Initialize GAPI.
- return decoder_->Initialize();
+ gles2::GLES2Decoder* parent_decoder = parent ? parent->decoder_.get() : NULL;
+ if (!decoder_->Initialize(parent_decoder,
+ size,
+ parent_texture_id)) {
+ Destroy();
+ return false;
+ }
+
+ return true;
}
void GPUProcessor::Destroy() {
- decoder_->Destroy();
+ // Destroy decoder if initialized.
+ if (decoder_.get()) {
+ decoder_->Destroy();
+ decoder_.reset();
+ }
+
+ parser_.reset();
}
} // namespace gpu
diff --git a/gpu/command_buffer/service/gpu_processor_unittest.cc b/gpu/command_buffer/service/gpu_processor_unittest.cc
index 51cfe01..95be087 100644
--- a/gpu/command_buffer/service/gpu_processor_unittest.cc
+++ b/gpu/command_buffer/service/gpu_processor_unittest.cc
@@ -58,10 +58,10 @@ class GPUProcessorTest : public testing::Test {
0,
async_api_.get());
- processor_ = new GPUProcessor(command_buffer_.get(),
- decoder_,
- parser_,
- 2);
+ processor_.reset(new GPUProcessor(command_buffer_.get(),
+ decoder_,
+ parser_,
+ 2));
}
virtual void TearDown() {
@@ -85,7 +85,7 @@ class GPUProcessorTest : public testing::Test {
gles2::MockGLES2Decoder* decoder_;
CommandParser* parser_;
scoped_ptr<AsyncAPIMock> async_api_;
- scoped_refptr<GPUProcessor> processor_;
+ scoped_ptr<GPUProcessor> processor_;
};
TEST_F(GPUProcessorTest, ProcessorDoesNothingIfRingBufferIsEmpty) {
diff --git a/gpu/command_buffer/service/gpu_processor_win.cc b/gpu/command_buffer/service/gpu_processor_win.cc
index bce783b..537872c 100644
--- a/gpu/command_buffer/service/gpu_processor_win.cc
+++ b/gpu/command_buffer/service/gpu_processor_win.cc
@@ -10,7 +10,10 @@ using ::base::SharedMemory;
namespace gpu {
-bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) {
+bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle,
+ GPUProcessor* parent,
+ const gfx::Size& size,
+ uint32 parent_texture_id) {
// Cannot reinitialize.
if (parser_.get())
return false;
@@ -31,14 +34,25 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) {
// Initialize GAPI immediately if the window handle is valid.
decoder_->set_hwnd(handle);
- return decoder_->Initialize();
+ gles2::GLES2Decoder* parent_decoder = parent ? parent->decoder_.get() : NULL;
+ if (!decoder_->Initialize(parent_decoder,
+ size,
+ parent_texture_id)) {
+ Destroy();
+ return false;
+ }
+
+ return true;
}
void GPUProcessor::Destroy() {
// Destroy decoder if initialized.
- if (parser_.get()) {
+ if (decoder_.get()) {
decoder_->Destroy();
decoder_->set_hwnd(NULL);
+ decoder_.reset();
}
+
+ parser_.reset();
}
} // namespace gpu
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index d6a1dce..4369199 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -265,12 +265,13 @@ TextureManager::TextureManager(
max_cube_map_texture_size)) {
}
-void TextureManager::CreateTextureInfo(GLuint texture_id) {
+TextureManager::TextureInfo* TextureManager::CreateTextureInfo(
+ GLuint texture_id) {
+ TextureInfo::Ref info(new TextureInfo(texture_id));
std::pair<TextureInfoMap::iterator, bool> result =
- texture_infos_.insert(
- std::make_pair(texture_id,
- TextureInfo::Ref(new TextureInfo(texture_id))));
+ texture_infos_.insert(std::make_pair(texture_id, info));
DCHECK(result.second);
+ return info.get();
}
TextureManager::TextureInfo* TextureManager::GetTextureInfo(
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 358cbbc..2dba85d 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -229,7 +229,7 @@ class TextureManager {
}
// Creates a new texture info.
- void CreateTextureInfo(GLuint texture_id);
+ TextureInfo* CreateTextureInfo(GLuint texture_id);
// Gets the texture info for the given texture.
TextureInfo* GetTextureInfo(GLuint texture_id);
diff --git a/gpu/demos/framework/window.cc b/gpu/demos/framework/window.cc
index 600514f..0a9db79 100644
--- a/gpu/demos/framework/window.cc
+++ b/gpu/demos/framework/window.cc
@@ -50,20 +50,23 @@ void Window::OnPaint() {
::gles2::GetGLContext()->SwapBuffers();
}
+// TODO(apatrick): It looks like all the resources allocated here leak. We
+// should fix that if we want to use this Window class for anything beyond this
+// simple use case.
bool Window::CreateRenderContext(gfx::PluginWindowHandle hwnd) {
scoped_ptr<CommandBufferService> command_buffer(new CommandBufferService);
if (!command_buffer->Initialize(kCommandBufferSize)) {
return false;
}
- scoped_refptr<GPUProcessor> gpu_processor(
+ GPUProcessor* gpu_processor(
new GPUProcessor(command_buffer.get()));
- if (!gpu_processor->Initialize(hwnd)) {
+ if (!gpu_processor->Initialize(hwnd, NULL, gfx::Size(), 0)) {
return false;
}
command_buffer->SetPutOffsetChangeCallback(
- NewCallback(gpu_processor.get(), &GPUProcessor::ProcessCommands));
+ NewCallback(gpu_processor, &GPUProcessor::ProcessCommands));
GLES2CmdHelper* helper = new GLES2CmdHelper(command_buffer.get());
if (!helper->Initialize()) {
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index 4e3106f..06818fc 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -247,6 +247,7 @@
'dependencies': [
'command_buffer_common',
'gl_libs',
+ '../gfx/gfx.gyp:gfx',
],
'sources': [
'command_buffer/service/common_decoder.cc',