diff options
author | apatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-25 22:08:35 +0000 |
---|---|---|
committer | apatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-25 22:08:35 +0000 |
commit | 6217d397bf501ec1ec5b7270d493006badd4d87a (patch) | |
tree | fce6c8e9c15b79bab29a9535ce9929dd9d53735b /gpu | |
parent | 21dedcc12d21ccb288244249522d77bc074c501e (diff) | |
download | chromium_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')
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', |