Index: gpu/command_buffer/service/gles2_cmd_decoder.cc |
=================================================================== |
--- gpu/command_buffer/service/gles2_cmd_decoder.cc (revision 42644) |
+++ gpu/command_buffer/service/gles2_cmd_decoder.cc (working copy) |
@@ -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 @@ |
#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 @@ |
// 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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
// 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 @@ |
#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 @@ |
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 @@ |
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 @@ |
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 @@ |
// 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 @@ |
} |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
#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::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 @@ |
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::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::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::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 @@ |
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. |