blob: 12b02d1d0d91dc72ef852fb1edfd4afbc9fa638c [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include <stdio.h>
#include <algorithm>
#include <list>
#include <map>
#include <string>
#include <vector>
#include "base/at_exit.h"
#include "base/atomicops.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#if defined(OS_MACOSX)
#include "base/mac/scoped_cftyperef.h"
#endif
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/string_number_conversions.h"
#include "build/build_config.h"
#define GLES2_GPU_SERVICE 1
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/common/id_allocator.h"
#include "gpu/command_buffer/service/buffer_manager.h"
#include "gpu/command_buffer/service/cmd_buffer_engine.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/framebuffer_manager.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
#include "gpu/command_buffer/service/gles2_cmd_validation.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/program_manager.h"
#include "gpu/command_buffer/service/query_manager.h"
#include "gpu/command_buffer/service/renderbuffer_manager.h"
#include "gpu/command_buffer/service/shader_manager.h"
#include "gpu/command_buffer/service/shader_translator.h"
#include "gpu/command_buffer/service/shader_translator_cache.h"
#include "gpu/command_buffer/service/stream_texture.h"
#include "gpu/command_buffer/service/stream_texture_manager.h"
#include "gpu/command_buffer/service/texture_definition.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/command_buffer/service/vertex_attrib_manager.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface.h"
#if defined(OS_MACOSX)
#include "ui/surface/io_surface_support_mac.h"
#endif
#if !defined(GL_DEPTH24_STENCIL8)
#define GL_DEPTH24_STENCIL8 0x88F0
#endif
#define TRACE_BACKBUFFER_MEMORY_TOTAL(decoder) \
TRACE_COUNTER_ID1( \
"GLES2DecoderImpl", "BackbufferMemory", decoder, \
decoder->GetBackbufferMemoryTotal())
namespace gpu {
namespace gles2 {
namespace {
static const char kOESDerivativeExtension[] = "GL_OES_standard_derivatives";
}
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
// a few others) are 32bits. If they are not 32bits the code will have to change
// to call those GL functions with service side memory and then copy the results
// to shared memory, converting the sizes.
COMPILE_ASSERT(sizeof(GLint) == sizeof(uint32), // NOLINT
GLint_not_same_size_as_uint32);
COMPILE_ASSERT(sizeof(GLsizei) == sizeof(uint32), // NOLINT
GLint_not_same_size_as_uint32);
COMPILE_ASSERT(sizeof(GLfloat) == sizeof(float), // NOLINT
GLfloat_not_same_size_as_float);
// TODO(kbr): the use of this anonymous namespace core dumps the
// linker on Mac OS X 10.6 when the symbol ordering file is used
// namespace {
// Returns the address of the first byte after a struct.
template <typename T>
const void* AddressAfterStruct(const T& pod) {
return reinterpret_cast<const uint8*>(&pod) + sizeof(pod);
}
// Returns the address of the frst byte after the struct or NULL if size >
// immediate_data_size.
template <typename RETURN_TYPE, typename COMMAND_TYPE>
RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod,
uint32 size,
uint32 immediate_data_size) {
return (size <= immediate_data_size) ?
static_cast<RETURN_TYPE>(const_cast<void*>(AddressAfterStruct(pod))) :
NULL;
}
// Computes the data size for certain gl commands like glUniform.
bool ComputeDataSize(
GLuint count,
size_t size,
unsigned int elements_per_unit,
uint32* dst) {
uint32 value;
if (!SafeMultiplyUint32(count, size, &value)) {
return false;
}
if (!SafeMultiplyUint32(value, elements_per_unit, &value)) {
return false;
}
*dst = value;
return true;
}
// A struct to hold info about each command.
struct CommandInfo {
int arg_flags; // How to handle the arguments for this command
int arg_count; // How many arguments are expected for this command.
};
// A table of CommandInfo for all the commands.
const CommandInfo g_command_info[] = {
#define GLES2_CMD_OP(name) { \
name::kArgFlags, \
sizeof(name) / sizeof(CommandBufferEntry) - 1, }, /* NOLINT */ \
GLES2_COMMAND_LIST(GLES2_CMD_OP)
#undef GLES2_CMD_OP
};
// Return true if a character belongs to the ASCII subset as defined in
// GLSL ES 1.0 spec section 3.1.
static bool CharacterIsValidForGLES(unsigned char c) {
// Printing characters are valid except " $ ` @ \ ' DEL.
if (c >= 32 && c <= 126 &&
c != '"' &&
c != '$' &&
c != '`' &&
c != '@' &&
c != '\\' &&
c != '\'') {
return true;
}
// Horizontal tab, line feed, vertical tab, form feed, carriage return
// are also valid.
if (c >= 9 && c <= 13) {
return true;
}
return false;
}
static bool StringIsValidForGLES(const char* str) {
for (; *str; ++str) {
if (!CharacterIsValidForGLES(*str)) {
return false;
}
}
return true;
}
static inline GLenum GetTexInternalFormat(GLenum internal_format) {
if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
if (internal_format == GL_BGRA_EXT || internal_format == GL_BGRA8_EXT)
return GL_RGBA8;
}
return internal_format;
}
static void WrappedTexImage2D(
GLenum target,
GLint level,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const void* pixels) {
GLenum gl_internal_format = GetTexInternalFormat(internal_format);
if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
if (type == GL_FLOAT) {
switch (format) {
case GL_RGBA:
gl_internal_format = GL_RGBA32F_ARB;
break;
case GL_RGB:
gl_internal_format = GL_RGB32F_ARB;
break;
case GL_LUMINANCE_ALPHA:
gl_internal_format = GL_LUMINANCE_ALPHA32F_ARB;
break;
case GL_LUMINANCE:
gl_internal_format = GL_LUMINANCE32F_ARB;
break;
case GL_ALPHA:
gl_internal_format = GL_ALPHA32F_ARB;
break;
default:
NOTREACHED();
break;
}
} else if (type == GL_HALF_FLOAT_OES) {
switch (format) {
case GL_RGBA:
gl_internal_format = GL_RGBA16F_ARB;
break;
case GL_RGB:
gl_internal_format = GL_RGB16F_ARB;
break;
case GL_LUMINANCE_ALPHA:
gl_internal_format = GL_LUMINANCE_ALPHA16F_ARB;
break;
case GL_LUMINANCE:
gl_internal_format = GL_LUMINANCE16F_ARB;
break;
case GL_ALPHA:
gl_internal_format = GL_ALPHA16F_ARB;
break;
default:
NOTREACHED();
break;
}
}
}
glTexImage2D(
target, level, gl_internal_format, width, height, border, format, type,
pixels);
}
// Wrapper for glEnable/glDisable that doesn't suck.
static void EnableDisable(GLenum pname, bool enable) {
if (enable) {
glEnable(pname);
} else {
glDisable(pname);
}
}
// 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);
};
// Temporarily changes a decoder's bound frame buffer to a resolved version of
// the multisampled offscreen render buffer if that buffer is multisampled, and,
// if it is bound or enforce_internal_framebuffer is true. If internal is
// true, the resolved framebuffer is not visible to the parent.
class ScopedResolvedFrameBufferBinder {
public:
ScopedResolvedFrameBufferBinder(GLES2DecoderImpl* decoder,
bool enforce_internal_framebuffer,
bool internal);
~ScopedResolvedFrameBufferBinder();
private:
GLES2DecoderImpl* decoder_;
bool resolve_and_bind_;
DISALLOW_COPY_AND_ASSIGN(ScopedResolvedFrameBufferBinder);
};
// 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, GLenum format);
// Copy the contents of the currently bound frame buffer.
void Copy(const gfx::Size& size, GLenum format);
// Destroy the render texture. This must be explicitly called before
// destroying this object.
void Destroy();
// Invalidate the texture. This can be used when a context is lost and it is
// not possible to make it current in order to free the resource.
void Invalidate();
GLuint id() const {
return id_;
}
gfx::Size size() const {
return size_;
}
size_t estimated_size() const {
return estimated_size_;
}
private:
GLES2DecoderImpl* decoder_;
GLuint id_;
gfx::Size size_;
size_t estimated_size_;
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, GLsizei samples);
// Destroy the render buffer. This must be explicitly called before destroying
// this object.
void Destroy();
// Invalidate the render buffer. This can be used when a context is lost and
// it is not possible to make it current in order to free the resource.
void Invalidate();
GLuint id() const {
return id_;
}
size_t estimated_size() const {
return estimated_size_;
}
private:
GLES2DecoderImpl* decoder_;
GLuint id_;
size_t estimated_size_;
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 render buffer to a frame buffer. Note that this unbinds any
// currently bound frame buffer.
void AttachRenderBuffer(GLenum target, RenderBuffer* render_buffer);
// Destroy the frame buffer. This must be explicitly called before destroying
// this object.
void Destroy();
// Invalidate the frame buffer. This can be used when a context is lost and it
// is not possible to make it current in order to free the resource.
void Invalidate();
// See glCheckFramebufferStatusEXT.
GLenum CheckStatus();
GLuint id() const {
return id_;
}
private:
GLES2DecoderImpl* decoder_;
GLuint id_;
DISALLOW_COPY_AND_ASSIGN(FrameBuffer);
};
// } // anonymous namespace.
bool GLES2Decoder::GetServiceTextureId(uint32 client_texture_id,
uint32* service_texture_id) {
return false;
}
GLES2Decoder::GLES2Decoder()
: debug_(false),
log_commands_(false),
log_synthesized_gl_errors_(true) {
}
GLES2Decoder::~GLES2Decoder() {
}
bool GLES2Decoder::testing_force_is_angle_;
void GLES2Decoder::set_testing_force_is_angle(bool force) {
testing_force_is_angle_ = force;
}
bool GLES2Decoder::IsAngle() {
#if defined(OS_WIN)
return testing_force_is_angle_ ||
gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2;
#else
return testing_force_is_angle_;
#endif
}
// This class implements GLES2Decoder so we don't have to expose all the GLES2
// cmd stuff to outside this class.
class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>,
public GLES2Decoder {
public:
static const int kMaxLogMessages = 256;
explicit GLES2DecoderImpl(ContextGroup* group);
~GLES2DecoderImpl();
// Overridden from AsyncAPIInterface.
virtual Error DoCommand(unsigned int command,
unsigned int arg_count,
const void* args);
// Overridden from AsyncAPIInterface.
virtual const char* GetCommandName(unsigned int command_id) const;
// Overridden from GLES2Decoder.
virtual bool Initialize(const scoped_refptr<gfx::GLSurface>& surface,
const scoped_refptr<gfx::GLContext>& context,
bool offscreen,
const gfx::Size& size,
const DisallowedFeatures& disallowed_features,
const char* allowed_extensions,
const std::vector<int32>& attribs);
virtual void Destroy(bool have_context);
virtual void SetSurface(
const scoped_refptr<gfx::GLSurface>& surface) OVERRIDE;
virtual bool SetParent(GLES2Decoder* parent_decoder,
uint32 parent_texture_id);
virtual bool ResizeOffscreenFrameBuffer(const gfx::Size& size);
void UpdateParentTextureInfo();
virtual bool MakeCurrent();
virtual void ReleaseCurrent();
virtual GLES2Util* GetGLES2Util() { return &util_; }
virtual gfx::GLContext* GetGLContext() { return context_.get(); }
virtual ContextGroup* GetContextGroup() { return group_.get(); }
virtual QueryManager* GetQueryManager() { return query_manager_.get(); }
virtual bool ProcessPendingQueries();
virtual void SetGLError(
GLenum error, const char* function_name, const char* msg);
virtual void SetResizeCallback(
const base::Callback<void(gfx::Size)>& callback);
virtual void SetMsgCallback(const MsgCallback& callback);
virtual void SetStreamTextureManager(StreamTextureManager* manager);
virtual bool GetServiceTextureId(uint32 client_texture_id,
uint32* service_texture_id);
virtual uint32 GetGLError() OVERRIDE;
// Restores the current state to the user's settings.
void RestoreCurrentFramebufferBindings();
void RestoreCurrentRenderbufferBindings();
void RestoreCurrentTexture2DBindings();
// Sets DEPTH_TEST, STENCIL_TEST and color mask for the current framebuffer.
void ApplyDirtyState();
// Reapply the texture parameters to the given texture.
void BindAndApplyTextureParameters(TextureManager::TextureInfo* info);
// These check the state of the currently bound framebuffer or the
// backbuffer if no framebuffer is bound.
bool BoundFramebufferHasColorAttachmentWithAlpha();
bool BoundFramebufferHasDepthAttachment();
bool BoundFramebufferHasStencilAttachment();
virtual error::ContextLostReason GetContextLostReason();
private:
friend class ScopedGLErrorSuppressor;
friend class ScopedResolvedFrameBufferBinder;
friend class Texture;
friend class RenderBuffer;
friend class FrameBuffer;
// State associated with each texture unit.
struct TextureUnit {
TextureUnit() : bind_target(GL_TEXTURE_2D) { }
// The last target that was bound to this texture unit.
GLenum bind_target;
// texture currently bound to this unit's GL_TEXTURE_2D with glBindTexture
TextureManager::TextureInfo::Ref bound_texture_2d;
// texture currently bound to this unit's GL_TEXTURE_CUBE_MAP with
// glBindTexture
TextureManager::TextureInfo::Ref bound_texture_cube_map;
// texture currently bound to this unit's GL_TEXTURE_EXTERNAL_OES with
// glBindTexture
TextureManager::TextureInfo::Ref bound_texture_external_oes;
// texture currently bound to this unit's GL_TEXTURE_RECTANGLE_ARB with
// glBindTexture
TextureManager::TextureInfo::Ref bound_texture_rectangle_arb;
TextureManager::TextureInfo::Ref GetInfoForSamplerType(GLenum type) {
DCHECK(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE ||
type == GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_2D_RECT_ARB);
switch (type) {
case GL_SAMPLER_2D:
return bound_texture_2d;
case GL_SAMPLER_CUBE:
return bound_texture_cube_map;
case GL_SAMPLER_EXTERNAL_OES:
return bound_texture_external_oes;
case GL_SAMPLER_2D_RECT_ARB:
return bound_texture_rectangle_arb;
}
NOTREACHED();
return NULL;
}
void Unbind(TextureManager::TextureInfo* texture) {
if (bound_texture_2d == texture) {
bound_texture_2d = NULL;
}
if (bound_texture_cube_map == texture) {
bound_texture_cube_map = NULL;
}
if (bound_texture_external_oes == texture) {
bound_texture_external_oes = NULL;
}
}
};
// Initialize or re-initialize the shader translator.
bool InitializeShaderTranslator();
void UpdateCapabilities();
// Helpers for the glGen and glDelete functions.
bool GenTexturesHelper(GLsizei n, const GLuint* client_ids);
void DeleteTexturesHelper(GLsizei n, const GLuint* client_ids);
bool GenBuffersHelper(GLsizei n, const GLuint* client_ids);
void DeleteBuffersHelper(GLsizei n, const GLuint* client_ids);
bool GenFramebuffersHelper(GLsizei n, const GLuint* client_ids);
void DeleteFramebuffersHelper(GLsizei n, const GLuint* client_ids);
bool GenRenderbuffersHelper(GLsizei n, const GLuint* client_ids);
void DeleteRenderbuffersHelper(GLsizei n, const GLuint* client_ids);
bool GenQueriesEXTHelper(GLsizei n, const GLuint* client_ids);
void DeleteQueriesEXTHelper(GLsizei n, const GLuint* client_ids);
// TODO(gman): Cache these pointers?
BufferManager* buffer_manager() {
return group_->buffer_manager();
}
RenderbufferManager* renderbuffer_manager() {
return group_->renderbuffer_manager();
}
FramebufferManager* framebuffer_manager() {
return group_->framebuffer_manager();
}
ProgramManager* program_manager() {
return group_->program_manager();
}
ShaderManager* shader_manager() {
return group_->shader_manager();
}
TextureManager* texture_manager() {
return group_->texture_manager();
}
MailboxManager* mailbox_manager() {
return group_->mailbox_manager();
}
bool IsOffscreenBufferMultisampled() const {
return offscreen_target_samples_ > 1;
}
// Creates a TextureInfo for the given texture.
TextureManager::TextureInfo* CreateTextureInfo(
GLuint client_id, GLuint service_id) {
return texture_manager()->CreateTextureInfo(client_id, service_id);
}
// Gets the texture info for the given texture. Returns NULL if none exists.
TextureManager::TextureInfo* GetTextureInfo(GLuint client_id) {
TextureManager::TextureInfo* info =
texture_manager()->GetTextureInfo(client_id);
return info;
}
// Deletes the texture info for the given texture.
void RemoveTextureInfo(GLuint client_id) {
texture_manager()->RemoveTextureInfo(client_id);
}
// Get the size (in pixels) of the currently bound frame buffer (either FBO
// or regular back buffer).
gfx::Size GetBoundReadFrameBufferSize();
// Get the format of the currently bound frame buffer (either FBO or regular
// back buffer)
GLenum GetBoundReadFrameBufferInternalFormat();
GLenum GetBoundDrawFrameBufferInternalFormat();
// Wrapper for CompressedTexImage2D commands.
error::Error DoCompressedTexImage2D(
GLenum target,
GLint level,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLint border,
GLsizei image_size,
const void* data);
// Wrapper for CompressedTexSubImage2D.
void DoCompressedTexSubImage2D(
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLsizei imageSize,
const void * data);
// Wrapper for CopyTexImage2D.
void DoCopyTexImage2D(
GLenum target,
GLint level,
GLenum internal_format,
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLint border);
// Wrapper for CopyTexSubImage2D.
void DoCopyTexSubImage2D(
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLint x,
GLint y,
GLsizei width,
GLsizei height);
// Wrapper for TexImage2D commands.
error::Error DoTexImage2D(
GLenum target,
GLint level,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const void* pixels,
uint32 pixels_size);
// Wrapper for TexSubImage2D.
void DoTexSubImage2D(
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
const void * data);
// Wrapper for TexImageIOSurface2DCHROMIUM.
void DoTexImageIOSurface2DCHROMIUM(
GLenum target,
GLsizei width,
GLsizei height,
GLuint io_surface_id,
GLuint plane);
void DoCopyTextureCHROMIUM(
GLenum target,
GLuint source_id,
GLuint target_id,
GLint level,
GLenum internal_format);
// Wrapper for TexStorage2DEXT.
void DoTexStorage2DEXT(
GLenum target,
GLint levels,
GLenum internal_format,
GLsizei width,
GLsizei height);
void DoProduceTextureCHROMIUM(GLenum target, const GLbyte* key);
void DoConsumeTextureCHROMIUM(GLenum target, const GLbyte* key);
// Creates a ProgramInfo for the given program.
ProgramManager::ProgramInfo* CreateProgramInfo(
GLuint client_id, GLuint service_id) {
return program_manager()->CreateProgramInfo(client_id, service_id);
}
// Gets the program info for the given program. Returns NULL if none exists.
ProgramManager::ProgramInfo* GetProgramInfo(GLuint client_id) {
return program_manager()->GetProgramInfo(client_id);
}
// Gets the program info for the given program. If it's not a program
// generates a GL error. Returns NULL if not program.
ProgramManager::ProgramInfo* GetProgramInfoNotShader(
GLuint client_id, const char* function_name) {
ProgramManager::ProgramInfo* info = GetProgramInfo(client_id);
if (!info) {
if (GetShaderInfo(client_id)) {
SetGLError(
GL_INVALID_OPERATION, function_name, "shader passed for program");
} else {
SetGLError(GL_INVALID_VALUE, function_name, "unknown program");
}
}
return info;
}
// Creates a ShaderInfo for the given shader.
ShaderManager::ShaderInfo* CreateShaderInfo(
GLuint client_id,
GLuint service_id,
GLenum shader_type) {
return shader_manager()->CreateShaderInfo(
client_id, service_id, shader_type);
}
// Gets the shader info for the given shader. Returns NULL if none exists.
ShaderManager::ShaderInfo* GetShaderInfo(GLuint client_id) {
return shader_manager()->GetShaderInfo(client_id);
}
// Gets the shader info for the given shader. If it's not a shader generates a
// GL error. Returns NULL if not shader.
ShaderManager::ShaderInfo* GetShaderInfoNotProgram(
GLuint client_id, const char* function_name) {
ShaderManager::ShaderInfo* info = GetShaderInfo(client_id);
if (!info) {
if (GetProgramInfo(client_id)) {
SetGLError(
GL_INVALID_OPERATION, function_name, "program passed for shader");
} else {
SetGLError(
GL_INVALID_VALUE, function_name, "unknown shader");
}
}
return info;
}
// Creates a buffer info for the given buffer.
void CreateBufferInfo(GLuint client_id, GLuint service_id) {
return buffer_manager()->CreateBufferInfo(client_id, service_id);
}
// Gets the buffer info for the given buffer.
BufferManager::BufferInfo* GetBufferInfo(GLuint client_id) {
BufferManager::BufferInfo* info =
buffer_manager()->GetBufferInfo(client_id);
return info;
}
// Removes any buffers in the VertexAtrribInfos and BufferInfos. This is used
// on glDeleteBuffers so we can make sure the user does not try to render
// with deleted buffers.
void RemoveBufferInfo(GLuint client_id);
// Creates a framebuffer info for the given framebuffer.
void CreateFramebufferInfo(GLuint client_id, GLuint service_id) {
return framebuffer_manager()->CreateFramebufferInfo(client_id, service_id);
}
// Gets the framebuffer info for the given framebuffer.
FramebufferManager::FramebufferInfo* GetFramebufferInfo(
GLuint client_id) {
FramebufferManager::FramebufferInfo* info =
framebuffer_manager()->GetFramebufferInfo(client_id);
return info;
}
// Removes the framebuffer info for the given framebuffer.
void RemoveFramebufferInfo(GLuint client_id) {
framebuffer_manager()->RemoveFramebufferInfo(client_id);
}
// Creates a renderbuffer info for the given renderbuffer.
void CreateRenderbufferInfo(GLuint client_id, GLuint service_id) {
return renderbuffer_manager()->CreateRenderbufferInfo(
client_id, service_id);
}
// Gets the renderbuffer info for the given renderbuffer.
RenderbufferManager::RenderbufferInfo* GetRenderbufferInfo(
GLuint client_id) {
RenderbufferManager::RenderbufferInfo* info =
renderbuffer_manager()->GetRenderbufferInfo(client_id);
return info;
}
// Removes the renderbuffer info for the given renderbuffer.
void RemoveRenderbufferInfo(GLuint client_id) {
renderbuffer_manager()->RemoveRenderbufferInfo(client_id);
}
void DoBindAttribLocation(GLuint client_id, GLuint index, const char* name);
void DoBindUniformLocationCHROMIUM(
GLuint client_id, GLint location, const char* name);
error::Error GetAttribLocationHelper(
GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset,
const std::string& name_str);
error::Error GetUniformLocationHelper(
GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset,
const std::string& name_str);
// Helper for glShaderSource.
error::Error ShaderSourceHelper(
GLuint client_id, const char* data, uint32 data_size);
// Clear any textures used by the current program.
bool ClearUnclearedTextures();
// Clear any uncleared level in texture.
// Returns false if there was a generated GL error.
bool ClearTexture(TextureManager::TextureInfo* info);
// Clears any uncleared attachments attached to the given frame buffer.
// Returns false if there was a generated GL error.
void ClearUnclearedAttachments(
GLenum target, FramebufferManager::FramebufferInfo* info);
// overridden from GLES2Decoder
virtual bool ClearLevel(
unsigned service_id,
unsigned bind_target,
unsigned target,
int level,
unsigned format,
unsigned type,
int width,
int height,
bool is_texture_immutable);
// Restore all GL state that affects clearing.
void RestoreClearState();
// Remembers the state of some capabilities.
// Returns: true if glEnable/glDisable should actually be called.
bool SetCapabilityState(GLenum cap, bool enabled);
// Check that the currently bound framebuffers are valid.
// Generates GL error if not.
bool CheckBoundFramebuffersValid(const char* func_name);
// Check if a framebuffer meets our requirements.
bool CheckFramebufferValid(
FramebufferManager::FramebufferInfo* framebuffer,
GLenum target,
const char* func_name);
// Checks if the current program exists and is valid. If not generates the
// appropriate GL error. Returns true if the current program is in a usable
// state.
bool CheckCurrentProgram(const char* function_name);
// Checks if the current program exists and is valid and that location is not
// -1. If the current program is not valid generates the appropriate GL
// error. Returns true if the current program is in a usable state and
// location is not -1.
bool CheckCurrentProgramForUniform(GLint location, const char* function_name);
// Gets the type of a uniform for a location in the current program. Sets GL
// errors if the current program is not valid. Returns true if the current
// program is valid and the location exists. Adjusts count so it
// does not overflow the uniform.
bool PrepForSetUniformByLocation(
GLint fake_location, const char* function_name,
GLint* real_location, GLenum* type, GLsizei* count);
// Gets the service id for any simulated backbuffer fbo.
GLuint GetBackbufferServiceId();
// Helper for glGetBooleanv, glGetFloatv and glGetIntegerv
bool GetHelper(GLenum pname, GLint* params, GLsizei* num_written);
// Wrapper for glCreateProgram
bool CreateProgramHelper(GLuint client_id);
// Wrapper for glCreateShader
bool CreateShaderHelper(GLenum type, GLuint client_id);
// Wrapper for glActiveTexture
void DoActiveTexture(GLenum texture_unit);
// Wrapper for glAttachShader
void DoAttachShader(GLuint client_program_id, GLint client_shader_id);
// Wrapper for glBindBuffer since we need to track the current targets.
void DoBindBuffer(GLenum target, GLuint buffer);
// Wrapper for glBindFramebuffer since we need to track the current targets.
void DoBindFramebuffer(GLenum target, GLuint framebuffer);
// Wrapper for glBindRenderbuffer since we need to track the current targets.
void DoBindRenderbuffer(GLenum target, GLuint renderbuffer);
// Wrapper for glBindTexture since we need to track the current targets.
void DoBindTexture(GLenum target, GLuint texture);
// Wrapper for glBlitFramebufferEXT.
void DoBlitFramebufferEXT(
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter);
// Wrapper for glBufferData.
void DoBufferData(
GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);
// Wrapper for glBufferSubData.
void DoBufferSubData(
GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);
// Wrapper for glCheckFramebufferStatus
GLenum DoCheckFramebufferStatus(GLenum target);
// Wrapper for glClear
error::Error DoClear(GLbitfield mask);
// Wrappers for clear and mask settings functions.
void DoClearColor(
GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
void DoClearDepthf(GLclampf depth);
void DoClearStencil(GLint s);
void DoColorMask(
GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
void DoDepthMask(GLboolean depth);
void DoStencilMask(GLuint mask);
void DoStencilMaskSeparate(GLenum face, GLuint mask);
// Wrapper for glCompileShader.
void DoCompileShader(GLuint shader);
// Helper for DeleteSharedIdsCHROMIUM commands.
void DoDeleteSharedIdsCHROMIUM(
GLuint namespace_id, GLsizei n, const GLuint* ids);
// Wrapper for glDetachShader
void DoDetachShader(GLuint client_program_id, GLint client_shader_id);
// Wrapper for glDisable
void DoDisable(GLenum cap);
// Wrapper for glDisableVertexAttribArray.
void DoDisableVertexAttribArray(GLuint index);
// Wrapper for glEnable
void DoEnable(GLenum cap);
// Wrapper for glEnableVertexAttribArray.
void DoEnableVertexAttribArray(GLuint index);
// Wrapper for glFinish.
void DoFinish();
// Wrapper for glFlush.
void DoFlush();
// Wrapper for glFramebufferRenderbufffer.
void DoFramebufferRenderbuffer(
GLenum target, GLenum attachment, GLenum renderbuffertarget,
GLuint renderbuffer);
// Wrapper for glFramebufferTexture2D.
void DoFramebufferTexture2D(
GLenum target, GLenum attachment, GLenum textarget, GLuint texture,
GLint level);
// Wrapper for glGenerateMipmap
void DoGenerateMipmap(GLenum target);
// Helper for GenSharedIdsCHROMIUM commands.
void DoGenSharedIdsCHROMIUM(
GLuint namespace_id, GLuint id_offset, GLsizei n, GLuint* ids);
// Wrapper for DoGetBooleanv.
void DoGetBooleanv(GLenum pname, GLboolean* params);
// Wrapper for DoGetFloatv.
void DoGetFloatv(GLenum pname, GLfloat* params);
// Wrapper for glGetFramebufferAttachmentParameteriv.
void DoGetFramebufferAttachmentParameteriv(
GLenum target, GLenum attachment, GLenum pname, GLint* params);
// Wrapper for glGetIntegerv.
void DoGetIntegerv(GLenum pname, GLint* params);
// Gets the max value in a range in a buffer.
GLuint DoGetMaxValueInBufferCHROMIUM(
GLuint buffer_id, GLsizei count, GLenum type, GLuint offset);
// Wrapper for glGetProgramiv.
void DoGetProgramiv(
GLuint program_id, GLenum pname, GLint* params);
// Wrapper for glRenderbufferParameteriv.
void DoGetRenderbufferParameteriv(
GLenum target, GLenum pname, GLint* params);
// Wrapper for glGetShaderiv
void DoGetShaderiv(GLuint shader, GLenum pname, GLint* params);
// Wrappers for glGetVertexAttrib.
void DoGetVertexAttribfv(GLuint index, GLenum pname, GLfloat *params);
void DoGetVertexAttribiv(GLuint index, GLenum pname, GLint *params);
// Wrappers for glIsXXX functions.
bool DoIsEnabled(GLenum cap);
bool DoIsBuffer(GLuint client_id);
bool DoIsFramebuffer(GLuint client_id);
bool DoIsProgram(GLuint client_id);
bool DoIsRenderbuffer(GLuint client_id);
bool DoIsShader(GLuint client_id);
bool DoIsTexture(GLuint client_id);
// Wrapper for glLinkProgram
void DoLinkProgram(GLuint program);
// Helper for RegisterSharedIdsCHROMIUM.
void DoRegisterSharedIdsCHROMIUM(
GLuint namespace_id, GLsizei n, const GLuint* ids);
// Wrapper for glRenderbufferStorage.
void DoRenderbufferStorage(
GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
// Wrapper for glRenderbufferStorageMultisampleEXT.
void DoRenderbufferStorageMultisample(
GLenum target, GLsizei samples, GLenum internalformat,
GLsizei width, GLsizei height);
// Wrapper for glReleaseShaderCompiler.
void DoReleaseShaderCompiler() { }
// Wrappers for glTexParameter functions.
void DoTexParameterf(GLenum target, GLenum pname, GLfloat param);
void DoTexParameteri(GLenum target, GLenum pname, GLint param);
void DoTexParameterfv(GLenum target, GLenum pname, const GLfloat* params);
void DoTexParameteriv(GLenum target, GLenum pname, const GLint* params);
// Wrappers for glUniform1i and glUniform1iv as according to the GLES2
// spec only these 2 functions can be used to set sampler uniforms.
void DoUniform1i(GLint fake_location, GLint v0);
void DoUniform1iv(GLint fake_location, GLsizei count, const GLint* value);
void DoUniform2iv(GLint fake_location, GLsizei count, const GLint* value);
void DoUniform3iv(GLint fake_location, GLsizei count, const GLint* value);
void DoUniform4iv(GLint fake_location, GLsizei count, const GLint* value);
// Wrappers for glUniformfv because some drivers don't correctly accept
// bool uniforms.
void DoUniform1fv(GLint fake_location, GLsizei count, const GLfloat* value);
void DoUniform2fv(GLint fake_location, GLsizei count, const GLfloat* value);
void DoUniform3fv(GLint fake_location, GLsizei count, const GLfloat* value);
void DoUniform4fv(GLint fake_location, GLsizei count, const GLfloat* value);
void DoUniformMatrix2fv(
GLint fake_location, GLsizei count, GLboolean transpose,
const GLfloat* value);
void DoUniformMatrix3fv(
GLint fake_location, GLsizei count, GLboolean transpose,
const GLfloat* value);
void DoUniformMatrix4fv(
GLint fake_location, GLsizei count, GLboolean transpose,
const GLfloat* value);
// Wrappers for glVertexAttrib??
void DoVertexAttrib1f(GLuint index, GLfloat v0);
void DoVertexAttrib2f(GLuint index, GLfloat v0, GLfloat v1);
void DoVertexAttrib3f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2);
void DoVertexAttrib4f(
GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void DoVertexAttrib1fv(GLuint index, const GLfloat *v);
void DoVertexAttrib2fv(GLuint index, const GLfloat *v);
void DoVertexAttrib3fv(GLuint index, const GLfloat *v);
void DoVertexAttrib4fv(GLuint index, const GLfloat *v);
// Wrapper for glViewport
void DoViewport(GLint x, GLint y, GLsizei width, GLsizei height);
// Wrapper for glUseProgram
void DoUseProgram(GLuint program);
// Wrapper for glValidateProgram.
void DoValidateProgram(GLuint program_client_id);
// Gets the number of values that will be returned by glGetXXX. Returns
// false if pname is unknown.
bool GetNumValuesReturnedForGLGet(GLenum pname, GLsizei* num_values);
// Gets the GLError and stores it in our wrapper. Effectively
// this lets us peek at the error without losing it.
GLenum PeekGLError();
// Copies the real GL errors to the wrapper. This is so we can
// make sure there are no native GL errors before calling some GL function
// so that on return we know any error generated was for that specific
// 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(
const char* function_name, GLuint max_vertex_accessed, GLsizei primcount);
// Returns true if successful, simulated will be true if attrib0 was
// simulated.
bool SimulateAttrib0(
const char* function_name, GLuint max_vertex_accessed, bool* simulated);
void RestoreStateForAttrib(GLuint attrib);
// Returns true if textures were set.
bool SetBlackTextureForNonRenderableTextures();
void RestoreStateForNonRenderableTextures();
// Returns true if GL_FIXED attribs were simulated.
bool SimulateFixedAttribs(
const char* function_name,
GLuint max_vertex_accessed, bool* simulated, GLsizei primcount);
void RestoreStateForSimulatedFixedAttribs();
// Handle DrawArrays and DrawElements for both instanced and non-instanced
// cases (primcount is 0 for non-instanced).
error::Error DoDrawArrays(
const char* function_name,
bool instanced, GLenum mode, GLint first, GLsizei count,
GLsizei primcount);
error::Error DoDrawElements(
const char* function_name,
bool instanced, GLenum mode, GLsizei count, GLenum type,
int32 offset, GLsizei primcount);
// Gets the buffer id for a given target.
BufferManager::BufferInfo* GetBufferInfoForTarget(GLenum target) {
DCHECK(target == GL_ARRAY_BUFFER || target == GL_ELEMENT_ARRAY_BUFFER);
BufferManager::BufferInfo* info = target == GL_ARRAY_BUFFER ?
bound_array_buffer_ : bound_element_array_buffer_;
return info;
}
// Gets the texture id for a given target.
TextureManager::TextureInfo* GetTextureInfoForTarget(GLenum target) {
TextureUnit& unit = texture_units_[active_texture_unit_];
TextureManager::TextureInfo* info = NULL;
switch (target) {
case GL_TEXTURE_2D:
info = unit.bound_texture_2d;
break;
case GL_TEXTURE_CUBE_MAP:
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
info = unit.bound_texture_cube_map;
break;
case GL_TEXTURE_EXTERNAL_OES:
info = unit.bound_texture_external_oes;
break;
case GL_TEXTURE_RECTANGLE_ARB:
info = unit.bound_texture_rectangle_arb;
break;
default:
NOTREACHED();
return NULL;
}
return info;
}
GLenum GetBindTargetForSamplerType(GLenum type) {
DCHECK(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE ||
type == GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_2D_RECT_ARB);
switch (type) {
case GL_SAMPLER_2D:
return GL_TEXTURE_2D;
case GL_SAMPLER_CUBE:
return GL_TEXTURE_CUBE_MAP;
case GL_SAMPLER_EXTERNAL_OES:
return GL_TEXTURE_EXTERNAL_OES;
case GL_SAMPLER_2D_RECT_ARB:
return GL_TEXTURE_RECTANGLE_ARB;
}
NOTREACHED();
return 0;
}
// Gets the framebuffer info for a particular target.
FramebufferManager::FramebufferInfo* GetFramebufferInfoForTarget(
GLenum target) {
FramebufferManager::FramebufferInfo* info = NULL;
switch (target) {
case GL_FRAMEBUFFER:
case GL_DRAW_FRAMEBUFFER:
info = bound_draw_framebuffer_;
break;
case GL_READ_FRAMEBUFFER:
info = bound_read_framebuffer_;
break;
default:
NOTREACHED();
break;
}
return info;
}
RenderbufferManager::RenderbufferInfo* GetRenderbufferInfoForTarget(
GLenum target) {
RenderbufferManager::RenderbufferInfo* info = NULL;
switch (target) {
case GL_RENDERBUFFER:
info = bound_renderbuffer_;
break;
default:
NOTREACHED();
break;
}
return info;
}
// Validates the program and location for a glGetUniform call and returns
// a SizeResult setup to receive the result. Returns true if glGetUniform
// should be called.
bool GetUniformSetup(
GLuint program, GLint fake_location,
uint32 shm_id, uint32 shm_offset,
error::Error* error, GLint* real_location, GLuint* service_id,
void** result, GLenum* result_type);
// Computes the estimated memory used for the backbuffer and passes it to
// the tracing system.
size_t GetBackbufferMemoryTotal();
// Returns true if the context was just lost due to e.g. GL_ARB_robustness.
bool WasContextLost();
#if defined(OS_MACOSX)
void ReleaseIOSurfaceForTexture(GLuint texture_id);
#endif
// Validates the combination of texture parameters. For example validates that
// for a given format the specific type, level and targets are valid.
// Synthesizes the correct GL error if invalid. Returns true if valid.
bool ValidateTextureParameters(
const char* function_name,
GLenum target, GLenum format, GLenum type, GLint level);
bool ValidateCompressedTexDimensions(
const char* function_name,
GLint level, GLsizei width, GLsizei height, GLenum format);
bool ValidateCompressedTexFuncData(
const char* function_name,
GLsizei width, GLsizei height, GLenum format, size_t size);
bool ValidateCompressedTexSubDimensions(
const char* function_name,
GLenum target, GLint level, GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height, GLenum format,
TextureManager::TextureInfo* texture);
void LogMessage(const std::string& msg);
void RenderWarning(const std::string& msg);
void PerformanceWarning(const std::string& msg);
bool ShouldDeferDraws() {
return !offscreen_target_frame_buffer_.get() &&
bound_draw_framebuffer_ == NULL &&
surface_->DeferDraws();
}
void ForceCompileShaderIfPending(ShaderManager::ShaderInfo* info);
// Generate a member function prototype for each command in an automated and
// typesafe way.
#define GLES2_CMD_OP(name) \
Error Handle ## name( \
uint32 immediate_data_size, \
const gles2::name& args); \
GLES2_COMMAND_LIST(GLES2_CMD_OP)
#undef GLES2_CMD_OP
// The GL context this decoder renders to on behalf of the client.
scoped_refptr<gfx::GLSurface> surface_;
scoped_refptr<gfx::GLContext> context_;
// The ContextGroup for this decoder uses to track resources.
ContextGroup::Ref group_;
// 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_;
// Current width and height of the offscreen frame buffer.
gfx::Size offscreen_size_;
// Current GL error bits.
uint32 error_bits_;
// Util to help with GL.
GLES2Util util_;
// pack alignment as last set by glPixelStorei
GLint pack_alignment_;
// unpack alignment as last set by glPixelStorei
GLint unpack_alignment_;
// unpack flip y as last set by glPixelStorei
bool unpack_flip_y_;
// unpack (un)premultiply alpha as last set by glPixelStorei
bool unpack_premultiply_alpha_;
bool unpack_unpremultiply_alpha_;
// The currently bound array buffer. If this is 0 it is illegal to call
// glVertexAttribPointer.
BufferManager::BufferInfo::Ref bound_array_buffer_;
// The currently bound element array buffer. If this is 0 it is illegal
// to call glDrawElements.
BufferManager::BufferInfo::Ref bound_element_array_buffer_;
// Class that manages vertex attribs.
scoped_ptr<VertexAttribManager> vertex_attrib_manager_;
// The buffer we bind to attrib 0 since OpenGL requires it (ES does not).
GLuint attrib_0_buffer_id_;
// The value currently in attrib_0.
VertexAttribManager::VertexAttribInfo::Vec4 attrib_0_value_;
// Whether or not the attrib_0 buffer holds the attrib_0_value.
bool attrib_0_buffer_matches_value_;
// The size of attrib 0.
GLsizei attrib_0_size_;
// The buffer used to simulate GL_FIXED attribs.
GLuint fixed_attrib_buffer_id_;
// The size of fiixed attrib buffer.
GLsizei fixed_attrib_buffer_size_;
// Current active texture by 0 - n index.
// In other words, if we call glActiveTexture(GL_TEXTURE2) this value would
// be 2.
GLuint active_texture_unit_;
// Which textures are bound to texture units through glActiveTexture.
scoped_array<TextureUnit> texture_units_;
// state saved for clearing so we can clear render buffers and then
// restore to these values.
GLclampf clear_red_;
GLclampf clear_green_;
GLclampf clear_blue_;
GLclampf clear_alpha_;
GLboolean mask_red_;
GLboolean mask_green_;
GLboolean mask_blue_;
GLboolean mask_alpha_;
GLint clear_stencil_;
GLuint mask_stencil_front_;
GLuint mask_stencil_back_;
GLclampf clear_depth_;
GLboolean mask_depth_;
bool enable_blend_;
bool enable_cull_face_;
bool enable_scissor_test_;
bool enable_depth_test_;
bool enable_stencil_test_;
bool state_dirty_;
// The program in use by glUseProgram
ProgramManager::ProgramInfo::Ref current_program_;
// The currently bound framebuffers
FramebufferManager::FramebufferInfo::Ref bound_read_framebuffer_;
FramebufferManager::FramebufferInfo::Ref bound_draw_framebuffer_;
// The currently bound renderbuffer
RenderbufferManager::RenderbufferInfo::Ref bound_renderbuffer_;
// The offscreen frame buffer that the client renders to. With EGL, the
// depth and stencil buffers are separate. With regular GL there is a single
// packed depth stencil buffer in offscreen_target_depth_render_buffer_.
// offscreen_target_stencil_render_buffer_ is unused.
scoped_ptr<FrameBuffer> offscreen_target_frame_buffer_;
scoped_ptr<Texture> offscreen_target_color_texture_;
scoped_ptr<RenderBuffer> offscreen_target_color_render_buffer_;
scoped_ptr<RenderBuffer> offscreen_target_depth_render_buffer_;
scoped_ptr<RenderBuffer> offscreen_target_stencil_render_buffer_;
GLenum offscreen_target_color_format_;
GLenum offscreen_target_depth_format_;
GLenum offscreen_target_stencil_format_;
GLsizei offscreen_target_samples_;
GLboolean offscreen_target_buffer_preserved_;
// The copy that is saved when SwapBuffers is called.
scoped_ptr<FrameBuffer> offscreen_saved_frame_buffer_;
scoped_ptr<Texture> offscreen_saved_color_texture_;
TextureManager::TextureInfo::Ref offscreen_saved_color_texture_info_;
// The copy that is used as the destination for multi-sample resolves.
scoped_ptr<FrameBuffer> offscreen_resolved_frame_buffer_;
scoped_ptr<Texture> offscreen_resolved_color_texture_;
GLenum offscreen_saved_color_format_;
scoped_ptr<QueryManager> query_manager_;
QueryManager::Query::Ref current_query_;
base::Callback<void(gfx::Size)> resize_callback_;
MsgCallback msg_callback_;
StreamTextureManager* stream_texture_manager_;
// The format of the back buffer_
GLenum back_buffer_color_format_;
bool back_buffer_has_depth_;
bool back_buffer_has_stencil_;
bool teximage2d_faster_than_texsubimage2d_;
bool bufferdata_faster_than_buffersubdata_;
// The last error message set.
std::string last_error_;
int log_message_count_;
// The current decoder error.
error::Error current_decoder_error_;
bool use_shader_translator_;
scoped_refptr<ShaderTranslator> vertex_translator_;
scoped_refptr<ShaderTranslator> fragment_translator_;
DisallowedFeatures disallowed_features_;
// Cached from ContextGroup
const Validators* validators_;
FeatureInfo::Ref feature_info_;
// This indicates all the following texSubImage2D calls that are part of the
// failed texImage2D call should be ignored.
bool tex_image_2d_failed_;
int frame_number_;
bool has_robustness_extension_;
GLenum reset_status_;
bool needs_mac_nvidia_driver_workaround_;
bool needs_glsl_built_in_function_emulation_;
// These flags are used to override the state of the shared feature_info_
// member. Because the same FeatureInfo instance may be shared among many
// contexts, the assumptions on the availablity of extensions in WebGL
// contexts may be broken. These flags override the shared state to preserve
// WebGL semantics.
bool force_webgl_glsl_validation_;
bool derivatives_explicitly_enabled_;
bool compile_shader_always_succeeds_;
#if defined(OS_MACOSX)
typedef std::map<GLuint, CFTypeRef> TextureToIOSurfaceMap;
TextureToIOSurfaceMap texture_to_io_surface_map_;
#endif
typedef std::vector<GLES2DecoderImpl*> ChildList;
ChildList children_;
scoped_ptr<CopyTextureCHROMIUMResourceManager> copy_texture_CHROMIUM_;
// Cached values of the currently assigned viewport dimensions.
GLint viewport_x_, viewport_y_;
GLsizei viewport_width_, viewport_height_;
GLsizei viewport_max_width_, viewport_max_height_;
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_);
decoder_->RestoreCurrentTexture2DBindings();
}
ScopedRenderBufferBinder::ScopedRenderBufferBinder(GLES2DecoderImpl* decoder,
GLuint id)
: decoder_(decoder) {
ScopedGLErrorSuppressor suppressor(decoder_);
glBindRenderbufferEXT(GL_RENDERBUFFER, id);
}
ScopedRenderBufferBinder::~ScopedRenderBufferBinder() {
ScopedGLErrorSuppressor suppressor(decoder_);
decoder_->RestoreCurrentRenderbufferBindings();
}
ScopedFrameBufferBinder::ScopedFrameBufferBinder(GLES2DecoderImpl* decoder,
GLuint id)
: decoder_(decoder) {
ScopedGLErrorSuppressor suppressor(decoder_);
glBindFramebufferEXT(GL_FRAMEBUFFER, id);
}
ScopedFrameBufferBinder::~ScopedFrameBufferBinder() {
ScopedGLErrorSuppressor suppressor(decoder_);
decoder_->RestoreCurrentFramebufferBindings();
}
ScopedResolvedFrameBufferBinder::ScopedResolvedFrameBufferBinder(
GLES2DecoderImpl* decoder, bool enforce_internal_framebuffer, bool internal)
: decoder_(decoder) {
resolve_and_bind_ = (decoder_->offscreen_target_frame_buffer_.get() &&
decoder_->IsOffscreenBufferMultisampled() &&
(!decoder_->bound_read_framebuffer_.get() ||
enforce_internal_framebuffer));
if (!resolve_and_bind_)
return;
ScopedGLErrorSuppressor suppressor(decoder_);
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT,
decoder_->offscreen_target_frame_buffer_->id());
GLuint targetid;
if (internal) {
if (!decoder_->offscreen_resolved_frame_buffer_.get()) {
decoder_->offscreen_resolved_frame_buffer_.reset(
new FrameBuffer(decoder_));
decoder_->offscreen_resolved_frame_buffer_->Create();
decoder_->offscreen_resolved_color_texture_.reset(new Texture(decoder_));
decoder_->offscreen_resolved_color_texture_->Create();
DCHECK(decoder_->offscreen_saved_color_format_);
decoder_->offscreen_resolved_color_texture_->AllocateStorage(
decoder_->offscreen_size_, decoder_->offscreen_saved_color_format_);
decoder_->offscreen_resolved_frame_buffer_->AttachRenderTexture(
decoder_->offscreen_resolved_color_texture_.get());
if (decoder_->offscreen_resolved_frame_buffer_->CheckStatus() !=
GL_FRAMEBUFFER_COMPLETE) {
LOG(ERROR) << "ScopedResolvedFrameBufferBinder failed "
<< "because offscreen resolved FBO was incomplete.";
return;
}
}
targetid = decoder_->offscreen_resolved_frame_buffer_->id();
} else {
targetid = decoder_->offscreen_saved_frame_buffer_->id();
}
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, targetid);
const int width = decoder_->offscreen_size_.width();
const int height = decoder_->offscreen_size_.height();
glDisable(GL_SCISSOR_TEST);
if (GLES2Decoder::IsAngle()) {
glBlitFramebufferANGLE(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
} else {
glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
glBindFramebufferEXT(GL_FRAMEBUFFER, targetid);
}
ScopedResolvedFrameBufferBinder::~ScopedResolvedFrameBufferBinder() {
if (!resolve_and_bind_)
return;
ScopedGLErrorSuppressor suppressor(decoder_);
decoder_->RestoreCurrentFramebufferBindings();
if (decoder_->enable_scissor_test_) {
glEnable(GL_SCISSOR_TEST);
}
}
Texture::Texture(GLES2DecoderImpl* decoder)
: decoder_(decoder),
id_(0),
estimated_size_(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_);
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);
// TODO(apatrick): Attempt to diagnose crbug.com/97775. If SwapBuffers is
// never called on an offscreen context, no data will ever be uploaded to the
// saved offscreen color texture (it is deferred until to when SwapBuffers
// is called). My idea is that some nvidia drivers might have a bug where
// deleting a texture that has never been populated might cause a
// crash.
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
estimated_size_ = 16u * 16u * 4u;
TRACE_BACKBUFFER_MEMORY_TOTAL(decoder_);
}
bool Texture::AllocateStorage(const gfx::Size& size, GLenum format) {
DCHECK_NE(id_, 0u);
ScopedGLErrorSuppressor suppressor(decoder_);
ScopedTexture2DBinder binder(decoder_, id_);
WrappedTexImage2D(GL_TEXTURE_2D,
0, // mip level
format,
size.width(),
size.height(),
0, // border
format,
GL_UNSIGNED_BYTE,
NULL);
size_ = size;
bool success = glGetError() == GL_NO_ERROR;
if (success) {
uint32 image_size = 0;
GLES2Util::ComputeImageDataSizes(
size.width(), size.height(), format, GL_UNSIGNED_BYTE, 4, &image_size,
NULL, NULL);
estimated_size_ = image_size;
TRACE_BACKBUFFER_MEMORY_TOTAL(decoder_);
}
return success;
}
void Texture::Copy(const gfx::Size& size, GLenum format) {
DCHECK_NE(id_, 0u);
ScopedGLErrorSuppressor suppressor(decoder_);
ScopedTexture2DBinder binder(decoder_, id_);
glCopyTexImage2D(GL_TEXTURE_2D,
0, // level
format,
0, 0,
size.width(),
size.height(),
0); // border
}
void Texture::Destroy() {
if (id_ != 0) {
ScopedGLErrorSuppressor suppressor(decoder_);
glDeleteTextures(1, &id_);
id_ = 0;
estimated_size_ = 0;
TRACE_BACKBUFFER_MEMORY_TOTAL(decoder_);
}
}
void Texture::Invalidate() {
id_ = 0;
}
RenderBuffer::RenderBuffer(GLES2DecoderImpl* decoder)
: decoder_(decoder),
id_(0),
estimated_size_(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,
GLsizei samples) {
ScopedGLErrorSuppressor suppressor(decoder_);
ScopedRenderBufferBinder binder(decoder_, id_);
if (samples <= 1) {
glRenderbufferStorageEXT(GL_RENDERBUFFER,
format,
size.width(),
size.height());
} else {
if (GLES2Decoder::IsAngle()) {
glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER,
samples,
format,
size.width(),
size.height());
} else {
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER,
samples,
format,
size.width(),
size.height());
}
}
bool success = glGetError() == GL_NO_ERROR;
if (success) {
estimated_size_ = size.width() * size.height() * samples *
GLES2Util::RenderbufferBytesPerPixel(format);
TRACE_BACKBUFFER_MEMORY_TOTAL(decoder_);
}
return success;
}
void RenderBuffer::Destroy() {
if (id_ != 0) {
ScopedGLErrorSuppressor suppressor(decoder_);
glDeleteRenderbuffersEXT(1, &id_);
id_ = 0;
estimated_size_ = 0;
TRACE_BACKBUFFER_MEMORY_TOTAL(decoder_);
}
}
void RenderBuffer::Invalidate() {
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::AttachRenderBuffer(GLenum target,
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,
target,
GL_RENDERBUFFER,
attach_id);
}
void FrameBuffer::Destroy() {
if (id_ != 0) {
ScopedGLErrorSuppressor suppressor(decoder_);
glDeleteFramebuffersEXT(1, &id_);
id_ = 0;
}
}
void FrameBuffer::Invalidate() {
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);
}
GLES2DecoderImpl::GLES2DecoderImpl(ContextGroup* group)
: GLES2Decoder(),
group_(group),
error_bits_(0),
pack_alignment_(4),
unpack_alignment_(4),
unpack_flip_y_(false),
unpack_premultiply_alpha_(false),
unpack_unpremultiply_alpha_(false),
attrib_0_buffer_id_(0),
attrib_0_buffer_matches_value_(true),
attrib_0_size_(0),
fixed_attrib_buffer_id_(0),
fixed_attrib_buffer_size_(0),
active_texture_unit_(0),
clear_red_(0),
clear_green_(0),
clear_blue_(0),
clear_alpha_(0),
mask_red_(true),
mask_green_(true),
mask_blue_(true),
mask_alpha_(true),
clear_stencil_(0),
mask_stencil_front_(-1),
mask_stencil_back_(-1),
clear_depth_(1.0f),
mask_depth_(true),
enable_blend_(false),
enable_cull_face_(false),
enable_scissor_test_(false),
enable_depth_test_(false),
enable_stencil_test_(false),
state_dirty_(true),
offscreen_target_color_format_(0),
offscreen_target_depth_format_(0),
offscreen_target_stencil_format_(0),
offscreen_target_samples_(0),
offscreen_target_buffer_preserved_(true),
offscreen_saved_color_format_(0),
stream_texture_manager_(NULL),
back_buffer_color_format_(0),
back_buffer_has_depth_(false),
back_buffer_has_stencil_(false),
teximage2d_faster_than_texsubimage2d_(true),
bufferdata_faster_than_buffersubdata_(true),
log_message_count_(0),
current_decoder_error_(error::kNoError),
use_shader_translator_(true),
validators_(group_->feature_info()->validators()),
feature_info_(group_->feature_info()),
tex_image_2d_failed_(false),
frame_number_(0),
has_robustness_extension_(false),
reset_status_(GL_NO_ERROR),
needs_mac_nvidia_driver_workaround_(false),
needs_glsl_built_in_function_emulation_(false),
force_webgl_glsl_validation_(false),
derivatives_explicitly_enabled_(false),
compile_shader_always_succeeds_(false),
viewport_x_(0),
viewport_y_(0),
viewport_width_(0),
viewport_height_(0),
viewport_max_width_(0),
viewport_max_height_(0) {
DCHECK(group);
attrib_0_value_.v[0] = 0.0f;
attrib_0_value_.v[1] = 0.0f;
attrib_0_value_.v[2] = 0.0f;
attrib_0_value_.v[3] = 1.0f;
// The shader translator is used for WebGL even when running on EGL
// because additional restrictions are needed (like only enabling
// GL_OES_standard_derivatives on demand). It is used for the unit
// tests because
// GLES2DecoderWithShaderTest.GetShaderInfoLogValidArgs passes the
// empty string to CompileShader and this is not a valid shader.
// TODO(apatrick): fix this test.
if ((gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2 &&
!feature_info_->feature_flags().chromium_webglsl &&
!force_webgl_glsl_validation_) ||
gfx::GetGLImplementation() == gfx::kGLImplementationMockGL ||
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableGLSLTranslator)) {
use_shader_translator_ = false;
}
// TODO(gman): Consider setting these based on GPU and/or driver.
if (IsAngle()) {
teximage2d_faster_than_texsubimage2d_ = false;
bufferdata_faster_than_buffersubdata_ = false;
}
}
GLES2DecoderImpl::~GLES2DecoderImpl() {
}
bool GLES2DecoderImpl::Initialize(
const scoped_refptr<gfx::GLSurface>& surface,
const scoped_refptr<gfx::GLContext>& context,
bool offscreen,
const gfx::Size& size,
const DisallowedFeatures& disallowed_features,
const char* allowed_extensions,
const std::vector<int32>& attribs) {
TRACE_EVENT0("gpu", "GLES2DecoderImpl::Initialize");
DCHECK(context->IsCurrent(surface.get()));
DCHECK(!context_.get());
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableGPUDebugging)) {
set_debug(true);
}
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableGPUCommandLogging)) {
set_log_commands(true);
}
compile_shader_always_succeeds_ = CommandLine::ForCurrentProcess()->HasSwitch(
switches::kCompileShaderAlwaysSucceeds);
// Take ownership of the context and surface. The surface can be replaced with
// SetSurface.
context_ = context;
surface_ = surface;
if (!group_->Initialize(disallowed_features, allowed_extensions)) {
LOG(ERROR) << "GpuScheduler::InitializeCommon failed because group "
<< "failed to initialize.";
group_ = NULL; // Must not destroy ContextGroup if it is not initialized.
Destroy(true);
return false;
}
CHECK_GL_ERROR();
disallowed_features_ = disallowed_features;
vertex_attrib_manager_.reset(new VertexAttribManager());
vertex_attrib_manager_->Initialize(group_->max_vertex_attribs());
query_manager_.reset(new QueryManager(this, feature_info_));
util_.set_num_compressed_texture_formats(
validators_->compressed_texture_format.GetValues().size());
if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
// We have to enable vertex array 0 on OpenGL or it won't render. Note that
// OpenGL ES 2.0 does not have this issue.
glEnableVertexAttribArray(0);
}
glGenBuffersARB(1, &attrib_0_buffer_id_);
glBindBuffer(GL_ARRAY_BUFFER, attrib_0_buffer_id_);
glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffersARB(1, &fixed_attrib_buffer_id_);
texture_units_.reset(
new TextureUnit[group_->max_texture_units()]);
for (uint32 tt = 0; tt < group_->max_texture_units(); ++tt) {
glActiveTexture(GL_TEXTURE0 + tt);
// We want the last bind to be 2D.
TextureManager::TextureInfo* info;
if (feature_info_->feature_flags().oes_egl_image_external) {
info = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_EXTERNAL_OES);
texture_units_[tt].bound_texture_external_oes = info;
glBindTexture(GL_TEXTURE_EXTERNAL_OES, info->service_id());
}
if (feature_info_->feature_flags().arb_texture_rectangle) {
info = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_RECTANGLE_ARB);
texture_units_[tt].bound_texture_rectangle_arb = info;
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, info->service_id());
}
info = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_CUBE_MAP);
texture_units_[tt].bound_texture_cube_map = info;
glBindTexture(GL_TEXTURE_CUBE_MAP, info->service_id());
info = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_2D);
texture_units_[tt].bound_texture_2d = info;
glBindTexture(GL_TEXTURE_2D, info->service_id());
}
glActiveTexture(GL_TEXTURE0);
CHECK_GL_ERROR();
ContextCreationAttribParser attrib_parser;
if (!attrib_parser.Parse(attribs))
return false;
// These are NOT if the back buffer has these proprorties. They are
// if we want the command buffer to enforce them regardless of what
// the real backbuffer is assuming the real back buffer gives us more than
// we ask for. In other words, if we ask for RGB and we get RGBA then we'll
// make it appear RGB. If on the other hand we ask for RGBA nd get RGB we
// can't do anything about that.
GLint v = 0;
glGetIntegerv(GL_ALPHA_BITS, &v);
// This checks if the user requested RGBA and we have RGBA then RGBA. If the
// user requested RGB then RGB. If the user did not specify a preference than
// use whatever we were given. Same for DEPTH and STENCIL.
back_buffer_color_format_ =
(attrib_parser.alpha_size_ != 0 && v > 0) ? GL_RGBA : GL_RGB;
glGetIntegerv(GL_DEPTH_BITS, &v);
back_buffer_has_depth_ = attrib_parser.depth_size_ != 0 && v > 0;
glGetIntegerv(GL_STENCIL_BITS, &v);
back_buffer_has_stencil_ = attrib_parser.stencil_size_ != 0 && v > 0;
if (offscreen) {
if (attrib_parser.samples_ > 0 && attrib_parser.sample_buffers_ > 0 &&
(context_->HasExtension("GL_EXT_framebuffer_multisample") ||
context_->HasExtension("GL_ANGLE_framebuffer_multisample"))) {
// Per ext_framebuffer_multisample spec, need max bound on sample count.
// max_sample_count must be initialized to a sane value. If
// glGetIntegerv() throws a GL error, it leaves its argument unchanged.
GLint max_sample_count = 1;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &max_sample_count);
offscreen_target_samples_ = std::min(attrib_parser.samples_,
max_sample_count);
} else {
offscreen_target_samples_ = 1;
}
offscreen_target_buffer_preserved_ = attrib_parser.buffer_preserved_;
if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) {
const bool rgb8_supported =
context_->HasExtension("GL_OES_rgb8_rgba8");
// The only available default render buffer formats in GLES2 have very
// little precision. Don't enable multisampling unless 8-bit render
// buffer formats are available--instead fall back to 8-bit textures.
if (rgb8_supported && offscreen_target_samples_ > 1) {
offscreen_target_color_format_ = attrib_parser.alpha_size_ > 0 ?
GL_RGBA8 : GL_RGB8;
} else {
offscreen_target_samples_ = 1;
offscreen_target_color_format_ = attrib_parser.alpha_size_ > 0 ?
GL_RGBA : GL_RGB;
}
// ANGLE only supports packed depth/stencil formats, so use it if it is
// available.
const bool depth24_stencil8_supported =
context_->HasExtension("GL_OES_packed_depth_stencil");
VLOG(1) << "GL_OES_packed_depth_stencil "
<< (depth24_stencil8_supported ? "" : "not ") << "supported.";
if ((attrib_parser.depth_size_ > 0 || attrib_parser.stencil_size_ > 0) &&
depth24_stencil8_supported) {
offscreen_target_depth_format_ = GL_DEPTH24_STENCIL8;
offscreen_target_stencil_format_ = 0;
} else {
// It may be the case that this depth/stencil combination is not
// supported, but this will be checked later by CheckFramebufferStatus.
offscreen_target_depth_format_ = attrib_parser.depth_size_ > 0 ?
GL_DEPTH_COMPONENT16 : 0;
offscreen_target_stencil_format_ = attrib_parser.stencil_size_ > 0 ?
GL_STENCIL_INDEX8 : 0;
}
} else {
offscreen_target_color_format_ = attrib_parser.alpha_size_ > 0 ?
GL_RGBA : GL_RGB;
// If depth is requested at all, use the packed depth stencil format if
// it's available, as some desktop GL drivers don't support any non-packed
// formats for depth attachments.
const bool depth24_stencil8_supported =
context_->HasExtension("GL_EXT_packed_depth_stencil");
VLOG(1) << "GL_EXT_packed_depth_stencil "
<< (depth24_stencil8_supported ? "" : "not ") << "supported.";
if ((attrib_parser.depth_size_ > 0 || attrib_parser.stencil_size_ > 0) &&
depth24_stencil8_supported) {
offscreen_target_depth_format_ = GL_DEPTH24_STENCIL8;
offscreen_target_stencil_format_ = 0;
} else {
offscreen_target_depth_format_ = attrib_parser.depth_size_ > 0 ?
GL_DEPTH_COMPONENT : 0;
offscreen_target_stencil_format_ = attrib_parser.stencil_size_ > 0 ?
GL_STENCIL_INDEX : 0;
}
}
offscreen_saved_color_format_ = attrib_parser.alpha_size_ > 0 ?
GL_RGBA : GL_RGB;
// 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();
// Due to GLES2 format limitations, either the color texture (for
// non-multisampling) or the color render buffer (for multisampling) will be
// attached to the offscreen frame buffer. The render buffer has more
// limited formats available to it, but the texture can't do multisampling.
if (IsOffscreenBufferMultisampled()) {
offscreen_target_color_render_buffer_.reset(new RenderBuffer(this));
offscreen_target_color_render_buffer_->Create();
} else {
offscreen_target_color_texture_.reset(new Texture(this));
offscreen_target_color_texture_->Create();
}
offscreen_target_depth_render_buffer_.reset(new RenderBuffer(this));
offscreen_target_depth_render_buffer_->Create();
offscreen_target_stencil_render_buffer_.reset(new RenderBuffer(this));
offscreen_target_stencil_render_buffer_->Create();
// Create the saved offscreen texture. The target frame buffer is copied
// here when SwapBuffers is called.
offscreen_saved_frame_buffer_.reset(new FrameBuffer(this));
offscreen_saved_frame_buffer_->Create();
//
offscreen_saved_color_texture_.reset(new Texture(this));
offscreen_saved_color_texture_->Create();
// Allocate the render buffers at their initial size and check the status
// of the frame buffers is okay.
if (!ResizeOffscreenFrameBuffer(size)) {
LOG(ERROR) << "Could not allocate offscreen buffer storage.";
Destroy(true);
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);
}
// Clear the backbuffer.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// OpenGL ES 2.0 implicitly enables the desktop GL capability
// VERTEX_PROGRAM_POINT_SIZE and doesn't expose this enum. This fact
// isn't well documented; it was discovered in the Khronos OpenGL ES
// mailing list archives. It also implicitly enables the desktop GL
// capability GL_POINT_SPRITE to provide access to the gl_PointCoord
// variable in fragment shaders.
if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
glEnable(GL_POINT_SPRITE);
}
has_robustness_extension_ =
context->HasExtension("GL_ARB_robustness") ||
context->HasExtension("GL_EXT_robustness");
if (!feature_info_->feature_flags().disable_workarounds) {
#if defined(OS_MACOSX)
needs_mac_nvidia_driver_workaround_ =
feature_info_->feature_flags().is_nvidia;
needs_glsl_built_in_function_emulation_ =
feature_info_->feature_flags().is_amd;
#endif
}
if (!InitializeShaderTranslator()) {
return false;
}
viewport_width_ = size.width();
viewport_height_ = size.height();
glViewport(viewport_x_, viewport_y_, viewport_width_, viewport_height_);
GLint viewport_params[4] = { 0 };
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_params);
viewport_max_width_ = viewport_params[0];
viewport_max_height_ = viewport_params[1];
// Set all the default state because some GL drivers get it wrong.
glActiveTexture(GL_TEXTURE0 + active_texture_unit_);
glLineWidth(1.0);
EnableDisable(GL_BLEND, enable_blend_);
glBlendColor(0.0f, 0.0, 0.0f, 0.0f);
glBlendFunc(GL_ONE, GL_ZERO);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
glClearColor(clear_red_, clear_green_, clear_blue_, clear_alpha_);
glColorMask(mask_red_, mask_green_, mask_blue_, mask_alpha_);
EnableDisable(GL_CULL_FACE, enable_cull_face_);
glCullFace(GL_BACK);
glClearDepth(clear_depth_);
glDepthFunc(GL_LESS);
glDepthRange(0.0f, 1.0f);
EnableDisable(GL_DEPTH_TEST, enable_depth_test_);
glEnable(GL_DITHER);
glFrontFace(GL_CCW);
glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE);
glLineWidth(1.0f);
glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment_);
glPolygonOffset(0.0f, 0.0f);
glDisable(GL_POLYGON_OFFSET_FILL);
glSampleCoverage(1.0, false);
glScissor(viewport_x_, viewport_y_, viewport_width_, viewport_height_);
EnableDisable(GL_SCISSOR_TEST, enable_scissor_test_);
EnableDisable(GL_STENCIL_TEST, enable_stencil_test_);
glClearStencil(clear_stencil_);
glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFFU);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilMaskSeparate(GL_FRONT, mask_stencil_front_);
glStencilMaskSeparate(GL_BACK, mask_stencil_back_);
glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment_);
DoBindBuffer(GL_ARRAY_BUFFER, 0);
DoBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
DoBindFramebuffer(GL_FRAMEBUFFER, 0);
DoBindRenderbuffer(GL_RENDERBUFFER, 0);
// AMD and Intel drivers on Mac OS apparently get gl_PointCoord
// backward from the spec and this setting makes them work
// correctly. rdar://problem/11883495
#if defined(OS_MACOSX)
if (!feature_info_->feature_flags().disable_workarounds &&
(feature_info_->feature_flags().is_amd ||
feature_info_->feature_flags().is_intel) &&
gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGL) {
glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT);
}
#endif
return true;
}
void GLES2DecoderImpl::UpdateCapabilities() {
util_.set_num_compressed_texture_formats(
validators_->compressed_texture_format.GetValues().size());
util_.set_num_shader_binary_formats(
validators_->shader_binary_format.GetValues().size());
}
bool GLES2DecoderImpl::InitializeShaderTranslator() {
TRACE_EVENT0("gpu", "GLES2DecoderImpl::InitializeShaderTranslator");
// Re-check the state of use_shader_translator_ each time this is called.
if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2 &&
(feature_info_->feature_flags().chromium_webglsl ||
force_webgl_glsl_validation_) &&
!use_shader_translator_) {
use_shader_translator_ = true;
}
if (!use_shader_translator_) {
return true;
}
ShBuiltInResources resources;
ShInitBuiltInResources(&resources);
resources.MaxVertexAttribs = group_->max_vertex_attribs();
resources.MaxVertexUniformVectors =
group_->max_vertex_uniform_vectors();
resources.MaxVaryingVectors = group_->max_varying_vectors();
resources.MaxVertexTextureImageUnits =
group_->max_vertex_texture_image_units();
resources.MaxCombinedTextureImageUnits = group_->max_texture_units();
resources.MaxTextureImageUnits = group_->max_texture_image_units();
resources.MaxFragmentUniformVectors =
group_->max_fragment_uniform_vectors();
resources.MaxDrawBuffers = 1;
if (force_webgl_glsl_validation_) {
resources.OES_standard_derivatives = derivatives_explicitly_enabled_;
} else {
resources.OES_standard_derivatives =
feature_info_->feature_flags().oes_standard_derivatives ? 1 : 0;
resources.ARB_texture_rectangle =
feature_info_->feature_flags().arb_texture_rectangle ? 1 : 0;
}
ShShaderSpec shader_spec = force_webgl_glsl_validation_ ||
feature_info_->feature_flags().chromium_webglsl ?
SH_WEBGL_SPEC : SH_GLES2_SPEC;
ShaderTranslatorInterface::GlslImplementationType implementation_type =
gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2 ?
ShaderTranslatorInterface::kGlslES : ShaderTranslatorInterface::kGlsl;
ShaderTranslatorInterface::GlslBuiltInFunctionBehavior function_behavior =
needs_glsl_built_in_function_emulation_ ?
ShaderTranslatorInterface::kGlslBuiltInFunctionEmulated :
ShaderTranslatorInterface::kGlslBuiltInFunctionOriginal;
ShaderTranslatorCache* cache = ShaderTranslatorCache::GetInstance();
vertex_translator_ = cache->GetTranslator(
SH_VERTEX_SHADER, shader_spec, &resources,
implementation_type, function_behavior);
if (!vertex_translator_.get()) {
LOG(ERROR) << "Could not initialize vertex shader translator.";
Destroy(true);
return false;
}
fragment_translator_ = cache->GetTranslator(
SH_FRAGMENT_SHADER, shader_spec, &resources,
implementation_type, function_behavior);
if (!fragment_translator_.get()) {
LOG(ERROR) << "Could not initialize fragment shader translator.";
Destroy(true);
return false;
}
return true;
}
bool GLES2DecoderImpl::GenBuffersHelper(GLsizei n, const GLuint* client_ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
if (GetBufferInfo(client_ids[ii])) {
return false;
}
}
scoped_array<GLuint> service_ids(new GLuint[n]);
glGenBuffersARB(n, service_ids.get());
for (GLsizei ii = 0; ii < n; ++ii) {
CreateBufferInfo(client_ids[ii], service_ids[ii]);
}
return true;
}
bool GLES2DecoderImpl::GenFramebuffersHelper(
GLsizei n, const GLuint* client_ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
if (GetFramebufferInfo(client_ids[ii])) {
return false;
}
}
scoped_array<GLuint> service_ids(new GLuint[n]);
glGenFramebuffersEXT(n, service_ids.get());
for (GLsizei ii = 0; ii < n; ++ii) {
CreateFramebufferInfo(client_ids[ii], service_ids[ii]);
}
return true;
}
bool GLES2DecoderImpl::GenRenderbuffersHelper(
GLsizei n, const GLuint* client_ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
if (GetRenderbufferInfo(client_ids[ii])) {
return false;
}
}
scoped_array<GLuint> service_ids(new GLuint[n]);
glGenRenderbuffersEXT(n, service_ids.get());
for (GLsizei ii = 0; ii < n; ++ii) {
CreateRenderbufferInfo(client_ids[ii], service_ids[ii]);
}
return true;
}
bool GLES2DecoderImpl::GenTexturesHelper(GLsizei n, const GLuint* client_ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
if (GetTextureInfo(client_ids[ii])) {
return false;
}
}
scoped_array<GLuint> service_ids(new GLuint[n]);
glGenTextures(n, service_ids.get());
for (GLsizei ii = 0; ii < n; ++ii) {
CreateTextureInfo(client_ids[ii], service_ids[ii]);
}
return true;
}
void GLES2DecoderImpl::DeleteBuffersHelper(
GLsizei n, const GLuint* client_ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
BufferManager::BufferInfo* buffer = GetBufferInfo(client_ids[ii]);
if (buffer && !buffer->IsDeleted()) {
vertex_attrib_manager_->Unbind(buffer);
if (bound_array_buffer_ == buffer) {
bound_array_buffer_ = NULL;
}
if (bound_element_array_buffer_ == buffer) {
bound_element_array_buffer_ = NULL;
}
RemoveBufferInfo(client_ids[ii]);
}
}
}
void GLES2DecoderImpl::DeleteFramebuffersHelper(
GLsizei n, const GLuint* client_ids) {
bool supports_seperate_framebuffer_binds =
feature_info_->feature_flags().chromium_framebuffer_multisample;
for (GLsizei ii = 0; ii < n; ++ii) {
FramebufferManager::FramebufferInfo* framebuffer =
GetFramebufferInfo(client_ids[ii]);
if (framebuffer && !framebuffer->IsDeleted()) {
if (framebuffer == bound_draw_framebuffer_) {
bound_draw_framebuffer_ = NULL;
state_dirty_ = true;
GLenum target = supports_seperate_framebuffer_binds ?
GL_DRAW_FRAMEBUFFER : GL_FRAMEBUFFER;
glBindFramebufferEXT(target, GetBackbufferServiceId());
}
if (framebuffer == bound_read_framebuffer_) {
bound_read_framebuffer_ = NULL;
GLenum target = supports_seperate_framebuffer_binds ?
GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER;
glBindFramebufferEXT(target, GetBackbufferServiceId());
}
RemoveFramebufferInfo(client_ids[ii]);
}
}
}
void GLES2DecoderImpl::DeleteRenderbuffersHelper(
GLsizei n, const GLuint* client_ids) {
bool supports_seperate_framebuffer_binds =
feature_info_->feature_flags().chromium_framebuffer_multisample;
for (GLsizei ii = 0; ii < n; ++ii) {
RenderbufferManager::RenderbufferInfo* renderbuffer =
GetRenderbufferInfo(client_ids[ii]);
if (renderbuffer && !renderbuffer->IsDeleted()) {
if (bound_renderbuffer_ == renderbuffer) {
bound_renderbuffer_ = NULL;
}
// Unbind from current framebuffers.
if (supports_seperate_framebuffer_binds) {
if (bound_read_framebuffer_) {
bound_read_framebuffer_->UnbindRenderbuffer(
GL_READ_FRAMEBUFFER, renderbuffer);
}
if (bound_draw_framebuffer_) {
bound_draw_framebuffer_->UnbindRenderbuffer(
GL_DRAW_FRAMEBUFFER, renderbuffer);
}
} else {
if (bound_draw_framebuffer_) {
bound_draw_framebuffer_->UnbindRenderbuffer(
GL_FRAMEBUFFER, renderbuffer);
}
}
state_dirty_ = true;
RemoveRenderbufferInfo(client_ids[ii]);
}
}
}
void GLES2DecoderImpl::DeleteTexturesHelper(
GLsizei n, const GLuint* client_ids) {
bool supports_seperate_framebuffer_binds =
feature_info_->feature_flags().chromium_framebuffer_multisample;
for (GLsizei ii = 0; ii < n; ++ii) {
TextureManager::TextureInfo* texture = GetTextureInfo(client_ids[ii]);
if (texture && !texture->IsDeleted()) {
if (texture->IsAttachedToFramebuffer()) {
state_dirty_ = true;
}
// Unbind texture from texture units.
for (size_t jj = 0; jj < group_->max_texture_units(); ++jj) {
texture_units_[ii].Unbind(texture);
}
// Unbind from current framebuffers.
if (supports_seperate_framebuffer_binds) {
if (bound_read_framebuffer_) {
bound_read_framebuffer_->UnbindTexture(GL_READ_FRAMEBUFFER, texture);
}
if (bound_draw_framebuffer_) {
bound_draw_framebuffer_->UnbindTexture(GL_DRAW_FRAMEBUFFER, texture);
}
} else {
if (bound_draw_framebuffer_) {
bound_draw_framebuffer_->UnbindTexture(GL_FRAMEBUFFER, texture);
}
}
GLuint service_id = texture->service_id();
if (texture->IsStreamTexture() && stream_texture_manager_) {
stream_texture_manager_->DestroyStreamTexture(service_id);
}
#if defined(OS_MACOSX)
if (texture->target() == GL_TEXTURE_RECTANGLE_ARB) {
ReleaseIOSurfaceForTexture(service_id);
}
#endif
RemoveTextureInfo(client_ids[ii]);
}
}
}
// } // anonymous namespace
bool GLES2DecoderImpl::MakeCurrent() {
if (!context_.get() || !context_->MakeCurrent(surface_.get()))
return false;
if (WasContextLost()) {
LOG(ERROR) << " GLES2DecoderImpl: Context lost during MakeCurrent.";
return false;
}
return true;
}
void GLES2DecoderImpl::ReleaseCurrent() {
if (context_.get())
context_->ReleaseCurrent(surface_.get());
}
void GLES2DecoderImpl::RestoreCurrentRenderbufferBindings() {
RenderbufferManager::RenderbufferInfo* renderbuffer =
GetRenderbufferInfoForTarget(GL_RENDERBUFFER);
glBindRenderbufferEXT(
GL_RENDERBUFFER, renderbuffer ? renderbuffer->service_id() : 0);
}
static void RebindCurrentFramebuffer(
GLenum target,
FramebufferManager::FramebufferInfo* info,
GLuint back_buffer_service_id) {
GLuint framebuffer_id = info ? info->service_id() : 0;
if (framebuffer_id == 0) {
framebuffer_id = back_buffer_service_id;
}
glBindFramebufferEXT(target, framebuffer_id);
}
void GLES2DecoderImpl::RestoreCurrentFramebufferBindings() {
state_dirty_ = true;
if (!feature_info_->feature_flags().chromium_framebuffer_multisample) {
RebindCurrentFramebuffer(
GL_FRAMEBUFFER,
bound_draw_framebuffer_.get(),
GetBackbufferServiceId());
} else {
RebindCurrentFramebuffer(
GL_READ_FRAMEBUFFER_EXT,
bound_read_framebuffer_.get(),
GetBackbufferServiceId());
RebindCurrentFramebuffer(
GL_DRAW_FRAMEBUFFER_EXT,
bound_draw_framebuffer_.get(),
GetBackbufferServiceId());
}
}
void GLES2DecoderImpl::RestoreCurrentTexture2DBindings() {
GLES2DecoderImpl::TextureUnit& info = texture_units_[0];
GLuint last_id;
if (info.bound_texture_2d) {
last_id = info.bound_texture_2d->service_id();
} else {
last_id = 0;
}
glBindTexture(GL_TEXTURE_2D, last_id);
glActiveTexture(GL_TEXTURE0 + active_texture_unit_);
}
bool GLES2DecoderImpl::CheckFramebufferValid(
FramebufferManager::FramebufferInfo* framebuffer,
GLenum target, const char* func_name) {
if (!framebuffer) {
return true;
}
if (framebuffer_manager()->IsComplete(framebuffer)) {
return true;
}
GLenum completeness = framebuffer->IsPossiblyComplete();
if (completeness != GL_FRAMEBUFFER_COMPLETE) {
SetGLError(
GL_INVALID_FRAMEBUFFER_OPERATION, func_name, "framebuffer incomplete");
return false;
}
// Are all the attachments cleared?
if (renderbuffer_manager()->HaveUnclearedRenderbuffers() ||
texture_manager()->HaveUnclearedMips()) {
if (!framebuffer->IsCleared()) {
// Can we clear them?
if (glCheckFramebufferStatusEXT(target) != GL_FRAMEBUFFER_COMPLETE) {
SetGLError(
GL_INVALID_FRAMEBUFFER_OPERATION, func_name,
"framebuffer incomplete (clear)");
return false;
}
ClearUnclearedAttachments(target, framebuffer);
}
}
if (!framebuffer_manager()->IsComplete(framebuffer)) {
if (glCheckFramebufferStatusEXT(target) != GL_FRAMEBUFFER_COMPLETE) {
SetGLError(
GL_INVALID_FRAMEBUFFER_OPERATION, func_name,
"framebuffer incomplete (check)");
return false;
}
framebuffer_manager()->MarkAsComplete(framebuffer);
}
// NOTE: At this point we don't know if the framebuffer is complete but
// we DO know that everything that needs to be cleared has been cleared.
return true;
}
bool GLES2DecoderImpl::CheckBoundFramebuffersValid(const char* func_name) {
if (!feature_info_->feature_flags().chromium_framebuffer_multisample) {
return CheckFramebufferValid(
bound_draw_framebuffer_, GL_FRAMEBUFFER_EXT, func_name);
}
return CheckFramebufferValid(
bound_draw_framebuffer_, GL_DRAW_FRAMEBUFFER_EXT, func_name) &&
CheckFramebufferValid(
bound_read_framebuffer_, GL_READ_FRAMEBUFFER_EXT, func_name);
}
gfx::Size GLES2DecoderImpl::GetBoundReadFrameBufferSize() {
FramebufferManager::FramebufferInfo* framebuffer =
GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER);
if (framebuffer != NULL) {
const FramebufferManager::FramebufferInfo::Attachment* attachment =
framebuffer->GetAttachment(GL_COLOR_ATTACHMENT0);
if (attachment) {
return gfx::Size(attachment->width(), attachment->height());
}
return gfx::Size(0, 0);
} else if (offscreen_target_frame_buffer_.get()) {
return offscreen_size_;
} else {
return surface_->GetSize();
}
}
GLenum GLES2DecoderImpl::GetBoundReadFrameBufferInternalFormat() {
FramebufferManager::FramebufferInfo* framebuffer =
GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER);
if (framebuffer != NULL) {
return framebuffer->GetColorAttachmentFormat();
} else if (offscreen_target_frame_buffer_.get()) {
return offscreen_target_color_format_;
} else {
return back_buffer_color_format_;
}
}
GLenum GLES2DecoderImpl::GetBoundDrawFrameBufferInternalFormat() {
FramebufferManager::FramebufferInfo* framebuffer =
GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER);