summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/service/gles2_cmd_decoder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gpu/command_buffer/service/gles2_cmd_decoder.cc')
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc727
1 files changed, 686 insertions, 41 deletions
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.