// 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 #include #include #include #include #include #include #include #include #include "base/at_exit.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/numerics/safe_math.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_synthetic_delay.h" #include "build/build_config.h" #define GLES2_GPU_SERVICE 1 #include "gpu/command_buffer/common/debug_marker_manager.h" #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/common/mailbox.h" #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h" #include "gpu/command_buffer/service/async_pixel_transfer_manager.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/context_state.h" #include "gpu/command_buffer/service/error_state.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_clear_framebuffer.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_state_tracer.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/command_buffer/service/gpu_tracer.h" #include "gpu/command_buffer/service/image_manager.h" #include "gpu/command_buffer/service/mailbox_manager.h" #include "gpu/command_buffer/service/memory_tracking.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/texture_manager.h" #include "gpu/command_buffer/service/valuebuffer_manager.h" #include "gpu/command_buffer/service/vertex_array_manager.h" #include "gpu/command_buffer/service/vertex_attrib_manager.h" #include "third_party/smhasher/src/City.h" #include "ui/gl/gl_fence.h" #include "ui/gl/gl_image.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface.h" #if defined(OS_MACOSX) #include // Note that this must be included after gl_bindings.h to avoid conflicts. #include #endif #if defined(OS_WIN) #include "base/win/win_util.h" #endif namespace gpu { namespace gles2 { namespace { const char kOESDerivativeExtension[] = "GL_OES_standard_derivatives"; const char kEXTFragDepthExtension[] = "GL_EXT_frag_depth"; const char kEXTDrawBuffersExtension[] = "GL_EXT_draw_buffers"; const char kEXTShaderTextureLodExtension[] = "GL_EXT_shader_texture_lod"; const GLfloat kIdentityMatrix[16] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; bool PrecisionMeetsSpecForHighpFloat(GLint rangeMin, GLint rangeMax, GLint precision) { return (rangeMin >= 62) && (rangeMax >= 62) && (precision >= 16); } void GetShaderPrecisionFormatImpl(GLenum shader_type, GLenum precision_type, GLint* range, GLint* precision) { switch (precision_type) { case GL_LOW_INT: case GL_MEDIUM_INT: case GL_HIGH_INT: // These values are for a 32-bit twos-complement integer format. range[0] = 31; range[1] = 30; *precision = 0; break; case GL_LOW_FLOAT: case GL_MEDIUM_FLOAT: case GL_HIGH_FLOAT: // These values are for an IEEE single-precision floating-point format. range[0] = 127; range[1] = 127; *precision = 23; break; default: NOTREACHED(); break; } if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2 && gfx::g_driver_gl.fn.glGetShaderPrecisionFormatFn) { // This function is sometimes defined even though it's really just // a stub, so we need to set range and precision as if it weren't // defined before calling it. // On Mac OS with some GPUs, calling this generates a // GL_INVALID_OPERATION error. Avoid calling it on non-GLES2 // platforms. glGetShaderPrecisionFormat(shader_type, precision_type, range, precision); // TODO(brianderson): Make the following official workarounds. // Some drivers have bugs where they report the ranges as a negative number. // Taking the absolute value here shouldn't hurt because negative numbers // aren't expected anyway. range[0] = abs(range[0]); range[1] = abs(range[1]); // If the driver reports a precision for highp float that isn't actually // highp, don't pretend like it's supported because shader compilation will // fail anyway. if (precision_type == GL_HIGH_FLOAT && !PrecisionMeetsSpecForHighpFloat(range[0], range[1], *precision)) { range[0] = 0; range[1] = 0; *precision = 0; } } } gfx::OverlayTransform GetGFXOverlayTransform(GLenum plane_transform) { switch (plane_transform) { case GL_OVERLAY_TRANSFORM_NONE_CHROMIUM: return gfx::OVERLAY_TRANSFORM_NONE; case GL_OVERLAY_TRANSFORM_FLIP_HORIZONTAL_CHROMIUM: return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL; case GL_OVERLAY_TRANSFORM_FLIP_VERTICAL_CHROMIUM: return gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL; case GL_OVERLAY_TRANSFORM_ROTATE_90_CHROMIUM: return gfx::OVERLAY_TRANSFORM_ROTATE_90; case GL_OVERLAY_TRANSFORM_ROTATE_180_CHROMIUM: return gfx::OVERLAY_TRANSFORM_ROTATE_180; case GL_OVERLAY_TRANSFORM_ROTATE_270_CHROMIUM: return gfx::OVERLAY_TRANSFORM_ROTATE_270; default: return gfx::OVERLAY_TRANSFORM_INVALID; } } template GLuint GetClientId(const MANAGER_TYPE* manager, const OBJECT_TYPE* object) { DCHECK(manager); GLuint client_id = 0; if (object) { manager->GetClientId(object->service_id(), &client_id); } return client_id; } struct Vec4f { explicit Vec4f(const Vec4& data) { data.GetValues(v); } GLfloat v[4]; }; } // namespace class GLES2DecoderImpl; // Local versions of the SET_GL_ERROR macros #define LOCAL_SET_GL_ERROR(error, function_name, msg) \ ERRORSTATE_SET_GL_ERROR(state_.GetErrorState(), error, function_name, msg) #define LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, value, label) \ ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(state_.GetErrorState(), \ function_name, value, label) #define LOCAL_SET_GL_ERROR_INVALID_PARAM(error, function_name, pname) \ ERRORSTATE_SET_GL_ERROR_INVALID_PARAM(state_.GetErrorState(), error, \ function_name, pname) #define LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(function_name) \ ERRORSTATE_COPY_REAL_GL_ERRORS_TO_WRAPPER(state_.GetErrorState(), \ function_name) #define LOCAL_PEEK_GL_ERROR(function_name) \ ERRORSTATE_PEEK_GL_ERROR(state_.GetErrorState(), function_name) #define LOCAL_CLEAR_REAL_GL_ERRORS(function_name) \ ERRORSTATE_CLEAR_REAL_GL_ERRORS(state_.GetErrorState(), function_name) #define LOCAL_PERFORMANCE_WARNING(msg) \ PerformanceWarning(__FILE__, __LINE__, msg) #define LOCAL_RENDER_WARNING(msg) \ RenderWarning(__FILE__, __LINE__, msg) // 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. static_assert(sizeof(GLint) == sizeof(uint32), // NOLINT "GLint should be the same size as uint32"); static_assert(sizeof(GLsizei) == sizeof(uint32), // NOLINT "GLsizei should be the same size as uint32"); static_assert(sizeof(GLfloat) == sizeof(float), // NOLINT "GLfloat should be the 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 const void* AddressAfterStruct(const T& pod) { return reinterpret_cast(&pod) + sizeof(pod); } // Returns the address of the frst byte after the struct or NULL if size > // immediate_data_size. template RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod, uint32 size, uint32 immediate_data_size) { return (size <= immediate_data_size) ? static_cast(const_cast(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; } // 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; } // This class prevents any GL errors that occur when it is in scope from // being reported to the client. class ScopedGLErrorSuppressor { public: explicit ScopedGLErrorSuppressor( const char* function_name, ErrorState* error_state); ~ScopedGLErrorSuppressor(); private: const char* function_name_; ErrorState* error_state_; DISALLOW_COPY_AND_ASSIGN(ScopedGLErrorSuppressor); }; // Temporarily changes a decoder's bound 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 ScopedTextureBinder { public: explicit ScopedTextureBinder(ContextState* state, GLuint id, GLenum target); ~ScopedTextureBinder(); private: ContextState* state_; GLenum target_; DISALLOW_COPY_AND_ASSIGN(ScopedTextureBinder); }; // Temporarily changes a decoder's bound render buffer and restore it when this // object goes out of scope. class ScopedRenderBufferBinder { public: explicit ScopedRenderBufferBinder(ContextState* state, GLuint id); ~ScopedRenderBufferBinder(); private: ContextState* state_; 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: explicit 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: explicit ScopedResolvedFrameBufferBinder(GLES2DecoderImpl* decoder, bool enforce_internal_framebuffer, bool internal); ~ScopedResolvedFrameBufferBinder(); private: GLES2DecoderImpl* decoder_; bool resolve_and_bind_; DISALLOW_COPY_AND_ASSIGN(ScopedResolvedFrameBufferBinder); }; class ScopedModifyPixels { public: explicit ScopedModifyPixels(TextureRef* ref); ~ScopedModifyPixels(); private: TextureRef* ref_; }; ScopedModifyPixels::ScopedModifyPixels(TextureRef* ref) : ref_(ref) { if (ref_) ref_->texture()->OnWillModifyPixels(); } ScopedModifyPixels::~ScopedModifyPixels() { if (ref_) ref_->texture()->OnDidModifyPixels(); } class ScopedRenderTo { public: explicit ScopedRenderTo(Framebuffer* framebuffer); ~ScopedRenderTo(); private: const Framebuffer* framebuffer_; }; ScopedRenderTo::ScopedRenderTo(Framebuffer* framebuffer) : framebuffer_(framebuffer) { if (framebuffer) framebuffer_->OnWillRenderTo(); } ScopedRenderTo::~ScopedRenderTo() { if (framebuffer_) framebuffer_->OnDidRenderTo(); } // Encapsulates an OpenGL texture. class BackTexture { public: explicit BackTexture(MemoryTracker* memory_tracker, ContextState* state); ~BackTexture(); // 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, bool zero); // 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_; } private: MemoryTypeTracker memory_tracker_; ContextState* state_; size_t bytes_allocated_; GLuint id_; gfx::Size size_; DISALLOW_COPY_AND_ASSIGN(BackTexture); }; // Encapsulates an OpenGL render buffer of any format. class BackRenderbuffer { public: explicit BackRenderbuffer( RenderbufferManager* renderbuffer_manager, MemoryTracker* memory_tracker, ContextState* state); ~BackRenderbuffer(); // Create a new render buffer. void Create(); // Set the initial size and format of a render buffer or resize it. bool AllocateStorage(const FeatureInfo* feature_info, 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_; } private: RenderbufferManager* renderbuffer_manager_; MemoryTypeTracker memory_tracker_; ContextState* state_; size_t bytes_allocated_; GLuint id_; DISALLOW_COPY_AND_ASSIGN(BackRenderbuffer); }; // Encapsulates an OpenGL frame buffer. class BackFramebuffer { public: explicit BackFramebuffer(GLES2DecoderImpl* decoder); ~BackFramebuffer(); // Create a new frame buffer. void Create(); // Attach a color render buffer to a frame buffer. void AttachRenderTexture(BackTexture* texture); // Attach a render buffer to a frame buffer. Note that this unbinds any // currently bound frame buffer. void AttachRenderBuffer(GLenum target, BackRenderbuffer* 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(BackFramebuffer); }; struct FenceCallback { FenceCallback() : fence(gfx::GLFence::Create()) { DCHECK(fence); } std::vector callbacks; scoped_ptr fence; }; class AsyncUploadTokenCompletionObserver : public AsyncPixelTransferCompletionObserver { public: explicit AsyncUploadTokenCompletionObserver(uint32 async_upload_token) : async_upload_token_(async_upload_token) { } void DidComplete(const AsyncMemoryParams& mem_params) override { DCHECK(mem_params.buffer().get()); void* data = mem_params.GetDataAddress(); AsyncUploadSync* sync = static_cast(data); sync->SetAsyncUploadToken(async_upload_token_); } private: ~AsyncUploadTokenCompletionObserver() override {} uint32 async_upload_token_; DISALLOW_COPY_AND_ASSIGN(AsyncUploadTokenCompletionObserver); }; // } // anonymous namespace. // static const unsigned int GLES2Decoder::kDefaultStencilMask = static_cast(-1); bool GLES2Decoder::GetServiceTextureId(uint32 client_texture_id, uint32* service_texture_id) { return false; } GLES2Decoder::GLES2Decoder() : initialized_(false), debug_(false), log_commands_(false), unsafe_es3_apis_enabled_(false) { } GLES2Decoder::~GLES2Decoder() { } void GLES2Decoder::BeginDecoding() {} void GLES2Decoder::EndDecoding() {} // This class implements GLES2Decoder so we don't have to expose all the GLES2 // cmd stuff to outside this class. class GLES2DecoderImpl : public GLES2Decoder, public FramebufferManager::TextureDetachObserver, public ErrorStateClient { public: explicit GLES2DecoderImpl(ContextGroup* group); ~GLES2DecoderImpl() override; // Overridden from AsyncAPIInterface. Error DoCommand(unsigned int command, unsigned int arg_count, const void* args) override; error::Error DoCommands(unsigned int num_commands, const void* buffer, int num_entries, int* entries_processed) override; template error::Error DoCommandsImpl(unsigned int num_commands, const void* buffer, int num_entries, int* entries_processed); // Overridden from AsyncAPIInterface. const char* GetCommandName(unsigned int command_id) const override; // Overridden from GLES2Decoder. bool Initialize(const scoped_refptr& surface, const scoped_refptr& context, bool offscreen, const gfx::Size& offscreen_size, const DisallowedFeatures& disallowed_features, const std::vector& attribs) override; void Destroy(bool have_context) override; void SetSurface(const scoped_refptr& surface) override; void ProduceFrontBuffer(const Mailbox& mailbox) override; bool ResizeOffscreenFrameBuffer(const gfx::Size& size) override; void UpdateParentTextureInfo(); bool MakeCurrent() override; GLES2Util* GetGLES2Util() override { return &util_; } gfx::GLContext* GetGLContext() override { return context_.get(); } ContextGroup* GetContextGroup() override { return group_.get(); } Capabilities GetCapabilities() override; void RestoreState(const ContextState* prev_state) override; void RestoreActiveTexture() const override { state_.RestoreActiveTexture(); } void RestoreAllTextureUnitBindings( const ContextState* prev_state) const override { state_.RestoreAllTextureUnitBindings(prev_state); } void RestoreActiveTextureUnitBinding(unsigned int target) const override { state_.RestoreActiveTextureUnitBinding(target); } void RestoreBufferBindings() const override { state_.RestoreBufferBindings(); } void RestoreGlobalState() const override { state_.RestoreGlobalState(NULL); } void RestoreProgramBindings() const override { state_.RestoreProgramBindings(); } void RestoreTextureUnitBindings(unsigned unit) const override { state_.RestoreTextureUnitBindings(unit, NULL); } void RestoreFramebufferBindings() const override; void RestoreRenderbufferBindings() override; void RestoreTextureState(unsigned service_id) const override; void ClearAllAttributes() const override; void RestoreAllAttributes() const override; QueryManager* GetQueryManager() override { return query_manager_.get(); } VertexArrayManager* GetVertexArrayManager() override { return vertex_array_manager_.get(); } ImageManager* GetImageManager() override { return image_manager_.get(); } ValuebufferManager* GetValuebufferManager() override { return valuebuffer_manager(); } bool ProcessPendingQueries(bool did_finish) override; bool HasMoreIdleWork() override; void PerformIdleWork() override; void WaitForReadPixels(base::Closure callback) override; void SetResizeCallback( const base::Callback& callback) override; Logger* GetLogger() override; void BeginDecoding() override; void EndDecoding() override; ErrorState* GetErrorState() override; const ContextState* GetContextState() override { return &state_; } void SetShaderCacheCallback(const ShaderCacheCallback& callback) override; void SetWaitSyncPointCallback(const WaitSyncPointCallback& callback) override; AsyncPixelTransferManager* GetAsyncPixelTransferManager() override; void ResetAsyncPixelTransferManagerForTest() override; void SetAsyncPixelTransferManagerForTest( AsyncPixelTransferManager* manager) override; void SetIgnoreCachedStateForTest(bool ignore) override; void ProcessFinishedAsyncTransfers(); bool GetServiceTextureId(uint32 client_texture_id, uint32* service_texture_id) override; uint32 GetTextureUploadCount() override; base::TimeDelta GetTotalTextureUploadTime() override; base::TimeDelta GetTotalProcessingCommandsTime() override; void AddProcessingCommandsTime(base::TimeDelta) override; // Restores the current state to the user's settings. void RestoreCurrentFramebufferBindings(); // Sets DEPTH_TEST, STENCIL_TEST and color mask for the current framebuffer. void ApplyDirtyState(); // These check the state of the currently bound framebuffer or the // backbuffer if no framebuffer is bound. // If all_draw_buffers is false, only check with COLOR_ATTACHMENT0, otherwise // check with all attached and enabled color attachments. bool BoundFramebufferHasColorAttachmentWithAlpha(bool all_draw_buffers); bool BoundFramebufferHasDepthAttachment(); bool BoundFramebufferHasStencilAttachment(); error::ContextLostReason GetContextLostReason() override; // Overridden from FramebufferManager::TextureDetachObserver: void OnTextureRefDetachedFromFramebuffer(TextureRef* texture) override; // Overriden from ErrorStateClient. void OnContextLostError() override; void OnOutOfMemoryError() override; // Ensure Renderbuffer corresponding to last DoBindRenderbuffer() is bound. void EnsureRenderbufferBound(); // Helpers to facilitate calling into compatible extensions. static void RenderbufferStorageMultisampleHelper( const FeatureInfo* feature_info, GLenum target, GLsizei samples, GLenum internal_format, GLsizei width, GLsizei height); void BlitFramebufferHelper(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); private: friend class ScopedFrameBufferBinder; friend class ScopedResolvedFrameBufferBinder; friend class BackFramebuffer; // 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 GenValuebuffersCHROMIUMHelper(GLsizei n, const GLuint* client_ids); void DeleteValuebuffersCHROMIUMHelper(GLsizei n, const GLuint* client_ids); bool GenQueriesEXTHelper(GLsizei n, const GLuint* client_ids); void DeleteQueriesEXTHelper(GLsizei n, const GLuint* client_ids); bool GenVertexArraysOESHelper(GLsizei n, const GLuint* client_ids); void DeleteVertexArraysOESHelper(GLsizei n, const GLuint* client_ids); // Helper for async upload token completion notification callback. base::Closure AsyncUploadTokenCompletionClosure(uint32 async_upload_token, uint32 sync_data_shm_id, uint32 sync_data_shm_offset); // Workarounds void OnFboChanged() const; void OnUseFramebuffer() const; error::ContextLostReason GetContextLostReasonFromResetStatus( GLenum reset_status) const; // 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(); } ValuebufferManager* valuebuffer_manager() { return group_->valuebuffer_manager(); } ProgramManager* program_manager() { return group_->program_manager(); } ShaderManager* shader_manager() { return group_->shader_manager(); } ShaderTranslatorCache* shader_translator_cache() { return group_->shader_translator_cache(); } const TextureManager* texture_manager() const { return group_->texture_manager(); } TextureManager* texture_manager() { return group_->texture_manager(); } MailboxManager* mailbox_manager() { return group_->mailbox_manager(); } ImageManager* image_manager() { return image_manager_.get(); } VertexArrayManager* vertex_array_manager() { return vertex_array_manager_.get(); } MemoryTracker* memory_tracker() { return group_->memory_tracker(); } bool EnsureGPUMemoryAvailable(size_t estimated_size) { MemoryTracker* tracker = memory_tracker(); if (tracker) { return tracker->EnsureGPUMemoryAvailable(estimated_size); } return true; } bool IsOffscreenBufferMultisampled() const { return offscreen_target_samples_ > 1; } // Creates a Texture for the given texture. TextureRef* CreateTexture( GLuint client_id, GLuint service_id) { return texture_manager()->CreateTexture(client_id, service_id); } // Gets the texture info for the given texture. Returns NULL if none exists. TextureRef* GetTexture(GLuint client_id) const { return texture_manager()->GetTexture(client_id); } // Deletes the texture info for the given texture. void RemoveTexture(GLuint client_id) { texture_manager()->RemoveTexture(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 GetBoundReadFrameBufferTextureType(); 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 CompressedTexImage3D commands. error::Error DoCompressedTexImage3D( GLenum target, GLint level, GLenum internal_format, GLsizei width, GLsizei height, GLsizei depth, 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 CompressedTexSubImage3D. void DoCompressedTexSubImage3D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei image_size, 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 SwapBuffers. void DoSwapBuffers(); // Wrapper for SwapInterval. void DoSwapInterval(int interval); // Wrapper for CopyTexSubImage2D. void DoCopyTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); // Validation for TexSubImage2D. bool ValidateTexSubImage2D( error::Error* error, const char* function_name, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * data); // Wrapper for TexSubImage2D. error::Error DoTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * data); // Wrapper for TexSubImage3D. error::Error DoTexSubImage3D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * data); // Extra validation for async tex(Sub)Image2D. bool ValidateAsyncTransfer( const char* function_name, TextureRef* texture_ref, GLenum target, GLint level, 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 dest_id, GLenum internal_format, GLenum dest_type); void DoCopySubTextureCHROMIUM(GLenum target, GLuint source_id, GLuint dest_id, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); // Wrapper for TexStorage2DEXT. void DoTexStorage2DEXT( GLenum target, GLint levels, GLenum internal_format, GLsizei width, GLsizei height); void DoProduceTextureCHROMIUM(GLenum target, const GLbyte* key); void DoProduceTextureDirectCHROMIUM(GLuint texture, GLenum target, const GLbyte* key); void ProduceTextureRef(std::string func_name, TextureRef* texture_ref, GLenum target, const GLbyte* data); void EnsureTextureForClientId(GLenum target, GLuint client_id); void DoConsumeTextureCHROMIUM(GLenum target, const GLbyte* key); void DoCreateAndConsumeTextureCHROMIUM(GLenum target, const GLbyte* key, GLuint client_id); bool DoIsValuebufferCHROMIUM(GLuint client_id); void DoBindValueBufferCHROMIUM(GLenum target, GLuint valuebuffer); void DoSubscribeValueCHROMIUM(GLenum target, GLenum subscription); void DoPopulateSubscribedValuesCHROMIUM(GLenum target); void DoUniformValueBufferCHROMIUM(GLint location, GLenum target, GLenum subscription); void DoBindTexImage2DCHROMIUM( GLenum target, GLint image_id); void DoReleaseTexImage2DCHROMIUM( GLenum target, GLint image_id); void DoTraceEndCHROMIUM(void); void DoDrawBuffersEXT(GLsizei count, const GLenum* bufs); void DoLoseContextCHROMIUM(GLenum current, GLenum other); void DoMatrixLoadfCHROMIUM(GLenum matrix_mode, const GLfloat* matrix); void DoMatrixLoadIdentityCHROMIUM(GLenum matrix_mode); // Creates a Program for the given program. Program* CreateProgram( GLuint client_id, GLuint service_id) { return program_manager()->CreateProgram(client_id, service_id); } // Gets the program info for the given program. Returns NULL if none exists. Program* GetProgram(GLuint client_id) { return program_manager()->GetProgram(client_id); } #if defined(NDEBUG) void LogClientServiceMapping( const char* /* function_name */, GLuint /* client_id */, GLuint /* service_id */) { } template void LogClientServiceForInfo( T* /* info */, GLuint /* client_id */, const char* /* function_name */) { } #else void LogClientServiceMapping( const char* function_name, GLuint client_id, GLuint service_id) { if (service_logging_) { VLOG(1) << "[" << logger_.GetLogPrefix() << "] " << function_name << ": client_id = " << client_id << ", service_id = " << service_id; } } template void LogClientServiceForInfo( T* info, GLuint client_id, const char* function_name) { if (info) { LogClientServiceMapping(function_name, client_id, info->service_id()); } } #endif // Gets the program info for the given program. If it's not a program // generates a GL error. Returns NULL if not program. Program* GetProgramInfoNotShader( GLuint client_id, const char* function_name) { Program* program = GetProgram(client_id); if (!program) { if (GetShader(client_id)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "shader passed for program"); } else { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "unknown program"); } } LogClientServiceForInfo(program, client_id, function_name); return program; } // Creates a Shader for the given shader. Shader* CreateShader( GLuint client_id, GLuint service_id, GLenum shader_type) { return shader_manager()->CreateShader( client_id, service_id, shader_type); } // Gets the shader info for the given shader. Returns NULL if none exists. Shader* GetShader(GLuint client_id) { return shader_manager()->GetShader(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. Shader* GetShaderInfoNotProgram( GLuint client_id, const char* function_name) { Shader* shader = GetShader(client_id); if (!shader) { if (GetProgram(client_id)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "program passed for shader"); } else { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, function_name, "unknown shader"); } } LogClientServiceForInfo(shader, client_id, function_name); return shader; } // Creates a buffer info for the given buffer. void CreateBuffer(GLuint client_id, GLuint service_id) { return buffer_manager()->CreateBuffer(client_id, service_id); } // Gets the buffer info for the given buffer. Buffer* GetBuffer(GLuint client_id) { Buffer* buffer = buffer_manager()->GetBuffer(client_id); return buffer; } // 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 RemoveBuffer(GLuint client_id); // Creates a framebuffer info for the given framebuffer. void CreateFramebuffer(GLuint client_id, GLuint service_id) { return framebuffer_manager()->CreateFramebuffer(client_id, service_id); } // Gets the framebuffer info for the given framebuffer. Framebuffer* GetFramebuffer(GLuint client_id) { return framebuffer_manager()->GetFramebuffer(client_id); } // Removes the framebuffer info for the given framebuffer. void RemoveFramebuffer(GLuint client_id) { framebuffer_manager()->RemoveFramebuffer(client_id); } // Creates a renderbuffer info for the given renderbuffer. void CreateRenderbuffer(GLuint client_id, GLuint service_id) { return renderbuffer_manager()->CreateRenderbuffer( client_id, service_id); } // Gets the renderbuffer info for the given renderbuffer. Renderbuffer* GetRenderbuffer(GLuint client_id) { return renderbuffer_manager()->GetRenderbuffer(client_id); } // Removes the renderbuffer info for the given renderbuffer. void RemoveRenderbuffer(GLuint client_id) { renderbuffer_manager()->RemoveRenderbuffer(client_id); } // Creates a valuebuffer info for the given valuebuffer. void CreateValuebuffer(GLuint client_id) { return valuebuffer_manager()->CreateValuebuffer(client_id); } // Gets the valuebuffer info for a given valuebuffer. Valuebuffer* GetValuebuffer(GLuint client_id) { return valuebuffer_manager()->GetValuebuffer(client_id); } // Removes the valuebuffer info for the given valuebuffer. void RemoveValuebuffer(GLuint client_id) { valuebuffer_manager()->RemoveValuebuffer(client_id); } // Gets the vertex attrib manager for the given vertex array. VertexAttribManager* GetVertexAttribManager(GLuint client_id) { VertexAttribManager* info = vertex_array_manager()->GetVertexAttribManager(client_id); return info; } // Removes the vertex attrib manager for the given vertex array. void RemoveVertexAttribManager(GLuint client_id) { vertex_array_manager()->RemoveVertexAttribManager(client_id); } // Creates a vertex attrib manager for the given vertex array. scoped_refptr CreateVertexAttribManager( GLuint client_id, GLuint service_id, bool client_visible) { return vertex_array_manager()->CreateVertexAttribManager( client_id, service_id, group_->max_vertex_attribs(), client_visible); } 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); error::Error GetFragDataLocationHelper( GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset, const std::string& name_str); // Wrapper for glShaderSource. void DoShaderSource( GLuint client_id, GLsizei count, const char** data, const GLint* length); // Wrapper for glTransformFeedbackVaryings. void DoTransformFeedbackVaryings( GLuint client_program_id, GLsizei count, const char* const* varyings, GLenum buffer_mode); // Clear any textures used by the current program. bool ClearUnclearedTextures(); // Clears any uncleared attachments attached to the given frame buffer. // Returns false if there was a generated GL error. void ClearUnclearedAttachments(GLenum target, Framebuffer* framebuffer); // overridden from GLES2Decoder bool ClearLevel(Texture* texture, unsigned target, int level, unsigned internal_format, unsigned format, unsigned type, int width, int height, bool is_texture_immutable) override; // 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 that the currently bound read framebuffer has a color image // attached. Generates GL error if not. bool CheckBoundReadFramebufferColorAttachment(const char* func_name); // Check that the currently bound read framebuffer's color image // isn't the target texture of the glCopyTex{Sub}Image2D. bool FormsTextureCopyingFeedbackLoop(TextureRef* texture, GLint level); // Check if a framebuffer meets our requirements. bool CheckFramebufferValid( Framebuffer* framebuffer, GLenum target, const char* func_name); // Check if the current valuebuffer exists and is valid. If not generates // the appropriate GL error. Returns true if the current valuebuffer is in // a usable state. bool CheckCurrentValuebuffer(const char* function_name); // Check if the current valuebuffer exists and is valiud and that the // value buffer is actually subscribed to the given subscription bool CheckCurrentValuebufferForSubscription(GLenum subscription, const char* function_name); // Check if the location can be used for the given subscription target. If not // generates the appropriate GL error. Returns true if the location is usable bool CheckSubscriptionTarget(GLint location, GLenum subscription, const char* function_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); // Checks if the current program samples a texture that is also the color // image of the current bound framebuffer, i.e., the source and destination // of the draw operation are the same. bool CheckDrawingFeedbackLoops(); // Checks if |api_type| is valid for the given uniform // If the api type is not valid generates the appropriate GL // error. Returns true if |api_type| is valid for the uniform bool CheckUniformForApiType(const Program::UniformInfo* info, const char* function_name, Program::UniformApiType api_type); // 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, Program::UniformApiType api_type, GLint* real_location, GLenum* type, GLsizei* count); // Gets the service id for any simulated backbuffer fbo. GLuint GetBackbufferServiceId() const; // Helper for glGetBooleanv, glGetFloatv and glGetIntegerv bool GetHelper(GLenum pname, GLint* params, GLsizei* num_written); // Helper for glGetVertexAttrib void GetVertexAttribHelper( const VertexAttrib* attrib, GLenum pname, GLint* param); // 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 glBindVertexArrayOES void DoBindVertexArrayOES(GLuint array); void EmulateVertexArrayState(); // Wrapper for glBlitFramebufferCHROMIUM. void DoBlitFramebufferCHROMIUM( GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); // 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 various state. void DoDepthRangef(GLclampf znear, GLclampf zfar); void DoSampleCoverage(GLclampf value, GLboolean invert); // Wrapper for glCompileShader. void DoCompileShader(GLuint shader); // 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 glDiscardFramebufferEXT, since we need to track undefined // attachments. void DoDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum* attachments); // 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 glFramebufferTexture2DMultisampleEXT. void DoFramebufferTexture2DMultisample( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); // Common implementation for both DoFramebufferTexture2D wrappers. void DoFramebufferTexture2DCommon(const char* name, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); // Wrapper for glFramebufferTextureLayer. void DoFramebufferTextureLayer( GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); // Wrapper for glGenerateMipmap void DoGenerateMipmap(GLenum target); // Helper for DoGetBooleanv, Floatv, and Intergerv to adjust pname // to account for different pname values defined in different extension // variants. GLenum AdjustGetPname(GLenum pname); // 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 glGetInteger64v. void DoGetInteger64v(GLenum pname, GLint64* 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 glGetBufferParameteriv. void DoGetBufferParameteriv( GLenum target, GLenum pname, GLint* params); // 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 glGetTexParameter. void DoGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params); void DoGetTexParameteriv(GLenum target, GLenum pname, GLint* params); void InitTextureMaxAnisotropyIfNeeded(GLenum target, GLenum pname); // Wrappers for glGetVertexAttrib. template void DoGetVertexAttribImpl(GLuint index, GLenum pname, T* params); void DoGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params); void DoGetVertexAttribiv(GLuint index, GLenum pname, GLint* params); void DoGetVertexAttribIiv(GLuint index, GLenum pname, GLint* params); void DoGetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* 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); bool DoIsVertexArrayOES(GLuint client_id); // Wrapper for glLinkProgram void DoLinkProgram(GLuint program); // Wrapper for glRenderbufferStorage. void DoRenderbufferStorage( GLenum target, GLenum internalformat, GLsizei width, GLsizei height); // Handler for glRenderbufferStorageMultisampleCHROMIUM. void DoRenderbufferStorageMultisampleCHROMIUM( GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); // Handler for glRenderbufferStorageMultisampleEXT // (multisampled_render_to_texture). void DoRenderbufferStorageMultisampleEXT( GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); // Common validation for multisample extensions. bool ValidateRenderbufferStorageMultisample(GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); // Verifies that the currently bound multisample renderbuffer is valid // Very slow! Only done on platforms with driver bugs that return invalid // buffers under memory pressure bool VerifyMultisampleRenderbufferIntegrity( GLuint renderbuffer, GLenum format); // Wrapper for glReleaseShaderCompiler. void DoReleaseShaderCompiler() { } // Wrappers for glSamplerParameter*v functions. void DoSamplerParameterfv( GLuint sampler, GLenum pname, const GLfloat* params); void DoSamplerParameteriv(GLuint sampler, GLenum pname, const GLint* params); // 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); template bool SetVertexAttribValue( const char* function_name, GLuint index, const T* 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); void DoVertexAttribI4i(GLuint index, GLint v0, GLint v1, GLint v2, GLint v3); void DoVertexAttribI4iv(GLuint index, const GLint* v); void DoVertexAttribI4ui( GLuint index, GLuint v0, GLuint v1, GLuint v2, GLuint v3); void DoVertexAttribI4uiv(GLuint index, const GLuint* 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); void DoInsertEventMarkerEXT(GLsizei length, const GLchar* marker); void DoPushGroupMarkerEXT(GLsizei length, const GLchar* group); void DoPopGroupMarkerEXT(void); // Gets the number of values that will be returned by glGetXXX. Returns // false if pname is unknown. bool GetNumValuesReturnedForGLGet(GLenum pname, GLsizei* num_values); // Checks if the current program and vertex attributes are valid for drawing. bool IsDrawValid( const char* function_name, GLuint max_vertex_accessed, bool instanced, 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, bool restore_array_binding); // If an image is bound to texture, this will call Will/DidUseTexImage // if needed. void DoWillUseTexImageIfNeeded(Texture* texture, GLenum textarget); void DoDidUseTexImageIfNeeded(Texture* texture, GLenum textarget); // Returns false if textures were replaced. bool PrepareTexturesForRender(); void RestoreStateForTextures(); // 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 always 1 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); 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. Framebuffer* GetFramebufferInfoForTarget(GLenum target) { Framebuffer* framebuffer = NULL; switch (target) { case GL_FRAMEBUFFER: case GL_DRAW_FRAMEBUFFER_EXT: framebuffer = framebuffer_state_.bound_draw_framebuffer.get(); break; case GL_READ_FRAMEBUFFER_EXT: framebuffer = framebuffer_state_.bound_read_framebuffer.get(); break; default: NOTREACHED(); break; } return framebuffer; } Renderbuffer* GetRenderbufferInfoForTarget( GLenum target) { Renderbuffer* renderbuffer = NULL; switch (target) { case GL_RENDERBUFFER: renderbuffer = state_.bound_renderbuffer.get(); break; default: NOTREACHED(); break; } return renderbuffer; } // 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. template bool GetUniformSetup(GLuint program, GLint fake_location, uint32 shm_id, uint32 shm_offset, error::Error* error, GLint* real_location, GLuint* service_id, SizedResult** result, GLenum* result_type, GLsizei* result_size); bool WasContextLost() const override; bool WasContextLostByRobustnessExtension() const override; void MarkContextLost(error::ContextLostReason reason) override; bool CheckResetStatus(); #if defined(OS_MACOSX) void ReleaseIOSurfaceForTexture(GLuint texture_id); #endif bool ValidateCompressedTexDimensions( const char* function_name, GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format); bool ValidateCompressedTexFuncData( const char* function_name, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei size); bool ValidateCompressedTexSubDimensions( const char* function_name, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, Texture* texture); bool ValidateCopyTextureCHROMIUM(const char* function_name, GLenum target, TextureRef* source_texture_ref, TextureRef* dest_texture_ref, GLenum dest_internal_format); void RenderWarning(const char* filename, int line, const std::string& msg); void PerformanceWarning( const char* filename, int line, const std::string& msg); const FeatureInfo::FeatureFlags& features() const { return feature_info_->feature_flags(); } const FeatureInfo::Workarounds& workarounds() const { return feature_info_->workarounds(); } bool ShouldDeferDraws() { return !offscreen_target_frame_buffer_.get() && framebuffer_state_.bound_draw_framebuffer.get() == NULL && surface_->DeferDraws(); } bool ShouldDeferReads() { return !offscreen_target_frame_buffer_.get() && framebuffer_state_.bound_read_framebuffer.get() == NULL && surface_->DeferDraws(); } bool IsRobustnessSupported() { return has_robustness_extension_ && context_->WasAllocatedUsingRobustnessExtension(); } error::Error WillAccessBoundFramebufferForDraw() { if (ShouldDeferDraws()) return error::kDeferCommandUntilLater; if (!offscreen_target_frame_buffer_.get() && !framebuffer_state_.bound_draw_framebuffer.get() && !surface_->SetBackbufferAllocation(true)) return error::kLostContext; return error::kNoError; } error::Error WillAccessBoundFramebufferForRead() { if (ShouldDeferReads()) return error::kDeferCommandUntilLater; if (!offscreen_target_frame_buffer_.get() && !framebuffer_state_.bound_read_framebuffer.get() && !surface_->SetBackbufferAllocation(true)) return error::kLostContext; return error::kNoError; } // Set remaining commands to process to 0 to force DoCommands to return // and allow context preemption and GPU watchdog checks in GpuScheduler(). void ExitCommandProcessingEarly() { commands_to_process_ = 0; } void ProcessPendingReadPixels(); void FinishReadPixels(const cmds::ReadPixels& c, GLuint buffer); // 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 void* data); GLES2_COMMAND_LIST(GLES2_CMD_OP) #undef GLES2_CMD_OP // The GL context this decoder renders to on behalf of the client. scoped_refptr surface_; scoped_refptr context_; // The ContextGroup for this decoder uses to track resources. scoped_refptr group_; DebugMarkerManager debug_marker_manager_; Logger logger_; // All the state for this context. ContextState state_; // Current width and height of the offscreen frame buffer. gfx::Size offscreen_size_; // Util to help with GL. GLES2Util util_; // 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 buffer we bind to attrib 0 since OpenGL requires it (ES does not). GLuint attrib_0_buffer_id_; // The value currently in attrib_0. 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_; // 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 offscreen_target_frame_buffer_; scoped_ptr offscreen_target_color_texture_; scoped_ptr offscreen_target_color_render_buffer_; scoped_ptr offscreen_target_depth_render_buffer_; scoped_ptr 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 offscreen_saved_frame_buffer_; scoped_ptr offscreen_saved_color_texture_; scoped_refptr offscreen_saved_color_texture_info_; // The copy that is used as the destination for multi-sample resolves. scoped_ptr offscreen_resolved_frame_buffer_; scoped_ptr offscreen_resolved_color_texture_; GLenum offscreen_saved_color_format_; scoped_ptr query_manager_; scoped_ptr vertex_array_manager_; scoped_ptr image_manager_; base::Callback resize_callback_; WaitSyncPointCallback wait_sync_point_callback_; ShaderCacheCallback shader_cache_callback_; scoped_ptr async_pixel_transfer_manager_; // The format of the back buffer_ GLenum back_buffer_color_format_; bool back_buffer_has_depth_; bool back_buffer_has_stencil_; bool surfaceless_; // Backbuffer attachments that are currently undefined. uint32 backbuffer_needs_clear_bits_; // The current decoder error communicates the decoder error through command // processing functions that do not return the error value. Should be set only // if not returning an error. error::Error current_decoder_error_; bool use_shader_translator_; scoped_refptr vertex_translator_; scoped_refptr fragment_translator_; DisallowedFeatures disallowed_features_; // Cached from ContextGroup const Validators* validators_; scoped_refptr feature_info_; int frame_number_; // Number of commands remaining to be processed in DoCommands(). int commands_to_process_; bool has_robustness_extension_; error::ContextLostReason context_lost_reason_; bool context_was_lost_; bool reset_by_robustness_extension_; bool supports_post_sub_buffer_; // 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 frag_depth_explicitly_enabled_; bool draw_buffers_explicitly_enabled_; bool shader_texture_lod_explicitly_enabled_; bool compile_shader_always_succeeds_; // An optional behaviour to lose the context and group when OOM. bool lose_context_when_out_of_memory_; // Log extra info. bool service_logging_; #if defined(OS_MACOSX) typedef std::map TextureToIOSurfaceMap; TextureToIOSurfaceMap texture_to_io_surface_map_; #endif scoped_ptr copy_texture_CHROMIUM_; scoped_ptr clear_framebuffer_blit_; // Cached values of the currently assigned viewport dimensions. GLsizei viewport_max_width_; GLsizei viewport_max_height_; // Command buffer stats. base::TimeDelta total_processing_commands_time_; // States related to each manager. DecoderTextureState texture_state_; DecoderFramebufferState framebuffer_state_; scoped_ptr gpu_tracer_; scoped_ptr gpu_state_tracer_; const unsigned char* cb_command_trace_category_; const unsigned char* gpu_decoder_category_; int gpu_trace_level_; bool gpu_trace_commands_; bool gpu_debug_commands_; std::queue > pending_readpixel_fences_; // Used to validate multisample renderbuffers if needed GLuint validation_texture_; GLuint validation_fbo_multisample_; GLuint validation_fbo_; typedef gpu::gles2::GLES2Decoder::Error (GLES2DecoderImpl::*CmdHandler)( uint32 immediate_data_size, const void* data); // A struct to hold info about each command. struct CommandInfo { CmdHandler cmd_handler; uint8 arg_flags; // How to handle the arguments for this command uint8 cmd_flags; // How to handle this command uint16 arg_count; // How many arguments are expected for this command. }; // A table of CommandInfo for all the commands. static const CommandInfo command_info[kNumCommands - kStartPoint]; DISALLOW_COPY_AND_ASSIGN(GLES2DecoderImpl); }; const GLES2DecoderImpl::CommandInfo GLES2DecoderImpl::command_info[] = { #define GLES2_CMD_OP(name) \ { \ &GLES2DecoderImpl::Handle##name, cmds::name::kArgFlags, \ cmds::name::cmd_flags, \ sizeof(cmds::name) / sizeof(CommandBufferEntry) - 1, \ } \ , /* NOLINT */ GLES2_COMMAND_LIST(GLES2_CMD_OP) #undef GLES2_CMD_OP }; ScopedGLErrorSuppressor::ScopedGLErrorSuppressor( const char* function_name, ErrorState* error_state) : function_name_(function_name), error_state_(error_state) { ERRORSTATE_COPY_REAL_GL_ERRORS_TO_WRAPPER(error_state_, function_name_); } ScopedGLErrorSuppressor::~ScopedGLErrorSuppressor() { ERRORSTATE_CLEAR_REAL_GL_ERRORS(error_state_, function_name_); } static void RestoreCurrentTextureBindings(ContextState* state, GLenum target) { TextureUnit& info = state->texture_units[0]; GLuint last_id; scoped_refptr texture_ref; switch (target) { case GL_TEXTURE_2D: texture_ref = info.bound_texture_2d; break; case GL_TEXTURE_CUBE_MAP: texture_ref = info.bound_texture_cube_map; break; case GL_TEXTURE_EXTERNAL_OES: texture_ref = info.bound_texture_external_oes; break; case GL_TEXTURE_RECTANGLE_ARB: texture_ref = info.bound_texture_rectangle_arb; break; default: NOTREACHED(); break; } if (texture_ref.get()) { last_id = texture_ref->service_id(); } else { last_id = 0; } glBindTexture(target, last_id); glActiveTexture(GL_TEXTURE0 + state->active_texture_unit); } ScopedTextureBinder::ScopedTextureBinder(ContextState* state, GLuint id, GLenum target) : state_(state), target_(target) { ScopedGLErrorSuppressor suppressor( "ScopedTextureBinder::ctor", state_->GetErrorState()); // TODO(apatrick): Check if there are any other states that need to be reset // before binding a new texture. glActiveTexture(GL_TEXTURE0); glBindTexture(target, id); } ScopedTextureBinder::~ScopedTextureBinder() { ScopedGLErrorSuppressor suppressor( "ScopedTextureBinder::dtor", state_->GetErrorState()); RestoreCurrentTextureBindings(state_, target_); } ScopedRenderBufferBinder::ScopedRenderBufferBinder(ContextState* state, GLuint id) : state_(state) { ScopedGLErrorSuppressor suppressor( "ScopedRenderBufferBinder::ctor", state_->GetErrorState()); glBindRenderbufferEXT(GL_RENDERBUFFER, id); } ScopedRenderBufferBinder::~ScopedRenderBufferBinder() { ScopedGLErrorSuppressor suppressor( "ScopedRenderBufferBinder::dtor", state_->GetErrorState()); state_->RestoreRenderbufferBindings(); } ScopedFrameBufferBinder::ScopedFrameBufferBinder(GLES2DecoderImpl* decoder, GLuint id) : decoder_(decoder) { ScopedGLErrorSuppressor suppressor( "ScopedFrameBufferBinder::ctor", decoder_->GetErrorState()); glBindFramebufferEXT(GL_FRAMEBUFFER, id); decoder->OnFboChanged(); } ScopedFrameBufferBinder::~ScopedFrameBufferBinder() { ScopedGLErrorSuppressor suppressor( "ScopedFrameBufferBinder::dtor", decoder_->GetErrorState()); 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_->framebuffer_state_.bound_read_framebuffer.get() || enforce_internal_framebuffer)); if (!resolve_and_bind_) return; ScopedGLErrorSuppressor suppressor( "ScopedResolvedFrameBufferBinder::ctor", decoder_->GetErrorState()); 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 BackFramebuffer(decoder_)); decoder_->offscreen_resolved_frame_buffer_->Create(); decoder_->offscreen_resolved_color_texture_.reset( new BackTexture(decoder->memory_tracker(), &decoder->state_)); 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_, false); 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(); decoder->state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); decoder->BlitFramebufferHelper(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( "ScopedResolvedFrameBufferBinder::dtor", decoder_->GetErrorState()); decoder_->RestoreCurrentFramebufferBindings(); if (decoder_->state_.enable_flags.scissor_test) { decoder_->state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, true); } } BackTexture::BackTexture( MemoryTracker* memory_tracker, ContextState* state) : memory_tracker_(memory_tracker, MemoryTracker::kUnmanaged), state_(state), bytes_allocated_(0), id_(0) { } BackTexture::~BackTexture() { // 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 BackTexture::Create() { ScopedGLErrorSuppressor suppressor("BackTexture::Create", state_->GetErrorState()); Destroy(); glGenTextures(1, &id_); ScopedTextureBinder binder(state_, id_, GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 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); bytes_allocated_ = 16u * 16u * 4u; memory_tracker_.TrackMemAlloc(bytes_allocated_); } bool BackTexture::AllocateStorage( const gfx::Size& size, GLenum format, bool zero) { DCHECK_NE(id_, 0u); ScopedGLErrorSuppressor suppressor("BackTexture::AllocateStorage", state_->GetErrorState()); ScopedTextureBinder binder(state_, id_, GL_TEXTURE_2D); uint32 image_size = 0; GLES2Util::ComputeImageDataSizes( size.width(), size.height(), 1, format, GL_UNSIGNED_BYTE, 8, &image_size, NULL, NULL); if (!memory_tracker_.EnsureGPUMemoryAvailable(image_size)) { return false; } scoped_ptr zero_data; if (zero) { zero_data.reset(new char[image_size]); memset(zero_data.get(), 0, image_size); } glTexImage2D(GL_TEXTURE_2D, 0, // mip level format, size.width(), size.height(), 0, // border format, GL_UNSIGNED_BYTE, zero_data.get()); size_ = size; bool success = glGetError() == GL_NO_ERROR; if (success) { memory_tracker_.TrackMemFree(bytes_allocated_); bytes_allocated_ = image_size; memory_tracker_.TrackMemAlloc(bytes_allocated_); } return success; } void BackTexture::Copy(const gfx::Size& size, GLenum format) { DCHECK_NE(id_, 0u); ScopedGLErrorSuppressor suppressor("BackTexture::Copy", state_->GetErrorState()); ScopedTextureBinder binder(state_, id_, GL_TEXTURE_2D); glCopyTexImage2D(GL_TEXTURE_2D, 0, // level format, 0, 0, size.width(), size.height(), 0); // border } void BackTexture::Destroy() { if (id_ != 0) { ScopedGLErrorSuppressor suppressor("BackTexture::Destroy", state_->GetErrorState()); glDeleteTextures(1, &id_); id_ = 0; } memory_tracker_.TrackMemFree(bytes_allocated_); bytes_allocated_ = 0; } void BackTexture::Invalidate() { id_ = 0; } BackRenderbuffer::BackRenderbuffer( RenderbufferManager* renderbuffer_manager, MemoryTracker* memory_tracker, ContextState* state) : renderbuffer_manager_(renderbuffer_manager), memory_tracker_(memory_tracker, MemoryTracker::kUnmanaged), state_(state), bytes_allocated_(0), id_(0) { } BackRenderbuffer::~BackRenderbuffer() { // 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 BackRenderbuffer::Create() { ScopedGLErrorSuppressor suppressor("BackRenderbuffer::Create", state_->GetErrorState()); Destroy(); glGenRenderbuffersEXT(1, &id_); } bool BackRenderbuffer::AllocateStorage(const FeatureInfo* feature_info, const gfx::Size& size, GLenum format, GLsizei samples) { ScopedGLErrorSuppressor suppressor( "BackRenderbuffer::AllocateStorage", state_->GetErrorState()); ScopedRenderBufferBinder binder(state_, id_); uint32 estimated_size = 0; if (!renderbuffer_manager_->ComputeEstimatedRenderbufferSize( size.width(), size.height(), samples, format, &estimated_size)) { return false; } if (!memory_tracker_.EnsureGPUMemoryAvailable(estimated_size)) { return false; } if (samples <= 1) { glRenderbufferStorageEXT(GL_RENDERBUFFER, format, size.width(), size.height()); } else { GLES2DecoderImpl::RenderbufferStorageMultisampleHelper(feature_info, GL_RENDERBUFFER, samples, format, size.width(), size.height()); } bool success = glGetError() == GL_NO_ERROR; if (success) { // Mark the previously allocated bytes as free. memory_tracker_.TrackMemFree(bytes_allocated_); bytes_allocated_ = estimated_size; // Track the newly allocated bytes. memory_tracker_.TrackMemAlloc(bytes_allocated_); } return success; } void BackRenderbuffer::Destroy() { if (id_ != 0) { ScopedGLErrorSuppressor suppressor("BackRenderbuffer::Destroy", state_->GetErrorState()); glDeleteRenderbuffersEXT(1, &id_); id_ = 0; } memory_tracker_.TrackMemFree(bytes_allocated_); bytes_allocated_ = 0; } void BackRenderbuffer::Invalidate() { id_ = 0; } BackFramebuffer::BackFramebuffer(GLES2DecoderImpl* decoder) : decoder_(decoder), id_(0) { } BackFramebuffer::~BackFramebuffer() { // 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 BackFramebuffer::Create() { ScopedGLErrorSuppressor suppressor("BackFramebuffer::Create", decoder_->GetErrorState()); Destroy(); glGenFramebuffersEXT(1, &id_); } void BackFramebuffer::AttachRenderTexture(BackTexture* texture) { DCHECK_NE(id_, 0u); ScopedGLErrorSuppressor suppressor( "BackFramebuffer::AttachRenderTexture", decoder_->GetErrorState()); ScopedFrameBufferBinder binder(decoder_, id_); GLuint attach_id = texture ? texture->id() : 0; glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, attach_id, 0); } void BackFramebuffer::AttachRenderBuffer(GLenum target, BackRenderbuffer* render_buffer) { DCHECK_NE(id_, 0u); ScopedGLErrorSuppressor suppressor( "BackFramebuffer::AttachRenderBuffer", decoder_->GetErrorState()); ScopedFrameBufferBinder binder(decoder_, id_); GLuint attach_id = render_buffer ? render_buffer->id() : 0; glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, target, GL_RENDERBUFFER, attach_id); } void BackFramebuffer::Destroy() { if (id_ != 0) { ScopedGLErrorSuppressor suppressor("BackFramebuffer::Destroy", decoder_->GetErrorState()); glDeleteFramebuffersEXT(1, &id_); id_ = 0; } } void BackFramebuffer::Invalidate() { id_ = 0; } GLenum BackFramebuffer::CheckStatus() { DCHECK_NE(id_, 0u); ScopedGLErrorSuppressor suppressor("BackFramebuffer::CheckStatus", decoder_->GetErrorState()); ScopedFrameBufferBinder binder(decoder_, id_); return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); } GLES2Decoder* GLES2Decoder::Create(ContextGroup* group) { return new GLES2DecoderImpl(group); } GLES2DecoderImpl::GLES2DecoderImpl(ContextGroup* group) : GLES2Decoder(), group_(group), logger_(&debug_marker_manager_), state_(group_->feature_info(), this, &logger_), 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), 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), back_buffer_color_format_(0), back_buffer_has_depth_(false), back_buffer_has_stencil_(false), surfaceless_(false), backbuffer_needs_clear_bits_(0), current_decoder_error_(error::kNoError), use_shader_translator_(true), validators_(group_->feature_info()->validators()), feature_info_(group_->feature_info()), frame_number_(0), has_robustness_extension_(false), context_lost_reason_(error::kUnknown), context_was_lost_(false), reset_by_robustness_extension_(false), supports_post_sub_buffer_(false), force_webgl_glsl_validation_(false), derivatives_explicitly_enabled_(false), frag_depth_explicitly_enabled_(false), draw_buffers_explicitly_enabled_(false), shader_texture_lod_explicitly_enabled_(false), compile_shader_always_succeeds_(false), lose_context_when_out_of_memory_(false), service_logging_(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableGPUServiceLoggingGPU)), viewport_max_width_(0), viewport_max_height_(0), texture_state_(group_->feature_info() ->workarounds() .texsubimage_faster_than_teximage), cb_command_trace_category_(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( TRACE_DISABLED_BY_DEFAULT("cb_command"))), gpu_decoder_category_(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( TRACE_DISABLED_BY_DEFAULT("gpu_decoder"))), gpu_trace_level_(2), gpu_trace_commands_(false), gpu_debug_commands_(false), validation_texture_(0), validation_fbo_multisample_(0), validation_fbo_(0) { DCHECK(group); // 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. if (gfx::GetGLImplementation() == gfx::kGLImplementationMockGL || base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableGLSLTranslator)) { use_shader_translator_ = false; } } GLES2DecoderImpl::~GLES2DecoderImpl() { } bool GLES2DecoderImpl::Initialize( const scoped_refptr& surface, const scoped_refptr& context, bool offscreen, const gfx::Size& offscreen_size, const DisallowedFeatures& disallowed_features, const std::vector& attribs) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::Initialize"); DCHECK(context->IsCurrent(surface.get())); DCHECK(!context_.get()); DCHECK(!offscreen || !offscreen_size.IsEmpty()); ContextCreationAttribHelper attrib_parser; if (!attrib_parser.Parse(attribs)) return false; surfaceless_ = surface->IsSurfaceless() && !offscreen; set_initialized(); gpu_state_tracer_ = GPUStateTracer::Create(&state_); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableGPUDebugging)) { set_debug(true); } if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableGPUCommandLogging)) { set_log_commands(true); } compile_shader_always_succeeds_ = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kCompileShaderAlwaysSucceeds); // Take ownership of the context and surface. The surface can be replaced with // SetSurface. context_ = context; surface_ = surface; // Create GPU Tracer for timing values. gpu_tracer_.reset(new GPUTracer(this)); // Save the loseContextWhenOutOfMemory context creation attribute. lose_context_when_out_of_memory_ = attrib_parser.lose_context_when_out_of_memory; // If the failIfMajorPerformanceCaveat context creation attribute was true // and we are using a software renderer, fail. if (attrib_parser.fail_if_major_perf_caveat && feature_info_->feature_flags().is_swiftshader) { group_ = NULL; // Must not destroy ContextGroup if it is not initialized. Destroy(true); return false; } if (!group_->Initialize(this, disallowed_features)) { 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(); if (attrib_parser.es3_context_required && feature_info_->IsES3Capable()) { feature_info_->EnableES3Validators(); set_unsafe_es3_apis_enabled(true); } disallowed_features_ = disallowed_features; state_.attrib_values.resize(group_->max_vertex_attribs()); vertex_array_manager_.reset(new VertexArrayManager()); GLuint default_vertex_attrib_service_id = 0; if (features().native_vertex_array_object) { glGenVertexArraysOES(1, &default_vertex_attrib_service_id); glBindVertexArrayOES(default_vertex_attrib_service_id); } state_.default_vertex_attrib_manager = CreateVertexAttribManager(0, default_vertex_attrib_service_id, false); state_.default_vertex_attrib_manager->Initialize( group_->max_vertex_attribs(), feature_info_->workarounds().init_vertex_attributes); // vertex_attrib_manager is set to default_vertex_attrib_manager by this call DoBindVertexArrayOES(0); query_manager_.reset(new QueryManager(this, feature_info_.get())); image_manager_.reset(new ImageManager); 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_); state_.texture_units.resize(group_->max_texture_units()); for (uint32 tt = 0; tt < state_.texture_units.size(); ++tt) { glActiveTexture(GL_TEXTURE0 + tt); // We want the last bind to be 2D. TextureRef* ref; if (features().oes_egl_image_external) { ref = texture_manager()->GetDefaultTextureInfo( GL_TEXTURE_EXTERNAL_OES); state_.texture_units[tt].bound_texture_external_oes = ref; glBindTexture(GL_TEXTURE_EXTERNAL_OES, ref ? ref->service_id() : 0); } if (features().arb_texture_rectangle) { ref = texture_manager()->GetDefaultTextureInfo( GL_TEXTURE_RECTANGLE_ARB); state_.texture_units[tt].bound_texture_rectangle_arb = ref; glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ref ? ref->service_id() : 0); } ref = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_CUBE_MAP); state_.texture_units[tt].bound_texture_cube_map = ref; glBindTexture(GL_TEXTURE_CUBE_MAP, ref ? ref->service_id() : 0); ref = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_2D); state_.texture_units[tt].bound_texture_2d = ref; glBindTexture(GL_TEXTURE_2D, ref ? ref->service_id() : 0); } glActiveTexture(GL_TEXTURE0); CHECK_GL_ERROR(); // cache ALPHA_BITS result for re-use with clear behaviour GLint alpha_bits = 0; if (offscreen) { if (attrib_parser.samples > 0 && attrib_parser.sample_buffers > 0 && features().chromium_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 || workarounds().disable_gl_rgb_format ? GL_RGBA : GL_RGB; } // ANGLE only supports packed depth/stencil formats, so use it if it is // available. const bool depth24_stencil8_supported = feature_info_->feature_flags().packed_depth24_stencil8; 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 || workarounds().disable_gl_rgb_format ? 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 = feature_info_->feature_flags().packed_depth24_stencil8; 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 || workarounds().disable_gl_rgb_format ? 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 BackFramebuffer(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 BackRenderbuffer( renderbuffer_manager(), memory_tracker(), &state_)); offscreen_target_color_render_buffer_->Create(); } else { offscreen_target_color_texture_.reset(new BackTexture( memory_tracker(), &state_)); offscreen_target_color_texture_->Create(); } offscreen_target_depth_render_buffer_.reset(new BackRenderbuffer( renderbuffer_manager(), memory_tracker(), &state_)); offscreen_target_depth_render_buffer_->Create(); offscreen_target_stencil_render_buffer_.reset(new BackRenderbuffer( renderbuffer_manager(), memory_tracker(), &state_)); 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 BackFramebuffer(this)); offscreen_saved_frame_buffer_->Create(); // offscreen_saved_color_texture_.reset(new BackTexture( memory_tracker(), &state_)); 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(offscreen_size)) { LOG(ERROR) << "Could not allocate offscreen buffer storage."; Destroy(true); return false; } state_.viewport_width = offscreen_size.width(); state_.viewport_height = offscreen_size.height(); // Allocate the offscreen saved color texture. DCHECK(offscreen_saved_color_format_); offscreen_saved_color_texture_->AllocateStorage( gfx::Size(1, 1), offscreen_saved_color_format_, true); offscreen_saved_frame_buffer_->AttachRenderTexture( offscreen_saved_color_texture_.get()); if (offscreen_saved_frame_buffer_->CheckStatus() != GL_FRAMEBUFFER_COMPLETE) { LOG(ERROR) << "Offscreen saved FBO was incomplete."; 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); } else { glBindFramebufferEXT(GL_FRAMEBUFFER, GetBackbufferServiceId()); // 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. if (!surfaceless_) { GLint depth_bits = 0; GLint stencil_bits = 0; bool default_fb = (GetBackbufferServiceId() == 0); if (feature_info_->gl_version_info().is_desktop_core_profile) { glGetFramebufferAttachmentParameterivEXT( GL_FRAMEBUFFER, default_fb ? GL_BACK_LEFT : GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, &alpha_bits); glGetFramebufferAttachmentParameterivEXT( GL_FRAMEBUFFER, default_fb ? GL_DEPTH : GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &depth_bits); glGetFramebufferAttachmentParameterivEXT( GL_FRAMEBUFFER, default_fb ? GL_STENCIL : GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencil_bits); } else { glGetIntegerv(GL_ALPHA_BITS, &alpha_bits); glGetIntegerv(GL_DEPTH_BITS, &depth_bits); glGetIntegerv(GL_STENCIL_BITS, &stencil_bits); } // 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 && alpha_bits > 0) ? GL_RGBA : GL_RGB; back_buffer_has_depth_ = attrib_parser.depth_size != 0 && depth_bits > 0; back_buffer_has_stencil_ = attrib_parser.stencil_size != 0 && stencil_bits > 0; } state_.viewport_width = surface->GetSize().width(); state_.viewport_height = surface->GetSize().height(); } // 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 (!feature_info_->gl_version_info().BehavesLikeGLES()) { glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); glEnable(GL_POINT_SPRITE); } has_robustness_extension_ = context->HasExtension("GL_ARB_robustness") || context->HasExtension("GL_KHR_robustness") || context->HasExtension("GL_EXT_robustness"); if (!InitializeShaderTranslator()) { return false; } GLint viewport_params[4] = { 0 }; glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_params); viewport_max_width_ = viewport_params[0]; viewport_max_height_ = viewport_params[1]; state_.scissor_width = state_.viewport_width; state_.scissor_height = state_.viewport_height; // Set all the default state because some GL drivers get it wrong. state_.InitCapabilities(NULL); state_.InitState(NULL); glActiveTexture(GL_TEXTURE0 + state_.active_texture_unit); DoBindBuffer(GL_ARRAY_BUFFER, 0); DoBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); DoBindFramebuffer(GL_FRAMEBUFFER, 0); DoBindRenderbuffer(GL_RENDERBUFFER, 0); DoBindValueBufferCHROMIUM(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, 0); bool call_gl_clear = !surfaceless_; #if defined(OS_ANDROID) // Temporary workaround for Android WebView because this clear ignores the // clip and corrupts that external UI of the App. Not calling glClear is ok // because the system already clears the buffer before each draw. Proper // fix might be setting the scissor clip properly before initialize. See // crbug.com/259023 for details. call_gl_clear = surface_->GetHandle(); #endif if (call_gl_clear) { // On configs where we report no alpha, if the underlying surface has // alpha, clear the surface alpha to 1.0 to be correct on ReadPixels/etc. bool clear_alpha = back_buffer_color_format_ == GL_RGB && alpha_bits > 0; if (clear_alpha) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } // Clear the backbuffer. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Restore alpha clear value if we changed it. if (clear_alpha) { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); } } supports_post_sub_buffer_ = surface->SupportsPostSubBuffer(); if (feature_info_->workarounds() .disable_post_sub_buffers_for_onscreen_surfaces && !surface->IsOffscreen()) supports_post_sub_buffer_ = false; if (feature_info_->workarounds().reverse_point_sprite_coord_origin) { glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT); } if (feature_info_->workarounds().unbind_fbo_on_context_switch) { context_->SetUnbindFboOnMakeCurrent(); } // Only compositor contexts are known to use only the subset of GL // that can be safely migrated between the iGPU and the dGPU. Mark // those contexts as safe to forcibly transition between the GPUs. // http://crbug.com/180876, http://crbug.com/227228 if (!offscreen) context_->SetSafeToForceGpuSwitch(); async_pixel_transfer_manager_.reset( AsyncPixelTransferManager::Create(context.get())); async_pixel_transfer_manager_->Initialize(texture_manager()); if (workarounds().gl_clear_broken) { DCHECK(!clear_framebuffer_blit_.get()); LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glClearWorkaroundInit"); clear_framebuffer_blit_.reset(new ClearFramebufferResourceManager(this)); if (LOCAL_PEEK_GL_ERROR("glClearWorkaroundInit") != GL_NO_ERROR) return false; } framebuffer_manager()->AddObserver(this); return true; } Capabilities GLES2DecoderImpl::GetCapabilities() { DCHECK(initialized()); Capabilities caps; caps.VisitPrecisions([](GLenum shader, GLenum type, Capabilities::ShaderPrecision* shader_precision) { GLint range[2] = {0, 0}; GLint precision = 0; GetShaderPrecisionFormatImpl(shader, type, range, &precision); shader_precision->min_range = range[0]; shader_precision->max_range = range[1]; shader_precision->precision = precision; }); DoGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &caps.max_combined_texture_image_units); DoGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &caps.max_cube_map_texture_size); DoGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &caps.max_fragment_uniform_vectors); DoGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &caps.max_renderbuffer_size); DoGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &caps.max_texture_image_units); DoGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.max_texture_size); DoGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.max_varying_vectors); DoGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.max_vertex_attribs); DoGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &caps.max_vertex_texture_image_units); DoGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &caps.max_vertex_uniform_vectors); DoGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &caps.num_compressed_texture_formats); DoGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &caps.num_shader_binary_formats); DoGetIntegerv(GL_BIND_GENERATES_RESOURCE_CHROMIUM, &caps.bind_generates_resource_chromium); if (unsafe_es3_apis_enabled()) { // TODO(zmo): Note that some parameter values could be more than 32-bit, // but for now we clamp them to 32-bit max. DoGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &caps.max_3d_texture_size); DoGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &caps.max_array_texture_layers); DoGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &caps.max_color_attachments); DoGetInteger64v(GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, &caps.max_combined_fragment_uniform_components); DoGetIntegerv(GL_MAX_COMBINED_UNIFORM_BLOCKS, &caps.max_combined_uniform_blocks); DoGetInteger64v(GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, &caps.max_combined_vertex_uniform_components); DoGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.max_draw_buffers); DoGetInteger64v(GL_MAX_ELEMENT_INDEX, &caps.max_element_index); DoGetIntegerv(GL_MAX_ELEMENTS_INDICES, &caps.max_elements_indices); DoGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &caps.max_elements_vertices); DoGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, &caps.max_fragment_input_components); DoGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &caps.max_fragment_uniform_blocks); DoGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &caps.max_fragment_uniform_components); DoGetIntegerv(GL_MAX_PROGRAM_TEXEL_OFFSET, &caps.max_program_texel_offset); DoGetInteger64v(GL_MAX_SERVER_WAIT_TIMEOUT, &caps.max_server_wait_timeout); // Work around Linux NVIDIA driver bug where GL_TIMEOUT_IGNORED is // returned. if (caps.max_server_wait_timeout < 0) caps.max_server_wait_timeout = 0; DoGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &caps.max_texture_lod_bias); DoGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &caps.max_transform_feedback_interleaved_components); DoGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &caps.max_transform_feedback_separate_attribs); DoGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, &caps.max_transform_feedback_separate_components); DoGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &caps.max_uniform_block_size); DoGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &caps.max_uniform_buffer_bindings); DoGetIntegerv(GL_MAX_VARYING_COMPONENTS, &caps.max_varying_components); DoGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &caps.max_vertex_output_components); DoGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &caps.max_vertex_uniform_blocks); DoGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &caps.max_vertex_uniform_components); DoGetIntegerv(GL_MIN_PROGRAM_TEXEL_OFFSET, &caps.min_program_texel_offset); DoGetIntegerv(GL_NUM_EXTENSIONS, &caps.num_extensions); DoGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &caps.num_program_binary_formats); DoGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &caps.uniform_buffer_offset_alignment); // TODO(zmo): once we switch to MANGLE, we should query version numbers. caps.major_version = 3; caps.minor_version = 0; } if (feature_info_->feature_flags().multisampled_render_to_texture || feature_info_->feature_flags().chromium_framebuffer_multisample || unsafe_es3_apis_enabled()) { DoGetIntegerv(GL_MAX_SAMPLES, &caps.max_samples); } caps.egl_image_external = feature_info_->feature_flags().oes_egl_image_external; caps.texture_format_atc = feature_info_->feature_flags().ext_texture_format_atc; caps.texture_format_bgra8888 = feature_info_->feature_flags().ext_texture_format_bgra8888; caps.texture_format_dxt1 = feature_info_->feature_flags().ext_texture_format_dxt1; caps.texture_format_dxt5 = feature_info_->feature_flags().ext_texture_format_dxt5; caps.texture_format_etc1 = feature_info_->feature_flags().oes_compressed_etc1_rgb8_texture; caps.texture_format_etc1_npot = caps.texture_format_etc1 && !workarounds().etc1_power_of_two_only; caps.texture_rectangle = feature_info_->feature_flags().arb_texture_rectangle; caps.texture_usage = feature_info_->feature_flags().angle_texture_usage; caps.texture_storage = feature_info_->feature_flags().ext_texture_storage; caps.discard_framebuffer = feature_info_->feature_flags().ext_discard_framebuffer; caps.sync_query = feature_info_->feature_flags().chromium_sync_query; #if defined(OS_MACOSX) // This is unconditionally true on mac, no need to test for it at runtime. caps.iosurface = true; #endif caps.post_sub_buffer = supports_post_sub_buffer_; caps.image = true; caps.blend_equation_advanced = feature_info_->feature_flags().blend_equation_advanced; caps.blend_equation_advanced_coherent = feature_info_->feature_flags().blend_equation_advanced_coherent; caps.texture_rg = feature_info_->feature_flags().ext_texture_rg; return caps; } 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"); 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 = group_->max_draw_buffers(); resources.MaxExpressionComplexity = 256; resources.MaxCallStackDepth = 256; GLint range[2] = { 0, 0 }; GLint precision = 0; GetShaderPrecisionFormatImpl(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range, &precision); resources.FragmentPrecisionHigh = PrecisionMeetsSpecForHighpFloat(range[0], range[1], precision); if (force_webgl_glsl_validation_) { resources.OES_standard_derivatives = derivatives_explicitly_enabled_; resources.EXT_frag_depth = frag_depth_explicitly_enabled_; resources.EXT_draw_buffers = draw_buffers_explicitly_enabled_; if (!draw_buffers_explicitly_enabled_) resources.MaxDrawBuffers = 1; resources.EXT_shader_texture_lod = shader_texture_lod_explicitly_enabled_; resources.NV_draw_buffers = draw_buffers_explicitly_enabled_ && features().nv_draw_buffers; } else { resources.OES_standard_derivatives = features().oes_standard_derivatives ? 1 : 0; resources.ARB_texture_rectangle = features().arb_texture_rectangle ? 1 : 0; resources.OES_EGL_image_external = features().oes_egl_image_external ? 1 : 0; resources.EXT_draw_buffers = features().ext_draw_buffers ? 1 : 0; resources.EXT_frag_depth = features().ext_frag_depth ? 1 : 0; resources.EXT_shader_texture_lod = features().ext_shader_texture_lod ? 1 : 0; resources.NV_draw_buffers = features().nv_draw_buffers ? 1 : 0; } ShShaderSpec shader_spec; if (force_webgl_glsl_validation_) { shader_spec = unsafe_es3_apis_enabled() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC; } else { shader_spec = unsafe_es3_apis_enabled() ? SH_GLES3_SPEC : SH_GLES2_SPEC; } if ((shader_spec == SH_WEBGL_SPEC || shader_spec == SH_WEBGL2_SPEC) && features().enable_shader_name_hashing) resources.HashFunction = &CityHash64; else resources.HashFunction = NULL; ShaderTranslatorInterface::GlslImplementationType implementation_type = gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2 ? ShaderTranslatorInterface::kGlslES : ShaderTranslatorInterface::kGlsl; int driver_bug_workarounds = 0; if (workarounds().needs_glsl_built_in_function_emulation) driver_bug_workarounds |= SH_EMULATE_BUILT_IN_FUNCTIONS; if (workarounds().init_gl_position_in_vertex_shader) driver_bug_workarounds |= SH_INIT_GL_POSITION; if (workarounds().unfold_short_circuit_as_ternary_operation) driver_bug_workarounds |= SH_UNFOLD_SHORT_CIRCUIT; if (workarounds().init_varyings_without_static_use) driver_bug_workarounds |= SH_INIT_VARYINGS_WITHOUT_STATIC_USE; if (workarounds().unroll_for_loop_with_sampler_array_index) driver_bug_workarounds |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX; if (workarounds().scalarize_vec_and_mat_constructor_args) driver_bug_workarounds |= SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS; if (workarounds().regenerate_struct_names) driver_bug_workarounds |= SH_REGENERATE_STRUCT_NAMES; if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEmulateShaderPrecision)) resources.WEBGL_debug_shader_precision = true; vertex_translator_ = shader_translator_cache()->GetTranslator( GL_VERTEX_SHADER, shader_spec, &resources, implementation_type, static_cast(driver_bug_workarounds)); if (!vertex_translator_.get()) { LOG(ERROR) << "Could not initialize vertex shader translator."; Destroy(true); return false; } fragment_translator_ = shader_translator_cache()->GetTranslator( GL_FRAGMENT_SHADER, shader_spec, &resources, implementation_type, static_cast(driver_bug_workarounds)); 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 (GetBuffer(client_ids[ii])) { return false; } } scoped_ptr service_ids(new GLuint[n]); glGenBuffersARB(n, service_ids.get()); for (GLsizei ii = 0; ii < n; ++ii) { CreateBuffer(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 (GetFramebuffer(client_ids[ii])) { return false; } } scoped_ptr service_ids(new GLuint[n]); glGenFramebuffersEXT(n, service_ids.get()); for (GLsizei ii = 0; ii < n; ++ii) { CreateFramebuffer(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 (GetRenderbuffer(client_ids[ii])) { return false; } } scoped_ptr service_ids(new GLuint[n]); glGenRenderbuffersEXT(n, service_ids.get()); for (GLsizei ii = 0; ii < n; ++ii) { CreateRenderbuffer(client_ids[ii], service_ids[ii]); } return true; } bool GLES2DecoderImpl::GenValuebuffersCHROMIUMHelper(GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { if (GetValuebuffer(client_ids[ii])) { return false; } } for (GLsizei ii = 0; ii < n; ++ii) { CreateValuebuffer(client_ids[ii]); } return true; } bool GLES2DecoderImpl::GenTexturesHelper(GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { if (GetTexture(client_ids[ii])) { return false; } } scoped_ptr service_ids(new GLuint[n]); glGenTextures(n, service_ids.get()); for (GLsizei ii = 0; ii < n; ++ii) { CreateTexture(client_ids[ii], service_ids[ii]); } return true; } void GLES2DecoderImpl::DeleteBuffersHelper( GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { Buffer* buffer = GetBuffer(client_ids[ii]); if (buffer && !buffer->IsDeleted()) { buffer->RemoveMappedRange(); state_.RemoveBoundBuffer(buffer); RemoveBuffer(client_ids[ii]); } } } void GLES2DecoderImpl::DeleteFramebuffersHelper( GLsizei n, const GLuint* client_ids) { bool supports_separate_framebuffer_binds = features().chromium_framebuffer_multisample; for (GLsizei ii = 0; ii < n; ++ii) { Framebuffer* framebuffer = GetFramebuffer(client_ids[ii]); if (framebuffer && !framebuffer->IsDeleted()) { if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) { GLenum target = supports_separate_framebuffer_binds ? GL_DRAW_FRAMEBUFFER_EXT : GL_FRAMEBUFFER; // Unbind attachments on FBO before deletion. if (workarounds().unbind_attachments_on_bound_render_fbo_delete) framebuffer->DoUnbindGLAttachmentsForWorkaround(target); glBindFramebufferEXT(target, GetBackbufferServiceId()); framebuffer_state_.bound_draw_framebuffer = NULL; framebuffer_state_.clear_state_dirty = true; } if (framebuffer == framebuffer_state_.bound_read_framebuffer.get()) { framebuffer_state_.bound_read_framebuffer = NULL; GLenum target = supports_separate_framebuffer_binds ? GL_READ_FRAMEBUFFER_EXT : GL_FRAMEBUFFER; glBindFramebufferEXT(target, GetBackbufferServiceId()); } OnFboChanged(); RemoveFramebuffer(client_ids[ii]); } } } void GLES2DecoderImpl::DeleteRenderbuffersHelper( GLsizei n, const GLuint* client_ids) { bool supports_separate_framebuffer_binds = features().chromium_framebuffer_multisample; for (GLsizei ii = 0; ii < n; ++ii) { Renderbuffer* renderbuffer = GetRenderbuffer(client_ids[ii]); if (renderbuffer && !renderbuffer->IsDeleted()) { if (state_.bound_renderbuffer.get() == renderbuffer) { state_.bound_renderbuffer = NULL; } // Unbind from current framebuffers. if (supports_separate_framebuffer_binds) { if (framebuffer_state_.bound_read_framebuffer.get()) { framebuffer_state_.bound_read_framebuffer ->UnbindRenderbuffer(GL_READ_FRAMEBUFFER_EXT, renderbuffer); } if (framebuffer_state_.bound_draw_framebuffer.get()) { framebuffer_state_.bound_draw_framebuffer ->UnbindRenderbuffer(GL_DRAW_FRAMEBUFFER_EXT, renderbuffer); } } else { if (framebuffer_state_.bound_draw_framebuffer.get()) { framebuffer_state_.bound_draw_framebuffer ->UnbindRenderbuffer(GL_FRAMEBUFFER, renderbuffer); } } framebuffer_state_.clear_state_dirty = true; RemoveRenderbuffer(client_ids[ii]); } } } void GLES2DecoderImpl::DeleteValuebuffersCHROMIUMHelper( GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { Valuebuffer* valuebuffer = GetValuebuffer(client_ids[ii]); if (valuebuffer) { if (state_.bound_valuebuffer.get() == valuebuffer) { state_.bound_valuebuffer = NULL; } RemoveValuebuffer(client_ids[ii]); } } } void GLES2DecoderImpl::DeleteTexturesHelper( GLsizei n, const GLuint* client_ids) { bool supports_separate_framebuffer_binds = features().chromium_framebuffer_multisample; for (GLsizei ii = 0; ii < n; ++ii) { TextureRef* texture_ref = GetTexture(client_ids[ii]); if (texture_ref) { Texture* texture = texture_ref->texture(); if (texture->IsAttachedToFramebuffer()) { framebuffer_state_.clear_state_dirty = true; } // Unbind texture_ref from texture_ref units. for (size_t jj = 0; jj < state_.texture_units.size(); ++jj) { state_.texture_units[jj].Unbind(texture_ref); } // Unbind from current framebuffers. if (supports_separate_framebuffer_binds) { if (framebuffer_state_.bound_read_framebuffer.get()) { framebuffer_state_.bound_read_framebuffer ->UnbindTexture(GL_READ_FRAMEBUFFER_EXT, texture_ref); } if (framebuffer_state_.bound_draw_framebuffer.get()) { framebuffer_state_.bound_draw_framebuffer ->UnbindTexture(GL_DRAW_FRAMEBUFFER_EXT, texture_ref); } } else { if (framebuffer_state_.bound_draw_framebuffer.get()) { framebuffer_state_.bound_draw_framebuffer ->UnbindTexture(GL_FRAMEBUFFER, texture_ref); } } #if defined(OS_MACOSX) GLuint service_id = texture->service_id(); if (texture->target() == GL_TEXTURE_RECTANGLE_ARB) { ReleaseIOSurfaceForTexture(service_id); } #endif RemoveTexture(client_ids[ii]); } } } // } // anonymous namespace bool GLES2DecoderImpl::MakeCurrent() { if (!context_.get()) return false; if (WasContextLost()) { LOG(ERROR) << " GLES2DecoderImpl: Trying to make lost context current."; return false; } if (!context_->MakeCurrent(surface_.get())) { LOG(ERROR) << " GLES2DecoderImpl: Context lost during MakeCurrent."; MarkContextLost(error::kMakeCurrentFailed); group_->LoseContexts(error::kUnknown); return false; } if (CheckResetStatus()) { LOG(ERROR) << " GLES2DecoderImpl: Context reset detected after MakeCurrent."; group_->LoseContexts(error::kUnknown); return false; } ProcessFinishedAsyncTransfers(); // Rebind the FBO if it was unbound by the context. if (workarounds().unbind_fbo_on_context_switch) RestoreFramebufferBindings(); framebuffer_state_.clear_state_dirty = true; return true; } void GLES2DecoderImpl::ProcessFinishedAsyncTransfers() { ProcessPendingReadPixels(); if (engine() && query_manager_.get()) query_manager_->ProcessPendingTransferQueries(); // TODO(epenner): Is there a better place to do this? // This needs to occur before we execute any batch of commands // from the client, as the client may have recieved an async // completion while issuing those commands. // "DidFlushStart" would be ideal if we had such a callback. async_pixel_transfer_manager_->BindCompletedAsyncTransfers(); } static void RebindCurrentFramebuffer( GLenum target, Framebuffer* framebuffer, GLuint back_buffer_service_id) { GLuint framebuffer_id = framebuffer ? framebuffer->service_id() : 0; if (framebuffer_id == 0) { framebuffer_id = back_buffer_service_id; } glBindFramebufferEXT(target, framebuffer_id); } void GLES2DecoderImpl::RestoreCurrentFramebufferBindings() { framebuffer_state_.clear_state_dirty = true; if (!features().chromium_framebuffer_multisample) { RebindCurrentFramebuffer( GL_FRAMEBUFFER, framebuffer_state_.bound_draw_framebuffer.get(), GetBackbufferServiceId()); } else { RebindCurrentFramebuffer( GL_READ_FRAMEBUFFER_EXT, framebuffer_state_.bound_read_framebuffer.get(), GetBackbufferServiceId()); RebindCurrentFramebuffer( GL_DRAW_FRAMEBUFFER_EXT, framebuffer_state_.bound_draw_framebuffer.get(), GetBackbufferServiceId()); } OnFboChanged(); } bool GLES2DecoderImpl::CheckFramebufferValid( Framebuffer* framebuffer, GLenum target, const char* func_name) { if (!framebuffer) { if (surfaceless_) return false; if (backbuffer_needs_clear_bits_) { glClearColor(0, 0, 0, (GLES2Util::GetChannelsForFormat( offscreen_target_color_format_) & 0x0008) != 0 ? 0 : 1.f); state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glClearStencil(0); state_.SetDeviceStencilMaskSeparate(GL_FRONT, kDefaultStencilMask); state_.SetDeviceStencilMaskSeparate(GL_BACK, kDefaultStencilMask); glClearDepth(1.0f); state_.SetDeviceDepthMask(GL_TRUE); state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); bool reset_draw_buffer = false; if ((backbuffer_needs_clear_bits_ & GL_COLOR_BUFFER_BIT) != 0 && group_->draw_buffer() == GL_NONE) { reset_draw_buffer = true; GLenum buf = GL_BACK; if (GetBackbufferServiceId() != 0) // emulated backbuffer buf = GL_COLOR_ATTACHMENT0; glDrawBuffersARB(1, &buf); } glClear(backbuffer_needs_clear_bits_); if (reset_draw_buffer) { GLenum buf = GL_NONE; glDrawBuffersARB(1, &buf); } backbuffer_needs_clear_bits_ = 0; RestoreClearState(); } return true; } if (framebuffer_manager()->IsComplete(framebuffer)) { return true; } GLenum completeness = framebuffer->IsPossiblyComplete(); if (completeness != GL_FRAMEBUFFER_COMPLETE) { LOCAL_SET_GL_ERROR( 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 (framebuffer->GetStatus(texture_manager(), target) != GL_FRAMEBUFFER_COMPLETE) { LOCAL_SET_GL_ERROR( GL_INVALID_FRAMEBUFFER_OPERATION, func_name, "framebuffer incomplete (clear)"); return false; } ClearUnclearedAttachments(target, framebuffer); } } if (!framebuffer_manager()->IsComplete(framebuffer)) { if (framebuffer->GetStatus(texture_manager(), target) != GL_FRAMEBUFFER_COMPLETE) { LOCAL_SET_GL_ERROR( 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 (!features().chromium_framebuffer_multisample) { bool valid = CheckFramebufferValid( framebuffer_state_.bound_draw_framebuffer.get(), GL_FRAMEBUFFER_EXT, func_name); if (valid) OnUseFramebuffer(); return valid; } return CheckFramebufferValid(framebuffer_state_.bound_draw_framebuffer.get(), GL_DRAW_FRAMEBUFFER_EXT, func_name) && CheckFramebufferValid(framebuffer_state_.bound_read_framebuffer.get(), GL_READ_FRAMEBUFFER_EXT, func_name); } bool GLES2DecoderImpl::CheckBoundReadFramebufferColorAttachment( const char* func_name) { Framebuffer* framebuffer = features().chromium_framebuffer_multisample ? framebuffer_state_.bound_read_framebuffer.get() : framebuffer_state_.bound_draw_framebuffer.get(); if (!framebuffer) return true; if (framebuffer->GetAttachment(GL_COLOR_ATTACHMENT0) == NULL) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, func_name, "no color image attached"); return false; } return true; } bool GLES2DecoderImpl::FormsTextureCopyingFeedbackLoop( TextureRef* texture, GLint level) { Framebuffer* framebuffer = features().chromium_framebuffer_multisample ? framebuffer_state_.bound_read_framebuffer.get() : framebuffer_state_.bound_draw_framebuffer.get(); if (!framebuffer) return false; const Framebuffer::Attachment* attachment = framebuffer->GetAttachment( GL_COLOR_ATTACHMENT0); if (!attachment) return false; return attachment->FormsFeedbackLoop(texture, level); } gfx::Size GLES2DecoderImpl::GetBoundReadFrameBufferSize() { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER_EXT); if (framebuffer != NULL) { const Framebuffer::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::GetBoundReadFrameBufferTextureType() { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER_EXT); if (framebuffer != NULL) { return framebuffer->GetColorAttachmentTextureType(); } else { return GL_UNSIGNED_BYTE; } } GLenum GLES2DecoderImpl::GetBoundReadFrameBufferInternalFormat() { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER_EXT); 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() { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); if (framebuffer != NULL) { return framebuffer->GetColorAttachmentFormat(); } else if (offscreen_target_frame_buffer_.get()) { return offscreen_target_color_format_; } else { return back_buffer_color_format_; } } void GLES2DecoderImpl::UpdateParentTextureInfo() { if (!offscreen_saved_color_texture_info_.get()) return; GLenum target = offscreen_saved_color_texture_info_->texture()->target(); glBindTexture(target, offscreen_saved_color_texture_info_->service_id()); texture_manager()->SetLevelInfo( offscreen_saved_color_texture_info_.get(), GL_TEXTURE_2D, 0, // level GL_RGBA, offscreen_size_.width(), offscreen_size_.height(), 1, // depth 0, // border GL_RGBA, GL_UNSIGNED_BYTE, true); texture_manager()->SetParameteri( "UpdateParentTextureInfo", GetErrorState(), offscreen_saved_color_texture_info_.get(), GL_TEXTURE_MAG_FILTER, GL_LINEAR); texture_manager()->SetParameteri( "UpdateParentTextureInfo", GetErrorState(), offscreen_saved_color_texture_info_.get(), GL_TEXTURE_MIN_FILTER, GL_LINEAR); texture_manager()->SetParameteri( "UpdateParentTextureInfo", GetErrorState(), offscreen_saved_color_texture_info_.get(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); texture_manager()->SetParameteri( "UpdateParentTextureInfo", GetErrorState(), offscreen_saved_color_texture_info_.get(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); glBindTexture(target, texture_ref ? texture_ref->service_id() : 0); } void GLES2DecoderImpl::SetResizeCallback( const base::Callback& callback) { resize_callback_ = callback; } Logger* GLES2DecoderImpl::GetLogger() { return &logger_; } void GLES2DecoderImpl::BeginDecoding() { gpu_tracer_->BeginDecoding(); gpu_trace_commands_ = gpu_tracer_->IsTracing() && *gpu_decoder_category_; gpu_debug_commands_ = log_commands() || debug() || gpu_trace_commands_ || (*cb_command_trace_category_ != 0); } void GLES2DecoderImpl::EndDecoding() { gpu_tracer_->EndDecoding(); } ErrorState* GLES2DecoderImpl::GetErrorState() { return state_.GetErrorState(); } void GLES2DecoderImpl::SetShaderCacheCallback( const ShaderCacheCallback& callback) { shader_cache_callback_ = callback; } void GLES2DecoderImpl::SetWaitSyncPointCallback( const WaitSyncPointCallback& callback) { wait_sync_point_callback_ = callback; } AsyncPixelTransferManager* GLES2DecoderImpl::GetAsyncPixelTransferManager() { return async_pixel_transfer_manager_.get(); } void GLES2DecoderImpl::ResetAsyncPixelTransferManagerForTest() { async_pixel_transfer_manager_.reset(); } void GLES2DecoderImpl::SetAsyncPixelTransferManagerForTest( AsyncPixelTransferManager* manager) { async_pixel_transfer_manager_ = make_scoped_ptr(manager); } bool GLES2DecoderImpl::GetServiceTextureId(uint32 client_texture_id, uint32* service_texture_id) { TextureRef* texture_ref = texture_manager()->GetTexture(client_texture_id); if (texture_ref) { *service_texture_id = texture_ref->service_id(); return true; } return false; } uint32 GLES2DecoderImpl::GetTextureUploadCount() { return texture_state_.texture_upload_count + async_pixel_transfer_manager_->GetTextureUploadCount(); } base::TimeDelta GLES2DecoderImpl::GetTotalTextureUploadTime() { return texture_state_.total_texture_upload_time + async_pixel_transfer_manager_->GetTotalTextureUploadTime(); } base::TimeDelta GLES2DecoderImpl::GetTotalProcessingCommandsTime() { return total_processing_commands_time_; } void GLES2DecoderImpl::AddProcessingCommandsTime(base::TimeDelta time) { total_processing_commands_time_ += time; } void GLES2DecoderImpl::Destroy(bool have_context) { if (!initialized()) return; DCHECK(!have_context || context_->IsCurrent(NULL)); // Unbind everything. state_.vertex_attrib_manager = NULL; state_.default_vertex_attrib_manager = NULL; state_.texture_units.clear(); state_.bound_array_buffer = NULL; state_.bound_copy_read_buffer = NULL; state_.bound_copy_write_buffer = NULL; state_.bound_pixel_pack_buffer = NULL; state_.bound_pixel_unpack_buffer = NULL; state_.bound_transform_feedback_buffer = NULL; state_.bound_uniform_buffer = NULL; state_.current_queries.clear(); framebuffer_state_.bound_read_framebuffer = NULL; framebuffer_state_.bound_draw_framebuffer = NULL; state_.bound_renderbuffer = NULL; state_.bound_valuebuffer = NULL; if (offscreen_saved_color_texture_info_.get()) { DCHECK(offscreen_target_color_texture_); DCHECK_EQ(offscreen_saved_color_texture_info_->service_id(), offscreen_saved_color_texture_->id()); offscreen_saved_color_texture_->Invalidate(); offscreen_saved_color_texture_info_ = NULL; } if (have_context) { if (copy_texture_CHROMIUM_.get()) { copy_texture_CHROMIUM_->Destroy(); copy_texture_CHROMIUM_.reset(); } clear_framebuffer_blit_.reset(); if (state_.current_program.get()) { program_manager()->UnuseProgram(shader_manager(), state_.current_program.get()); } if (attrib_0_buffer_id_) { glDeleteBuffersARB(1, &attrib_0_buffer_id_); } if (fixed_attrib_buffer_id_) { glDeleteBuffersARB(1, &fixed_attrib_buffer_id_); } if (validation_texture_) { glDeleteTextures(1, &validation_texture_); glDeleteFramebuffersEXT(1, &validation_fbo_multisample_); glDeleteFramebuffersEXT(1, &validation_fbo_); } 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_color_render_buffer_.get()) offscreen_target_color_render_buffer_->Destroy(); if (offscreen_target_depth_render_buffer_.get()) offscreen_target_depth_render_buffer_->Destroy(); if (offscreen_target_stencil_render_buffer_.get()) offscreen_target_stencil_render_buffer_->Destroy(); if (offscreen_saved_frame_buffer_.get()) offscreen_saved_frame_buffer_->Destroy(); if (offscreen_saved_color_texture_.get()) offscreen_saved_color_texture_->Destroy(); if (offscreen_resolved_frame_buffer_.get()) offscreen_resolved_frame_buffer_->Destroy(); if (offscreen_resolved_color_texture_.get()) offscreen_resolved_color_texture_->Destroy(); } else { if (offscreen_target_frame_buffer_.get()) offscreen_target_frame_buffer_->Invalidate(); if (offscreen_target_color_texture_.get()) offscreen_target_color_texture_->Invalidate(); if (offscreen_target_color_render_buffer_.get()) offscreen_target_color_render_buffer_->Invalidate(); if (offscreen_target_depth_render_buffer_.get()) offscreen_target_depth_render_buffer_->Invalidate(); if (offscreen_target_stencil_render_buffer_.get()) offscreen_target_stencil_render_buffer_->Invalidate(); if (offscreen_saved_frame_buffer_.get()) offscreen_saved_frame_buffer_->Invalidate(); if (offscreen_saved_color_texture_.get()) offscreen_saved_color_texture_->Invalidate(); if (offscreen_resolved_frame_buffer_.get()) offscreen_resolved_frame_buffer_->Invalidate(); if (offscreen_resolved_color_texture_.get()) offscreen_resolved_color_texture_->Invalidate(); } // Current program must be cleared after calling ProgramManager::UnuseProgram. // Otherwise, we can leak objects. http://crbug.com/258772. // state_.current_program must be reset before group_ is reset because // the later deletes the ProgramManager object that referred by // state_.current_program object. state_.current_program = NULL; copy_texture_CHROMIUM_.reset(); clear_framebuffer_blit_.reset(); if (query_manager_.get()) { query_manager_->Destroy(have_context); query_manager_.reset(); } if (vertex_array_manager_ .get()) { vertex_array_manager_->Destroy(have_context); vertex_array_manager_.reset(); } if (image_manager_.get()) { image_manager_->Destroy(have_context); image_manager_.reset(); } offscreen_target_frame_buffer_.reset(); offscreen_target_color_texture_.reset(); offscreen_target_color_render_buffer_.reset(); offscreen_target_depth_render_buffer_.reset(); offscreen_target_stencil_render_buffer_.reset(); offscreen_saved_frame_buffer_.reset(); offscreen_saved_color_texture_.reset(); offscreen_resolved_frame_buffer_.reset(); offscreen_resolved_color_texture_.reset(); // Need to release these before releasing |group_| which may own the // ShaderTranslatorCache. fragment_translator_ = NULL; vertex_translator_ = NULL; // Should destroy the transfer manager before the texture manager held // by the context group. async_pixel_transfer_manager_.reset(); // Destroy the GPU Tracer which may own some in process GPU Timings. if (gpu_tracer_) { gpu_tracer_->Destroy(have_context); gpu_tracer_.reset(); } if (group_.get()) { framebuffer_manager()->RemoveObserver(this); group_->Destroy(this, have_context); group_ = NULL; } if (context_.get()) { context_->ReleaseCurrent(NULL); context_ = NULL; } #if defined(OS_MACOSX) for (TextureToIOSurfaceMap::iterator it = texture_to_io_surface_map_.begin(); it != texture_to_io_surface_map_.end(); ++it) { CFRelease(it->second); } texture_to_io_surface_map_.clear(); #endif } void GLES2DecoderImpl::SetSurface( const scoped_refptr& surface) { DCHECK(context_->IsCurrent(NULL)); DCHECK(surface_.get()); surface_ = surface; RestoreCurrentFramebufferBindings(); } void GLES2DecoderImpl::ProduceFrontBuffer(const Mailbox& mailbox) { if (!offscreen_saved_color_texture_.get()) { LOG(ERROR) << "Called ProduceFrontBuffer on a non-offscreen context"; return; } if (!offscreen_saved_color_texture_info_.get()) { GLuint service_id = offscreen_saved_color_texture_->id(); offscreen_saved_color_texture_info_ = TextureRef::Create( texture_manager(), 0, service_id); texture_manager()->SetTarget(offscreen_saved_color_texture_info_.get(), GL_TEXTURE_2D); UpdateParentTextureInfo(); } mailbox_manager()->ProduceTexture( mailbox, offscreen_saved_color_texture_info_->texture()); } bool GLES2DecoderImpl::ResizeOffscreenFrameBuffer(const gfx::Size& size) { bool is_offscreen = !!offscreen_target_frame_buffer_.get(); if (!is_offscreen) { LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer called " << " with an onscreen framebuffer."; return false; } if (offscreen_size_ == size) return true; offscreen_size_ = size; int w = offscreen_size_.width(); int h = offscreen_size_.height(); if (w < 0 || h < 0 || h >= (INT_MAX / 4) / (w ? w : 1)) { LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer failed " << "to allocate storage due to excessive dimensions."; return false; } // Reallocate the offscreen target buffers. DCHECK(offscreen_target_color_format_); if (IsOffscreenBufferMultisampled()) { if (!offscreen_target_color_render_buffer_->AllocateStorage( feature_info_.get(), offscreen_size_, offscreen_target_color_format_, offscreen_target_samples_)) { LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer failed " << "to allocate storage for offscreen target color buffer."; return false; } } else { if (!offscreen_target_color_texture_->AllocateStorage( offscreen_size_, offscreen_target_color_format_, false)) { LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer failed " << "to allocate storage for offscreen target color texture."; return false; } } if (offscreen_target_depth_format_ && !offscreen_target_depth_render_buffer_->AllocateStorage( feature_info_.get(), offscreen_size_, offscreen_target_depth_format_, offscreen_target_samples_)) { LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer failed " << "to allocate storage for offscreen target depth buffer."; return false; } if (offscreen_target_stencil_format_ && !offscreen_target_stencil_render_buffer_->AllocateStorage( feature_info_.get(), offscreen_size_, offscreen_target_stencil_format_, offscreen_target_samples_)) { LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer failed " << "to allocate storage for offscreen target stencil buffer."; return false; } // Attach the offscreen target buffers to the target frame buffer. if (IsOffscreenBufferMultisampled()) { offscreen_target_frame_buffer_->AttachRenderBuffer( GL_COLOR_ATTACHMENT0, offscreen_target_color_render_buffer_.get()); } else { offscreen_target_frame_buffer_->AttachRenderTexture( offscreen_target_color_texture_.get()); } if (offscreen_target_depth_format_) { offscreen_target_frame_buffer_->AttachRenderBuffer( GL_DEPTH_ATTACHMENT, offscreen_target_depth_render_buffer_.get()); } const bool packed_depth_stencil = offscreen_target_depth_format_ == GL_DEPTH24_STENCIL8; if (packed_depth_stencil) { offscreen_target_frame_buffer_->AttachRenderBuffer( GL_STENCIL_ATTACHMENT, offscreen_target_depth_render_buffer_.get()); } else if (offscreen_target_stencil_format_) { offscreen_target_frame_buffer_->AttachRenderBuffer( GL_STENCIL_ATTACHMENT, offscreen_target_stencil_render_buffer_.get()); } if (offscreen_target_frame_buffer_->CheckStatus() != GL_FRAMEBUFFER_COMPLETE) { LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer failed " << "because offscreen FBO was incomplete."; return false; } // Clear the target frame buffer. { ScopedFrameBufferBinder binder(this, offscreen_target_frame_buffer_->id()); glClearColor(0, 0, 0, (GLES2Util::GetChannelsForFormat( offscreen_target_color_format_) & 0x0008) != 0 ? 0 : 1.f); state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glClearStencil(0); state_.SetDeviceStencilMaskSeparate(GL_FRONT, kDefaultStencilMask); state_.SetDeviceStencilMaskSeparate(GL_BACK, kDefaultStencilMask); glClearDepth(0); state_.SetDeviceDepthMask(GL_TRUE); state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); RestoreClearState(); } // Destroy the offscreen resolved framebuffers. if (offscreen_resolved_frame_buffer_.get()) offscreen_resolved_frame_buffer_->Destroy(); if (offscreen_resolved_color_texture_.get()) offscreen_resolved_color_texture_->Destroy(); offscreen_resolved_color_texture_.reset(); offscreen_resolved_frame_buffer_.reset(); return true; } error::Error GLES2DecoderImpl::HandleResizeCHROMIUM(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::ResizeCHROMIUM& c = *static_cast(cmd_data); if (!offscreen_target_frame_buffer_.get() && surface_->DeferDraws()) return error::kDeferCommandUntilLater; GLuint width = static_cast(c.width); GLuint height = static_cast(c.height); GLfloat scale_factor = c.scale_factor; TRACE_EVENT2("gpu", "glResizeChromium", "width", width, "height", height); width = std::max(1U, width); height = std::max(1U, height); #if defined(OS_POSIX) && !defined(OS_MACOSX) && \ !defined(UI_COMPOSITOR_IMAGE_TRANSPORT) // Make sure that we are done drawing to the back buffer before resizing. glFinish(); #endif bool is_offscreen = !!offscreen_target_frame_buffer_.get(); if (is_offscreen) { if (!ResizeOffscreenFrameBuffer(gfx::Size(width, height))) { LOG(ERROR) << "GLES2DecoderImpl: Context lost because " << "ResizeOffscreenFrameBuffer failed."; return error::kLostContext; } } if (!resize_callback_.is_null()) { resize_callback_.Run(gfx::Size(width, height), scale_factor); DCHECK(context_->IsCurrent(surface_.get())); if (!context_->IsCurrent(surface_.get())) { LOG(ERROR) << "GLES2DecoderImpl: Context lost because context no longer " << "current after resize callback."; return error::kLostContext; } } return error::kNoError; } const char* GLES2DecoderImpl::GetCommandName(unsigned int command_id) const { if (command_id > kStartPoint && command_id < kNumCommands) { return gles2::GetCommandName(static_cast(command_id)); } return GetCommonCommandName(static_cast(command_id)); } // Decode a command, and call the corresponding GL functions. // NOTE: DoCommand() is slower than calling DoCommands() on larger batches // of commands at once, and is now only used for tests that need to track // individual commands. error::Error GLES2DecoderImpl::DoCommand(unsigned int command, unsigned int arg_count, const void* cmd_data) { return DoCommands(1, cmd_data, arg_count + 1, 0); } // Decode multiple commands, and call the corresponding GL functions. // NOTE: 'buffer' is a pointer to the command buffer. As such, it could be // changed by a (malicious) client at any time, so if validation has to happen, // it should operate on a copy of them. // NOTE: This is duplicating code from AsyncAPIInterface::DoCommands() in the // interest of performance in this critical execution loop. template error::Error GLES2DecoderImpl::DoCommandsImpl(unsigned int num_commands, const void* buffer, int num_entries, int* entries_processed) { commands_to_process_ = num_commands; error::Error result = error::kNoError; const CommandBufferEntry* cmd_data = static_cast(buffer); int process_pos = 0; unsigned int command = 0; while (process_pos < num_entries && result == error::kNoError && commands_to_process_--) { const unsigned int size = cmd_data->value_header.size; command = cmd_data->value_header.command; if (size == 0) { result = error::kInvalidSize; break; } if (static_cast(size) + process_pos > num_entries) { result = error::kOutOfBounds; break; } if (DebugImpl) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cb_command"), GetCommandName(command)); if (log_commands()) { LOG(ERROR) << "[" << logger_.GetLogPrefix() << "]" << "cmd: " << GetCommandName(command); } } const unsigned int arg_count = size - 1; unsigned int command_index = command - kStartPoint - 1; if (command_index < arraysize(command_info)) { const CommandInfo& info = command_info[command_index]; unsigned int info_arg_count = static_cast(info.arg_count); if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) || (info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) { bool doing_gpu_trace = false; if (DebugImpl && gpu_trace_commands_) { if (CMD_FLAG_GET_TRACE_LEVEL(info.cmd_flags) <= gpu_trace_level_) { doing_gpu_trace = true; gpu_tracer_->Begin(TRACE_DISABLED_BY_DEFAULT("gpu_decoder"), GetCommandName(command), kTraceDecoder); } } uint32 immediate_data_size = (arg_count - info_arg_count) * sizeof(CommandBufferEntry); // NOLINT result = (this->*info.cmd_handler)(immediate_data_size, cmd_data); if (DebugImpl && doing_gpu_trace) gpu_tracer_->End(kTraceDecoder); if (DebugImpl && debug()) { GLenum error; while ((error = glGetError()) != GL_NO_ERROR) { LOG(ERROR) << "[" << logger_.GetLogPrefix() << "] " << "GL ERROR: " << GLES2Util::GetStringEnum(error) << " : " << GetCommandName(command); LOCAL_SET_GL_ERROR(error, "DoCommand", "GL error from driver"); } } } else { result = error::kInvalidArguments; } } else { result = DoCommonCommand(command, arg_count, cmd_data); } if (DebugImpl) { TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("cb_command"), GetCommandName(command)); } if (result == error::kNoError && current_decoder_error_ != error::kNoError) { result = current_decoder_error_; current_decoder_error_ = error::kNoError; } if (result != error::kDeferCommandUntilLater) { process_pos += size; cmd_data += size; } } if (entries_processed) *entries_processed = process_pos; if (error::IsError(result)) { LOG(ERROR) << "Error: " << result << " for Command " << GetCommandName(command); } return result; } error::Error GLES2DecoderImpl::DoCommands(unsigned int num_commands, const void* buffer, int num_entries, int* entries_processed) { if (gpu_debug_commands_) { return DoCommandsImpl( num_commands, buffer, num_entries, entries_processed); } else { return DoCommandsImpl( num_commands, buffer, num_entries, entries_processed); } } void GLES2DecoderImpl::RemoveBuffer(GLuint client_id) { buffer_manager()->RemoveBuffer(client_id); } void GLES2DecoderImpl::DoFinish() { glFinish(); ProcessPendingReadPixels(); ProcessPendingQueries(true); } void GLES2DecoderImpl::DoFlush() { glFlush(); ProcessPendingQueries(false); } void GLES2DecoderImpl::DoActiveTexture(GLenum texture_unit) { GLuint texture_index = texture_unit - GL_TEXTURE0; if (texture_index >= state_.texture_units.size()) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glActiveTexture", texture_unit, "texture_unit"); return; } state_.active_texture_unit = texture_index; glActiveTexture(texture_unit); } void GLES2DecoderImpl::DoBindBuffer(GLenum target, GLuint client_id) { Buffer* buffer = NULL; GLuint service_id = 0; if (client_id != 0) { buffer = GetBuffer(client_id); if (!buffer) { if (!group_->bind_generates_resource()) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindBuffer", "id not generated by glGenBuffers"); return; } // It's a new id so make a buffer buffer for it. glGenBuffersARB(1, &service_id); CreateBuffer(client_id, service_id); buffer = GetBuffer(client_id); } } LogClientServiceForInfo(buffer, client_id, "glBindBuffer"); if (buffer) { if (!buffer_manager()->SetTarget(buffer, target)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBindBuffer", "buffer bound to more than 1 target"); return; } service_id = buffer->service_id(); } state_.SetBoundBuffer(target, buffer); glBindBuffer(target, service_id); } bool GLES2DecoderImpl::BoundFramebufferHasColorAttachmentWithAlpha( bool all_draw_buffers) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); if (!all_draw_buffers || !framebuffer) { return (GLES2Util::GetChannelsForFormat( GetBoundDrawFrameBufferInternalFormat()) & 0x0008) != 0; } return framebuffer->HasAlphaMRT(); } bool GLES2DecoderImpl::BoundFramebufferHasDepthAttachment() { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); if (framebuffer) { return framebuffer->HasDepthAttachment(); } if (offscreen_target_frame_buffer_.get()) { return offscreen_target_depth_format_ != 0; } return back_buffer_has_depth_; } bool GLES2DecoderImpl::BoundFramebufferHasStencilAttachment() { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); if (framebuffer) { return framebuffer->HasStencilAttachment(); } if (offscreen_target_frame_buffer_.get()) { return offscreen_target_stencil_format_ != 0 || offscreen_target_depth_format_ == GL_DEPTH24_STENCIL8; } return back_buffer_has_stencil_; } void GLES2DecoderImpl::ApplyDirtyState() { if (framebuffer_state_.clear_state_dirty) { bool have_alpha = BoundFramebufferHasColorAttachmentWithAlpha(true); state_.SetDeviceColorMask(state_.color_mask_red, state_.color_mask_green, state_.color_mask_blue, state_.color_mask_alpha && have_alpha); bool have_depth = BoundFramebufferHasDepthAttachment(); state_.SetDeviceDepthMask(state_.depth_mask && have_depth); bool have_stencil = BoundFramebufferHasStencilAttachment(); state_.SetDeviceStencilMaskSeparate( GL_FRONT, have_stencil ? state_.stencil_front_writemask : 0); state_.SetDeviceStencilMaskSeparate( GL_BACK, have_stencil ? state_.stencil_back_writemask : 0); state_.SetDeviceCapabilityState( GL_DEPTH_TEST, state_.enable_flags.depth_test && have_depth); state_.SetDeviceCapabilityState( GL_STENCIL_TEST, state_.enable_flags.stencil_test && have_stencil); framebuffer_state_.clear_state_dirty = false; } } GLuint GLES2DecoderImpl::GetBackbufferServiceId() const { return (offscreen_target_frame_buffer_.get()) ? offscreen_target_frame_buffer_->id() : (surface_.get() ? surface_->GetBackingFrameBufferObject() : 0); } void GLES2DecoderImpl::RestoreState(const ContextState* prev_state) { TRACE_EVENT1("gpu", "GLES2DecoderImpl::RestoreState", "context", logger_.GetLogPrefix()); // Restore the Framebuffer first because of bugs in Intel drivers. // Intel drivers incorrectly clip the viewport settings to // the size of the current framebuffer object. RestoreFramebufferBindings(); state_.RestoreState(prev_state); } void GLES2DecoderImpl::RestoreFramebufferBindings() const { GLuint service_id = framebuffer_state_.bound_draw_framebuffer.get() ? framebuffer_state_.bound_draw_framebuffer->service_id() : GetBackbufferServiceId(); if (!features().chromium_framebuffer_multisample) { glBindFramebufferEXT(GL_FRAMEBUFFER, service_id); } else { glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, service_id); service_id = framebuffer_state_.bound_read_framebuffer.get() ? framebuffer_state_.bound_read_framebuffer->service_id() : GetBackbufferServiceId(); glBindFramebufferEXT(GL_READ_FRAMEBUFFER, service_id); } OnFboChanged(); } void GLES2DecoderImpl::RestoreRenderbufferBindings() { state_.RestoreRenderbufferBindings(); } void GLES2DecoderImpl::RestoreTextureState(unsigned service_id) const { Texture* texture = texture_manager()->GetTextureForServiceId(service_id); if (texture) { GLenum target = texture->target(); glBindTexture(target, service_id); glTexParameteri( target, GL_TEXTURE_WRAP_S, texture->wrap_s()); glTexParameteri( target, GL_TEXTURE_WRAP_T, texture->wrap_t()); glTexParameteri( target, GL_TEXTURE_MIN_FILTER, texture->min_filter()); glTexParameteri( target, GL_TEXTURE_MAG_FILTER, texture->mag_filter()); RestoreTextureUnitBindings(state_.active_texture_unit); } } void GLES2DecoderImpl::ClearAllAttributes() const { // Must use native VAO 0, as RestoreAllAttributes can't fully restore // other VAOs. if (feature_info_->feature_flags().native_vertex_array_object) glBindVertexArrayOES(0); for (uint32 i = 0; i < group_->max_vertex_attribs(); ++i) { if (i != 0) // Never disable attribute 0 glDisableVertexAttribArray(i); if (features().angle_instanced_arrays) glVertexAttribDivisorANGLE(i, 0); } } void GLES2DecoderImpl::RestoreAllAttributes() const { state_.RestoreVertexAttribs(); } void GLES2DecoderImpl::SetIgnoreCachedStateForTest(bool ignore) { state_.SetIgnoreCachedStateForTest(ignore); } void GLES2DecoderImpl::OnFboChanged() const { if (workarounds().restore_scissor_on_fbo_change) state_.fbo_binding_for_scissor_workaround_dirty = true; if (workarounds().gl_begin_gl_end_on_fbo_change_to_backbuffer) { GLint bound_fbo_unsigned = -1; glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &bound_fbo_unsigned); GLuint bound_fbo = static_cast(bound_fbo_unsigned); if (surface_ && surface_->GetBackingFrameBufferObject() == bound_fbo) surface_->NotifyWasBound(); } } // Called after the FBO is checked for completeness. void GLES2DecoderImpl::OnUseFramebuffer() const { if (state_.fbo_binding_for_scissor_workaround_dirty) { state_.fbo_binding_for_scissor_workaround_dirty = false; // The driver forgets the correct scissor when modifying the FBO binding. glScissor(state_.scissor_x, state_.scissor_y, state_.scissor_width, state_.scissor_height); // crbug.com/222018 - Also on QualComm, the flush here avoids flicker, // it's unclear how this bug works. glFlush(); } } void GLES2DecoderImpl::DoBindFramebuffer(GLenum target, GLuint client_id) { Framebuffer* framebuffer = NULL; GLuint service_id = 0; if (client_id != 0) { framebuffer = GetFramebuffer(client_id); if (!framebuffer) { if (!group_->bind_generates_resource()) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindFramebuffer", "id not generated by glGenFramebuffers"); return; } // It's a new id so make a framebuffer framebuffer for it. glGenFramebuffersEXT(1, &service_id); CreateFramebuffer(client_id, service_id); framebuffer = GetFramebuffer(client_id); } else { service_id = framebuffer->service_id(); } framebuffer->MarkAsValid(); } LogClientServiceForInfo(framebuffer, client_id, "glBindFramebuffer"); if (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER_EXT) { framebuffer_state_.bound_draw_framebuffer = framebuffer; } // vmiura: This looks like dup code if (target == GL_FRAMEBUFFER || target == GL_READ_FRAMEBUFFER_EXT) { framebuffer_state_.bound_read_framebuffer = framebuffer; } framebuffer_state_.clear_state_dirty = true; // If we are rendering to the backbuffer get the FBO id for any simulated // backbuffer. if (framebuffer == NULL) { service_id = GetBackbufferServiceId(); } glBindFramebufferEXT(target, service_id); OnFboChanged(); } void GLES2DecoderImpl::DoBindRenderbuffer(GLenum target, GLuint client_id) { Renderbuffer* renderbuffer = NULL; GLuint service_id = 0; if (client_id != 0) { renderbuffer = GetRenderbuffer(client_id); if (!renderbuffer) { if (!group_->bind_generates_resource()) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindRenderbuffer", "id not generated by glGenRenderbuffers"); return; } // It's a new id so make a renderbuffer for it. glGenRenderbuffersEXT(1, &service_id); CreateRenderbuffer(client_id, service_id); renderbuffer = GetRenderbuffer(client_id); } else { service_id = renderbuffer->service_id(); } renderbuffer->MarkAsValid(); } LogClientServiceForInfo(renderbuffer, client_id, "glBindRenderbuffer"); state_.bound_renderbuffer = renderbuffer; state_.bound_renderbuffer_valid = true; glBindRenderbufferEXT(GL_RENDERBUFFER, service_id); } void GLES2DecoderImpl::DoBindTexture(GLenum target, GLuint client_id) { TextureRef* texture_ref = NULL; GLuint service_id = 0; if (client_id != 0) { texture_ref = GetTexture(client_id); if (!texture_ref) { if (!group_->bind_generates_resource()) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindTexture", "id not generated by glGenTextures"); return; } // It's a new id so make a texture texture for it. glGenTextures(1, &service_id); DCHECK_NE(0u, service_id); CreateTexture(client_id, service_id); texture_ref = GetTexture(client_id); } } else { texture_ref = texture_manager()->GetDefaultTextureInfo(target); } // Check the texture exists if (texture_ref) { Texture* texture = texture_ref->texture(); // Check that we are not trying to bind it to a different target. if (texture->target() != 0 && texture->target() != target) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindTexture", "texture bound to more than 1 target."); return; } LogClientServiceForInfo(texture, client_id, "glBindTexture"); if (texture->target() == 0) { texture_manager()->SetTarget(texture_ref, target); } glBindTexture(target, texture->service_id()); } else { glBindTexture(target, 0); } TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; unit.bind_target = target; switch (target) { case GL_TEXTURE_2D: unit.bound_texture_2d = texture_ref; break; case GL_TEXTURE_CUBE_MAP: unit.bound_texture_cube_map = texture_ref; break; case GL_TEXTURE_EXTERNAL_OES: unit.bound_texture_external_oes = texture_ref; break; case GL_TEXTURE_RECTANGLE_ARB: unit.bound_texture_rectangle_arb = texture_ref; break; case GL_TEXTURE_3D: unit.bound_texture_3d = texture_ref; break; case GL_TEXTURE_2D_ARRAY: unit.bound_texture_2d_array = texture_ref; break; default: NOTREACHED(); // Validation should prevent us getting here. break; } } void GLES2DecoderImpl::DoDisableVertexAttribArray(GLuint index) { if (state_.vertex_attrib_manager->Enable(index, false)) { if (index != 0 || gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) { glDisableVertexAttribArray(index); } } else { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glDisableVertexAttribArray", "index out of range"); } } void GLES2DecoderImpl::DoDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum* attachments) { if (workarounds().disable_discard_framebuffer) return; Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_FRAMEBUFFER); // Validates the attachments. If one of them fails // the whole command fails. for (GLsizei i = 0; i < numAttachments; ++i) { if ((framebuffer && !validators_->attachment.IsValid(attachments[i])) || (!framebuffer && !validators_->backbuffer_attachment.IsValid(attachments[i]))) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glDiscardFramebufferEXT", attachments[i], "attachments"); return; } } // Marks each one of them as not cleared for (GLsizei i = 0; i < numAttachments; ++i) { if (framebuffer) { framebuffer->MarkAttachmentAsCleared(renderbuffer_manager(), texture_manager(), attachments[i], false); } else { switch (attachments[i]) { case GL_COLOR_EXT: backbuffer_needs_clear_bits_ |= GL_COLOR_BUFFER_BIT; break; case GL_DEPTH_EXT: backbuffer_needs_clear_bits_ |= GL_DEPTH_BUFFER_BIT; case GL_STENCIL_EXT: backbuffer_needs_clear_bits_ |= GL_STENCIL_BUFFER_BIT; break; default: NOTREACHED(); break; } } } // If the default framebuffer is bound but we are still rendering to an // FBO, translate attachment names that refer to default framebuffer // channels to corresponding framebuffer attachments. scoped_ptr translated_attachments(new GLenum[numAttachments]); for (GLsizei i = 0; i < numAttachments; ++i) { GLenum attachment = attachments[i]; if (!framebuffer && GetBackbufferServiceId()) { switch (attachment) { case GL_COLOR_EXT: attachment = GL_COLOR_ATTACHMENT0; break; case GL_DEPTH_EXT: attachment = GL_DEPTH_ATTACHMENT; break; case GL_STENCIL_EXT: attachment = GL_STENCIL_ATTACHMENT; break; default: NOTREACHED(); return; } } translated_attachments[i] = attachment; } ScopedRenderTo do_render(framebuffer); if (feature_info_->gl_version_info().is_es3) { glInvalidateFramebuffer( target, numAttachments, translated_attachments.get()); } else { glDiscardFramebufferEXT( target, numAttachments, translated_attachments.get()); } } void GLES2DecoderImpl::DoEnableVertexAttribArray(GLuint index) { if (state_.vertex_attrib_manager->Enable(index, true)) { glEnableVertexAttribArray(index); } else { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glEnableVertexAttribArray", "index out of range"); } } void GLES2DecoderImpl::DoGenerateMipmap(GLenum target) { TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref || !texture_manager()->CanGenerateMipmaps(texture_ref)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glGenerateMipmap", "Can not generate mips"); return; } if (target == GL_TEXTURE_CUBE_MAP) { for (int i = 0; i < 6; ++i) { GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; if (!texture_manager()->ClearTextureLevel(this, texture_ref, face, 0)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glGenerateMipmap", "dimensions too big"); return; } } } else { if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, 0)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glGenerateMipmap", "dimensions too big"); return; } } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glGenerateMipmap"); // Workaround for Mac driver bug. In the large scheme of things setting // glTexParamter twice for glGenerateMipmap is probably not a lage performance // hit so there's probably no need to make this conditional. The bug appears // to be that if the filtering mode is set to something that doesn't require // mipmaps for rendering, or is never set to something other than the default, // then glGenerateMipmap misbehaves. if (workarounds().set_texture_filter_before_generating_mipmap) { glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); } glGenerateMipmapEXT(target); if (workarounds().set_texture_filter_before_generating_mipmap) { glTexParameteri(target, GL_TEXTURE_MIN_FILTER, texture_ref->texture()->min_filter()); } GLenum error = LOCAL_PEEK_GL_ERROR("glGenerateMipmap"); if (error == GL_NO_ERROR) { texture_manager()->MarkMipmapsGenerated(texture_ref); } } bool GLES2DecoderImpl::GetHelper( GLenum pname, GLint* params, GLsizei* num_written) { DCHECK(num_written); if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) { switch (pname) { case GL_IMPLEMENTATION_COLOR_READ_FORMAT: *num_written = 1; // Return the GL implementation's preferred format and (see below type) // if we have the GL extension that exposes this. This allows the GPU // client to use the implementation's preferred format for glReadPixels // for optimisation. // // A conflicting extension (GL_ARB_ES2_compatibility) specifies an error // case when requested on integer/floating point buffers but which is // acceptable on GLES2 and with the GL_OES_read_format extension. // // Therefore if an error occurs we swallow the error and use the // internal implementation. if (params) { if (context_->HasExtension("GL_OES_read_format")) { ScopedGLErrorSuppressor suppressor("GLES2DecoderImpl::GetHelper", GetErrorState()); glGetIntegerv(pname, params); if (glGetError() == GL_NO_ERROR) return true; } *params = GLES2Util::GetPreferredGLReadPixelsFormat( GetBoundReadFrameBufferInternalFormat()); } return true; case GL_IMPLEMENTATION_COLOR_READ_TYPE: *num_written = 1; if (params) { if (context_->HasExtension("GL_OES_read_format")) { ScopedGLErrorSuppressor suppressor("GLES2DecoderImpl::GetHelper", GetErrorState()); glGetIntegerv(pname, params); if (glGetError() == GL_NO_ERROR) return true; } *params = GLES2Util::GetPreferredGLReadPixelsType( GetBoundReadFrameBufferInternalFormat(), GetBoundReadFrameBufferTextureType()); } return true; case GL_MAX_FRAGMENT_UNIFORM_VECTORS: *num_written = 1; if (params) { *params = group_->max_fragment_uniform_vectors(); } return true; case GL_MAX_VARYING_VECTORS: *num_written = 1; if (params) { *params = group_->max_varying_vectors(); } return true; case GL_MAX_VERTEX_UNIFORM_VECTORS: *num_written = 1; if (params) { *params = group_->max_vertex_uniform_vectors(); } return true; } } if (unsafe_es3_apis_enabled()) { switch (pname) { case GL_MAX_VARYING_COMPONENTS: { if (feature_info_->gl_version_info().is_es) { // We can just delegate this query to the driver. return false; } // GL_MAX_VARYING_COMPONENTS is deprecated in the desktop // OpenGL core profile, so for simplicity, just compute it // from GL_MAX_VARYING_VECTORS on non-OpenGL ES // configurations. GLint max_varying_vectors = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &max_varying_vectors); *num_written = 1; if (params) { *params = max_varying_vectors * 4; } return true; } } } switch (pname) { case GL_MAX_VIEWPORT_DIMS: if (offscreen_target_frame_buffer_.get()) { *num_written = 2; if (params) { params[0] = renderbuffer_manager()->max_renderbuffer_size(); params[1] = renderbuffer_manager()->max_renderbuffer_size(); } return true; } return false; case GL_MAX_SAMPLES: *num_written = 1; if (params) { params[0] = renderbuffer_manager()->max_samples(); } return true; case GL_MAX_RENDERBUFFER_SIZE: *num_written = 1; if (params) { params[0] = renderbuffer_manager()->max_renderbuffer_size(); } return true; case GL_MAX_TEXTURE_SIZE: *num_written = 1; if (params) { params[0] = texture_manager()->MaxSizeForTarget(GL_TEXTURE_2D); } return true; case GL_MAX_CUBE_MAP_TEXTURE_SIZE: *num_written = 1; if (params) { params[0] = texture_manager()->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP); } return true; case GL_MAX_COLOR_ATTACHMENTS_EXT: *num_written = 1; if (params) { params[0] = group_->max_color_attachments(); } return true; case GL_MAX_DRAW_BUFFERS_ARB: *num_written = 1; if (params) { params[0] = group_->max_draw_buffers(); } return true; case GL_ALPHA_BITS: *num_written = 1; if (params) { GLint v = 0; if (feature_info_->gl_version_info().is_desktop_core_profile) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); if (framebuffer) { glGetFramebufferAttachmentParameterivEXT( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, &v); } else { v = (back_buffer_color_format_ == GL_RGBA ? 8 : 0); } } else { glGetIntegerv(GL_ALPHA_BITS, &v); } params[0] = BoundFramebufferHasColorAttachmentWithAlpha(false) ? v : 0; } return true; case GL_DEPTH_BITS: *num_written = 1; if (params) { GLint v = 0; if (feature_info_->gl_version_info().is_desktop_core_profile) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); if (framebuffer) { glGetFramebufferAttachmentParameterivEXT( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &v); } else { v = (back_buffer_has_depth_ ? 24 : 0); } } else { glGetIntegerv(GL_DEPTH_BITS, &v); } params[0] = BoundFramebufferHasDepthAttachment() ? v : 0; } return true; case GL_RED_BITS: case GL_GREEN_BITS: case GL_BLUE_BITS: *num_written = 1; if (params) { GLint v = 0; if (feature_info_->gl_version_info().is_desktop_core_profile) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); if (framebuffer) { GLenum framebuffer_enum = 0; switch (pname) { case GL_RED_BITS: framebuffer_enum = GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE; break; case GL_GREEN_BITS: framebuffer_enum = GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE; break; case GL_BLUE_BITS: framebuffer_enum = GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE; break; } glGetFramebufferAttachmentParameterivEXT( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, framebuffer_enum, &v); } else { v = 8; } } else { glGetIntegerv(pname, &v); } params[0] = v; } return true; case GL_STENCIL_BITS: *num_written = 1; if (params) { GLint v = 0; if (feature_info_->gl_version_info().is_desktop_core_profile) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); if (framebuffer) { glGetFramebufferAttachmentParameterivEXT( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &v); } else { v = (back_buffer_has_stencil_ ? 8 : 0); } } else { glGetIntegerv(GL_STENCIL_BITS, &v); } params[0] = BoundFramebufferHasStencilAttachment() ? v : 0; } return true; case GL_COMPRESSED_TEXTURE_FORMATS: *num_written = validators_->compressed_texture_format.GetValues().size(); if (params) { for (GLint ii = 0; ii < *num_written; ++ii) { params[ii] = validators_->compressed_texture_format.GetValues()[ii]; } } return true; case GL_NUM_COMPRESSED_TEXTURE_FORMATS: *num_written = 1; if (params) { *params = validators_->compressed_texture_format.GetValues().size(); } return true; case GL_NUM_SHADER_BINARY_FORMATS: *num_written = 1; if (params) { *params = validators_->shader_binary_format.GetValues().size(); } return true; case GL_SHADER_BINARY_FORMATS: *num_written = validators_->shader_binary_format.GetValues().size(); if (params) { for (GLint ii = 0; ii < *num_written; ++ii) { params[ii] = validators_->shader_binary_format.GetValues()[ii]; } } return true; case GL_SHADER_COMPILER: *num_written = 1; if (params) { *params = GL_TRUE; } return true; case GL_ARRAY_BUFFER_BINDING: *num_written = 1; if (params) { *params = GetClientId( buffer_manager(), state_.bound_array_buffer.get()); } return true; case GL_ELEMENT_ARRAY_BUFFER_BINDING: *num_written = 1; if (params) { *params = GetClientId( buffer_manager(), state_.vertex_attrib_manager->element_array_buffer()); } return true; case GL_COPY_READ_BUFFER_BINDING: *num_written = 1; if (params) { *params = GetClientId( buffer_manager(), state_.bound_copy_read_buffer.get()); } return true; case GL_COPY_WRITE_BUFFER_BINDING: *num_written = 1; if (params) { *params = GetClientId( buffer_manager(), state_.bound_copy_write_buffer.get()); } return true; case GL_PIXEL_PACK_BUFFER_BINDING: *num_written = 1; if (params) { *params = GetClientId( buffer_manager(), state_.bound_pixel_pack_buffer.get()); } return true; case GL_PIXEL_UNPACK_BUFFER_BINDING: *num_written = 1; if (params) { *params = GetClientId( buffer_manager(), state_.bound_pixel_unpack_buffer.get()); } return true; case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: *num_written = 1; if (params) { *params = GetClientId( buffer_manager(), state_.bound_transform_feedback_buffer.get()); } return true; case GL_UNIFORM_BUFFER_BINDING: *num_written = 1; if (params) { *params = GetClientId( buffer_manager(), state_.bound_uniform_buffer.get()); } return true; case GL_FRAMEBUFFER_BINDING: // case GL_DRAW_FRAMEBUFFER_BINDING_EXT: (same as GL_FRAMEBUFFER_BINDING) *num_written = 1; if (params) { *params = GetClientId( framebuffer_manager(), GetFramebufferInfoForTarget(GL_FRAMEBUFFER)); } return true; case GL_READ_FRAMEBUFFER_BINDING_EXT: *num_written = 1; if (params) { *params = GetClientId( framebuffer_manager(), GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER_EXT)); } return true; case GL_RENDERBUFFER_BINDING: *num_written = 1; if (params) { Renderbuffer* renderbuffer = GetRenderbufferInfoForTarget(GL_RENDERBUFFER); if (renderbuffer) { *params = renderbuffer->client_id(); } else { *params = 0; } } return true; case GL_CURRENT_PROGRAM: *num_written = 1; if (params) { *params = GetClientId(program_manager(), state_.current_program.get()); } return true; case GL_VERTEX_ARRAY_BINDING_OES: *num_written = 1; if (params) { if (state_.vertex_attrib_manager.get() != state_.default_vertex_attrib_manager.get()) { GLuint client_id = 0; vertex_array_manager_->GetClientId( state_.vertex_attrib_manager->service_id(), &client_id); *params = client_id; } else { *params = 0; } } return true; case GL_TEXTURE_BINDING_2D: *num_written = 1; if (params) { TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; if (unit.bound_texture_2d.get()) { *params = unit.bound_texture_2d->client_id(); } else { *params = 0; } } return true; case GL_TEXTURE_BINDING_CUBE_MAP: *num_written = 1; if (params) { TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; if (unit.bound_texture_cube_map.get()) { *params = unit.bound_texture_cube_map->client_id(); } else { *params = 0; } } return true; case GL_TEXTURE_BINDING_EXTERNAL_OES: *num_written = 1; if (params) { TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; if (unit.bound_texture_external_oes.get()) { *params = unit.bound_texture_external_oes->client_id(); } else { *params = 0; } } return true; case GL_TEXTURE_BINDING_RECTANGLE_ARB: *num_written = 1; if (params) { TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; if (unit.bound_texture_rectangle_arb.get()) { *params = unit.bound_texture_rectangle_arb->client_id(); } else { *params = 0; } } return true; case GL_UNPACK_FLIP_Y_CHROMIUM: *num_written = 1; if (params) { params[0] = unpack_flip_y_; } return true; case GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM: *num_written = 1; if (params) { params[0] = unpack_premultiply_alpha_; } return true; case GL_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM: *num_written = 1; if (params) { params[0] = unpack_unpremultiply_alpha_; } return true; case GL_BIND_GENERATES_RESOURCE_CHROMIUM: *num_written = 1; if (params) { params[0] = group_->bind_generates_resource() ? 1 : 0; } return true; default: if (pname >= GL_DRAW_BUFFER0_ARB && pname < GL_DRAW_BUFFER0_ARB + group_->max_draw_buffers()) { *num_written = 1; if (params) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_FRAMEBUFFER); if (framebuffer) { params[0] = framebuffer->GetDrawBuffer(pname); } else { // backbuffer if (pname == GL_DRAW_BUFFER0_ARB) params[0] = group_->draw_buffer(); else params[0] = GL_NONE; } } return true; } *num_written = util_.GLGetNumValuesReturned(pname); return false; } } bool GLES2DecoderImpl::GetNumValuesReturnedForGLGet( GLenum pname, GLsizei* num_values) { if (state_.GetStateAsGLint(pname, NULL, num_values)) { return true; } return GetHelper(pname, NULL, num_values); } GLenum GLES2DecoderImpl::AdjustGetPname(GLenum pname) { if (GL_MAX_SAMPLES == pname && features().use_img_for_multisampled_render_to_texture) { return GL_MAX_SAMPLES_IMG; } return pname; } void GLES2DecoderImpl::DoGetBooleanv(GLenum pname, GLboolean* params) { DCHECK(params); GLsizei num_written = 0; if (GetNumValuesReturnedForGLGet(pname, &num_written)) { scoped_ptr values(new GLint[num_written]); if (!state_.GetStateAsGLint(pname, values.get(), &num_written)) { GetHelper(pname, values.get(), &num_written); } for (GLsizei ii = 0; ii < num_written; ++ii) { params[ii] = static_cast(values[ii]); } } else { pname = AdjustGetPname(pname); glGetBooleanv(pname, params); } } void GLES2DecoderImpl::DoGetFloatv(GLenum pname, GLfloat* params) { DCHECK(params); GLsizei num_written = 0; if (!state_.GetStateAsGLfloat(pname, params, &num_written)) { if (GetHelper(pname, NULL, &num_written)) { scoped_ptr values(new GLint[num_written]); GetHelper(pname, values.get(), &num_written); for (GLsizei ii = 0; ii < num_written; ++ii) { params[ii] = static_cast(values[ii]); } } else { pname = AdjustGetPname(pname); glGetFloatv(pname, params); } } } void GLES2DecoderImpl::DoGetInteger64v(GLenum pname, GLint64* params) { DCHECK(params); if (unsafe_es3_apis_enabled()) { switch (pname) { case GL_MAX_ELEMENT_INDEX: { if (feature_info_->gl_version_info().IsAtLeastGLES(3, 0) || feature_info_->gl_version_info().IsAtLeastGL(4, 3)) { glGetInteger64v(GL_MAX_ELEMENT_INDEX, params); } else { // Assume that desktop GL implementations can generally support // 32-bit indices. if (params) { *params = std::numeric_limits::max(); } } return; } } } pname = AdjustGetPname(pname); glGetInteger64v(pname, params); } void GLES2DecoderImpl::DoGetIntegerv(GLenum pname, GLint* params) { DCHECK(params); GLsizei num_written; if (!state_.GetStateAsGLint(pname, params, &num_written) && !GetHelper(pname, params, &num_written)) { pname = AdjustGetPname(pname); glGetIntegerv(pname, params); } } void GLES2DecoderImpl::DoGetProgramiv( GLuint program_id, GLenum pname, GLint* params) { Program* program = GetProgramInfoNotShader(program_id, "glGetProgramiv"); if (!program) { return; } program->GetProgramiv(pname, params); } void GLES2DecoderImpl::DoGetBufferParameteriv( GLenum target, GLenum pname, GLint* params) { // Just delegate it. Some validation is actually done before this. buffer_manager()->ValidateAndDoGetBufferParameteriv( &state_, target, pname, params); } void GLES2DecoderImpl::DoBindAttribLocation( GLuint program_id, GLuint index, const char* name) { if (!StringIsValidForGLES(name)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glBindAttribLocation", "Invalid character"); return; } if (ProgramManager::IsInvalidPrefix(name, strlen(name))) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBindAttribLocation", "reserved prefix"); return; } if (index >= group_->max_vertex_attribs()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glBindAttribLocation", "index out of range"); return; } Program* program = GetProgramInfoNotShader( program_id, "glBindAttribLocation"); if (!program) { return; } // At this point, the program's shaders may not be translated yet, // therefore, we may not find the hashed attribute name. // glBindAttribLocation call with original name is useless. // So instead, we should simply cache the binding, and then call // Program::ExecuteBindAttribLocationCalls() right before link. program->SetAttribLocationBinding(name, static_cast(index)); // TODO(zmo): Get rid of the following glBindAttribLocation call. glBindAttribLocation(program->service_id(), index, name); } error::Error GLES2DecoderImpl::HandleBindAttribLocationBucket( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::BindAttribLocationBucket& c = *static_cast(cmd_data); GLuint program = static_cast(c.program); GLuint index = static_cast(c.index); Bucket* bucket = GetBucket(c.name_bucket_id); if (!bucket || bucket->size() == 0) { return error::kInvalidArguments; } std::string name_str; if (!bucket->GetAsString(&name_str)) { return error::kInvalidArguments; } DoBindAttribLocation(program, index, name_str.c_str()); return error::kNoError; } void GLES2DecoderImpl::DoBindUniformLocationCHROMIUM( GLuint program_id, GLint location, const char* name) { if (!StringIsValidForGLES(name)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glBindUniformLocationCHROMIUM", "Invalid character"); return; } if (ProgramManager::IsInvalidPrefix(name, strlen(name))) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBindUniformLocationCHROMIUM", "reserved prefix"); return; } if (location < 0 || static_cast(location) >= (group_->max_fragment_uniform_vectors() + group_->max_vertex_uniform_vectors()) * 4) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glBindUniformLocationCHROMIUM", "location out of range"); return; } Program* program = GetProgramInfoNotShader( program_id, "glBindUniformLocationCHROMIUM"); if (!program) { return; } if (!program->SetUniformLocationBinding(name, location)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glBindUniformLocationCHROMIUM", "location out of range"); } } error::Error GLES2DecoderImpl::HandleBindUniformLocationCHROMIUMBucket( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::BindUniformLocationCHROMIUMBucket& c = *static_cast( cmd_data); GLuint program = static_cast(c.program); GLint location = static_cast(c.location); Bucket* bucket = GetBucket(c.name_bucket_id); if (!bucket || bucket->size() == 0) { return error::kInvalidArguments; } std::string name_str; if (!bucket->GetAsString(&name_str)) { return error::kInvalidArguments; } DoBindUniformLocationCHROMIUM(program, location, name_str.c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleDeleteShader(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::DeleteShader& c = *static_cast(cmd_data); GLuint client_id = c.shader; if (client_id) { Shader* shader = GetShader(client_id); if (shader) { if (!shader->IsDeleted()) { shader_manager()->Delete(shader); } } else { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glDeleteShader", "unknown shader"); } } return error::kNoError; } error::Error GLES2DecoderImpl::HandleDeleteProgram(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::DeleteProgram& c = *static_cast(cmd_data); GLuint client_id = c.program; if (client_id) { Program* program = GetProgram(client_id); if (program) { if (!program->IsDeleted()) { program_manager()->MarkAsDeleted(shader_manager(), program); } } else { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glDeleteProgram", "unknown program"); } } return error::kNoError; } error::Error GLES2DecoderImpl::DoClear(GLbitfield mask) { DCHECK(!ShouldDeferDraws()); if (CheckBoundFramebuffersValid("glClear")) { ApplyDirtyState(); ScopedRenderTo do_render(framebuffer_state_.bound_draw_framebuffer.get()); if (workarounds().gl_clear_broken) { ScopedGLErrorSuppressor suppressor("GLES2DecoderImpl::ClearWorkaround", GetErrorState()); if (!BoundFramebufferHasDepthAttachment()) mask &= ~GL_DEPTH_BUFFER_BIT; if (!BoundFramebufferHasStencilAttachment()) mask &= ~GL_STENCIL_BUFFER_BIT; clear_framebuffer_blit_->ClearFramebuffer( this, GetBoundReadFrameBufferSize(), mask, state_.color_clear_red, state_.color_clear_green, state_.color_clear_blue, state_.color_clear_alpha, state_.depth_clear, state_.stencil_clear); return error::kNoError; } glClear(mask); } return error::kNoError; } void GLES2DecoderImpl::DoFramebufferRenderbuffer( GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint client_renderbuffer_id) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); if (!framebuffer) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glFramebufferRenderbuffer", "no framebuffer bound"); return; } GLuint service_id = 0; Renderbuffer* renderbuffer = NULL; if (client_renderbuffer_id) { renderbuffer = GetRenderbuffer(client_renderbuffer_id); if (!renderbuffer) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glFramebufferRenderbuffer", "unknown renderbuffer"); return; } service_id = renderbuffer->service_id(); } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glFramebufferRenderbuffer"); glFramebufferRenderbufferEXT( target, attachment, renderbuffertarget, service_id); GLenum error = LOCAL_PEEK_GL_ERROR("glFramebufferRenderbuffer"); if (error == GL_NO_ERROR) { framebuffer->AttachRenderbuffer(attachment, renderbuffer); } if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) { framebuffer_state_.clear_state_dirty = true; } OnFboChanged(); } void GLES2DecoderImpl::DoDisable(GLenum cap) { if (SetCapabilityState(cap, false)) { glDisable(cap); } } void GLES2DecoderImpl::DoEnable(GLenum cap) { if (SetCapabilityState(cap, true)) { glEnable(cap); } } void GLES2DecoderImpl::DoDepthRangef(GLclampf znear, GLclampf zfar) { state_.z_near = std::min(1.0f, std::max(0.0f, znear)); state_.z_far = std::min(1.0f, std::max(0.0f, zfar)); glDepthRange(znear, zfar); } void GLES2DecoderImpl::DoSampleCoverage(GLclampf value, GLboolean invert) { state_.sample_coverage_value = std::min(1.0f, std::max(0.0f, value)); state_.sample_coverage_invert = (invert != 0); glSampleCoverage(state_.sample_coverage_value, invert); } // Assumes framebuffer is complete. void GLES2DecoderImpl::ClearUnclearedAttachments( GLenum target, Framebuffer* framebuffer) { if (target == GL_READ_FRAMEBUFFER_EXT) { // bind this to the DRAW point, clear then bind back to READ // TODO(gman): I don't think there is any guarantee that an FBO that // is complete on the READ attachment will be complete as a DRAW // attachment. glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, framebuffer->service_id()); } GLbitfield clear_bits = 0; if (framebuffer->HasUnclearedColorAttachments()) { glClearColor( 0.0f, 0.0f, 0.0f, (GLES2Util::GetChannelsForFormat( framebuffer->GetColorAttachmentFormat()) & 0x0008) != 0 ? 0.0f : 1.0f); state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); clear_bits |= GL_COLOR_BUFFER_BIT; if (feature_info_->feature_flags().ext_draw_buffers) framebuffer->PrepareDrawBuffersForClear(); } if (framebuffer->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT) || framebuffer->HasUnclearedAttachment(GL_DEPTH_STENCIL_ATTACHMENT)) { glClearStencil(0); state_.SetDeviceStencilMaskSeparate(GL_FRONT, kDefaultStencilMask); state_.SetDeviceStencilMaskSeparate(GL_BACK, kDefaultStencilMask); clear_bits |= GL_STENCIL_BUFFER_BIT; } if (framebuffer->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT) || framebuffer->HasUnclearedAttachment(GL_DEPTH_STENCIL_ATTACHMENT)) { glClearDepth(1.0f); state_.SetDeviceDepthMask(GL_TRUE); clear_bits |= GL_DEPTH_BUFFER_BIT; } state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); glClear(clear_bits); if ((clear_bits & GL_COLOR_BUFFER_BIT) != 0 && feature_info_->feature_flags().ext_draw_buffers) framebuffer->RestoreDrawBuffersAfterClear(); framebuffer_manager()->MarkAttachmentsAsCleared( framebuffer, renderbuffer_manager(), texture_manager()); RestoreClearState(); if (target == GL_READ_FRAMEBUFFER_EXT) { glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, framebuffer->service_id()); Framebuffer* draw_framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); GLuint service_id = draw_framebuffer ? draw_framebuffer->service_id() : GetBackbufferServiceId(); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, service_id); } } void GLES2DecoderImpl::RestoreClearState() { framebuffer_state_.clear_state_dirty = true; glClearColor( state_.color_clear_red, state_.color_clear_green, state_.color_clear_blue, state_.color_clear_alpha); glClearStencil(state_.stencil_clear); glClearDepth(state_.depth_clear); if (state_.enable_flags.scissor_test) { state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, true); } } GLenum GLES2DecoderImpl::DoCheckFramebufferStatus(GLenum target) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); if (!framebuffer) { return GL_FRAMEBUFFER_COMPLETE; } GLenum completeness = framebuffer->IsPossiblyComplete(); if (completeness != GL_FRAMEBUFFER_COMPLETE) { return completeness; } return framebuffer->GetStatus(texture_manager(), target); } void GLES2DecoderImpl::DoFramebufferTexture2D( GLenum target, GLenum attachment, GLenum textarget, GLuint client_texture_id, GLint level) { DoFramebufferTexture2DCommon( "glFramebufferTexture2D", target, attachment, textarget, client_texture_id, level, 0); } void GLES2DecoderImpl::DoFramebufferTexture2DMultisample( GLenum target, GLenum attachment, GLenum textarget, GLuint client_texture_id, GLint level, GLsizei samples) { DoFramebufferTexture2DCommon( "glFramebufferTexture2DMultisample", target, attachment, textarget, client_texture_id, level, samples); } void GLES2DecoderImpl::DoFramebufferTexture2DCommon( const char* name, GLenum target, GLenum attachment, GLenum textarget, GLuint client_texture_id, GLint level, GLsizei samples) { if (samples > renderbuffer_manager()->max_samples()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glFramebufferTexture2DMultisample", "samples too large"); return; } Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); if (!framebuffer) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, name, "no framebuffer bound."); return; } GLuint service_id = 0; TextureRef* texture_ref = NULL; if (client_texture_id) { texture_ref = GetTexture(client_texture_id); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, name, "unknown texture_ref"); return; } service_id = texture_ref->service_id(); } if (!texture_manager()->ValidForTarget(textarget, level, 0, 0, 1)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, name, "level out of range"); return; } if (texture_ref) DoWillUseTexImageIfNeeded(texture_ref->texture(), textarget); LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(name); if (0 == samples) { glFramebufferTexture2DEXT(target, attachment, textarget, service_id, level); } else { if (features().use_img_for_multisampled_render_to_texture) { glFramebufferTexture2DMultisampleIMG(target, attachment, textarget, service_id, level, samples); } else { glFramebufferTexture2DMultisampleEXT(target, attachment, textarget, service_id, level, samples); } } GLenum error = LOCAL_PEEK_GL_ERROR(name); if (error == GL_NO_ERROR) { framebuffer->AttachTexture(attachment, texture_ref, textarget, level, samples); } if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) { framebuffer_state_.clear_state_dirty = true; } if (texture_ref) DoDidUseTexImageIfNeeded(texture_ref->texture(), textarget); OnFboChanged(); } void GLES2DecoderImpl::DoFramebufferTextureLayer( GLenum target, GLenum attachment, GLuint client_texture_id, GLint level, GLint layer) { // TODO(zmo): Unsafe ES3 API, missing states update. GLuint service_id = 0; TextureRef* texture_ref = NULL; if (client_texture_id) { texture_ref = GetTexture(client_texture_id); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glFramebufferTextureLayer", "unknown texture_ref"); return; } service_id = texture_ref->service_id(); } glFramebufferTextureLayer(target, attachment, service_id, level, layer); } void GLES2DecoderImpl::DoGetFramebufferAttachmentParameteriv( GLenum target, GLenum attachment, GLenum pname, GLint* params) { Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); if (!framebuffer) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glGetFramebufferAttachmentParameteriv", "no framebuffer bound"); return; } if (pname == GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) { const Framebuffer::Attachment* attachment_object = framebuffer->GetAttachment(attachment); *params = attachment_object ? attachment_object->object_name() : 0; } else { if (pname == GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT && features().use_img_for_multisampled_render_to_texture) { pname = GL_TEXTURE_SAMPLES_IMG; } glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, params); } } void GLES2DecoderImpl::DoGetRenderbufferParameteriv( GLenum target, GLenum pname, GLint* params) { Renderbuffer* renderbuffer = GetRenderbufferInfoForTarget(GL_RENDERBUFFER); if (!renderbuffer) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glGetRenderbufferParameteriv", "no renderbuffer bound"); return; } EnsureRenderbufferBound(); switch (pname) { case GL_RENDERBUFFER_INTERNAL_FORMAT: *params = renderbuffer->internal_format(); break; case GL_RENDERBUFFER_WIDTH: *params = renderbuffer->width(); break; case GL_RENDERBUFFER_HEIGHT: *params = renderbuffer->height(); break; case GL_RENDERBUFFER_SAMPLES_EXT: if (features().use_img_for_multisampled_render_to_texture) { glGetRenderbufferParameterivEXT(target, GL_RENDERBUFFER_SAMPLES_IMG, params); } else { glGetRenderbufferParameterivEXT(target, GL_RENDERBUFFER_SAMPLES_EXT, params); } default: glGetRenderbufferParameterivEXT(target, pname, params); break; } } void GLES2DecoderImpl::DoBlitFramebufferCHROMIUM( GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { DCHECK(!ShouldDeferReads() && !ShouldDeferDraws()); if (!CheckBoundFramebuffersValid("glBlitFramebufferCHROMIUM")) { return; } state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); ScopedRenderTo do_render(framebuffer_state_.bound_draw_framebuffer.get()); BlitFramebufferHelper( srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, state_.enable_flags.scissor_test); } void GLES2DecoderImpl::EnsureRenderbufferBound() { if (!state_.bound_renderbuffer_valid) { state_.bound_renderbuffer_valid = true; glBindRenderbufferEXT(GL_RENDERBUFFER, state_.bound_renderbuffer.get() ? state_.bound_renderbuffer->service_id() : 0); } } void GLES2DecoderImpl::RenderbufferStorageMultisampleHelper( const FeatureInfo* feature_info, GLenum target, GLsizei samples, GLenum internal_format, GLsizei width, GLsizei height) { // TODO(sievers): This could be resolved at the GL binding level, but the // binding process is currently a bit too 'brute force'. if (feature_info->gl_version_info().is_angle) { glRenderbufferStorageMultisampleANGLE( target, samples, internal_format, width, height); } else if (feature_info->feature_flags().use_core_framebuffer_multisample) { glRenderbufferStorageMultisample( target, samples, internal_format, width, height); } else { glRenderbufferStorageMultisampleEXT( target, samples, internal_format, width, height); } } void GLES2DecoderImpl::BlitFramebufferHelper(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { // TODO(sievers): This could be resolved at the GL binding level, but the // binding process is currently a bit too 'brute force'. if (feature_info_->gl_version_info().is_angle) { glBlitFramebufferANGLE( srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } else if (feature_info_->feature_flags().use_core_framebuffer_multisample) { glBlitFramebuffer( srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } else { glBlitFramebufferEXT( srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } } bool GLES2DecoderImpl::ValidateRenderbufferStorageMultisample( GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { if (samples > renderbuffer_manager()->max_samples()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glRenderbufferStorageMultisample", "samples too large"); return false; } if (width > renderbuffer_manager()->max_renderbuffer_size() || height > renderbuffer_manager()->max_renderbuffer_size()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glRenderbufferStorageMultisample", "dimensions too large"); return false; } uint32 estimated_size = 0; if (!renderbuffer_manager()->ComputeEstimatedRenderbufferSize( width, height, samples, internalformat, &estimated_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glRenderbufferStorageMultisample", "dimensions too large"); return false; } if (!EnsureGPUMemoryAvailable(estimated_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glRenderbufferStorageMultisample", "out of memory"); return false; } return true; } void GLES2DecoderImpl::DoRenderbufferStorageMultisampleCHROMIUM( GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { Renderbuffer* renderbuffer = GetRenderbufferInfoForTarget(GL_RENDERBUFFER); if (!renderbuffer) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glRenderbufferStorageMultisampleCHROMIUM", "no renderbuffer bound"); return; } if (!ValidateRenderbufferStorageMultisample( samples, internalformat, width, height)) { return; } EnsureRenderbufferBound(); GLenum impl_format = renderbuffer_manager()->InternalRenderbufferFormatToImplFormat( internalformat); LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER( "glRenderbufferStorageMultisampleCHROMIUM"); RenderbufferStorageMultisampleHelper( feature_info_.get(), target, samples, impl_format, width, height); GLenum error = LOCAL_PEEK_GL_ERROR("glRenderbufferStorageMultisampleCHROMIUM"); if (error == GL_NO_ERROR) { if (workarounds().validate_multisample_buffer_allocation) { if (!VerifyMultisampleRenderbufferIntegrity( renderbuffer->service_id(), impl_format)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glRenderbufferStorageMultisampleCHROMIUM", "out of memory"); return; } } // TODO(gman): If renderbuffers tracked which framebuffers they were // attached to we could just mark those framebuffers as not complete. framebuffer_manager()->IncFramebufferStateChangeCount(); renderbuffer_manager()->SetInfo( renderbuffer, samples, internalformat, width, height); } } // This is the handler for multisampled_render_to_texture extensions. void GLES2DecoderImpl::DoRenderbufferStorageMultisampleEXT( GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { Renderbuffer* renderbuffer = GetRenderbufferInfoForTarget(GL_RENDERBUFFER); if (!renderbuffer) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glRenderbufferStorageMultisampleEXT", "no renderbuffer bound"); return; } if (!ValidateRenderbufferStorageMultisample( samples, internalformat, width, height)) { return; } EnsureRenderbufferBound(); GLenum impl_format = renderbuffer_manager()->InternalRenderbufferFormatToImplFormat( internalformat); LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glRenderbufferStorageMultisampleEXT"); if (features().use_img_for_multisampled_render_to_texture) { glRenderbufferStorageMultisampleIMG( target, samples, impl_format, width, height); } else { glRenderbufferStorageMultisampleEXT( target, samples, impl_format, width, height); } GLenum error = LOCAL_PEEK_GL_ERROR("glRenderbufferStorageMultisampleEXT"); if (error == GL_NO_ERROR) { // TODO(gman): If renderbuffers tracked which framebuffers they were // attached to we could just mark those framebuffers as not complete. framebuffer_manager()->IncFramebufferStateChangeCount(); renderbuffer_manager()->SetInfo( renderbuffer, samples, internalformat, width, height); } } // This function validates the allocation of a multisampled renderbuffer // by clearing it to a key color, blitting the contents to a texture, and // reading back the color to ensure it matches the key. bool GLES2DecoderImpl::VerifyMultisampleRenderbufferIntegrity( GLuint renderbuffer, GLenum format) { // Only validate color buffers. // These formats have been selected because they are very common or are known // to be used by the WebGL backbuffer. If problems are observed with other // color formats they can be added here. switch (format) { case GL_RGB: case GL_RGB8: case GL_RGBA: case GL_RGBA8: break; default: return true; } GLint draw_framebuffer, read_framebuffer; // Cache framebuffer and texture bindings. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer); glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer); if (!validation_texture_) { GLint bound_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &bound_texture); // Create additional resources needed for the verification. glGenTextures(1, &validation_texture_); glGenFramebuffersEXT(1, &validation_fbo_multisample_); glGenFramebuffersEXT(1, &validation_fbo_); // Texture only needs to be 1x1. glBindTexture(GL_TEXTURE_2D, validation_texture_); // TODO(erikchen): When Chrome on Mac is linked against an OSX 10.9+ SDK, a // multisample will fail if the color format of the source and destination // do not match. Here, we assume that the source is GL_RGBA, and make the // destination GL_RGBA. http://crbug.com/484203 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glBindFramebufferEXT(GL_FRAMEBUFFER, validation_fbo_); glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, validation_texture_, 0); glBindTexture(GL_TEXTURE_2D, bound_texture); } glBindFramebufferEXT(GL_FRAMEBUFFER, validation_fbo_multisample_); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); // Cache current state and reset it to the values we require. GLboolean scissor_enabled = false; glGetBooleanv(GL_SCISSOR_TEST, &scissor_enabled); if (scissor_enabled) state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); GLboolean color_mask[4] = {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE}; glGetBooleanv(GL_COLOR_WRITEMASK, color_mask); state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); GLfloat clear_color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; glGetFloatv(GL_COLOR_CLEAR_VALUE, clear_color); glClearColor(1.0f, 0.0f, 1.0f, 1.0f); // Clear the buffer to the desired key color. glClear(GL_COLOR_BUFFER_BIT); // Blit from the multisample buffer to a standard texture. glBindFramebufferEXT(GL_READ_FRAMEBUFFER, validation_fbo_multisample_); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, validation_fbo_); BlitFramebufferHelper( 0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); // Read a pixel from the buffer. glBindFramebufferEXT(GL_FRAMEBUFFER, validation_fbo_); unsigned char pixel[3] = {0, 0, 0}; glReadPixels(0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &pixel); // Detach the renderbuffer. glBindFramebufferEXT(GL_FRAMEBUFFER, validation_fbo_multisample_); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); // Restore cached state. if (scissor_enabled) state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, true); state_.SetDeviceColorMask( color_mask[0], color_mask[1], color_mask[2], color_mask[3]); glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, draw_framebuffer); glBindFramebufferEXT(GL_READ_FRAMEBUFFER, read_framebuffer); // Return true if the pixel matched the desired key color. return (pixel[0] == 0xFF && pixel[1] == 0x00 && pixel[2] == 0xFF); } void GLES2DecoderImpl::DoRenderbufferStorage( GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { Renderbuffer* renderbuffer = GetRenderbufferInfoForTarget(GL_RENDERBUFFER); if (!renderbuffer) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glRenderbufferStorage", "no renderbuffer bound"); return; } if (width > renderbuffer_manager()->max_renderbuffer_size() || height > renderbuffer_manager()->max_renderbuffer_size()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glRenderbufferStorage", "dimensions too large"); return; } uint32 estimated_size = 0; if (!renderbuffer_manager()->ComputeEstimatedRenderbufferSize( width, height, 1, internalformat, &estimated_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glRenderbufferStorage", "dimensions too large"); return; } if (!EnsureGPUMemoryAvailable(estimated_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glRenderbufferStorage", "out of memory"); return; } EnsureRenderbufferBound(); LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glRenderbufferStorage"); glRenderbufferStorageEXT( target, renderbuffer_manager()->InternalRenderbufferFormatToImplFormat( internalformat), width, height); GLenum error = LOCAL_PEEK_GL_ERROR("glRenderbufferStorage"); if (error == GL_NO_ERROR) { // TODO(gman): If tetxures tracked which framebuffers they were attached to // we could just mark those framebuffers as not complete. framebuffer_manager()->IncFramebufferStateChangeCount(); renderbuffer_manager()->SetInfo( renderbuffer, 1, internalformat, width, height); } } void GLES2DecoderImpl::DoLinkProgram(GLuint program_id) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoLinkProgram"); Program* program = GetProgramInfoNotShader( program_id, "glLinkProgram"); if (!program) { return; } LogClientServiceForInfo(program, program_id, "glLinkProgram"); if (program->Link(shader_manager(), workarounds().count_all_in_varyings_packing ? Program::kCountAll : Program::kCountOnlyStaticallyUsed, shader_cache_callback_)) { if (program == state_.current_program.get()) { if (workarounds().use_current_program_after_successful_link) glUseProgram(program->service_id()); if (workarounds().clear_uniforms_before_first_program_use) program_manager()->ClearUniforms(program); } } // LinkProgram can be very slow. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); } void GLES2DecoderImpl::DoSamplerParameterfv( GLuint sampler, GLenum pname, const GLfloat* params) { DCHECK(params); glSamplerParameterf(sampler, pname, params[0]); } void GLES2DecoderImpl::DoSamplerParameteriv( GLuint sampler, GLenum pname, const GLint* params) { DCHECK(params); glSamplerParameteri(sampler, pname, params[0]); } void GLES2DecoderImpl::DoTexParameterf( GLenum target, GLenum pname, GLfloat param) { TextureRef* texture = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexParameterf", "unknown texture"); return; } texture_manager()->SetParameterf( "glTexParameterf", GetErrorState(), texture, pname, param); } void GLES2DecoderImpl::DoTexParameteri( GLenum target, GLenum pname, GLint param) { TextureRef* texture = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexParameteri", "unknown texture"); return; } texture_manager()->SetParameteri( "glTexParameteri", GetErrorState(), texture, pname, param); } void GLES2DecoderImpl::DoTexParameterfv( GLenum target, GLenum pname, const GLfloat* params) { TextureRef* texture = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexParameterfv", "unknown texture"); return; } texture_manager()->SetParameterf( "glTexParameterfv", GetErrorState(), texture, pname, *params); } void GLES2DecoderImpl::DoTexParameteriv( GLenum target, GLenum pname, const GLint* params) { TextureRef* texture = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glTexParameteriv", "unknown texture"); return; } texture_manager()->SetParameteri( "glTexParameteriv", GetErrorState(), texture, pname, *params); } bool GLES2DecoderImpl::CheckCurrentValuebuffer(const char* function_name) { if (!state_.bound_valuebuffer.get()) { // There is no valuebuffer bound LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "no valuebuffer in use"); return false; } return true; } bool GLES2DecoderImpl::CheckCurrentValuebufferForSubscription( GLenum subscription, const char* function_name) { if (!CheckCurrentValuebuffer(function_name)) { return false; } if (!state_.bound_valuebuffer.get()->IsSubscribed(subscription)) { // The valuebuffer is not subscribed to the target LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "valuebuffer is not subscribed"); return false; } return true; } bool GLES2DecoderImpl::CheckSubscriptionTarget(GLint location, GLenum subscription, const char* function_name) { if (!CheckCurrentProgramForUniform(location, function_name)) { return false; } GLint real_location = -1; GLint array_index = -1; const Program::UniformInfo* info = state_.current_program->GetUniformInfoByFakeLocation( location, &real_location, &array_index); if (!info) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "unknown location"); return false; } if ((ValuebufferManager::ApiTypeForSubscriptionTarget(subscription) & info->accepts_api_type) == 0) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "wrong type for subscription"); return false; } return true; } bool GLES2DecoderImpl::CheckCurrentProgram(const char* function_name) { if (!state_.current_program.get()) { // The program does not exist. LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "no program in use"); return false; } if (!state_.current_program->InUse()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "program not linked"); return false; } return true; } bool GLES2DecoderImpl::CheckCurrentProgramForUniform( GLint location, const char* function_name) { if (!CheckCurrentProgram(function_name)) { return false; } return location != -1; } bool GLES2DecoderImpl::CheckDrawingFeedbackLoops() { Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_FRAMEBUFFER); if (!framebuffer) return false; const Framebuffer::Attachment* attachment = framebuffer->GetAttachment(GL_COLOR_ATTACHMENT0); if (!attachment) return false; DCHECK(state_.current_program.get()); const Program::SamplerIndices& sampler_indices = state_.current_program->sampler_indices(); for (size_t ii = 0; ii < sampler_indices.size(); ++ii) { const Program::UniformInfo* uniform_info = state_.current_program->GetUniformInfo(sampler_indices[ii]); DCHECK(uniform_info); if (uniform_info->type != GL_SAMPLER_2D) continue; for (size_t jj = 0; jj < uniform_info->texture_units.size(); ++jj) { GLuint texture_unit_index = uniform_info->texture_units[jj]; if (texture_unit_index >= state_.texture_units.size()) continue; TextureUnit& texture_unit = state_.texture_units[texture_unit_index]; TextureRef* texture_ref = texture_unit.GetInfoForSamplerType(GL_SAMPLER_2D).get(); if (attachment->IsTexture(texture_ref)) return true; } } return false; } bool GLES2DecoderImpl::CheckUniformForApiType( const Program::UniformInfo* info, const char* function_name, Program::UniformApiType api_type) { DCHECK(info); if ((api_type & info->accepts_api_type) == 0) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "wrong uniform function for type"); return false; } return true; } bool GLES2DecoderImpl::PrepForSetUniformByLocation( GLint fake_location, const char* function_name, Program::UniformApiType api_type, GLint* real_location, GLenum* type, GLsizei* count) { DCHECK(type); DCHECK(count); DCHECK(real_location); if (!CheckCurrentProgramForUniform(fake_location, function_name)) { return false; } GLint array_index = -1; const Program::UniformInfo* info = state_.current_program->GetUniformInfoByFakeLocation( fake_location, real_location, &array_index); if (!info) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "unknown location"); return false; } if (!CheckUniformForApiType(info, function_name, api_type)) { return false; } if (*count > 1 && !info->is_array) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "count > 1 for non-array"); return false; } *count = std::min(info->size - array_index, *count); if (*count <= 0) { return false; } *type = info->type; return true; } void GLES2DecoderImpl::DoUniform1i(GLint fake_location, GLint v0) { GLenum type = 0; GLsizei count = 1; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform1i", Program::kUniform1i, &real_location, &type, &count)) { return; } if (!state_.current_program->SetSamplers( state_.texture_units.size(), fake_location, 1, &v0)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glUniform1i", "texture unit out of range"); return; } glUniform1i(real_location, v0); } void GLES2DecoderImpl::DoUniform1iv( GLint fake_location, GLsizei count, const GLint *value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform1iv", Program::kUniform1i, &real_location, &type, &count)) { return; } if (type == GL_SAMPLER_2D || type == GL_SAMPLER_2D_RECT_ARB || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_EXTERNAL_OES) { if (!state_.current_program->SetSamplers( state_.texture_units.size(), fake_location, count, value)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glUniform1iv", "texture unit out of range"); return; } } glUniform1iv(real_location, count, value); } void GLES2DecoderImpl::DoUniform1fv( GLint fake_location, GLsizei count, const GLfloat* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform1fv", Program::kUniform1f, &real_location, &type, &count)) { return; } if (type == GL_BOOL) { scoped_ptr temp(new GLint[count]); for (GLsizei ii = 0; ii < count; ++ii) { temp[ii] = static_cast(value[ii] != 0.0f); } DoUniform1iv(real_location, count, temp.get()); } else { glUniform1fv(real_location, count, value); } } void GLES2DecoderImpl::DoUniform2fv( GLint fake_location, GLsizei count, const GLfloat* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform2fv", Program::kUniform2f, &real_location, &type, &count)) { return; } if (type == GL_BOOL_VEC2) { GLsizei num_values = count * 2; scoped_ptr temp(new GLint[num_values]); for (GLsizei ii = 0; ii < num_values; ++ii) { temp[ii] = static_cast(value[ii] != 0.0f); } glUniform2iv(real_location, count, temp.get()); } else { glUniform2fv(real_location, count, value); } } void GLES2DecoderImpl::DoUniform3fv( GLint fake_location, GLsizei count, const GLfloat* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform3fv", Program::kUniform3f, &real_location, &type, &count)) { return; } if (type == GL_BOOL_VEC3) { GLsizei num_values = count * 3; scoped_ptr temp(new GLint[num_values]); for (GLsizei ii = 0; ii < num_values; ++ii) { temp[ii] = static_cast(value[ii] != 0.0f); } glUniform3iv(real_location, count, temp.get()); } else { glUniform3fv(real_location, count, value); } } void GLES2DecoderImpl::DoUniform4fv( GLint fake_location, GLsizei count, const GLfloat* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform4fv", Program::kUniform4f, &real_location, &type, &count)) { return; } if (type == GL_BOOL_VEC4) { GLsizei num_values = count * 4; scoped_ptr temp(new GLint[num_values]); for (GLsizei ii = 0; ii < num_values; ++ii) { temp[ii] = static_cast(value[ii] != 0.0f); } glUniform4iv(real_location, count, temp.get()); } else { glUniform4fv(real_location, count, value); } } void GLES2DecoderImpl::DoUniform2iv( GLint fake_location, GLsizei count, const GLint* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform2iv", Program::kUniform2i, &real_location, &type, &count)) { return; } glUniform2iv(real_location, count, value); } void GLES2DecoderImpl::DoUniform3iv( GLint fake_location, GLsizei count, const GLint* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform3iv", Program::kUniform3i, &real_location, &type, &count)) { return; } glUniform3iv(real_location, count, value); } void GLES2DecoderImpl::DoUniform4iv( GLint fake_location, GLsizei count, const GLint* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniform4iv", Program::kUniform4i, &real_location, &type, &count)) { return; } glUniform4iv(real_location, count, value); } void GLES2DecoderImpl::DoUniformMatrix2fv( GLint fake_location, GLsizei count, GLboolean transpose, const GLfloat* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniformMatrix2fv", Program::kUniformMatrix2f, &real_location, &type, &count)) { return; } glUniformMatrix2fv(real_location, count, transpose, value); } void GLES2DecoderImpl::DoUniformMatrix3fv( GLint fake_location, GLsizei count, GLboolean transpose, const GLfloat* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniformMatrix3fv", Program::kUniformMatrix3f, &real_location, &type, &count)) { return; } glUniformMatrix3fv(real_location, count, transpose, value); } void GLES2DecoderImpl::DoUniformMatrix4fv( GLint fake_location, GLsizei count, GLboolean transpose, const GLfloat* value) { GLenum type = 0; GLint real_location = -1; if (!PrepForSetUniformByLocation(fake_location, "glUniformMatrix4fv", Program::kUniformMatrix4f, &real_location, &type, &count)) { return; } glUniformMatrix4fv(real_location, count, transpose, value); } void GLES2DecoderImpl::DoUseProgram(GLuint program_id) { GLuint service_id = 0; Program* program = NULL; if (program_id) { program = GetProgramInfoNotShader(program_id, "glUseProgram"); if (!program) { return; } if (!program->IsValid()) { // Program was not linked successfully. (ie, glLinkProgram) LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glUseProgram", "program not linked"); return; } service_id = program->service_id(); } if (state_.current_program.get()) { program_manager()->UnuseProgram(shader_manager(), state_.current_program.get()); } state_.current_program = program; LogClientServiceMapping("glUseProgram", program_id, service_id); glUseProgram(service_id); if (state_.current_program.get()) { program_manager()->UseProgram(state_.current_program.get()); if (workarounds().clear_uniforms_before_first_program_use) program_manager()->ClearUniforms(program); } } void GLES2DecoderImpl::RenderWarning( const char* filename, int line, const std::string& msg) { logger_.LogMessage(filename, line, std::string("RENDER WARNING: ") + msg); } void GLES2DecoderImpl::PerformanceWarning( const char* filename, int line, const std::string& msg) { logger_.LogMessage(filename, line, std::string("PERFORMANCE WARNING: ") + msg); } void GLES2DecoderImpl::DoWillUseTexImageIfNeeded( Texture* texture, GLenum textarget) { // Image is already in use if texture is attached to a framebuffer. if (texture && !texture->IsAttachedToFramebuffer()) { gfx::GLImage* image = texture->GetLevelImage(textarget, 0); if (image) { ScopedGLErrorSuppressor suppressor( "GLES2DecoderImpl::DoWillUseTexImageIfNeeded", GetErrorState()); glBindTexture(textarget, texture->service_id()); image->WillUseTexImage(); RestoreCurrentTextureBindings(&state_, textarget); } } } void GLES2DecoderImpl::DoDidUseTexImageIfNeeded( Texture* texture, GLenum textarget) { // Image is still in use if texture is attached to a framebuffer. if (texture && !texture->IsAttachedToFramebuffer()) { gfx::GLImage* image = texture->GetLevelImage(textarget, 0); if (image) { ScopedGLErrorSuppressor suppressor( "GLES2DecoderImpl::DoDidUseTexImageIfNeeded", GetErrorState()); glBindTexture(textarget, texture->service_id()); image->DidUseTexImage(); RestoreCurrentTextureBindings(&state_, textarget); } } } bool GLES2DecoderImpl::PrepareTexturesForRender() { DCHECK(state_.current_program.get()); if (!texture_manager()->HaveUnrenderableTextures() && !texture_manager()->HaveImages()) { return true; } bool textures_set = false; const Program::SamplerIndices& sampler_indices = state_.current_program->sampler_indices(); for (size_t ii = 0; ii < sampler_indices.size(); ++ii) { const Program::UniformInfo* uniform_info = state_.current_program->GetUniformInfo(sampler_indices[ii]); DCHECK(uniform_info); for (size_t jj = 0; jj < uniform_info->texture_units.size(); ++jj) { GLuint texture_unit_index = uniform_info->texture_units[jj]; if (texture_unit_index < state_.texture_units.size()) { TextureUnit& texture_unit = state_.texture_units[texture_unit_index]; TextureRef* texture_ref = texture_unit.GetInfoForSamplerType(uniform_info->type).get(); GLenum textarget = GetBindTargetForSamplerType(uniform_info->type); if (!texture_ref || !texture_manager()->CanRender(texture_ref)) { textures_set = true; glActiveTexture(GL_TEXTURE0 + texture_unit_index); glBindTexture( textarget, texture_manager()->black_texture_id(uniform_info->type)); if (!texture_ref) { LOCAL_RENDER_WARNING( std::string("there is no texture bound to the unit ") + base::IntToString(texture_unit_index)); } else { LOCAL_RENDER_WARNING( std::string("texture bound to texture unit ") + base::IntToString(texture_unit_index) + " is not renderable. It maybe non-power-of-2 and have" " incompatible texture filtering."); } continue; } if (textarget != GL_TEXTURE_CUBE_MAP) { Texture* texture = texture_ref->texture(); gfx::GLImage* image = texture->GetLevelImage(textarget, 0); if (image && !texture->IsAttachedToFramebuffer()) { ScopedGLErrorSuppressor suppressor( "GLES2DecoderImpl::PrepareTexturesForRender", GetErrorState()); textures_set = true; glActiveTexture(GL_TEXTURE0 + texture_unit_index); image->WillUseTexImage(); continue; } } } // else: should this be an error? } } return !textures_set; } void GLES2DecoderImpl::RestoreStateForTextures() { DCHECK(state_.current_program.get()); const Program::SamplerIndices& sampler_indices = state_.current_program->sampler_indices(); for (size_t ii = 0; ii < sampler_indices.size(); ++ii) { const Program::UniformInfo* uniform_info = state_.current_program->GetUniformInfo(sampler_indices[ii]); DCHECK(uniform_info); for (size_t jj = 0; jj < uniform_info->texture_units.size(); ++jj) { GLuint texture_unit_index = uniform_info->texture_units[jj]; if (texture_unit_index < state_.texture_units.size()) { TextureUnit& texture_unit = state_.texture_units[texture_unit_index]; TextureRef* texture_ref = texture_unit.GetInfoForSamplerType(uniform_info->type).get(); if (!texture_ref || !texture_manager()->CanRender(texture_ref)) { glActiveTexture(GL_TEXTURE0 + texture_unit_index); // Get the texture_ref info that was previously bound here. texture_ref = texture_unit.bind_target == GL_TEXTURE_2D ? texture_unit.bound_texture_2d.get() : texture_unit.bound_texture_cube_map.get(); glBindTexture(texture_unit.bind_target, texture_ref ? texture_ref->service_id() : 0); continue; } if (texture_unit.bind_target != GL_TEXTURE_CUBE_MAP) { Texture* texture = texture_ref->texture(); gfx::GLImage* image = texture->GetLevelImage(texture_unit.bind_target, 0); if (image && !texture->IsAttachedToFramebuffer()) { ScopedGLErrorSuppressor suppressor( "GLES2DecoderImpl::RestoreStateForTextures", GetErrorState()); glActiveTexture(GL_TEXTURE0 + texture_unit_index); image->DidUseTexImage(); continue; } } } } } // Set the active texture back to whatever the user had it as. glActiveTexture(GL_TEXTURE0 + state_.active_texture_unit); } bool GLES2DecoderImpl::ClearUnclearedTextures() { // Only check if there are some uncleared textures. if (!texture_manager()->HaveUnsafeTextures()) { return true; } // 1: Check all textures we are about to render with. if (state_.current_program.get()) { const Program::SamplerIndices& sampler_indices = state_.current_program->sampler_indices(); for (size_t ii = 0; ii < sampler_indices.size(); ++ii) { const Program::UniformInfo* uniform_info = state_.current_program->GetUniformInfo(sampler_indices[ii]); DCHECK(uniform_info); for (size_t jj = 0; jj < uniform_info->texture_units.size(); ++jj) { GLuint texture_unit_index = uniform_info->texture_units[jj]; if (texture_unit_index < state_.texture_units.size()) { TextureUnit& texture_unit = state_.texture_units[texture_unit_index]; TextureRef* texture_ref = texture_unit.GetInfoForSamplerType(uniform_info->type).get(); if (texture_ref && !texture_ref->texture()->SafeToRenderFrom()) { if (!texture_manager()->ClearRenderableLevels(this, texture_ref)) { return false; } } } } } } return true; } bool GLES2DecoderImpl::IsDrawValid( const char* function_name, GLuint max_vertex_accessed, bool instanced, GLsizei primcount) { DCHECK(instanced || primcount == 1); // NOTE: We specifically do not check current_program->IsValid() because // it could never be invalid since glUseProgram would have failed. While // glLinkProgram could later mark the program as invalid the previous // valid program will still function if it is still the current program. if (!state_.current_program.get()) { // The program does not exist. // But GL says no ERROR. LOCAL_RENDER_WARNING("Drawing with no current shader program."); return false; } if (CheckDrawingFeedbackLoops()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "Source and destination textures of the draw are the same."); return false; } return state_.vertex_attrib_manager ->ValidateBindings(function_name, this, feature_info_.get(), state_.current_program.get(), max_vertex_accessed, instanced, primcount); } bool GLES2DecoderImpl::SimulateAttrib0( const char* function_name, GLuint max_vertex_accessed, bool* simulated) { DCHECK(simulated); *simulated = false; if (feature_info_->gl_version_info().BehavesLikeGLES()) return true; const VertexAttrib* attrib = state_.vertex_attrib_manager->GetVertexAttrib(0); // If it's enabled or it's not used then we don't need to do anything. bool attrib_0_used = state_.current_program->GetAttribInfoByLocation(0) != NULL; if (attrib->enabled() && attrib_0_used) { return true; } // Make a buffer with a single repeated vec4 value enough to // simulate the constant value that is supposed to be here. // This is required to emulate GLES2 on GL. GLuint num_vertices = max_vertex_accessed + 1; uint32 size_needed = 0; if (num_vertices == 0 || !SafeMultiplyUint32(num_vertices, sizeof(Vec4f), &size_needed) || size_needed > 0x7FFFFFFFU) { LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, function_name, "Simulating attrib 0"); return false; } LOCAL_PERFORMANCE_WARNING( "Attribute 0 is disabled. This has signficant performance penalty"); LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(function_name); glBindBuffer(GL_ARRAY_BUFFER, attrib_0_buffer_id_); bool new_buffer = static_cast(size_needed) > attrib_0_size_; if (new_buffer) { glBufferData(GL_ARRAY_BUFFER, size_needed, NULL, GL_DYNAMIC_DRAW); GLenum error = glGetError(); if (error != GL_NO_ERROR) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, function_name, "Simulating attrib 0"); return false; } } const Vec4& value = state_.attrib_values[0]; if (new_buffer || (attrib_0_used && (!attrib_0_buffer_matches_value_ || !value.Equal(attrib_0_value_)))){ // TODO(zmo): This is not 100% correct because we might lose data when // casting to float type, but it is a corner case and once we migrate to // core profiles on desktop GL, it is no longer relevant. Vec4f fvalue(value); std::vector temp(num_vertices, fvalue); glBufferSubData(GL_ARRAY_BUFFER, 0, size_needed, &temp[0].v[0]); attrib_0_buffer_matches_value_ = true; attrib_0_value_ = value; attrib_0_size_ = size_needed; } glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL); if (attrib->divisor()) glVertexAttribDivisorANGLE(0, 0); *simulated = true; return true; } void GLES2DecoderImpl::RestoreStateForAttrib( GLuint attrib_index, bool restore_array_binding) { const VertexAttrib* attrib = state_.vertex_attrib_manager->GetVertexAttrib(attrib_index); if (restore_array_binding) { const void* ptr = reinterpret_cast(attrib->offset()); Buffer* buffer = attrib->buffer(); glBindBuffer(GL_ARRAY_BUFFER, buffer ? buffer->service_id() : 0); glVertexAttribPointer( attrib_index, attrib->size(), attrib->type(), attrib->normalized(), attrib->gl_stride(), ptr); } if (attrib->divisor()) glVertexAttribDivisorANGLE(attrib_index, attrib->divisor()); glBindBuffer( GL_ARRAY_BUFFER, state_.bound_array_buffer.get() ? state_.bound_array_buffer->service_id() : 0); // Never touch vertex attribute 0's state (in particular, never // disable it) when running on desktop GL because it will never be // re-enabled. if (attrib_index != 0 || gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) { if (attrib->enabled()) { glEnableVertexAttribArray(attrib_index); } else { glDisableVertexAttribArray(attrib_index); } } } bool GLES2DecoderImpl::SimulateFixedAttribs( const char* function_name, GLuint max_vertex_accessed, bool* simulated, GLsizei primcount) { DCHECK(simulated); *simulated = false; if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) return true; if (!state_.vertex_attrib_manager->HaveFixedAttribs()) { return true; } LOCAL_PERFORMANCE_WARNING( "GL_FIXED attributes have a signficant performance penalty"); // NOTE: we could be smart and try to check if a buffer is used // twice in 2 different attribs, find the overlapping parts and therefore // duplicate the minimum amount of data but this whole code path is not meant // to be used normally. It's just here to pass that OpenGL ES 2.0 conformance // tests so we just add to the buffer attrib used. GLuint elements_needed = 0; const VertexAttribManager::VertexAttribList& enabled_attribs = state_.vertex_attrib_manager->GetEnabledVertexAttribs(); for (VertexAttribManager::VertexAttribList::const_iterator it = enabled_attribs.begin(); it != enabled_attribs.end(); ++it) { const VertexAttrib* attrib = *it; const Program::VertexAttrib* attrib_info = state_.current_program->GetAttribInfoByLocation(attrib->index()); GLuint max_accessed = attrib->MaxVertexAccessed(primcount, max_vertex_accessed); GLuint num_vertices = max_accessed + 1; if (num_vertices == 0) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, function_name, "Simulating attrib 0"); return false; } if (attrib_info && attrib->CanAccess(max_accessed) && attrib->type() == GL_FIXED) { uint32 elements_used = 0; if (!SafeMultiplyUint32(num_vertices, attrib->size(), &elements_used) || !SafeAddUint32(elements_needed, elements_used, &elements_needed)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, function_name, "simulating GL_FIXED attribs"); return false; } } } const uint32 kSizeOfFloat = sizeof(float); // NOLINT uint32 size_needed = 0; if (!SafeMultiplyUint32(elements_needed, kSizeOfFloat, &size_needed) || size_needed > 0x7FFFFFFFU) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, function_name, "simulating GL_FIXED attribs"); return false; } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(function_name); glBindBuffer(GL_ARRAY_BUFFER, fixed_attrib_buffer_id_); if (static_cast(size_needed) > fixed_attrib_buffer_size_) { glBufferData(GL_ARRAY_BUFFER, size_needed, NULL, GL_DYNAMIC_DRAW); GLenum error = glGetError(); if (error != GL_NO_ERROR) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, function_name, "simulating GL_FIXED attribs"); return false; } } // Copy the elements and convert to float GLintptr offset = 0; for (VertexAttribManager::VertexAttribList::const_iterator it = enabled_attribs.begin(); it != enabled_attribs.end(); ++it) { const VertexAttrib* attrib = *it; const Program::VertexAttrib* attrib_info = state_.current_program->GetAttribInfoByLocation(attrib->index()); GLuint max_accessed = attrib->MaxVertexAccessed(primcount, max_vertex_accessed); GLuint num_vertices = max_accessed + 1; if (num_vertices == 0) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, function_name, "Simulating attrib 0"); return false; } if (attrib_info && attrib->CanAccess(max_accessed) && attrib->type() == GL_FIXED) { int num_elements = attrib->size() * num_vertices; const int src_size = num_elements * sizeof(int32); const int dst_size = num_elements * sizeof(float); scoped_ptr data(new float[num_elements]); const int32* src = reinterpret_cast( attrib->buffer()->GetRange(attrib->offset(), src_size)); const int32* end = src + num_elements; float* dst = data.get(); while (src != end) { *dst++ = static_cast(*src++) / 65536.0f; } glBufferSubData(GL_ARRAY_BUFFER, offset, dst_size, data.get()); glVertexAttribPointer( attrib->index(), attrib->size(), GL_FLOAT, false, 0, reinterpret_cast(offset)); offset += dst_size; } } *simulated = true; return true; } void GLES2DecoderImpl::RestoreStateForSimulatedFixedAttribs() { // There's no need to call glVertexAttribPointer because we shadow all the // settings and passing GL_FIXED to it will not work. glBindBuffer( GL_ARRAY_BUFFER, state_.bound_array_buffer.get() ? state_.bound_array_buffer->service_id() : 0); } error::Error GLES2DecoderImpl::DoDrawArrays( const char* function_name, bool instanced, GLenum mode, GLint first, GLsizei count, GLsizei primcount) { error::Error error = WillAccessBoundFramebufferForDraw(); if (error != error::kNoError) return error; if (!validators_->draw_mode.IsValid(mode)) { LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, mode, "mode"); return error::kNoError; } if (count < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "count < 0"); return error::kNoError; } if (primcount < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "primcount < 0"); return error::kNoError; } if (!CheckBoundFramebuffersValid(function_name)) { return error::kNoError; } // We have to check this here because the prototype for glDrawArrays // is GLint not GLsizei. if (first < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "first < 0"); return error::kNoError; } if (count == 0 || primcount == 0) { LOCAL_RENDER_WARNING("Render count or primcount is 0."); return error::kNoError; } GLuint max_vertex_accessed = first + count - 1; if (IsDrawValid(function_name, max_vertex_accessed, instanced, primcount)) { if (!ClearUnclearedTextures()) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "out of memory"); return error::kNoError; } bool simulated_attrib_0 = false; if (!SimulateAttrib0( function_name, max_vertex_accessed, &simulated_attrib_0)) { return error::kNoError; } bool simulated_fixed_attribs = false; if (SimulateFixedAttribs( function_name, max_vertex_accessed, &simulated_fixed_attribs, primcount)) { bool textures_set = !PrepareTexturesForRender(); ApplyDirtyState(); ScopedRenderTo do_render(framebuffer_state_.bound_draw_framebuffer.get()); if (!instanced) { glDrawArrays(mode, first, count); } else { glDrawArraysInstancedANGLE(mode, first, count, primcount); } if (textures_set) { RestoreStateForTextures(); } if (simulated_fixed_attribs) { RestoreStateForSimulatedFixedAttribs(); } } if (simulated_attrib_0) { // We don't have to restore attrib 0 generic data at the end of this // function even if it is simulated. This is because we will simulate // it in each draw call, and attrib 0 generic data queries use cached // values instead of passing down to the underlying driver. RestoreStateForAttrib(0, false); } } return error::kNoError; } error::Error GLES2DecoderImpl::HandleDrawArrays(uint32 immediate_data_size, const void* cmd_data) { // TODO(zmo): crbug.com/481184 // On Desktop GL with versions lower than 4.3, we need to emulate // GL_PRIMITIVE_RESTART_FIXED_INDEX using glPrimitiveRestartIndex(). const cmds::DrawArrays& c = *static_cast(cmd_data); return DoDrawArrays("glDrawArrays", false, static_cast(c.mode), static_cast(c.first), static_cast(c.count), 1); } error::Error GLES2DecoderImpl::HandleDrawArraysInstancedANGLE( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::DrawArraysInstancedANGLE& c = *static_cast(cmd_data); if (!features().angle_instanced_arrays) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glDrawArraysInstancedANGLE", "function not available"); return error::kNoError; } return DoDrawArrays("glDrawArraysIntancedANGLE", true, static_cast(c.mode), static_cast(c.first), static_cast(c.count), static_cast(c.primcount)); } error::Error GLES2DecoderImpl::DoDrawElements( const char* function_name, bool instanced, GLenum mode, GLsizei count, GLenum type, int32 offset, GLsizei primcount) { error::Error error = WillAccessBoundFramebufferForDraw(); if (error != error::kNoError) return error; if (!state_.vertex_attrib_manager->element_array_buffer()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "No element array buffer bound"); return error::kNoError; } if (count < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "count < 0"); return error::kNoError; } if (offset < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "offset < 0"); return error::kNoError; } if (!validators_->draw_mode.IsValid(mode)) { LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, mode, "mode"); return error::kNoError; } if (!validators_->index_type.IsValid(type)) { LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, type, "type"); return error::kNoError; } if (primcount < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "primcount < 0"); return error::kNoError; } if (!CheckBoundFramebuffersValid(function_name)) { return error::kNoError; } if (count == 0 || primcount == 0) { return error::kNoError; } GLuint max_vertex_accessed; Buffer* element_array_buffer = state_.vertex_attrib_manager->element_array_buffer(); if (!element_array_buffer->GetMaxValueForRange( offset, count, type, &max_vertex_accessed)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "range out of bounds for buffer"); return error::kNoError; } if (IsDrawValid(function_name, max_vertex_accessed, instanced, primcount)) { if (!ClearUnclearedTextures()) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "out of memory"); return error::kNoError; } bool simulated_attrib_0 = false; if (!SimulateAttrib0( function_name, max_vertex_accessed, &simulated_attrib_0)) { return error::kNoError; } bool simulated_fixed_attribs = false; if (SimulateFixedAttribs( function_name, max_vertex_accessed, &simulated_fixed_attribs, primcount)) { bool textures_set = !PrepareTexturesForRender(); ApplyDirtyState(); // TODO(gman): Refactor to hide these details in BufferManager or // VertexAttribManager. const GLvoid* indices = reinterpret_cast(offset); bool used_client_side_array = false; if (element_array_buffer->IsClientSideArray()) { used_client_side_array = true; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); indices = element_array_buffer->GetRange(offset, 0); } ScopedRenderTo do_render(framebuffer_state_.bound_draw_framebuffer.get()); if (!instanced) { glDrawElements(mode, count, type, indices); } else { glDrawElementsInstancedANGLE(mode, count, type, indices, primcount); } if (used_client_side_array) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer->service_id()); } if (textures_set) { RestoreStateForTextures(); } if (simulated_fixed_attribs) { RestoreStateForSimulatedFixedAttribs(); } } if (simulated_attrib_0) { // We don't have to restore attrib 0 generic data at the end of this // function even if it is simulated. This is because we will simulate // it in each draw call, and attrib 0 generic data queries use cached // values instead of passing down to the underlying driver. RestoreStateForAttrib(0, false); } } return error::kNoError; } error::Error GLES2DecoderImpl::HandleDrawElements(uint32 immediate_data_size, const void* cmd_data) { // TODO(zmo): crbug.com/481184 // On Desktop GL with versions lower than 4.3, we need to emulate // GL_PRIMITIVE_RESTART_FIXED_INDEX using glPrimitiveRestartIndex(). const gles2::cmds::DrawElements& c = *static_cast(cmd_data); return DoDrawElements("glDrawElements", false, static_cast(c.mode), static_cast(c.count), static_cast(c.type), static_cast(c.index_offset), 1); } error::Error GLES2DecoderImpl::HandleDrawElementsInstancedANGLE( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::DrawElementsInstancedANGLE& c = *static_cast(cmd_data); if (!features().angle_instanced_arrays) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glDrawElementsInstancedANGLE", "function not available"); return error::kNoError; } return DoDrawElements("glDrawElementsInstancedANGLE", true, static_cast(c.mode), static_cast(c.count), static_cast(c.type), static_cast(c.index_offset), static_cast(c.primcount)); } GLuint GLES2DecoderImpl::DoGetMaxValueInBufferCHROMIUM( GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) { GLuint max_vertex_accessed = 0; Buffer* buffer = GetBuffer(buffer_id); if (!buffer) { // TODO(gman): Should this be a GL error or a command buffer error? LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "GetMaxValueInBufferCHROMIUM", "unknown buffer"); } else { if (!buffer->GetMaxValueForRange( offset, count, type, &max_vertex_accessed)) { // TODO(gman): Should this be a GL error or a command buffer error? LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "GetMaxValueInBufferCHROMIUM", "range out of bounds for buffer"); } } return max_vertex_accessed; } void GLES2DecoderImpl::DoShaderSource( GLuint client_id, GLsizei count, const char** data, const GLint* length) { std::string str; for (GLsizei ii = 0; ii < count; ++ii) { if (length && length[ii] > 0) str.append(data[ii], length[ii]); else str.append(data[ii]); } Shader* shader = GetShaderInfoNotProgram(client_id, "glShaderSource"); if (!shader) { return; } // Note: We don't actually call glShaderSource here. We wait until // we actually compile the shader. shader->set_source(str); } void GLES2DecoderImpl::DoTransformFeedbackVaryings( GLuint client_program_id, GLsizei count, const char* const* varyings, GLenum buffer_mode) { Program* program = GetProgramInfoNotShader( client_program_id, "glTransformFeedbackVaryings"); if (!program) { return; } program->TransformFeedbackVaryings(count, varyings, buffer_mode); glTransformFeedbackVaryings( program->service_id(), count, varyings, buffer_mode); } void GLES2DecoderImpl::DoCompileShader(GLuint client_id) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoCompileShader"); Shader* shader = GetShaderInfoNotProgram(client_id, "glCompileShader"); if (!shader) { return; } scoped_refptr translator; if (use_shader_translator_) { translator = shader->shader_type() == GL_VERTEX_SHADER ? vertex_translator_ : fragment_translator_; } const Shader::TranslatedShaderSourceType source_type = feature_info_->feature_flags().angle_translated_shader_source ? Shader::kANGLE : Shader::kGL; shader->RequestCompile(translator, source_type); } void GLES2DecoderImpl::DoGetShaderiv( GLuint shader_id, GLenum pname, GLint* params) { Shader* shader = GetShaderInfoNotProgram(shader_id, "glGetShaderiv"); if (!shader) { return; } // Compile now for statuses that require it. switch (pname) { case GL_COMPILE_STATUS: case GL_INFO_LOG_LENGTH: case GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE: shader->DoCompile(); break; default: break; } switch (pname) { case GL_SHADER_SOURCE_LENGTH: *params = shader->source().size(); if (*params) ++(*params); return; case GL_COMPILE_STATUS: *params = compile_shader_always_succeeds_ ? true : shader->valid(); return; case GL_INFO_LOG_LENGTH: *params = shader->log_info().size(); if (*params) ++(*params); return; case GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE: *params = shader->translated_source().size(); if (*params) ++(*params); return; default: break; } glGetShaderiv(shader->service_id(), pname, params); } error::Error GLES2DecoderImpl::HandleGetShaderSource(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetShaderSource& c = *static_cast(cmd_data); GLuint shader_id = c.shader; uint32 bucket_id = static_cast(c.bucket_id); Bucket* bucket = CreateBucket(bucket_id); Shader* shader = GetShaderInfoNotProgram(shader_id, "glGetShaderSource"); if (!shader || shader->source().empty()) { bucket->SetSize(0); return error::kNoError; } bucket->SetFromString(shader->source().c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetTranslatedShaderSourceANGLE( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetTranslatedShaderSourceANGLE& c = *static_cast( cmd_data); GLuint shader_id = c.shader; uint32 bucket_id = static_cast(c.bucket_id); Bucket* bucket = CreateBucket(bucket_id); Shader* shader = GetShaderInfoNotProgram( shader_id, "glGetTranslatedShaderSourceANGLE"); if (!shader) { bucket->SetSize(0); return error::kNoError; } // Make sure translator has been utilized in compile. shader->DoCompile(); bucket->SetFromString(shader->translated_source().c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetProgramInfoLog( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetProgramInfoLog& c = *static_cast(cmd_data); GLuint program_id = c.program; uint32 bucket_id = static_cast(c.bucket_id); Bucket* bucket = CreateBucket(bucket_id); Program* program = GetProgramInfoNotShader( program_id, "glGetProgramInfoLog"); if (!program || !program->log_info()) { bucket->SetFromString(""); return error::kNoError; } bucket->SetFromString(program->log_info()->c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetShaderInfoLog( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetShaderInfoLog& c = *static_cast(cmd_data); GLuint shader_id = c.shader; uint32 bucket_id = static_cast(c.bucket_id); Bucket* bucket = CreateBucket(bucket_id); Shader* shader = GetShaderInfoNotProgram(shader_id, "glGetShaderInfoLog"); if (!shader) { bucket->SetFromString(""); return error::kNoError; } // Shader must be compiled in order to get the info log. shader->DoCompile(); bucket->SetFromString(shader->log_info().c_str()); return error::kNoError; } bool GLES2DecoderImpl::DoIsEnabled(GLenum cap) { return state_.GetEnabled(cap); } bool GLES2DecoderImpl::DoIsBuffer(GLuint client_id) { const Buffer* buffer = GetBuffer(client_id); return buffer && buffer->IsValid() && !buffer->IsDeleted(); } bool GLES2DecoderImpl::DoIsFramebuffer(GLuint client_id) { const Framebuffer* framebuffer = GetFramebuffer(client_id); return framebuffer && framebuffer->IsValid() && !framebuffer->IsDeleted(); } bool GLES2DecoderImpl::DoIsProgram(GLuint client_id) { // IsProgram is true for programs as soon as they are created, until they are // deleted and no longer in use. const Program* program = GetProgram(client_id); return program != NULL && !program->IsDeleted(); } bool GLES2DecoderImpl::DoIsRenderbuffer(GLuint client_id) { const Renderbuffer* renderbuffer = GetRenderbuffer(client_id); return renderbuffer && renderbuffer->IsValid() && !renderbuffer->IsDeleted(); } bool GLES2DecoderImpl::DoIsShader(GLuint client_id) { // IsShader is true for shaders as soon as they are created, until they // are deleted and not attached to any programs. const Shader* shader = GetShader(client_id); return shader != NULL && !shader->IsDeleted(); } bool GLES2DecoderImpl::DoIsTexture(GLuint client_id) { const TextureRef* texture_ref = GetTexture(client_id); return texture_ref && texture_ref->texture()->IsValid(); } void GLES2DecoderImpl::DoAttachShader( GLuint program_client_id, GLint shader_client_id) { Program* program = GetProgramInfoNotShader( program_client_id, "glAttachShader"); if (!program) { return; } Shader* shader = GetShaderInfoNotProgram(shader_client_id, "glAttachShader"); if (!shader) { return; } if (!program->AttachShader(shader_manager(), shader)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glAttachShader", "can not attach more than one shader of the same type."); return; } glAttachShader(program->service_id(), shader->service_id()); } void GLES2DecoderImpl::DoDetachShader( GLuint program_client_id, GLint shader_client_id) { Program* program = GetProgramInfoNotShader( program_client_id, "glDetachShader"); if (!program) { return; } Shader* shader = GetShaderInfoNotProgram(shader_client_id, "glDetachShader"); if (!shader) { return; } if (!program->DetachShader(shader_manager(), shader)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glDetachShader", "shader not attached to program"); return; } glDetachShader(program->service_id(), shader->service_id()); } void GLES2DecoderImpl::DoValidateProgram(GLuint program_client_id) { Program* program = GetProgramInfoNotShader( program_client_id, "glValidateProgram"); if (!program) { return; } program->Validate(); } void GLES2DecoderImpl::GetVertexAttribHelper( const VertexAttrib* attrib, GLenum pname, GLint* params) { switch (pname) { case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: { Buffer* buffer = attrib->buffer(); if (buffer && !buffer->IsDeleted()) { GLuint client_id; buffer_manager()->GetClientId(buffer->service_id(), &client_id); *params = client_id; } break; } case GL_VERTEX_ATTRIB_ARRAY_ENABLED: *params = attrib->enabled(); break; case GL_VERTEX_ATTRIB_ARRAY_SIZE: *params = attrib->size(); break; case GL_VERTEX_ATTRIB_ARRAY_STRIDE: *params = attrib->gl_stride(); break; case GL_VERTEX_ATTRIB_ARRAY_TYPE: *params = attrib->type(); break; case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: *params = attrib->normalized(); break; case GL_VERTEX_ATTRIB_ARRAY_DIVISOR: *params = attrib->divisor(); break; case GL_VERTEX_ATTRIB_ARRAY_INTEGER: *params = attrib->integer(); break; default: NOTREACHED(); break; } } void GLES2DecoderImpl::DoGetTexParameterfv( GLenum target, GLenum pname, GLfloat* params) { InitTextureMaxAnisotropyIfNeeded(target, pname); glGetTexParameterfv(target, pname, params); } void GLES2DecoderImpl::DoGetTexParameteriv( GLenum target, GLenum pname, GLint* params) { InitTextureMaxAnisotropyIfNeeded(target, pname); glGetTexParameteriv(target, pname, params); } void GLES2DecoderImpl::InitTextureMaxAnisotropyIfNeeded( GLenum target, GLenum pname) { if (!workarounds().init_texture_max_anisotropy) return; if (pname != GL_TEXTURE_MAX_ANISOTROPY_EXT || !validators_->texture_parameter.IsValid(pname)) { return; } TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glGetTexParamter{fi}v", "unknown texture for target"); return; } Texture* texture = texture_ref->texture(); texture->InitTextureMaxAnisotropyIfNeeded(target); } template void GLES2DecoderImpl::DoGetVertexAttribImpl( GLuint index, GLenum pname, T* params) { VertexAttrib* attrib = state_.vertex_attrib_manager->GetVertexAttrib(index); if (!attrib) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glGetVertexAttrib", "index out of range"); return; } switch (pname) { case GL_CURRENT_VERTEX_ATTRIB: state_.attrib_values[index].GetValues(params); break; default: { GLint value = 0; GetVertexAttribHelper(attrib, pname, &value); *params = static_cast(value); break; } } } void GLES2DecoderImpl::DoGetVertexAttribfv( GLuint index, GLenum pname, GLfloat* params) { DoGetVertexAttribImpl(index, pname, params); } void GLES2DecoderImpl::DoGetVertexAttribiv( GLuint index, GLenum pname, GLint* params) { DoGetVertexAttribImpl(index, pname, params); } void GLES2DecoderImpl::DoGetVertexAttribIiv( GLuint index, GLenum pname, GLint* params) { DoGetVertexAttribImpl(index, pname, params); } void GLES2DecoderImpl::DoGetVertexAttribIuiv( GLuint index, GLenum pname, GLuint* params) { DoGetVertexAttribImpl(index, pname, params); } template bool GLES2DecoderImpl::SetVertexAttribValue( const char* function_name, GLuint index, const T* value) { if (index >= state_.attrib_values.size()) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "index out of range"); return false; } state_.attrib_values[index].SetValues(value); return true; } void GLES2DecoderImpl::DoVertexAttrib1f(GLuint index, GLfloat v0) { GLfloat v[4] = { v0, 0.0f, 0.0f, 1.0f, }; if (SetVertexAttribValue("glVertexAttrib1f", index, v)) { glVertexAttrib1f(index, v0); } } void GLES2DecoderImpl::DoVertexAttrib2f(GLuint index, GLfloat v0, GLfloat v1) { GLfloat v[4] = { v0, v1, 0.0f, 1.0f, }; if (SetVertexAttribValue("glVertexAttrib2f", index, v)) { glVertexAttrib2f(index, v0, v1); } } void GLES2DecoderImpl::DoVertexAttrib3f( GLuint index, GLfloat v0, GLfloat v1, GLfloat v2) { GLfloat v[4] = { v0, v1, v2, 1.0f, }; if (SetVertexAttribValue("glVertexAttrib3f", index, v)) { glVertexAttrib3f(index, v0, v1, v2); } } void GLES2DecoderImpl::DoVertexAttrib4f( GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { GLfloat v[4] = { v0, v1, v2, v3, }; if (SetVertexAttribValue("glVertexAttrib4f", index, v)) { glVertexAttrib4f(index, v0, v1, v2, v3); } } void GLES2DecoderImpl::DoVertexAttrib1fv(GLuint index, const GLfloat* v) { GLfloat t[4] = { v[0], 0.0f, 0.0f, 1.0f, }; if (SetVertexAttribValue("glVertexAttrib1fv", index, t)) { glVertexAttrib1fv(index, v); } } void GLES2DecoderImpl::DoVertexAttrib2fv(GLuint index, const GLfloat* v) { GLfloat t[4] = { v[0], v[1], 0.0f, 1.0f, }; if (SetVertexAttribValue("glVertexAttrib2fv", index, t)) { glVertexAttrib2fv(index, v); } } void GLES2DecoderImpl::DoVertexAttrib3fv(GLuint index, const GLfloat* v) { GLfloat t[4] = { v[0], v[1], v[2], 1.0f, }; if (SetVertexAttribValue("glVertexAttrib3fv", index, t)) { glVertexAttrib3fv(index, v); } } void GLES2DecoderImpl::DoVertexAttrib4fv(GLuint index, const GLfloat* v) { if (SetVertexAttribValue("glVertexAttrib4fv", index, v)) { glVertexAttrib4fv(index, v); } } void GLES2DecoderImpl::DoVertexAttribI4i( GLuint index, GLint v0, GLint v1, GLint v2, GLint v3) { GLint v[4] = { v0, v1, v2, v3 }; if (SetVertexAttribValue("glVertexAttribI4i", index, v)) { glVertexAttribI4i(index, v0, v1, v2, v3); } } void GLES2DecoderImpl::DoVertexAttribI4iv(GLuint index, const GLint* v) { if (SetVertexAttribValue("glVertexAttribI4iv", index, v)) { glVertexAttribI4iv(index, v); } } void GLES2DecoderImpl::DoVertexAttribI4ui( GLuint index, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { GLuint v[4] = { v0, v1, v2, v3 }; if (SetVertexAttribValue("glVertexAttribI4ui", index, v)) { glVertexAttribI4ui(index, v0, v1, v2, v3); } } void GLES2DecoderImpl::DoVertexAttribI4uiv(GLuint index, const GLuint* v) { if (SetVertexAttribValue("glVertexAttribI4uiv", index, v)) { glVertexAttribI4uiv(index, v); } } error::Error GLES2DecoderImpl::HandleVertexAttribIPointer( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::VertexAttribIPointer& c = *static_cast(cmd_data); if (!state_.bound_array_buffer.get() || state_.bound_array_buffer->IsDeleted()) { if (state_.vertex_attrib_manager.get() == state_.default_vertex_attrib_manager.get()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribIPointer", "no array buffer bound"); return error::kNoError; } else if (c.offset != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribIPointer", "client side arrays are not allowed"); return error::kNoError; } } GLuint indx = c.indx; GLint size = c.size; GLenum type = c.type; GLsizei stride = c.stride; GLsizei offset = c.offset; const void* ptr = reinterpret_cast(offset); if (!validators_->vertex_attrib_i_type.IsValid(type)) { LOCAL_SET_GL_ERROR_INVALID_ENUM("glVertexAttribIPointer", type, "type"); return error::kNoError; } if (!validators_->vertex_attrib_size.IsValid(size)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribIPointer", "size GL_INVALID_VALUE"); return error::kNoError; } if (indx >= group_->max_vertex_attribs()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribIPointer", "index out of range"); return error::kNoError; } if (stride < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribIPointer", "stride < 0"); return error::kNoError; } if (stride > 255) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribIPointer", "stride > 255"); return error::kNoError; } if (offset < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribIPointer", "offset < 0"); return error::kNoError; } GLsizei component_size = GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type); // component_size must be a power of two to use & as optimized modulo. DCHECK(GLES2Util::IsPOT(component_size)); if (offset & (component_size - 1)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glVertexAttribIPointer", "offset not valid for type"); return error::kNoError; } if (stride & (component_size - 1)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glVertexAttribIPointer", "stride not valid for type"); return error::kNoError; } state_.vertex_attrib_manager ->SetAttribInfo(indx, state_.bound_array_buffer.get(), size, type, GL_FALSE, stride, stride != 0 ? stride : component_size * size, offset, GL_TRUE); glVertexAttribIPointer(indx, size, type, stride, ptr); return error::kNoError; } error::Error GLES2DecoderImpl::HandleVertexAttribPointer( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::VertexAttribPointer& c = *static_cast(cmd_data); if (!state_.bound_array_buffer.get() || state_.bound_array_buffer->IsDeleted()) { if (state_.vertex_attrib_manager.get() == state_.default_vertex_attrib_manager.get()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribPointer", "no array buffer bound"); return error::kNoError; } else if (c.offset != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribPointer", "client side arrays are not allowed"); return error::kNoError; } } GLuint indx = c.indx; GLint size = c.size; GLenum type = c.type; GLboolean normalized = static_cast(c.normalized); GLsizei stride = c.stride; GLsizei offset = c.offset; const void* ptr = reinterpret_cast(offset); if (!validators_->vertex_attrib_type.IsValid(type)) { LOCAL_SET_GL_ERROR_INVALID_ENUM("glVertexAttribPointer", type, "type"); return error::kNoError; } if (!validators_->vertex_attrib_size.IsValid(size)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribPointer", "size GL_INVALID_VALUE"); return error::kNoError; } if (indx >= group_->max_vertex_attribs()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribPointer", "index out of range"); return error::kNoError; } if (stride < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribPointer", "stride < 0"); return error::kNoError; } if (stride > 255) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribPointer", "stride > 255"); return error::kNoError; } if (offset < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribPointer", "offset < 0"); return error::kNoError; } GLsizei component_size = GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type); // component_size must be a power of two to use & as optimized modulo. DCHECK(GLES2Util::IsPOT(component_size)); if (offset & (component_size - 1)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glVertexAttribPointer", "offset not valid for type"); return error::kNoError; } if (stride & (component_size - 1)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glVertexAttribPointer", "stride not valid for type"); return error::kNoError; } state_.vertex_attrib_manager ->SetAttribInfo(indx, state_.bound_array_buffer.get(), size, type, normalized, stride, stride != 0 ? stride : component_size * size, offset, GL_FALSE); // We support GL_FIXED natively on EGL/GLES2 implementations if (type != GL_FIXED || feature_info_->gl_version_info().is_es) { glVertexAttribPointer(indx, size, type, normalized, stride, ptr); } return error::kNoError; } void GLES2DecoderImpl::DoViewport(GLint x, GLint y, GLsizei width, GLsizei height) { state_.viewport_x = x; state_.viewport_y = y; state_.viewport_width = std::min(width, viewport_max_width_); state_.viewport_height = std::min(height, viewport_max_height_); glViewport(x, y, width, height); } error::Error GLES2DecoderImpl::HandleVertexAttribDivisorANGLE( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::VertexAttribDivisorANGLE& c = *static_cast(cmd_data); if (!features().angle_instanced_arrays) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glVertexAttribDivisorANGLE", "function not available"); return error::kNoError; } GLuint index = c.index; GLuint divisor = c.divisor; if (index >= group_->max_vertex_attribs()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glVertexAttribDivisorANGLE", "index out of range"); return error::kNoError; } state_.vertex_attrib_manager->SetDivisor( index, divisor); glVertexAttribDivisorANGLE(index, divisor); return error::kNoError; } template static void WriteAlphaData( void* pixels, uint32 row_count, uint32 channel_count, uint32 alpha_channel_index, uint32 unpadded_row_size, uint32 padded_row_size, pixel_data_type alpha_value) { DCHECK_GT(channel_count, 0U); DCHECK_EQ(unpadded_row_size % sizeof(pixel_data_type), 0U); uint32 unpadded_row_size_in_elements = unpadded_row_size / sizeof(pixel_data_type); DCHECK_EQ(padded_row_size % sizeof(pixel_data_type), 0U); uint32 padded_row_size_in_elements = padded_row_size / sizeof(pixel_data_type); pixel_data_type* dst = static_cast(pixels) + alpha_channel_index; for (uint32 yy = 0; yy < row_count; ++yy) { pixel_data_type* end = dst + unpadded_row_size_in_elements; for (pixel_data_type* d = dst; d < end; d += channel_count) { *d = alpha_value; } dst += padded_row_size_in_elements; } } void GLES2DecoderImpl::FinishReadPixels( const cmds::ReadPixels& c, GLuint buffer) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::FinishReadPixels"); GLsizei width = c.width; GLsizei height = c.height; GLenum format = c.format; GLenum type = c.type; typedef cmds::ReadPixels::Result Result; uint32 pixels_size; Result* result = NULL; if (c.result_shm_id != 0) { result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { if (buffer != 0) { glDeleteBuffersARB(1, &buffer); } return; } } GLES2Util::ComputeImageDataSizes( width, height, 1, format, type, state_.pack_alignment, &pixels_size, NULL, NULL); void* pixels = GetSharedMemoryAs( c.pixels_shm_id, c.pixels_shm_offset, pixels_size); if (!pixels) { if (buffer != 0) { glDeleteBuffersARB(1, &buffer); } return; } if (buffer != 0) { glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, buffer); void* data; if (features().map_buffer_range) { data = glMapBufferRange( GL_PIXEL_PACK_BUFFER_ARB, 0, pixels_size, GL_MAP_READ_BIT); } else { data = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); } if (!data) { LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glMapBuffer", "Unable to map memory for readback."); return; } memcpy(pixels, data, pixels_size); // GL_PIXEL_PACK_BUFFER_ARB is currently unused, so we don't // have to restore the state. glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); glDeleteBuffersARB(1, &buffer); } if (result != NULL) { *result = true; } GLenum read_format = GetBoundReadFrameBufferInternalFormat(); uint32 channels_exist = GLES2Util::GetChannelsForFormat(read_format); if ((channels_exist & 0x0008) == 0 && workarounds().clear_alpha_in_readpixels) { // Set the alpha to 255 because some drivers are buggy in this regard. uint32 temp_size; uint32 unpadded_row_size; uint32 padded_row_size; if (!GLES2Util::ComputeImageDataSizes( width, 2, 1, format, type, state_.pack_alignment, &temp_size, &unpadded_row_size, &padded_row_size)) { return; } uint32 channel_count = 0; uint32 alpha_channel = 0; switch (format) { case GL_RGBA: case GL_BGRA_EXT: channel_count = 4; alpha_channel = 3; break; case GL_ALPHA: channel_count = 1; alpha_channel = 0; break; } if (channel_count > 0) { switch (type) { case GL_UNSIGNED_BYTE: WriteAlphaData( pixels, height, channel_count, alpha_channel, unpadded_row_size, padded_row_size, 0xFF); break; case GL_FLOAT: WriteAlphaData( pixels, height, channel_count, alpha_channel, unpadded_row_size, padded_row_size, 1.0f); break; case GL_HALF_FLOAT: WriteAlphaData( pixels, height, channel_count, alpha_channel, unpadded_row_size, padded_row_size, 0x3C00); break; } } } } error::Error GLES2DecoderImpl::HandleReadPixels(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::ReadPixels& c = *static_cast(cmd_data); TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandleReadPixels"); error::Error fbo_error = WillAccessBoundFramebufferForRead(); if (fbo_error != error::kNoError) return fbo_error; GLint x = c.x; GLint y = c.y; GLsizei width = c.width; GLsizei height = c.height; GLenum format = c.format; GLenum type = c.type; GLboolean async = static_cast(c.async); if (width < 0 || height < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glReadPixels", "dimensions < 0"); return error::kNoError; } typedef cmds::ReadPixels::Result Result; uint32 pixels_size; if (!GLES2Util::ComputeImageDataSizes( width, height, 1, format, type, state_.pack_alignment, &pixels_size, NULL, NULL)) { return error::kOutOfBounds; } void* pixels = GetSharedMemoryAs( c.pixels_shm_id, c.pixels_shm_offset, pixels_size); if (!pixels) { return error::kOutOfBounds; } Result* result = NULL; if (c.result_shm_id != 0) { result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { return error::kOutOfBounds; } } if (!validators_->read_pixel_format.IsValid(format)) { LOCAL_SET_GL_ERROR_INVALID_ENUM("glReadPixels", format, "format"); return error::kNoError; } if (!validators_->read_pixel_type.IsValid(type)) { LOCAL_SET_GL_ERROR_INVALID_ENUM("glReadPixels", type, "type"); return error::kNoError; } if ((format != GL_RGBA && format != GL_BGRA_EXT && format != GL_RGB && format != GL_ALPHA) || type != GL_UNSIGNED_BYTE) { // format and type are acceptable enums but not guaranteed to be supported // for this framebuffer. Have to ask gl if they are valid. GLint preferred_format = 0; DoGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &preferred_format); GLint preferred_type = 0; DoGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &preferred_type); if (format != static_cast(preferred_format) || type != static_cast(preferred_type)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glReadPixels", "format and type incompatible " "with the current read framebuffer"); return error::kNoError; } } if (width == 0 || height == 0) { return error::kNoError; } // Get the size of the current fbo or backbuffer. gfx::Size max_size = GetBoundReadFrameBufferSize(); int32 max_x; int32 max_y; if (!SafeAddInt32(x, width, &max_x) || !SafeAddInt32(y, height, &max_y)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glReadPixels", "dimensions out of range"); return error::kNoError; } if (!CheckBoundReadFramebufferColorAttachment("glReadPixels")) { return error::kNoError; } if (!CheckBoundFramebuffersValid("glReadPixels")) { return error::kNoError; } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glReadPixels"); ScopedResolvedFrameBufferBinder binder(this, false, true); if (x < 0 || y < 0 || max_x > max_size.width() || max_y > max_size.height()) { // The user requested an out of range area. Get the results 1 line // at a time. uint32 temp_size; uint32 unpadded_row_size; uint32 padded_row_size; if (!GLES2Util::ComputeImageDataSizes( width, 2, 1, format, type, state_.pack_alignment, &temp_size, &unpadded_row_size, &padded_row_size)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glReadPixels", "dimensions out of range"); return error::kNoError; } GLint dest_x_offset = std::max(-x, 0); uint32 dest_row_offset; if (!GLES2Util::ComputeImageDataSizes( dest_x_offset, 1, 1, format, type, state_.pack_alignment, &dest_row_offset, NULL, NULL)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glReadPixels", "dimensions out of range"); return error::kNoError; } // Copy each row into the larger dest rect. int8* dst = static_cast(pixels); GLint read_x = std::max(0, x); GLint read_end_x = std::max(0, std::min(max_size.width(), max_x)); GLint read_width = read_end_x - read_x; for (GLint yy = 0; yy < height; ++yy) { GLint ry = y + yy; // Clear the row. memset(dst, 0, unpadded_row_size); // If the row is in range, copy it. if (ry >= 0 && ry < max_size.height() && read_width > 0) { glReadPixels( read_x, ry, read_width, 1, format, type, dst + dest_row_offset); } dst += padded_row_size; } } else { if (async && features().use_async_readpixels) { GLuint buffer = 0; glGenBuffersARB(1, &buffer); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, buffer); // For ANGLE client version 2, GL_STREAM_READ is not available. const GLenum usage_hint = feature_info_->gl_version_info().is_angle ? GL_STATIC_DRAW : GL_STREAM_READ; glBufferData(GL_PIXEL_PACK_BUFFER_ARB, pixels_size, NULL, usage_hint); GLenum error = glGetError(); if (error == GL_NO_ERROR) { glReadPixels(x, y, width, height, format, type, 0); pending_readpixel_fences_.push(linked_ptr( new FenceCallback())); WaitForReadPixels(base::Bind( &GLES2DecoderImpl::FinishReadPixels, base::internal::SupportsWeakPtrBase::StaticAsWeakPtr (this), c, buffer)); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); return error::kNoError; } else { // On error, unbind pack buffer and fall through to sync readpixels glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); glDeleteBuffersARB(1, &buffer); } } glReadPixels(x, y, width, height, format, type, pixels); } GLenum error = LOCAL_PEEK_GL_ERROR("glReadPixels"); if (error == GL_NO_ERROR) { if (result != NULL) { *result = true; } FinishReadPixels(c, 0); } return error::kNoError; } error::Error GLES2DecoderImpl::HandlePixelStorei(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::PixelStorei& c = *static_cast(cmd_data); GLenum pname = c.pname; GLenum param = c.param; if (!validators_->pixel_store.IsValid(pname)) { LOCAL_SET_GL_ERROR_INVALID_ENUM("glPixelStorei", pname, "pname"); return error::kNoError; } switch (pname) { case GL_PACK_ALIGNMENT: case GL_UNPACK_ALIGNMENT: if (!validators_->pixel_store_alignment.IsValid(param)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glPixelStorei", "param GL_INVALID_VALUE"); return error::kNoError; } break; case GL_UNPACK_FLIP_Y_CHROMIUM: unpack_flip_y_ = (param != 0); return error::kNoError; case GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM: unpack_premultiply_alpha_ = (param != 0); return error::kNoError; case GL_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM: unpack_unpremultiply_alpha_ = (param != 0); return error::kNoError; default: break; } glPixelStorei(pname, param); switch (pname) { case GL_PACK_ALIGNMENT: state_.pack_alignment = param; break; case GL_PACK_REVERSE_ROW_ORDER_ANGLE: state_.pack_reverse_row_order = (param != 0); break; case GL_UNPACK_ALIGNMENT: state_.unpack_alignment = param; break; default: // Validation should have prevented us from getting here. NOTREACHED(); break; } return error::kNoError; } error::Error GLES2DecoderImpl::HandlePostSubBufferCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::PostSubBufferCHROMIUM& c = *static_cast(cmd_data); TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandlePostSubBufferCHROMIUM"); { TRACE_EVENT_SYNTHETIC_DELAY("gpu.PresentingFrame"); } if (!supports_post_sub_buffer_) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glPostSubBufferCHROMIUM", "command not supported by surface"); return error::kNoError; } bool is_tracing; TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("gpu.debug"), &is_tracing); if (is_tracing) { bool is_offscreen = !!offscreen_target_frame_buffer_.get(); ScopedFrameBufferBinder binder(this, GetBackbufferServiceId()); gpu_state_tracer_->TakeSnapshotWithCurrentFramebuffer( is_offscreen ? offscreen_size_ : surface_->GetSize()); } if (surface_->PostSubBuffer(c.x, c.y, c.width, c.height)) { return error::kNoError; } else { LOG(ERROR) << "Context lost because PostSubBuffer failed."; return error::kLostContext; } } error::Error GLES2DecoderImpl::HandleScheduleOverlayPlaneCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::ScheduleOverlayPlaneCHROMIUM& c = *static_cast(cmd_data); TextureRef* ref = texture_manager()->GetTexture(c.overlay_texture_id); if (!ref) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glScheduleOverlayPlaneCHROMIUM", "unknown texture"); return error::kNoError; } gfx::GLImage* image = ref->texture()->GetLevelImage(ref->texture()->target(), 0); if (!image) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glScheduleOverlayPlaneCHROMIUM", "unsupported texture format"); return error::kNoError; } gfx::OverlayTransform transform = GetGFXOverlayTransform(c.plane_transform); if (transform == gfx::OVERLAY_TRANSFORM_INVALID) { LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, "glScheduleOverlayPlaneCHROMIUM", "invalid transform enum"); return error::kNoError; } if (!surface_->ScheduleOverlayPlane( c.plane_z_order, transform, image, gfx::Rect(c.bounds_x, c.bounds_y, c.bounds_width, c.bounds_height), gfx::RectF(c.uv_x, c.uv_y, c.uv_width, c.uv_height))) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glScheduleOverlayPlaneCHROMIUM", "failed to schedule overlay"); } return error::kNoError; } error::Error GLES2DecoderImpl::GetAttribLocationHelper( GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset, const std::string& name_str) { if (!StringIsValidForGLES(name_str.c_str())) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glGetAttribLocation", "Invalid character"); return error::kNoError; } Program* program = GetProgramInfoNotShader( client_id, "glGetAttribLocation"); if (!program) { return error::kNoError; } if (!program->IsValid()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glGetAttribLocation", "program not linked"); return error::kNoError; } GLint* location = GetSharedMemoryAs( location_shm_id, location_shm_offset, sizeof(GLint)); if (!location) { return error::kOutOfBounds; } // Require the client to init this incase the context is lost and we are no // longer executing commands. if (*location != -1) { return error::kGenericError; } *location = program->GetAttribLocation(name_str); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetAttribLocation( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetAttribLocation& c = *static_cast(cmd_data); Bucket* bucket = GetBucket(c.name_bucket_id); if (!bucket) { return error::kInvalidArguments; } std::string name_str; if (!bucket->GetAsString(&name_str)) { return error::kInvalidArguments; } return GetAttribLocationHelper( c.program, c.location_shm_id, c.location_shm_offset, name_str); } error::Error GLES2DecoderImpl::GetUniformLocationHelper( GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset, const std::string& name_str) { if (!StringIsValidForGLES(name_str.c_str())) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glGetUniformLocation", "Invalid character"); return error::kNoError; } Program* program = GetProgramInfoNotShader( client_id, "glGetUniformLocation"); if (!program) { return error::kNoError; } if (!program->IsValid()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glGetUniformLocation", "program not linked"); return error::kNoError; } GLint* location = GetSharedMemoryAs( location_shm_id, location_shm_offset, sizeof(GLint)); if (!location) { return error::kOutOfBounds; } // Require the client to init this incase the context is lost an we are no // longer executing commands. if (*location != -1) { return error::kGenericError; } *location = program->GetUniformFakeLocation(name_str); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetUniformLocation( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetUniformLocation& c = *static_cast(cmd_data); Bucket* bucket = GetBucket(c.name_bucket_id); if (!bucket) { return error::kInvalidArguments; } std::string name_str; if (!bucket->GetAsString(&name_str)) { return error::kInvalidArguments; } return GetUniformLocationHelper( c.program, c.location_shm_id, c.location_shm_offset, name_str); } error::Error GLES2DecoderImpl::HandleGetUniformIndices( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetUniformIndices& c = *static_cast(cmd_data); Bucket* bucket = GetBucket(c.names_bucket_id); if (!bucket) { return error::kInvalidArguments; } GLsizei count = 0; std::vector names; std::vector len; if (!bucket->GetAsStrings(&count, &names, &len) || count <= 0) { return error::kInvalidArguments; } typedef cmds::GetUniformIndices::Result Result; Result* result = GetSharedMemoryAs( c.indices_shm_id, c.indices_shm_offset, Result::ComputeSize(static_cast(count))); GLuint* indices = result ? result->GetData() : NULL; if (indices == NULL) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } Program* program = GetProgramInfoNotShader(c.program, "glGetUniformIndices"); if (!program) { return error::kNoError; } GLuint service_id = program->service_id(); GLint link_status = GL_FALSE; glGetProgramiv(service_id, GL_LINK_STATUS, &link_status); if (link_status != GL_TRUE) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glGetUniformIndices", "program not linked"); return error::kNoError; } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetUniformIndices"); glGetUniformIndices(service_id, count, &names[0], indices); GLenum error = glGetError(); if (error == GL_NO_ERROR) { result->SetNumResults(count); } else { LOCAL_SET_GL_ERROR(error, "GetUniformIndices", ""); } return error::kNoError; } error::Error GLES2DecoderImpl::GetFragDataLocationHelper( GLuint client_id, uint32 location_shm_id, uint32 location_shm_offset, const std::string& name_str) { GLint* location = GetSharedMemoryAs( location_shm_id, location_shm_offset, sizeof(GLint)); if (!location) { return error::kOutOfBounds; } // Require the client to init this incase the context is lost and we are no // longer executing commands. if (*location != -1) { return error::kGenericError; } Program* program = GetProgramInfoNotShader( client_id, "glGetFragDataLocation"); if (!program) { return error::kNoError; } *location = glGetFragDataLocation(program->service_id(), name_str.c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetFragDataLocation( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetFragDataLocation& c = *static_cast(cmd_data); Bucket* bucket = GetBucket(c.name_bucket_id); if (!bucket) { return error::kInvalidArguments; } std::string name_str; if (!bucket->GetAsString(&name_str)) { return error::kInvalidArguments; } return GetFragDataLocationHelper( c.program, c.location_shm_id, c.location_shm_offset, name_str); } error::Error GLES2DecoderImpl::HandleGetUniformBlockIndex( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetUniformBlockIndex& c = *static_cast(cmd_data); Bucket* bucket = GetBucket(c.name_bucket_id); if (!bucket) { return error::kInvalidArguments; } std::string name_str; if (!bucket->GetAsString(&name_str)) { return error::kInvalidArguments; } GLuint* index = GetSharedMemoryAs( c.index_shm_id, c.index_shm_offset, sizeof(GLuint)); if (!index) { return error::kOutOfBounds; } // Require the client to init this in case the context is lost and we are no // longer executing commands. if (*index != GL_INVALID_INDEX) { return error::kGenericError; } Program* program = GetProgramInfoNotShader( c.program, "glGetUniformBlockIndex"); if (!program) { return error::kNoError; } *index = glGetUniformBlockIndex(program->service_id(), name_str.c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetString(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetString& c = *static_cast(cmd_data); GLenum name = static_cast(c.name); if (!validators_->string_type.IsValid(name)) { LOCAL_SET_GL_ERROR_INVALID_ENUM("glGetString", name, "name"); return error::kNoError; } const char* str = nullptr; std::string extensions; switch (name) { case GL_VERSION: str = "OpenGL ES 2.0 Chromium"; break; case GL_SHADING_LANGUAGE_VERSION: str = "OpenGL ES GLSL ES 1.0 Chromium"; break; case GL_RENDERER: case GL_VENDOR: // Return the unmasked VENDOR/RENDERER string for WebGL contexts. // They are used by WEBGL_debug_renderer_info. if (!force_webgl_glsl_validation_) str = "Chromium"; else str = reinterpret_cast(glGetString(name)); break; case GL_EXTENSIONS: { // For WebGL contexts, strip out the OES derivatives and // EXT frag depth extensions if they have not been enabled. if (force_webgl_glsl_validation_) { extensions = feature_info_->extensions(); if (!derivatives_explicitly_enabled_) { size_t offset = extensions.find(kOESDerivativeExtension); if (std::string::npos != offset) { extensions.replace(offset, arraysize(kOESDerivativeExtension), std::string()); } } if (!frag_depth_explicitly_enabled_) { size_t offset = extensions.find(kEXTFragDepthExtension); if (std::string::npos != offset) { extensions.replace(offset, arraysize(kEXTFragDepthExtension), std::string()); } } if (!draw_buffers_explicitly_enabled_) { size_t offset = extensions.find(kEXTDrawBuffersExtension); if (std::string::npos != offset) { extensions.replace(offset, arraysize(kEXTDrawBuffersExtension), std::string()); } } if (!shader_texture_lod_explicitly_enabled_) { size_t offset = extensions.find(kEXTShaderTextureLodExtension); if (std::string::npos != offset) { extensions.replace(offset, arraysize(kEXTShaderTextureLodExtension), std::string()); } } } else { extensions = feature_info_->extensions().c_str(); } if (supports_post_sub_buffer_) extensions += " GL_CHROMIUM_post_sub_buffer"; str = extensions.c_str(); } break; default: str = reinterpret_cast(glGetString(name)); break; } Bucket* bucket = CreateBucket(c.bucket_id); bucket->SetFromString(str); return error::kNoError; } error::Error GLES2DecoderImpl::HandleBufferData(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::BufferData& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLsizeiptr size = static_cast(c.size); uint32 data_shm_id = static_cast(c.data_shm_id); uint32 data_shm_offset = static_cast(c.data_shm_offset); GLenum usage = static_cast(c.usage); const void* data = NULL; if (data_shm_id != 0 || data_shm_offset != 0) { data = GetSharedMemoryAs(data_shm_id, data_shm_offset, size); if (!data) { return error::kOutOfBounds; } } buffer_manager()->ValidateAndDoBufferData(&state_, target, size, data, usage); return error::kNoError; } void GLES2DecoderImpl::DoBufferSubData( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data) { // Just delegate it. Some validation is actually done before this. buffer_manager()->ValidateAndDoBufferSubData( &state_, target, offset, size, data); } bool GLES2DecoderImpl::ClearLevel( Texture* texture, unsigned target, int level, unsigned internal_format, unsigned format, unsigned type, int width, int height, bool is_texture_immutable) { uint32 channels = GLES2Util::GetChannelsForFormat(format); if (feature_info_->feature_flags().angle_depth_texture && (channels & GLES2Util::kDepth) != 0) { // It's a depth format and ANGLE doesn't allow texImage2D or texSubImage2D // on depth formats. GLuint fb = 0; glGenFramebuffersEXT(1, &fb); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fb); bool have_stencil = (channels & GLES2Util::kStencil) != 0; GLenum attachment = have_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, attachment, target, texture->service_id(), level); // ANGLE promises a depth only attachment ok. if (glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE) { return false; } glClearStencil(0); state_.SetDeviceStencilMaskSeparate(GL_FRONT, kDefaultStencilMask); state_.SetDeviceStencilMaskSeparate(GL_BACK, kDefaultStencilMask); glClearDepth(1.0f); state_.SetDeviceDepthMask(GL_TRUE); state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); glClear(GL_DEPTH_BUFFER_BIT | (have_stencil ? GL_STENCIL_BUFFER_BIT : 0)); RestoreClearState(); glDeleteFramebuffersEXT(1, &fb); Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER_EXT); GLuint fb_service_id = framebuffer ? framebuffer->service_id() : GetBackbufferServiceId(); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fb_service_id); return true; } static const uint32 kMaxZeroSize = 1024 * 1024 * 4; uint32 size; uint32 padded_row_size; if (!GLES2Util::ComputeImageDataSizes( width, height, 1, format, type, state_.unpack_alignment, &size, NULL, &padded_row_size)) { return false; } TRACE_EVENT1("gpu", "GLES2DecoderImpl::ClearLevel", "size", size); int tile_height; if (size > kMaxZeroSize) { if (kMaxZeroSize < padded_row_size) { // That'd be an awfully large texture. return false; } // We should never have a large total size with a zero row size. DCHECK_GT(padded_row_size, 0U); tile_height = kMaxZeroSize / padded_row_size; if (!GLES2Util::ComputeImageDataSizes( width, tile_height, 1, format, type, state_.unpack_alignment, &size, NULL, NULL)) { return false; } } else { tile_height = height; } // Assumes the size has already been checked. scoped_ptr zero(new char[size]); memset(zero.get(), 0, size); glBindTexture(texture->target(), texture->service_id()); bool has_images = texture->HasImages(); GLint y = 0; while (y < height) { GLint h = y + tile_height > height ? height - y : tile_height; if (is_texture_immutable || h != height || has_images) { glTexSubImage2D(target, level, 0, y, width, h, format, type, zero.get()); } else { glTexImage2D( target, level, internal_format, width, h, 0, format, type, zero.get()); } y += tile_height; } TextureRef* bound_texture = texture_manager()->GetTextureInfoForTarget(&state_, texture->target()); glBindTexture(texture->target(), bound_texture ? bound_texture->service_id() : 0); return true; } namespace { const int kS3TCBlockWidth = 4; const int kS3TCBlockHeight = 4; const int kS3TCDXT1BlockSize = 8; const int kS3TCDXT3AndDXT5BlockSize = 16; const int kEACAndETC2BlockSize = 4; bool IsValidDXTSize(GLint level, GLsizei size) { return (size == 1) || (size == 2) || !(size % kS3TCBlockWidth); } bool IsValidPVRTCSize(GLint level, GLsizei size) { return GLES2Util::IsPOT(size); } } // anonymous namespace. bool GLES2DecoderImpl::ValidateCompressedTexFuncData( const char* function_name, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei size) { base::CheckedNumeric bytes_required(0); switch (format) { case GL_ATC_RGB_AMD: case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL_ETC1_RGB8_OES: bytes_required = (width + kS3TCBlockWidth - 1) / kS3TCBlockWidth; bytes_required *= (height + kS3TCBlockHeight - 1) / kS3TCBlockHeight; bytes_required *= kS3TCDXT1BlockSize; break; case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: bytes_required = (width + kS3TCBlockWidth - 1) / kS3TCBlockWidth; bytes_required *= (height + kS3TCBlockHeight - 1) / kS3TCBlockHeight; bytes_required *= kS3TCDXT3AndDXT5BlockSize; break; case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: bytes_required = std::max(width, 8); bytes_required *= std::max(height, 8); bytes_required *= 4; bytes_required += 7; bytes_required /= 8; break; case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: bytes_required = std::max(width, 16); bytes_required *= std::max(height, 8); bytes_required *= 2; bytes_required += 7; bytes_required /= 8; break; // ES3 formats. case GL_COMPRESSED_R11_EAC: case GL_COMPRESSED_SIGNED_R11_EAC: case GL_COMPRESSED_RGB8_ETC2: case GL_COMPRESSED_SRGB8_ETC2: case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: bytes_required = (width + kEACAndETC2BlockSize - 1) / kEACAndETC2BlockSize; bytes_required *= (height + kEACAndETC2BlockSize - 1) / kEACAndETC2BlockSize; bytes_required *= 8; bytes_required *= depth; break; case GL_COMPRESSED_RG11_EAC: case GL_COMPRESSED_SIGNED_RG11_EAC: case GL_COMPRESSED_RGBA8_ETC2_EAC: case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: bytes_required = (width + kEACAndETC2BlockSize - 1) / kEACAndETC2BlockSize; bytes_required *= (height + kEACAndETC2BlockSize - 1) / kEACAndETC2BlockSize; bytes_required *= 16; bytes_required *= depth; break; default: LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, format, "format"); return false; } if (!bytes_required.IsValid() || size != bytes_required.ValueOrDefault(0)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, function_name, "size is not correct for dimensions"); return false; } return true; } bool GLES2DecoderImpl::ValidateCompressedTexDimensions( const char* function_name, GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format) { switch (format) { case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: DCHECK_EQ(1, depth); // 2D formats. if (!IsValidDXTSize(level, width) || !IsValidDXTSize(level, height)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "width or height invalid for level"); return false; } return true; case GL_ATC_RGB_AMD: case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: case GL_ETC1_RGB8_OES: DCHECK_EQ(1, depth); // 2D formats. if (width <= 0 || height <= 0) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "width or height invalid for level"); return false; } return true; case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: DCHECK_EQ(1, depth); // 2D formats. if (!IsValidPVRTCSize(level, width) || !IsValidPVRTCSize(level, height)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "width or height invalid for level"); return false; } return true; // ES3 formats. case GL_COMPRESSED_R11_EAC: case GL_COMPRESSED_SIGNED_R11_EAC: case GL_COMPRESSED_RG11_EAC: case GL_COMPRESSED_SIGNED_RG11_EAC: case GL_COMPRESSED_RGB8_ETC2: case GL_COMPRESSED_SRGB8_ETC2: case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: case GL_COMPRESSED_RGBA8_ETC2_EAC: case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: if (width <= 0 || height <= 0 || depth <= 0) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "width, height, or depth invalid"); return false; } if (target == GL_TEXTURE_3D) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "target invalid for format"); return false; } return true; default: return false; } } bool GLES2DecoderImpl::ValidateCompressedTexSubDimensions( const char* function_name, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, Texture* texture) { if (xoffset < 0 || yoffset < 0 || zoffset < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, function_name, "x/y/z offset < 0"); return false; } switch (format) { case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: { const int kBlockWidth = 4; const int kBlockHeight = 4; if ((xoffset % kBlockWidth) || (yoffset % kBlockHeight)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "xoffset or yoffset not multiple of 4"); return false; } GLsizei tex_width = 0; GLsizei tex_height = 0; if (!texture->GetLevelSize(target, level, &tex_width, &tex_height, nullptr) || width - xoffset > tex_width || height - yoffset > tex_height) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "dimensions out of range"); return false; } return ValidateCompressedTexDimensions( function_name, target, level, width, height, 1, format); } case GL_ATC_RGB_AMD: case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "not supported for ATC textures"); return false; } case GL_ETC1_RGB8_OES: { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "not supported for ECT1_RGB8_OES textures"); return false; } case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: { if ((xoffset != 0) || (yoffset != 0)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "xoffset and yoffset must be zero"); return false; } GLsizei tex_width = 0; GLsizei tex_height = 0; if (!texture->GetLevelSize(target, level, &tex_width, &tex_height, nullptr) || width != tex_width || height != tex_height) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "dimensions must match existing texture level dimensions"); return false; } return ValidateCompressedTexDimensions( function_name, target, level, width, height, 1, format); } // ES3 formats case GL_COMPRESSED_R11_EAC: case GL_COMPRESSED_SIGNED_R11_EAC: case GL_COMPRESSED_RG11_EAC: case GL_COMPRESSED_SIGNED_RG11_EAC: case GL_COMPRESSED_RGB8_ETC2: case GL_COMPRESSED_SRGB8_ETC2: case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: case GL_COMPRESSED_RGBA8_ETC2_EAC: case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: { const int kBlockSize = 4; GLsizei tex_width, tex_height; if (target == GL_TEXTURE_3D || !texture->GetLevelSize(target, level, &tex_width, &tex_height, nullptr) || (xoffset % kBlockSize) || (yoffset % kBlockSize) || ((width % kBlockSize) && xoffset + width != tex_width) || ((height % kBlockSize) && yoffset + height != tex_height)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "dimensions must match existing texture level dimensions"); return false; } return true; } default: return false; } } error::Error GLES2DecoderImpl::DoCompressedTexImage2D( GLenum target, GLint level, GLenum internal_format, GLsizei width, GLsizei height, GLint border, GLsizei image_size, const void* data) { if (!validators_->texture_target.IsValid(target)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glCompressedTexImage2D", target, "target"); return error::kNoError; } if (!validators_->compressed_texture_format.IsValid( internal_format)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glCompressedTexImage2D", internal_format, "internal_format"); return error::kNoError; } if (!texture_manager()->ValidForTarget(target, level, width, height, 1) || border != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexImage2D", "dimensions out of range"); return error::kNoError; } TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexImage2D", "unknown texture target"); return error::kNoError; } Texture* texture = texture_ref->texture(); if (texture->IsImmutable()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCompressedTexImage2D", "texture is immutable"); return error::kNoError; } if (!ValidateCompressedTexDimensions("glCompressedTexImage2D", target, level, width, height, 1, internal_format) || !ValidateCompressedTexFuncData("glCompressedTexImage2D", width, height, 1, internal_format, image_size)) { return error::kNoError; } if (!EnsureGPUMemoryAvailable(image_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glCompressedTexImage2D", "out of memory"); return error::kNoError; } if (texture->IsAttachedToFramebuffer()) { framebuffer_state_.clear_state_dirty = true; } scoped_ptr zero; if (!data) { zero.reset(new int8[image_size]); memset(zero.get(), 0, image_size); data = zero.get(); } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glCompressedTexImage2D"); glCompressedTexImage2D( target, level, internal_format, width, height, border, image_size, data); GLenum error = LOCAL_PEEK_GL_ERROR("glCompressedTexImage2D"); if (error == GL_NO_ERROR) { texture_manager()->SetLevelInfo( texture_ref, target, level, internal_format, width, height, 1, border, 0, 0, true); } // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleCompressedTexImage2D( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::CompressedTexImage2D& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLenum internal_format = static_cast(c.internalformat); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLint border = static_cast(c.border); GLsizei image_size = static_cast(c.imageSize); uint32 data_shm_id = static_cast(c.data_shm_id); uint32 data_shm_offset = static_cast(c.data_shm_offset); const void* data = NULL; if (data_shm_id != 0 || data_shm_offset != 0) { data = GetSharedMemoryAs( data_shm_id, data_shm_offset, image_size); if (!data) { return error::kOutOfBounds; } } return DoCompressedTexImage2D( target, level, internal_format, width, height, border, image_size, data); } error::Error GLES2DecoderImpl::HandleCompressedTexImage2DBucket( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::CompressedTexImage2DBucket& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLenum internal_format = static_cast(c.internalformat); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLint border = static_cast(c.border); Bucket* bucket = GetBucket(c.bucket_id); if (!bucket) { return error::kInvalidArguments; } uint32 data_size = bucket->size(); GLsizei imageSize = data_size; const void* data = bucket->GetData(0, data_size); if (!data) { return error::kInvalidArguments; } return DoCompressedTexImage2D( target, level, internal_format, width, height, border, imageSize, data); } error::Error GLES2DecoderImpl::HandleCompressedTexSubImage2DBucket( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::CompressedTexSubImage2DBucket& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLint xoffset = static_cast(c.xoffset); GLint yoffset = static_cast(c.yoffset); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLenum format = static_cast(c.format); Bucket* bucket = GetBucket(c.bucket_id); if (!bucket) { return error::kInvalidArguments; } uint32 data_size = bucket->size(); GLsizei imageSize = data_size; const void* data = bucket->GetData(0, data_size); if (!data) { return error::kInvalidArguments; } if (!validators_->texture_target.IsValid(target)) { LOCAL_SET_GL_ERROR( GL_INVALID_ENUM, "glCompressedTexSubImage2D", "target"); return error::kNoError; } if (!validators_->compressed_texture_format.IsValid(format)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glCompressedTexSubImage2D", format, "format"); return error::kNoError; } if (width < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexSubImage2D", "width < 0"); return error::kNoError; } if (height < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexSubImage2D", "height < 0"); return error::kNoError; } if (imageSize < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexSubImage2D", "imageSize < 0"); return error::kNoError; } DoCompressedTexSubImage2D( target, level, xoffset, yoffset, width, height, format, imageSize, data); return error::kNoError; } error::Error GLES2DecoderImpl::DoCompressedTexImage3D( GLenum target, GLint level, GLenum internal_format, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei image_size, const void* data) { if (!validators_->texture_3_d_target.IsValid(target)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glCompressedTexImage3D", target, "target"); return error::kNoError; } if (!validators_->compressed_texture_format.IsValid( internal_format)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glCompressedTexImage3D", internal_format, "internal_format"); return error::kNoError; } if (!texture_manager()->ValidForTarget(target, level, width, height, depth) || border != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexImage3D", "dimensions out of range"); return error::kNoError; } TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexImage3D", "unknown texture target"); return error::kNoError; } Texture* texture = texture_ref->texture(); if (texture->IsImmutable()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCompressedTexImage3D", "texture is immutable"); return error::kNoError; } if (!ValidateCompressedTexDimensions("glCompressedTexImage3D", target, level, width, height, depth, internal_format) || !ValidateCompressedTexFuncData("glCompressedTexImage3D", width, height, depth, internal_format, image_size)) { return error::kNoError; } if (!EnsureGPUMemoryAvailable(image_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glCompressedTexImage3D", "out of memory"); return error::kNoError; } if (texture->IsAttachedToFramebuffer()) { framebuffer_state_.clear_state_dirty = true; } scoped_ptr zero; if (!data) { zero.reset(new int8[image_size]); memset(zero.get(), 0, image_size); data = zero.get(); } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glCompressedTexImage3D"); glCompressedTexImage3D(target, level, internal_format, width, height, depth, border, image_size, data); GLenum error = LOCAL_PEEK_GL_ERROR("glCompressedTexImage3D"); if (error == GL_NO_ERROR) { texture_manager()->SetLevelInfo( texture_ref, target, level, internal_format, width, height, depth, border, 0, 0, true); } // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleCompressedTexImage3D( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::CompressedTexImage3D& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLenum internal_format = static_cast(c.internalformat); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLsizei depth = static_cast(c.depth); GLint border = static_cast(c.border); GLsizei image_size = static_cast(c.imageSize); uint32 data_shm_id = static_cast(c.data_shm_id); uint32 data_shm_offset = static_cast(c.data_shm_offset); const void* data = NULL; if (data_shm_id != 0 || data_shm_offset != 0) { data = GetSharedMemoryAs( data_shm_id, data_shm_offset, image_size); if (!data) { return error::kOutOfBounds; } } return DoCompressedTexImage3D(target, level, internal_format, width, height, depth, border, image_size, data); } error::Error GLES2DecoderImpl::HandleCompressedTexImage3DBucket( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::CompressedTexImage3DBucket& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLenum internal_format = static_cast(c.internalformat); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLsizei depth = static_cast(c.depth); GLint border = static_cast(c.border); Bucket* bucket = GetBucket(c.bucket_id); if (!bucket) { return error::kInvalidArguments; } uint32 data_size = bucket->size(); GLsizei imageSize = data_size; const void* data = bucket->GetData(0, data_size); if (!data) { return error::kInvalidArguments; } return DoCompressedTexImage3D(target, level, internal_format, width, height, depth, border, imageSize, data); } void GLES2DecoderImpl::DoCompressedTexSubImage3D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei image_size, const void* data) { if (!validators_->texture_3_d_target.IsValid(target)) { LOCAL_SET_GL_ERROR( GL_INVALID_ENUM, "glCompressedTexSubImage3D", "target"); return; } if (!validators_->compressed_texture_format.IsValid(format)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glCompressedTexSubImage3D", format, "format"); return; } if (width < 0 || height < 0 || depth < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexSubImage3D", "size < 0"); return; } if (image_size < 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexSubImage3D", "imageSize < 0"); return; } TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCompressedTexSubImage3D", "unknown texture for target"); return; } Texture* texture = texture_ref->texture(); GLenum type = 0, internal_format = 0; if (!texture->GetLevelType(target, level, &type, &internal_format)) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCompressedTexSubImage3D", "level does not exist"); return; } if (internal_format != format) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCompressedTexSubImage3D", "format does not match internal format"); return; } if (!texture->ValidForTexture(target, level, xoffset, yoffset, zoffset, width, height, depth, type)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCompressedTexSubImage3D", "bad dimensions"); return; } if (!ValidateCompressedTexFuncData("glCompressedTexSubImage3D", width, height, depth, format, image_size) || !ValidateCompressedTexSubDimensions("glCompressedTexSubImage3D", target, level, xoffset, yoffset, zoffset, width, height, depth, format, texture)) { return; } // Note: There is no need to deal with texture cleared tracking here // because the validation above means you can only get here if the level // is already a matching compressed format and in that case // CompressedTexImage3D already cleared the texture. glCompressedTexSubImage3D( target, level, xoffset, yoffset, zoffset, width, height, depth, format, image_size, data); // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); } error::Error GLES2DecoderImpl::HandleCompressedTexSubImage3DBucket( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::CompressedTexSubImage3DBucket& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLint xoffset = static_cast(c.xoffset); GLint yoffset = static_cast(c.yoffset); GLint zoffset = static_cast(c.zoffset); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLsizei depth = static_cast(c.depth); GLenum format = static_cast(c.format); Bucket* bucket = GetBucket(c.bucket_id); if (!bucket) { return error::kInvalidArguments; } uint32 data_size = bucket->size(); GLsizei image_size = data_size; const void* data = bucket->GetData(0, data_size); if (!data) { return error::kInvalidArguments; } DoCompressedTexSubImage3D( target, level, xoffset, yoffset, zoffset, width, height, depth, format, image_size, data); return error::kNoError; } error::Error GLES2DecoderImpl::HandleTexImage2D(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::TexImage2D& c = *static_cast(cmd_data); TRACE_EVENT2("gpu", "GLES2DecoderImpl::HandleTexImage2D", "width", c.width, "height", c.height); // Set as failed for now, but if it successed, this will be set to not failed. texture_state_.tex_image_failed = true; GLenum target = static_cast(c.target); GLint level = static_cast(c.level); // TODO(kloveless): Change TexImage2D command to use unsigned integer // for internalformat. GLenum internal_format = static_cast(c.internalformat); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLint border = static_cast(c.border); GLenum format = static_cast(c.format); GLenum type = static_cast(c.type); uint32 pixels_shm_id = static_cast(c.pixels_shm_id); uint32 pixels_shm_offset = static_cast(c.pixels_shm_offset); uint32 pixels_size; if (!GLES2Util::ComputeImageDataSizes( width, height, 1, format, type, state_.unpack_alignment, &pixels_size, NULL, NULL)) { return error::kOutOfBounds; } const void* pixels = NULL; if (pixels_shm_id != 0 || pixels_shm_offset != 0) { pixels = GetSharedMemoryAs( pixels_shm_id, pixels_shm_offset, pixels_size); if (!pixels) { return error::kOutOfBounds; } } // For testing only. Allows us to stress the ability to respond to OOM errors. if (workarounds().simulate_out_of_memory_on_large_textures && (width * height >= 4096 * 4096)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glTexImage2D", "synthetic out of memory"); return error::kNoError; } TextureManager::DoTexImageArguments args = { target, level, internal_format, width, height, 1, border, format, type, pixels, pixels_size, TextureManager::DoTexImageArguments::kTexImage2D }; texture_manager()->ValidateAndDoTexImage( &texture_state_, &state_, &framebuffer_state_, "glTexImage2D", args); // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleTexImage3D(uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::TexImage3D& c = *static_cast(cmd_data); TRACE_EVENT2("gpu", "GLES2DecoderImpl::HandleTexImage3D", "widthXheight", c.width * c.height, "depth", c.depth); // Set as failed for now, but if it successed, this will be set to not failed. texture_state_.tex_image_failed = true; GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLenum internal_format = static_cast(c.internalformat); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLsizei depth = static_cast(c.depth); GLint border = static_cast(c.border); GLenum format = static_cast(c.format); GLenum type = static_cast(c.type); uint32 pixels_shm_id = static_cast(c.pixels_shm_id); uint32 pixels_shm_offset = static_cast(c.pixels_shm_offset); uint32 pixels_size; if (!GLES2Util::ComputeImageDataSizes( width, height, depth, format, type, state_.unpack_alignment, &pixels_size, NULL, NULL)) { return error::kOutOfBounds; } const void* pixels = NULL; if (pixels_shm_id != 0 || pixels_shm_offset != 0) { pixels = GetSharedMemoryAs( pixels_shm_id, pixels_shm_offset, pixels_size); if (!pixels) { return error::kOutOfBounds; } } // For testing only. Allows us to stress the ability to respond to OOM errors. if (workarounds().simulate_out_of_memory_on_large_textures && (width * height * depth >= 4096 * 4096)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glTexImage3D", "synthetic out of memory"); return error::kNoError; } TextureManager::DoTexImageArguments args = { target, level, internal_format, width, height, depth, border, format, type, pixels, pixels_size, TextureManager::DoTexImageArguments::kTexImage3D }; texture_manager()->ValidateAndDoTexImage( &texture_state_, &state_, &framebuffer_state_, "glTexImage3D", args); // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); return error::kNoError; } void GLES2DecoderImpl::DoCompressedTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei image_size, const void * data) { TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCompressedTexSubImage2D", "unknown texture for target"); return; } Texture* texture = texture_ref->texture(); GLenum type = 0; GLenum internal_format = 0; if (!texture->GetLevelType(target, level, &type, &internal_format)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCompressedTexSubImage2D", "level does not exist."); return; } if (internal_format != format) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCompressedTexSubImage2D", "format does not match internal format."); return; } if (!texture->ValidForTexture(target, level, xoffset, yoffset, 0, width, height, 1, type)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCompressedTexSubImage2D", "bad dimensions."); return; } if (!ValidateCompressedTexFuncData("glCompressedTexSubImage2D", width, height, 1, format, image_size) || !ValidateCompressedTexSubDimensions("glCompressedTexSubImage2D", target, level, xoffset, yoffset, 0, width, height, 1, format, texture)) { return; } // Note: There is no need to deal with texture cleared tracking here // because the validation above means you can only get here if the level // is already a matching compressed format and in that case // CompressedTexImage2D already cleared the texture. glCompressedTexSubImage2D( target, level, xoffset, yoffset, width, height, format, image_size, data); // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); } static void Clip( GLint start, GLint range, GLint sourceRange, GLint* out_start, GLint* out_range) { DCHECK(out_start); DCHECK(out_range); if (start < 0) { range += start; start = 0; } GLint end = start + range; if (end > sourceRange) { range -= end - sourceRange; } *out_start = start; *out_range = range; } void GLES2DecoderImpl::DoCopyTexImage2D( GLenum target, GLint level, GLenum internal_format, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { DCHECK(!ShouldDeferReads()); TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexImage2D", "unknown texture for target"); return; } Texture* texture = texture_ref->texture(); if (texture->IsImmutable()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexImage2D", "texture is immutable"); return; } if (!texture_manager()->ValidForTarget(target, level, width, height, 1) || border != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCopyTexImage2D", "dimensions out of range"); return; } if (!texture_manager()->ValidateFormatAndTypeCombination( state_.GetErrorState(), "glCopyTexImage2D", internal_format, GL_UNSIGNED_BYTE)) { return; } // Check we have compatible formats. GLenum read_format = GetBoundReadFrameBufferInternalFormat(); uint32 channels_exist = GLES2Util::GetChannelsForFormat(read_format); uint32 channels_needed = GLES2Util::GetChannelsForFormat(internal_format); if ((channels_needed & channels_exist) != channels_needed) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexImage2D", "incompatible format"); return; } if ((channels_needed & (GLES2Util::kDepth | GLES2Util::kStencil)) != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexImage2D", "can not be used with depth or stencil textures"); return; } uint32 estimated_size = 0; if (!GLES2Util::ComputeImageDataSizes( width, height, 1, internal_format, GL_UNSIGNED_BYTE, state_.unpack_alignment, &estimated_size, NULL, NULL)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glCopyTexImage2D", "dimensions too large"); return; } if (!EnsureGPUMemoryAvailable(estimated_size)) { LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopyTexImage2D", "out of memory"); return; } if (!CheckBoundReadFramebufferColorAttachment("glCopyTexImage2D")) { return; } if (FormsTextureCopyingFeedbackLoop(texture_ref, level)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexImage2D", "source and destination textures are the same"); return; } if (!CheckBoundFramebuffersValid("glCopyTexImage2D")) { return; } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glCopyTexImage2D"); ScopedResolvedFrameBufferBinder binder(this, false, true); gfx::Size size = GetBoundReadFrameBufferSize(); if (texture->IsAttachedToFramebuffer()) { framebuffer_state_.clear_state_dirty = true; } // Clip to size to source dimensions GLint copyX = 0; GLint copyY = 0; GLint copyWidth = 0; GLint copyHeight = 0; Clip(x, width, size.width(), ©X, ©Width); Clip(y, height, size.height(), ©Y, ©Height); if (copyX != x || copyY != y || copyWidth != width || copyHeight != height) { // some part was clipped so clear the texture. if (!ClearLevel(texture, target, level, internal_format, internal_format, GL_UNSIGNED_BYTE, width, height, texture->IsImmutable())) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glCopyTexImage2D", "dimensions too big"); return; } if (copyHeight > 0 && copyWidth > 0) { GLint dx = copyX - x; GLint dy = copyY - y; GLint destX = dx; GLint destY = dy; ScopedModifyPixels modify(texture_ref); glCopyTexSubImage2D(target, level, destX, destY, copyX, copyY, copyWidth, copyHeight); } } else { ScopedModifyPixels modify(texture_ref); glCopyTexImage2D(target, level, internal_format, copyX, copyY, copyWidth, copyHeight, border); } GLenum error = LOCAL_PEEK_GL_ERROR("glCopyTexImage2D"); if (error == GL_NO_ERROR) { texture_manager()->SetLevelInfo( texture_ref, target, level, internal_format, width, height, 1, border, internal_format, GL_UNSIGNED_BYTE, true); } // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); } void GLES2DecoderImpl::DoCopyTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { DCHECK(!ShouldDeferReads()); TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexSubImage2D", "unknown texture for target"); return; } Texture* texture = texture_ref->texture(); GLenum type = 0; GLenum format = 0; if (!texture->GetLevelType(target, level, &type, &format) || !texture->ValidForTexture( target, level, xoffset, yoffset, 0, width, height, 1, type)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCopyTexSubImage2D", "bad dimensions."); return; } if (async_pixel_transfer_manager_->AsyncTransferIsInProgress(texture_ref)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexSubImage2D", "async upload pending for texture"); return; } // Check we have compatible formats. GLenum read_format = GetBoundReadFrameBufferInternalFormat(); uint32 channels_exist = GLES2Util::GetChannelsForFormat(read_format); uint32 channels_needed = GLES2Util::GetChannelsForFormat(format); if (!channels_needed || (channels_needed & channels_exist) != channels_needed) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexSubImage2D", "incompatible format"); return; } if ((channels_needed & (GLES2Util::kDepth | GLES2Util::kStencil)) != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopySubImage2D", "can not be used with depth or stencil textures"); return; } if (!CheckBoundReadFramebufferColorAttachment("glCopyTexSubImage2D")) { return; } if (FormsTextureCopyingFeedbackLoop(texture_ref, level)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCopyTexSubImage2D", "source and destination textures are the same"); return; } if (!CheckBoundFramebuffersValid("glCopyTexSubImage2D")) { return; } ScopedResolvedFrameBufferBinder binder(this, false, true); gfx::Size size = GetBoundReadFrameBufferSize(); GLint copyX = 0; GLint copyY = 0; GLint copyWidth = 0; GLint copyHeight = 0; Clip(x, width, size.width(), ©X, ©Width); Clip(y, height, size.height(), ©Y, ©Height); if (xoffset != 0 || yoffset != 0 || width != size.width() || height != size.height()) { if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, level)) { LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopyTexSubImage2D", "dimensions too big"); return; } } else { // Write all pixels in below. texture_manager()->SetLevelCleared(texture_ref, target, level, true); } if (copyX != x || copyY != y || copyWidth != width || copyHeight != height) { // some part was clipped so clear the sub rect. uint32 pixels_size = 0; if (!GLES2Util::ComputeImageDataSizes( width, height, 1, format, type, state_.unpack_alignment, &pixels_size, NULL, NULL)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCopyTexSubImage2D", "dimensions too large"); return; } scoped_ptr zero(new char[pixels_size]); memset(zero.get(), 0, pixels_size); ScopedModifyPixels modify(texture_ref); glTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, zero.get()); } if (copyHeight > 0 && copyWidth > 0) { GLint dx = copyX - x; GLint dy = copyY - y; GLint destX = xoffset + dx; GLint destY = yoffset + dy; ScopedModifyPixels modify(texture_ref); glCopyTexSubImage2D(target, level, destX, destY, copyX, copyY, copyWidth, copyHeight); } // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); } bool GLES2DecoderImpl::ValidateTexSubImage2D( error::Error* error, const char* function_name, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * data) { (*error) = error::kNoError; if (!validators_->texture_target.IsValid(target)) { LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, target, "target"); return false; } if (width < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "width < 0"); return false; } if (height < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "height < 0"); return false; } TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "unknown texture for target"); return false; } Texture* texture = texture_ref->texture(); GLenum current_type = 0; GLenum internal_format = 0; if (!texture->GetLevelType(target, level, ¤t_type, &internal_format)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "level does not exist."); return false; } if (!texture_manager()->ValidateTextureParameters(state_.GetErrorState(), function_name, format, type, internal_format, level)) { return false; } if (type != current_type) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "type does not match type of texture."); return false; } if (async_pixel_transfer_manager_->AsyncTransferIsInProgress(texture_ref)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "async upload pending for texture"); return false; } if (!texture->ValidForTexture( target, level, xoffset, yoffset, 0, width, height, 1, type)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "bad dimensions."); return false; } if ((GLES2Util::GetChannelsForFormat(format) & (GLES2Util::kDepth | GLES2Util::kStencil)) != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "can not supply data for depth or stencil textures"); return false; } if (data == NULL) { (*error) = error::kOutOfBounds; return false; } return true; } error::Error GLES2DecoderImpl::DoTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * data) { error::Error error = error::kNoError; if (!ValidateTexSubImage2D(&error, "glTexSubImage2D", target, level, xoffset, yoffset, width, height, format, type, data)) { return error; } TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); Texture* texture = texture_ref->texture(); GLsizei tex_width = 0; GLsizei tex_height = 0; bool ok = texture->GetLevelSize( target, level, &tex_width, &tex_height, nullptr); DCHECK(ok); if (xoffset != 0 || yoffset != 0 || width != tex_width || height != tex_height) { if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, level)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glTexSubImage2D", "dimensions too big"); return error::kNoError; } ScopedTextureUploadTimer timer(&texture_state_); glTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, data); return error::kNoError; } if (!texture_state_.texsubimage_faster_than_teximage && !texture->IsImmutable() && !texture->HasImages()) { ScopedTextureUploadTimer timer(&texture_state_); GLenum internal_format; GLenum tex_type; texture->GetLevelType(target, level, &tex_type, &internal_format); // NOTE: In OpenGL ES 2.0 border is always zero. If that changes we'll need // to look it up. glTexImage2D( target, level, internal_format, width, height, 0, format, type, data); } else { ScopedTextureUploadTimer timer(&texture_state_); glTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, data); } texture_manager()->SetLevelCleared(texture_ref, target, level, true); // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleTexSubImage2D(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::TexSubImage2D& c = *static_cast(cmd_data); TRACE_EVENT2("gpu", "GLES2DecoderImpl::HandleTexSubImage2D", "width", c.width, "height", c.height); GLboolean internal = static_cast(c.internal); if (internal == GL_TRUE && texture_state_.tex_image_failed) return error::kNoError; GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLint xoffset = static_cast(c.xoffset); GLint yoffset = static_cast(c.yoffset); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLenum format = static_cast(c.format); GLenum type = static_cast(c.type); uint32 data_size; if (!GLES2Util::ComputeImageDataSizes( width, height, 1, format, type, state_.unpack_alignment, &data_size, NULL, NULL)) { return error::kOutOfBounds; } const void* pixels = GetSharedMemoryAs( c.pixels_shm_id, c.pixels_shm_offset, data_size); return DoTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, pixels); } error::Error GLES2DecoderImpl::DoTexSubImage3D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * data) { TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_ENUM, "glTexSubImage3D", "invalid target"); } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glTexSubImage3D"); ScopedTextureUploadTimer timer(&texture_state_); glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data); GLenum error = LOCAL_PEEK_GL_ERROR("glTexSubImage3D"); if (error == GL_NO_ERROR) { // TODO(zmo): This is not 100% correct because only part of the level // image is cleared. texture_manager()->SetLevelCleared(texture_ref, target, level, true); } // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleTexSubImage3D(uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::TexSubImage3D& c = *static_cast(cmd_data); TRACE_EVENT2("gpu", "GLES2DecoderImpl::HandleTexSubImage3D", "widthXheight", c.width * c.height, "depth", c.depth); GLboolean internal = static_cast(c.internal); if (internal == GL_TRUE && texture_state_.tex_image_failed) return error::kNoError; GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLint xoffset = static_cast(c.xoffset); GLint yoffset = static_cast(c.yoffset); GLint zoffset = static_cast(c.zoffset); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLsizei depth = static_cast(c.depth); GLenum format = static_cast(c.format); GLenum type = static_cast(c.type); uint32 data_size; if (!GLES2Util::ComputeImageDataSizes( width, height, depth, format, type, state_.unpack_alignment, &data_size, NULL, NULL)) { return error::kOutOfBounds; } const void* pixels = GetSharedMemoryAs( c.pixels_shm_id, c.pixels_shm_offset, data_size); return DoTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); } error::Error GLES2DecoderImpl::HandleGetVertexAttribPointerv( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetVertexAttribPointerv& c = *static_cast(cmd_data); GLuint index = static_cast(c.index); GLenum pname = static_cast(c.pname); typedef cmds::GetVertexAttribPointerv::Result Result; Result* result = GetSharedMemoryAs( c.pointer_shm_id, c.pointer_shm_offset, Result::ComputeSize(1)); if (!result) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } if (!validators_->vertex_pointer.IsValid(pname)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glGetVertexAttribPointerv", pname, "pname"); return error::kNoError; } if (index >= group_->max_vertex_attribs()) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glGetVertexAttribPointerv", "index out of range."); return error::kNoError; } result->SetNumResults(1); *result->GetData() = state_.vertex_attrib_manager->GetVertexAttrib(index)->offset(); return error::kNoError; } template bool GLES2DecoderImpl::GetUniformSetup(GLuint program_id, GLint fake_location, uint32 shm_id, uint32 shm_offset, error::Error* error, GLint* real_location, GLuint* service_id, SizedResult** result_pointer, GLenum* result_type, GLsizei* result_size) { DCHECK(error); DCHECK(service_id); DCHECK(result_pointer); DCHECK(result_type); DCHECK(result_size); DCHECK(real_location); *error = error::kNoError; // Make sure we have enough room for the result on failure. SizedResult* result; result = GetSharedMemoryAs*>( shm_id, shm_offset, SizedResult::ComputeSize(0)); if (!result) { *error = error::kOutOfBounds; return false; } *result_pointer = result; // Set the result size to 0 so the client does not have to check for success. result->SetNumResults(0); Program* program = GetProgramInfoNotShader(program_id, "glGetUniform"); if (!program) { return false; } if (!program->IsValid()) { // Program was not linked successfully. (ie, glLinkProgram) LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glGetUniform", "program not linked"); return false; } *service_id = program->service_id(); GLint array_index = -1; const Program::UniformInfo* uniform_info = program->GetUniformInfoByFakeLocation( fake_location, real_location, &array_index); if (!uniform_info) { // No such location. LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glGetUniform", "unknown location"); return false; } GLenum type = uniform_info->type; uint32 num_elements = GLES2Util::GetElementCountForUniformType(type); if (num_elements == 0) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glGetUniform", "unknown type"); return false; } result = GetSharedMemoryAs*>( shm_id, shm_offset, SizedResult::ComputeSize(num_elements)); if (!result) { *error = error::kOutOfBounds; return false; } result->SetNumResults(num_elements); *result_size = num_elements * sizeof(T); *result_type = type; return true; } error::Error GLES2DecoderImpl::HandleGetUniformiv(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetUniformiv& c = *static_cast(cmd_data); GLuint program = c.program; GLint fake_location = c.location; GLuint service_id; GLenum result_type; GLsizei result_size; GLint real_location = -1; Error error; cmds::GetUniformiv::Result* result; if (GetUniformSetup(program, fake_location, c.params_shm_id, c.params_shm_offset, &error, &real_location, &service_id, &result, &result_type, &result_size)) { glGetUniformiv( service_id, real_location, result->GetData()); } return error; } error::Error GLES2DecoderImpl::HandleGetUniformuiv(uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetUniformuiv& c = *static_cast(cmd_data); GLuint program = c.program; GLint fake_location = c.location; GLuint service_id; GLenum result_type; GLsizei result_size; GLint real_location = -1; Error error; cmds::GetUniformuiv::Result* result; if (GetUniformSetup(program, fake_location, c.params_shm_id, c.params_shm_offset, &error, &real_location, &service_id, &result, &result_type, &result_size)) { glGetUniformuiv( service_id, real_location, result->GetData()); } return error; } error::Error GLES2DecoderImpl::HandleGetUniformfv(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetUniformfv& c = *static_cast(cmd_data); GLuint program = c.program; GLint fake_location = c.location; GLuint service_id; GLint real_location = -1; Error error; cmds::GetUniformfv::Result* result; GLenum result_type; GLsizei result_size; if (GetUniformSetup(program, fake_location, c.params_shm_id, c.params_shm_offset, &error, &real_location, &service_id, &result, &result_type, &result_size)) { if (result_type == GL_BOOL || result_type == GL_BOOL_VEC2 || result_type == GL_BOOL_VEC3 || result_type == GL_BOOL_VEC4) { GLsizei num_values = result_size / sizeof(GLfloat); scoped_ptr temp(new GLint[num_values]); glGetUniformiv(service_id, real_location, temp.get()); GLfloat* dst = result->GetData(); for (GLsizei ii = 0; ii < num_values; ++ii) { dst[ii] = (temp[ii] != 0); } } else { glGetUniformfv(service_id, real_location, result->GetData()); } } return error; } error::Error GLES2DecoderImpl::HandleGetShaderPrecisionFormat( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetShaderPrecisionFormat& c = *static_cast(cmd_data); GLenum shader_type = static_cast(c.shadertype); GLenum precision_type = static_cast(c.precisiontype); typedef cmds::GetShaderPrecisionFormat::Result Result; Result* result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->success != 0) { return error::kInvalidArguments; } if (!validators_->shader_type.IsValid(shader_type)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glGetShaderPrecisionFormat", shader_type, "shader_type"); return error::kNoError; } if (!validators_->shader_precision.IsValid(precision_type)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glGetShaderPrecisionFormat", precision_type, "precision_type"); return error::kNoError; } result->success = 1; // true GLint range[2] = { 0, 0 }; GLint precision = 0; GetShaderPrecisionFormatImpl(shader_type, precision_type, range, &precision); result->min_range = range[0]; result->max_range = range[1]; result->precision = precision; return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetAttachedShaders( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetAttachedShaders& c = *static_cast(cmd_data); uint32 result_size = c.result_size; GLuint program_id = static_cast(c.program); Program* program = GetProgramInfoNotShader( program_id, "glGetAttachedShaders"); if (!program) { return error::kNoError; } typedef cmds::GetAttachedShaders::Result Result; uint32 max_count = Result::ComputeMaxResults(result_size); Result* result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, Result::ComputeSize(max_count)); if (!result) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } GLsizei count = 0; glGetAttachedShaders( program->service_id(), max_count, &count, result->GetData()); for (GLsizei ii = 0; ii < count; ++ii) { if (!shader_manager()->GetClientId(result->GetData()[ii], &result->GetData()[ii])) { NOTREACHED(); return error::kGenericError; } } result->SetNumResults(count); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetActiveUniform( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetActiveUniform& c = *static_cast(cmd_data); GLuint program_id = c.program; GLuint index = c.index; uint32 name_bucket_id = c.name_bucket_id; typedef cmds::GetActiveUniform::Result Result; Result* result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->success != 0) { return error::kInvalidArguments; } Program* program = GetProgramInfoNotShader( program_id, "glGetActiveUniform"); if (!program) { return error::kNoError; } const Program::UniformInfo* uniform_info = program->GetUniformInfo(index); if (!uniform_info) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glGetActiveUniform", "index out of range"); return error::kNoError; } result->success = 1; // true. result->size = uniform_info->size; result->type = uniform_info->type; Bucket* bucket = CreateBucket(name_bucket_id); bucket->SetFromString(uniform_info->name.c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetActiveUniformBlockiv( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetActiveUniformBlockiv& c = *static_cast(cmd_data); GLuint program_id = c.program; GLuint index = static_cast(c.index); GLenum pname = static_cast(c.pname); Program* program = GetProgramInfoNotShader( program_id, "glGetActiveUniformBlockiv"); if (!program) { return error::kNoError; } GLuint service_id = program->service_id(); GLint link_status = GL_FALSE; glGetProgramiv(service_id, GL_LINK_STATUS, &link_status); if (link_status != GL_TRUE) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glGetActiveActiveUniformBlockiv", "program not linked"); return error::kNoError; } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetActiveUniformBlockiv"); GLsizei num_values = 1; if (pname == GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES) { GLint num = 0; glGetActiveUniformBlockiv( service_id, index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &num); GLenum error = glGetError(); if (error != GL_NO_ERROR) { // Assume this will the same error if calling with pname. LOCAL_SET_GL_ERROR(error, "GetActiveUniformBlockiv", ""); return error::kNoError; } num_values = static_cast(num); } typedef cmds::GetActiveUniformBlockiv::Result Result; Result* result = GetSharedMemoryAs( c.params_shm_id, c.params_shm_offset, Result::ComputeSize(num_values)); GLint* params = result ? result->GetData() : NULL; if (params == NULL) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } glGetActiveUniformBlockiv(service_id, index, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { result->SetNumResults(num_values); } else { LOCAL_SET_GL_ERROR(error, "GetActiveUniformBlockiv", ""); } return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetActiveUniformBlockName( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetActiveUniformBlockName& c = *static_cast(cmd_data); GLuint program_id = c.program; GLuint index = c.index; uint32 name_bucket_id = c.name_bucket_id; typedef cmds::GetActiveUniformBlockName::Result Result; Result* result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { return error::kOutOfBounds; } // Check that the client initialized the result. if (*result != 0) { return error::kInvalidArguments; } Program* program = GetProgramInfoNotShader( program_id, "glGetActiveUniformBlockName"); if (!program) { return error::kNoError; } GLuint service_id = program->service_id(); GLint link_status = GL_FALSE; glGetProgramiv(service_id, GL_LINK_STATUS, &link_status); if (link_status != GL_TRUE) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glGetActiveActiveUniformBlockName", "program not linked"); return error::kNoError; } GLint max_length = 0; glGetProgramiv( service_id, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_length); // Increase one so &buffer[0] is always valid. GLsizei buf_size = static_cast(max_length) + 1; std::vector buffer(buf_size); GLsizei length = 0; glGetActiveUniformBlockName( service_id, index, buf_size, &length, &buffer[0]); if (length == 0) { *result = 0; return error::kNoError; } *result = 1; Bucket* bucket = CreateBucket(name_bucket_id); DCHECK_GT(buf_size, length); DCHECK_EQ(0, buffer[length]); bucket->SetFromString(&buffer[0]); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetActiveUniformsiv( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetActiveUniformsiv& c = *static_cast(cmd_data); GLuint program_id = c.program; GLenum pname = static_cast(c.pname); Bucket* bucket = GetBucket(c.indices_bucket_id); if (!bucket) { return error::kInvalidArguments; } GLsizei count = static_cast(bucket->size() / sizeof(GLuint)); const GLuint* indices = bucket->GetDataAs(0, bucket->size()); typedef cmds::GetActiveUniformsiv::Result Result; Result* result = GetSharedMemoryAs( c.params_shm_id, c.params_shm_offset, Result::ComputeSize(count)); GLint* params = result ? result->GetData() : NULL; if (params == NULL) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } Program* program = GetProgramInfoNotShader( program_id, "glGetActiveUniformsiv"); if (!program) { return error::kNoError; } GLuint service_id = program->service_id(); GLint link_status = GL_FALSE; glGetProgramiv(service_id, GL_LINK_STATUS, &link_status); if (link_status != GL_TRUE) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glGetActiveUniformsiv", "program not linked"); return error::kNoError; } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetActiveUniformsiv"); glGetActiveUniformsiv(service_id, count, indices, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { result->SetNumResults(count); } else { LOCAL_SET_GL_ERROR(error, "GetActiveUniformsiv", ""); } return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetActiveAttrib(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetActiveAttrib& c = *static_cast(cmd_data); GLuint program_id = c.program; GLuint index = c.index; uint32 name_bucket_id = c.name_bucket_id; typedef cmds::GetActiveAttrib::Result Result; Result* result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->success != 0) { return error::kInvalidArguments; } Program* program = GetProgramInfoNotShader( program_id, "glGetActiveAttrib"); if (!program) { return error::kNoError; } const Program::VertexAttrib* attrib_info = program->GetAttribInfo(index); if (!attrib_info) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glGetActiveAttrib", "index out of range"); return error::kNoError; } result->success = 1; // true. result->size = attrib_info->size; result->type = attrib_info->type; Bucket* bucket = CreateBucket(name_bucket_id); bucket->SetFromString(attrib_info->name.c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleShaderBinary(uint32 immediate_data_size, const void* cmd_data) { #if 1 // No binary shader support. LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glShaderBinary", "not supported"); return error::kNoError; #else GLsizei n = static_cast(c.n); if (n < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glShaderBinary", "n < 0"); return error::kNoError; } GLsizei length = static_cast(c.length); if (length < 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glShaderBinary", "length < 0"); return error::kNoError; } uint32 data_size; if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) { return error::kOutOfBounds; } const GLuint* shaders = GetSharedMemoryAs( c.shaders_shm_id, c.shaders_shm_offset, data_size); GLenum binaryformat = static_cast(c.binaryformat); const void* binary = GetSharedMemoryAs( c.binary_shm_id, c.binary_shm_offset, length); if (shaders == NULL || binary == NULL) { return error::kOutOfBounds; } scoped_ptr service_ids(new GLuint[n]); for (GLsizei ii = 0; ii < n; ++ii) { Shader* shader = GetShader(shaders[ii]); if (!shader) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glShaderBinary", "unknown shader"); return error::kNoError; } service_ids[ii] = shader->service_id(); } // TODO(gman): call glShaderBinary return error::kNoError; #endif } void GLES2DecoderImpl::DoSwapBuffers() { bool is_offscreen = !!offscreen_target_frame_buffer_.get(); int this_frame_number = frame_number_++; // TRACE_EVENT for gpu tests: TRACE_EVENT_INSTANT2("test_gpu", "SwapBuffersLatency", TRACE_EVENT_SCOPE_THREAD, "GLImpl", static_cast(gfx::GetGLImplementation()), "width", (is_offscreen ? offscreen_size_.width() : surface_->GetSize().width())); TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoSwapBuffers", "offscreen", is_offscreen, "frame", this_frame_number); { TRACE_EVENT_SYNTHETIC_DELAY("gpu.PresentingFrame"); } ScopedGPUTrace scoped_gpu_trace(gpu_tracer_.get(), kTraceDecoder, "gpu_toplevel", "SwapBuffer"); bool is_tracing; TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("gpu.debug"), &is_tracing); if (is_tracing) { ScopedFrameBufferBinder binder(this, GetBackbufferServiceId()); gpu_state_tracer_->TakeSnapshotWithCurrentFramebuffer( is_offscreen ? offscreen_size_ : surface_->GetSize()); } // If offscreen then don't actually SwapBuffers to the display. Just copy // the rendered frame to another frame buffer. if (is_offscreen) { TRACE_EVENT2("gpu", "Offscreen", "width", offscreen_size_.width(), "height", offscreen_size_.height()); if (offscreen_size_ != offscreen_saved_color_texture_->size()) { // Workaround for NVIDIA driver bug on OS X; crbug.com/89557, // crbug.com/94163. TODO(kbr): figure out reproduction so Apple will // fix this. if (workarounds().needs_offscreen_buffer_workaround) { offscreen_saved_frame_buffer_->Create(); glFinish(); } // Allocate the offscreen saved color texture. DCHECK(offscreen_saved_color_format_); offscreen_saved_color_texture_->AllocateStorage( offscreen_size_, offscreen_saved_color_format_, false); offscreen_saved_frame_buffer_->AttachRenderTexture( offscreen_saved_color_texture_.get()); if (offscreen_size_.width() != 0 && offscreen_size_.height() != 0) { if (offscreen_saved_frame_buffer_->CheckStatus() != GL_FRAMEBUFFER_COMPLETE) { LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer failed " << "because offscreen saved FBO was incomplete."; MarkContextLost(error::kUnknown); group_->LoseContexts(error::kUnknown); return; } // Clear the offscreen color texture. // TODO(piman): Is this still necessary? { ScopedFrameBufferBinder binder(this, offscreen_saved_frame_buffer_->id()); glClearColor(0, 0, 0, 0); state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); glClear(GL_COLOR_BUFFER_BIT); RestoreClearState(); } } UpdateParentTextureInfo(); } if (offscreen_size_.width() == 0 || offscreen_size_.height() == 0) return; ScopedGLErrorSuppressor suppressor( "GLES2DecoderImpl::DoSwapBuffers", GetErrorState()); if (IsOffscreenBufferMultisampled()) { // For multisampled buffers, resolve the frame buffer. ScopedResolvedFrameBufferBinder binder(this, true, false); } else { ScopedFrameBufferBinder binder(this, offscreen_target_frame_buffer_->id()); if (offscreen_target_buffer_preserved_) { // Copy the target frame buffer to the saved offscreen texture. offscreen_saved_color_texture_->Copy( offscreen_saved_color_texture_->size(), offscreen_saved_color_format_); } else { // Flip the textures in the parent context via the texture manager. if (!!offscreen_saved_color_texture_info_.get()) offscreen_saved_color_texture_info_->texture()-> SetServiceId(offscreen_target_color_texture_->id()); offscreen_saved_color_texture_.swap(offscreen_target_color_texture_); offscreen_target_frame_buffer_->AttachRenderTexture( offscreen_target_color_texture_.get()); } // Ensure the side effects of the copy are visible to the parent // context. There is no need to do this for ANGLE because it uses a // single D3D device for all contexts. if (!feature_info_->gl_version_info().is_angle) glFlush(); } } else { if (!surface_->SwapBuffers()) { LOG(ERROR) << "Context lost because SwapBuffers failed."; if (!CheckResetStatus()) { MarkContextLost(error::kUnknown); group_->LoseContexts(error::kUnknown); } } } // This may be a slow command. Exit command processing to allow for // context preemption and GPU watchdog checks. ExitCommandProcessingEarly(); } void GLES2DecoderImpl::DoSwapInterval(int interval) { context_->SetSwapInterval(interval); } error::Error GLES2DecoderImpl::HandleEnableFeatureCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::EnableFeatureCHROMIUM& c = *static_cast(cmd_data); Bucket* bucket = GetBucket(c.bucket_id); if (!bucket || bucket->size() == 0) { return error::kInvalidArguments; } typedef cmds::EnableFeatureCHROMIUM::Result Result; Result* result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { return error::kOutOfBounds; } // Check that the client initialized the result. if (*result != 0) { return error::kInvalidArguments; } std::string feature_str; if (!bucket->GetAsString(&feature_str)) { return error::kInvalidArguments; } // TODO(gman): make this some kind of table to function pointer thingy. if (feature_str.compare("pepper3d_allow_buffers_on_multiple_targets") == 0) { buffer_manager()->set_allow_buffers_on_multiple_targets(true); } else if (feature_str.compare("pepper3d_support_fixed_attribs") == 0) { buffer_manager()->set_allow_fixed_attribs(true); // TODO(gman): decide how to remove the need for this const_cast. // I could make validators_ non const but that seems bad as this is the only // place it is needed. I could make some special friend class of validators // just to allow this to set them. That seems silly. I could refactor this // code to use the extension mechanism or the initialization attributes to // turn this feature on. Given that the only real point of this is to make // the conformance tests pass and given that there is lots of real work that // needs to be done it seems like refactoring for one to one of those // methods is a very low priority. const_cast(validators_)->vertex_attrib_type.AddValue(GL_FIXED); } else if (feature_str.compare("webgl_enable_glsl_webgl_validation") == 0) { force_webgl_glsl_validation_ = true; InitializeShaderTranslator(); } else { return error::kNoError; } *result = 1; // true. return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetRequestableExtensionsCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetRequestableExtensionsCHROMIUM& c = *static_cast( cmd_data); Bucket* bucket = CreateBucket(c.bucket_id); scoped_refptr info(new FeatureInfo()); info->Initialize(disallowed_features_); bucket->SetFromString(info->extensions().c_str()); return error::kNoError; } error::Error GLES2DecoderImpl::HandleRequestExtensionCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::RequestExtensionCHROMIUM& c = *static_cast(cmd_data); Bucket* bucket = GetBucket(c.bucket_id); if (!bucket || bucket->size() == 0) { return error::kInvalidArguments; } std::string feature_str; if (!bucket->GetAsString(&feature_str)) { return error::kInvalidArguments; } bool desire_webgl_glsl_validation = feature_str.find("GL_CHROMIUM_webglsl") != std::string::npos; bool desire_standard_derivatives = false; bool desire_frag_depth = false; bool desire_draw_buffers = false; bool desire_shader_texture_lod = false; if (force_webgl_glsl_validation_) { desire_standard_derivatives = feature_str.find("GL_OES_standard_derivatives") != std::string::npos; desire_frag_depth = feature_str.find("GL_EXT_frag_depth") != std::string::npos; desire_draw_buffers = feature_str.find("GL_EXT_draw_buffers") != std::string::npos; desire_shader_texture_lod = feature_str.find("GL_EXT_shader_texture_lod") != std::string::npos; } if (desire_webgl_glsl_validation != force_webgl_glsl_validation_ || desire_standard_derivatives != derivatives_explicitly_enabled_ || desire_frag_depth != frag_depth_explicitly_enabled_ || desire_draw_buffers != draw_buffers_explicitly_enabled_) { force_webgl_glsl_validation_ |= desire_webgl_glsl_validation; derivatives_explicitly_enabled_ |= desire_standard_derivatives; frag_depth_explicitly_enabled_ |= desire_frag_depth; draw_buffers_explicitly_enabled_ |= desire_draw_buffers; shader_texture_lod_explicitly_enabled_ |= desire_shader_texture_lod; InitializeShaderTranslator(); } UpdateCapabilities(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetProgramInfoCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::GetProgramInfoCHROMIUM& c = *static_cast(cmd_data); GLuint program_id = static_cast(c.program); uint32 bucket_id = c.bucket_id; Bucket* bucket = CreateBucket(bucket_id); bucket->SetSize(sizeof(ProgramInfoHeader)); // in case we fail. Program* program = NULL; program = GetProgram(program_id); if (!program || !program->IsValid()) { return error::kNoError; } program->GetProgramInfo(program_manager(), bucket); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetUniformBlocksCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetUniformBlocksCHROMIUM& c = *static_cast(cmd_data); GLuint program_id = static_cast(c.program); uint32 bucket_id = c.bucket_id; Bucket* bucket = CreateBucket(bucket_id); bucket->SetSize(sizeof(UniformBlocksHeader)); // in case we fail. Program* program = NULL; program = GetProgram(program_id); if (!program || !program->IsValid()) { return error::kNoError; } program->GetUniformBlocks(bucket); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetUniformsES3CHROMIUM( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetUniformsES3CHROMIUM& c = *static_cast(cmd_data); GLuint program_id = static_cast(c.program); uint32 bucket_id = c.bucket_id; Bucket* bucket = CreateBucket(bucket_id); bucket->SetSize(sizeof(UniformsES3Header)); // in case we fail. Program* program = NULL; program = GetProgram(program_id); if (!program || !program->IsValid()) { return error::kNoError; } program->GetUniformsES3(bucket); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetTransformFeedbackVarying( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetTransformFeedbackVarying& c = *static_cast(cmd_data); GLuint program_id = c.program; GLuint index = c.index; uint32 name_bucket_id = c.name_bucket_id; typedef cmds::GetTransformFeedbackVarying::Result Result; Result* result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { return error::kOutOfBounds; } // Check that the client initialized the result. if (result->success != 0) { return error::kInvalidArguments; } Program* program = GetProgramInfoNotShader( program_id, "glGetTransformFeedbackVarying"); if (!program) { return error::kNoError; } GLuint service_id = program->service_id(); GLint link_status = GL_FALSE; glGetProgramiv(service_id, GL_LINK_STATUS, &link_status); if (link_status != GL_TRUE) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glGetTransformFeedbackVarying", "program not linked"); return error::kNoError; } GLint max_length = 0; glGetProgramiv( service_id, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &max_length); max_length = std::max(1, max_length); std::vector buffer(max_length); GLsizei length = 0; GLsizei size = 0; GLenum type = 0; LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetTransformFeedbackVarying"); glGetTransformFeedbackVarying( service_id, index, max_length, &length, &size, &type, &buffer[0]); GLenum error = glGetError(); if (error != GL_NO_ERROR) { LOCAL_SET_GL_ERROR(error, "glGetTransformFeedbackVarying", ""); return error::kNoError; } result->success = 1; // true. result->size = static_cast(size); result->type = static_cast(type); Bucket* bucket = CreateBucket(name_bucket_id); DCHECK(length >= 0 && length < max_length); buffer[length] = '\0'; // Just to be safe. bucket->SetFromString(&buffer[0]); return error::kNoError; } error::Error GLES2DecoderImpl::HandleGetTransformFeedbackVaryingsCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::GetTransformFeedbackVaryingsCHROMIUM& c = *static_cast( cmd_data); GLuint program_id = static_cast(c.program); uint32 bucket_id = c.bucket_id; Bucket* bucket = CreateBucket(bucket_id); bucket->SetSize(sizeof(TransformFeedbackVaryingsHeader)); // in case we fail. Program* program = NULL; program = GetProgram(program_id); if (!program || !program->IsValid()) { return error::kNoError; } program->GetTransformFeedbackVaryings(bucket); return error::kNoError; } error::ContextLostReason GLES2DecoderImpl::GetContextLostReason() { return context_lost_reason_; } error::ContextLostReason GLES2DecoderImpl::GetContextLostReasonFromResetStatus( GLenum reset_status) const { switch (reset_status) { case GL_NO_ERROR: // TODO(kbr): improve the precision of the error code in this case. // Consider delegating to context for error code if MakeCurrent fails. return error::kUnknown; case GL_GUILTY_CONTEXT_RESET_ARB: return error::kGuilty; case GL_INNOCENT_CONTEXT_RESET_ARB: return error::kInnocent; case GL_UNKNOWN_CONTEXT_RESET_ARB: return error::kUnknown; } NOTREACHED(); return error::kUnknown; } bool GLES2DecoderImpl::WasContextLost() const { return context_was_lost_; } bool GLES2DecoderImpl::WasContextLostByRobustnessExtension() const { return WasContextLost() && reset_by_robustness_extension_; } void GLES2DecoderImpl::MarkContextLost(error::ContextLostReason reason) { // Only lose the context once. if (WasContextLost()) return; // Don't make GL calls in here, the context might not be current. context_lost_reason_ = reason; current_decoder_error_ = error::kLostContext; context_was_lost_ = true; // Some D3D drivers cannot recover from device lost in the GPU process // sandbox. Allow a new GPU process to launch. if (workarounds().exit_on_context_lost) { LOG(ERROR) << "Exiting GPU process because some drivers cannot reset" << " a D3D device in the Chrome GPU process sandbox."; #if defined(OS_WIN) base::win::SetShouldCrashOnProcessDetach(false); #endif exit(0); } } bool GLES2DecoderImpl::CheckResetStatus() { DCHECK(!WasContextLost()); DCHECK(context_->IsCurrent(NULL)); if (IsRobustnessSupported()) { // If the reason for the call was a GL error, we can try to determine the // reset status more accurately. GLenum driver_status = glGetGraphicsResetStatusARB(); if (driver_status == GL_NO_ERROR) return false; LOG(ERROR) << (surface_->IsOffscreen() ? "Offscreen" : "Onscreen") << " context lost via ARB/EXT_robustness. Reset status = " << GLES2Util::GetStringEnum(driver_status); // Don't pretend we know which client was responsible. if (workarounds().use_virtualized_gl_contexts) driver_status = GL_UNKNOWN_CONTEXT_RESET_ARB; switch (driver_status) { case GL_GUILTY_CONTEXT_RESET_ARB: MarkContextLost(error::kGuilty); break; case GL_INNOCENT_CONTEXT_RESET_ARB: MarkContextLost(error::kInnocent); break; case GL_UNKNOWN_CONTEXT_RESET_ARB: MarkContextLost(error::kUnknown); break; default: NOTREACHED(); return false; } reset_by_robustness_extension_ = true; return true; } return false; } error::Error GLES2DecoderImpl::HandleInsertSyncPointCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { return error::kUnknownCommand; } error::Error GLES2DecoderImpl::HandleWaitSyncPointCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::WaitSyncPointCHROMIUM& c = *static_cast(cmd_data); uint32 sync_point = c.sync_point; if (wait_sync_point_callback_.is_null()) return error::kNoError; return wait_sync_point_callback_.Run(sync_point) ? error::kNoError : error::kDeferCommandUntilLater; } error::Error GLES2DecoderImpl::HandleDiscardBackbufferCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { if (surface_->DeferDraws()) return error::kDeferCommandUntilLater; if (!surface_->SetBackbufferAllocation(false)) return error::kLostContext; backbuffer_needs_clear_bits_ |= GL_COLOR_BUFFER_BIT; backbuffer_needs_clear_bits_ |= GL_DEPTH_BUFFER_BIT; backbuffer_needs_clear_bits_ |= GL_STENCIL_BUFFER_BIT; return error::kNoError; } bool GLES2DecoderImpl::GenQueriesEXTHelper( GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { if (query_manager_->GetQuery(client_ids[ii])) { return false; } } query_manager_->GenQueries(n, client_ids); return true; } void GLES2DecoderImpl::DeleteQueriesEXTHelper( GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { QueryManager::Query* query = query_manager_->GetQuery(client_ids[ii]); if (query && !query->IsDeleted()) { ContextState::QueryMap::iterator it = state_.current_queries.find(query->target()); if (it != state_.current_queries.end()) state_.current_queries.erase(it); query->Destroy(true); } query_manager_->RemoveQuery(client_ids[ii]); } } bool GLES2DecoderImpl::ProcessPendingQueries(bool did_finish) { if (query_manager_.get() == NULL) { return false; } if (!query_manager_->ProcessPendingQueries(did_finish)) { current_decoder_error_ = error::kOutOfBounds; } return query_manager_->HavePendingQueries(); } // Note that if there are no pending readpixels right now, // this function will call the callback immediately. void GLES2DecoderImpl::WaitForReadPixels(base::Closure callback) { if (features().use_async_readpixels && !pending_readpixel_fences_.empty()) { pending_readpixel_fences_.back()->callbacks.push_back(callback); } else { callback.Run(); } } void GLES2DecoderImpl::ProcessPendingReadPixels() { while (!pending_readpixel_fences_.empty() && pending_readpixel_fences_.front()->fence->HasCompleted()) { std::vector callbacks = pending_readpixel_fences_.front()->callbacks; pending_readpixel_fences_.pop(); for (size_t i = 0; i < callbacks.size(); i++) { callbacks[i].Run(); } } } bool GLES2DecoderImpl::HasMoreIdleWork() { return !pending_readpixel_fences_.empty() || async_pixel_transfer_manager_->NeedsProcessMorePendingTransfers(); } void GLES2DecoderImpl::PerformIdleWork() { ProcessPendingReadPixels(); if (!async_pixel_transfer_manager_->NeedsProcessMorePendingTransfers()) return; async_pixel_transfer_manager_->ProcessMorePendingTransfers(); ProcessFinishedAsyncTransfers(); } error::Error GLES2DecoderImpl::HandleBeginQueryEXT(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::BeginQueryEXT& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLuint client_id = static_cast(c.id); int32 sync_shm_id = static_cast(c.sync_data_shm_id); uint32 sync_shm_offset = static_cast(c.sync_data_shm_offset); switch (target) { case GL_COMMANDS_ISSUED_CHROMIUM: case GL_LATENCY_QUERY_CHROMIUM: case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM: case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: case GL_GET_ERROR_QUERY_CHROMIUM: break; case GL_COMMANDS_COMPLETED_CHROMIUM: if (!features().chromium_sync_query) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBeginQueryEXT", "not enabled for commands completed queries"); return error::kNoError; } break; default: if (!features().occlusion_query_boolean) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBeginQueryEXT", "not enabled for occlusion queries"); return error::kNoError; } break; } if (state_.current_queries.find(target) != state_.current_queries.end()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBeginQueryEXT", "query already in progress"); return error::kNoError; } if (client_id == 0) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBeginQueryEXT", "id is 0"); return error::kNoError; } QueryManager::Query* query = query_manager_->GetQuery(client_id); if (!query) { if (!query_manager_->IsValidQuery(client_id)) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBeginQueryEXT", "id not made by glGenQueriesEXT"); return error::kNoError; } query = query_manager_->CreateQuery( target, client_id, sync_shm_id, sync_shm_offset); } if (query->target() != target) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBeginQueryEXT", "target does not match"); return error::kNoError; } else if (query->shm_id() != sync_shm_id || query->shm_offset() != sync_shm_offset) { DLOG(ERROR) << "Shared memory used by query not the same as before"; return error::kInvalidArguments; } if (!query_manager_->BeginQuery(query)) { return error::kOutOfBounds; } state_.current_queries[target] = query; return error::kNoError; } error::Error GLES2DecoderImpl::HandleEndQueryEXT(uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::EndQueryEXT& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); uint32 submit_count = static_cast(c.submit_count); ContextState::QueryMap::iterator it = state_.current_queries.find(target); if (it == state_.current_queries.end()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glEndQueryEXT", "No active query"); return error::kNoError; } QueryManager::Query* query = it->second.get(); if (!query_manager_->EndQuery(query, submit_count)) { return error::kOutOfBounds; } query_manager_->ProcessPendingTransferQueries(); state_.current_queries.erase(it); return error::kNoError; } bool GLES2DecoderImpl::GenVertexArraysOESHelper( GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { if (GetVertexAttribManager(client_ids[ii])) { return false; } } if (!features().native_vertex_array_object) { // Emulated VAO for (GLsizei ii = 0; ii < n; ++ii) { CreateVertexAttribManager(client_ids[ii], 0, true); } } else { scoped_ptr service_ids(new GLuint[n]); glGenVertexArraysOES(n, service_ids.get()); for (GLsizei ii = 0; ii < n; ++ii) { CreateVertexAttribManager(client_ids[ii], service_ids[ii], true); } } return true; } void GLES2DecoderImpl::DeleteVertexArraysOESHelper( GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { VertexAttribManager* vao = GetVertexAttribManager(client_ids[ii]); if (vao && !vao->IsDeleted()) { if (state_.vertex_attrib_manager.get() == vao) { DoBindVertexArrayOES(0); } RemoveVertexAttribManager(client_ids[ii]); } } } void GLES2DecoderImpl::DoBindVertexArrayOES(GLuint client_id) { VertexAttribManager* vao = NULL; if (client_id != 0) { vao = GetVertexAttribManager(client_id); if (!vao) { // Unlike most Bind* methods, the spec explicitly states that VertexArray // only allows names that have been previously generated. As such, we do // not generate new names here. LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBindVertexArrayOES", "bad vertex array id."); current_decoder_error_ = error::kNoError; return; } } else { vao = state_.default_vertex_attrib_manager.get(); } // Only set the VAO state if it's changed if (state_.vertex_attrib_manager.get() != vao) { state_.vertex_attrib_manager = vao; if (!features().native_vertex_array_object) { EmulateVertexArrayState(); } else { GLuint service_id = vao->service_id(); glBindVertexArrayOES(service_id); } } } // Used when OES_vertex_array_object isn't natively supported void GLES2DecoderImpl::EmulateVertexArrayState() { // Setup the Vertex attribute state for (uint32 vv = 0; vv < group_->max_vertex_attribs(); ++vv) { RestoreStateForAttrib(vv, true); } // Setup the element buffer Buffer* element_array_buffer = state_.vertex_attrib_manager->element_array_buffer(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer ? element_array_buffer->service_id() : 0); } bool GLES2DecoderImpl::DoIsVertexArrayOES(GLuint client_id) { const VertexAttribManager* vao = GetVertexAttribManager(client_id); return vao && vao->IsValid() && !vao->IsDeleted(); } #if defined(OS_MACOSX) void GLES2DecoderImpl::ReleaseIOSurfaceForTexture(GLuint texture_id) { TextureToIOSurfaceMap::iterator it = texture_to_io_surface_map_.find( texture_id); if (it != texture_to_io_surface_map_.end()) { // Found a previous IOSurface bound to this texture; release it. IOSurfaceRef surface = it->second; CFRelease(surface); texture_to_io_surface_map_.erase(it); } } #endif void GLES2DecoderImpl::DoTexImageIOSurface2DCHROMIUM( GLenum target, GLsizei width, GLsizei height, GLuint io_surface_id, GLuint plane) { #if defined(OS_MACOSX) if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glTexImageIOSurface2DCHROMIUM", "only supported on desktop GL."); return; } if (target != GL_TEXTURE_RECTANGLE_ARB) { // This might be supported in the future, and if we could require // support for binding an IOSurface to a NPOT TEXTURE_2D texture, we // could delete a lot of code. For now, perform strict validation so we // know what's going on. LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glTexImageIOSurface2DCHROMIUM", "requires TEXTURE_RECTANGLE_ARB target"); return; } // Default target might be conceptually valid, but disallow it to avoid // accidents. TextureRef* texture_ref = texture_manager()->GetTextureInfoForTargetUnlessDefault(&state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glTexImageIOSurface2DCHROMIUM", "no rectangle texture bound"); return; } // Look up the new IOSurface. Note that because of asynchrony // between processes this might fail; during live resizing the // plugin process might allocate and release an IOSurface before // this process gets a chance to look it up. Hold on to any old // IOSurface in this case. IOSurfaceRef surface = IOSurfaceLookup(io_surface_id); if (!surface) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glTexImageIOSurface2DCHROMIUM", "no IOSurface with the given ID"); return; } // Release any IOSurface previously bound to this texture. ReleaseIOSurfaceForTexture(texture_ref->service_id()); // Make sure we release the IOSurface even if CGLTexImageIOSurface2D fails. texture_to_io_surface_map_.insert( std::make_pair(texture_ref->service_id(), surface)); CGLContextObj context = static_cast(context_->GetHandle()); CGLError err = CGLTexImageIOSurface2D( context, target, GL_RGBA, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface, plane); if (err != kCGLNoError) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glTexImageIOSurface2DCHROMIUM", "error in CGLTexImageIOSurface2D"); return; } texture_manager()->SetLevelInfo( texture_ref, target, 0, GL_RGBA, width, height, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, true); #else LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexImageIOSurface2DCHROMIUM", "not supported."); #endif } static GLenum ExtractFormatFromStorageFormat(GLenum internalformat) { switch (internalformat) { case GL_RGB565: return GL_RGB; case GL_RGBA4: return GL_RGBA; case GL_RGB5_A1: return GL_RGBA; case GL_RGB8_OES: return GL_RGB; case GL_RGBA8_OES: return GL_RGBA; case GL_LUMINANCE8_ALPHA8_EXT: return GL_LUMINANCE_ALPHA; case GL_LUMINANCE8_EXT: return GL_LUMINANCE; case GL_ALPHA8_EXT: return GL_ALPHA; case GL_RGBA32F_EXT: return GL_RGBA; case GL_RGB32F_EXT: return GL_RGB; case GL_ALPHA32F_EXT: return GL_ALPHA; case GL_LUMINANCE32F_EXT: return GL_LUMINANCE; case GL_LUMINANCE_ALPHA32F_EXT: return GL_LUMINANCE_ALPHA; case GL_RGBA16F_EXT: return GL_RGBA; case GL_RGB16F_EXT: return GL_RGB; case GL_ALPHA16F_EXT: return GL_ALPHA; case GL_LUMINANCE16F_EXT: return GL_LUMINANCE; case GL_LUMINANCE_ALPHA16F_EXT: return GL_LUMINANCE_ALPHA; case GL_BGRA8_EXT: return GL_BGRA_EXT; case GL_SRGB8_ALPHA8_EXT: return GL_SRGB_ALPHA_EXT; default: return GL_NONE; } } bool GLES2DecoderImpl::ValidateCopyTextureCHROMIUM( const char* function_name, GLenum target, TextureRef* source_texture_ref, TextureRef* dest_texture_ref, GLenum dest_internal_format) { if (!source_texture_ref || !dest_texture_ref) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "unknown texture id"); return false; } if (GL_TEXTURE_2D != target) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid texture target"); return false; } Texture* source_texture = source_texture_ref->texture(); Texture* dest_texture = dest_texture_ref->texture(); if (source_texture == dest_texture) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "source and destination textures are the same"); return false; } if (dest_texture->target() != GL_TEXTURE_2D || (source_texture->target() != GL_TEXTURE_2D && source_texture->target() != GL_TEXTURE_RECTANGLE_ARB && source_texture->target() != GL_TEXTURE_EXTERNAL_OES)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid texture target binding"); return false; } GLenum source_type = 0; GLenum source_internal_format = 0; source_texture->GetLevelType(source_texture->target(), 0, &source_type, &source_internal_format); // The destination format should be GL_RGB, or GL_RGBA. GL_ALPHA, // GL_LUMINANCE, and GL_LUMINANCE_ALPHA are not supported because they are not // renderable on some platforms. bool valid_dest_format = dest_internal_format == GL_RGB || dest_internal_format == GL_RGBA || dest_internal_format == GL_BGRA_EXT; bool valid_source_format = source_internal_format == GL_ALPHA || source_internal_format == GL_RGB || source_internal_format == GL_RGBA || source_internal_format == GL_LUMINANCE || source_internal_format == GL_LUMINANCE_ALPHA || source_internal_format == GL_BGRA_EXT; if (!valid_source_format || !valid_dest_format) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "invalid internal format"); return false; } return true; } void GLES2DecoderImpl::DoCopyTextureCHROMIUM(GLenum target, GLuint source_id, GLuint dest_id, GLenum internal_format, GLenum dest_type) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoCopyTextureCHROMIUM"); TextureRef* source_texture_ref = GetTexture(source_id); TextureRef* dest_texture_ref = GetTexture(dest_id); Texture* source_texture = source_texture_ref->texture(); Texture* dest_texture = dest_texture_ref->texture(); int source_width = 0; int source_height = 0; gfx::GLImage* image = source_texture->GetLevelImage(source_texture->target(), 0); if (image) { gfx::Size size = image->GetSize(); source_width = size.width(); source_height = size.height(); if (source_width <= 0 || source_height <= 0) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCopyTextureChromium", "invalid image size"); return; } } else { if (!source_texture->GetLevelSize(source_texture->target(), 0, &source_width, &source_height, nullptr)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopyTextureChromium", "source texture has no level 0"); return; } // Check that this type of texture is allowed. if (!texture_manager()->ValidForTarget(source_texture->target(), 0, source_width, source_height, 1)) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glCopyTextureCHROMIUM", "Bad dimensions"); return; } } GLenum source_type = 0; GLenum source_internal_format = 0; source_texture->GetLevelType( source_texture->target(), 0, &source_type, &source_internal_format); if (dest_texture->IsImmutable()) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopyTextureCHROMIUM", "texture is immutable"); return; } if (!ValidateCopyTextureCHROMIUM("glCopyTextureCHROMIUM", target, source_texture_ref, dest_texture_ref, internal_format)) { return; } // Clear the source texture if necessary. if (!texture_manager()->ClearTextureLevel(this, source_texture_ref, source_texture->target(), 0)) { LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopyTextureCHROMIUM", "dimensions too big"); return; } // Defer initializing the CopyTextureCHROMIUMResourceManager until it is // needed because it takes 10s of milliseconds to initialize. if (!copy_texture_CHROMIUM_.get()) { LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glCopyTextureCHROMIUM"); copy_texture_CHROMIUM_.reset(new CopyTextureCHROMIUMResourceManager()); copy_texture_CHROMIUM_->Initialize(this); RestoreCurrentFramebufferBindings(); if (LOCAL_PEEK_GL_ERROR("glCopyTextureCHROMIUM") != GL_NO_ERROR) return; } GLenum dest_type_previous = dest_type; GLenum dest_internal_format = internal_format; int dest_width = 0; int dest_height = 0; bool dest_level_defined = dest_texture->GetLevelSize( GL_TEXTURE_2D, 0, &dest_width, &dest_height, nullptr); if (dest_level_defined) { dest_texture->GetLevelType(GL_TEXTURE_2D, 0, &dest_type_previous, &dest_internal_format); } // Resize the destination texture to the dimensions of the source texture. if (!dest_level_defined || dest_width != source_width || dest_height != source_height || dest_internal_format != internal_format || dest_type_previous != dest_type) { // Ensure that the glTexImage2D succeeds. LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glCopyTextureCHROMIUM"); glBindTexture(GL_TEXTURE_2D, dest_texture->service_id()); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, source_width, source_height, 0, internal_format, dest_type, NULL); GLenum error = LOCAL_PEEK_GL_ERROR("glCopyTextureCHROMIUM"); if (error != GL_NO_ERROR) { RestoreCurrentTextureBindings(&state_, GL_TEXTURE_2D); return; } texture_manager()->SetLevelInfo( dest_texture_ref, GL_TEXTURE_2D, 0, internal_format, source_width, source_height, 1, 0, internal_format, dest_type, true); } else { texture_manager()->SetLevelCleared(dest_texture_ref, GL_TEXTURE_2D, 0, true); } ScopedModifyPixels modify(dest_texture_ref); // Try using GLImage::CopyTexSubImage when possible. bool unpack_premultiply_alpha_change = unpack_premultiply_alpha_ ^ unpack_unpremultiply_alpha_; if (image && !unpack_flip_y_ && !unpack_premultiply_alpha_change) { glBindTexture(GL_TEXTURE_2D, dest_texture->service_id()); if (image->CopyTexSubImage(GL_TEXTURE_2D, gfx::Point(0, 0), gfx::Rect(0, 0, source_width, source_height))) { return; } } DoWillUseTexImageIfNeeded(source_texture, source_texture->target()); // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix // before presenting. if (source_texture->target() == GL_TEXTURE_EXTERNAL_OES) { // TODO(hkuang): get the StreamTexture transform matrix in GPU process // instead of using kIdentityMatrix crbug.com/226218. copy_texture_CHROMIUM_->DoCopyTextureWithTransform( this, source_texture->target(), source_texture->service_id(), dest_texture->service_id(), source_width, source_height, unpack_flip_y_, unpack_premultiply_alpha_, unpack_unpremultiply_alpha_, kIdentityMatrix); } else { copy_texture_CHROMIUM_->DoCopyTexture( this, source_texture->target(), source_texture->service_id(), source_internal_format, dest_texture->service_id(), internal_format, source_width, source_height, unpack_flip_y_, unpack_premultiply_alpha_, unpack_unpremultiply_alpha_); } DoDidUseTexImageIfNeeded(source_texture, source_texture->target()); } void GLES2DecoderImpl::DoCopySubTextureCHROMIUM(GLenum target, GLuint source_id, GLuint dest_id, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoCopySubTextureCHROMIUM"); TextureRef* source_texture_ref = GetTexture(source_id); TextureRef* dest_texture_ref = GetTexture(dest_id); Texture* source_texture = source_texture_ref->texture(); Texture* dest_texture = dest_texture_ref->texture(); int source_width = 0; int source_height = 0; gfx::GLImage* image = source_texture->GetLevelImage(source_texture->target(), 0); if (image) { gfx::Size size = image->GetSize(); source_width = size.width(); source_height = size.height(); if (source_width <= 0 || source_height <= 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTextureCHROMIUM", "invalid image size"); return; } } else { if (!source_texture->GetLevelSize(source_texture->target(), 0, &source_width, &source_height, nullptr)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTextureCHROMIUM", "source texture has no level 0"); return; } // Check that this type of texture is allowed. if (!texture_manager()->ValidForTarget(source_texture->target(), 0, source_width, source_height, 1)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTextureCHROMIUM", "source texture bad dimensions"); return; } } GLenum source_type = 0; GLenum source_internal_format = 0; source_texture->GetLevelType(source_texture->target(), 0, &source_type, &source_internal_format); if (!source_texture->ValidForTexture(source_texture->target(), 0, x, y, 0, width, height, 1, source_type)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTextureCHROMIUM", "source texture bad dimensions."); return; } GLenum dest_type = 0; GLenum dest_internal_format = 0; bool dest_level_defined = dest_texture->GetLevelType( dest_texture->target(), 0, &dest_type, &dest_internal_format); if (!dest_level_defined) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopySubTextureCHROMIUM", "destination texture is not defined"); return; } if (!dest_texture->ValidForTexture(dest_texture->target(), 0, xoffset, yoffset, 0, width, height, 1, dest_type)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTextureCHROMIUM", "destination texture bad dimensions."); return; } if (!ValidateCopyTextureCHROMIUM("glCopySubTextureCHROMIUM", target, source_texture_ref, dest_texture_ref, dest_internal_format)) { return; } // Clear the source texture if necessary. if (!texture_manager()->ClearTextureLevel(this, source_texture_ref, source_texture->target(), 0)) { LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopySubTextureCHROMIUM", "source texture dimensions too big"); return; } // Defer initializing the CopyTextureCHROMIUMResourceManager until it is // needed because it takes 10s of milliseconds to initialize. if (!copy_texture_CHROMIUM_.get()) { LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glCopySubTextureCHROMIUM"); copy_texture_CHROMIUM_.reset(new CopyTextureCHROMIUMResourceManager()); copy_texture_CHROMIUM_->Initialize(this); RestoreCurrentFramebufferBindings(); if (LOCAL_PEEK_GL_ERROR("glCopySubTextureCHROMIUM") != GL_NO_ERROR) return; } int dest_width = 0; int dest_height = 0; bool ok = dest_texture->GetLevelSize( GL_TEXTURE_2D, 0, &dest_width, &dest_height, nullptr); DCHECK(ok); if (xoffset != 0 || yoffset != 0 || width != dest_width || height != dest_height) { if (!texture_manager()->ClearTextureLevel(this, dest_texture_ref, target, 0)) { LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopySubTextureCHROMIUM", "destination texture dimensions too big"); return; } } else { texture_manager()->SetLevelCleared(dest_texture_ref, GL_TEXTURE_2D, 0, true); } ScopedModifyPixels modify(dest_texture_ref); // Try using GLImage::CopyTexSubImage when possible. bool unpack_premultiply_alpha_change = unpack_premultiply_alpha_ ^ unpack_unpremultiply_alpha_; if (image && !unpack_flip_y_ && !unpack_premultiply_alpha_change) { glBindTexture(GL_TEXTURE_2D, dest_texture->service_id()); if (image->CopyTexSubImage(GL_TEXTURE_2D, gfx::Point(xoffset, yoffset), gfx::Rect(x, y, width, height))) { return; } } DoWillUseTexImageIfNeeded(source_texture, source_texture->target()); // TODO(hkuang): get the StreamTexture transform matrix in GPU process. // crbug.com/226218. copy_texture_CHROMIUM_->DoCopySubTexture( this, source_texture->target(), source_texture->service_id(), source_internal_format, dest_texture->service_id(), dest_internal_format, xoffset, yoffset, x, y, width, height, dest_width, dest_height, source_width, source_height, unpack_flip_y_, unpack_premultiply_alpha_, unpack_unpremultiply_alpha_); DoDidUseTexImageIfNeeded(source_texture, source_texture->target()); } static GLenum ExtractTypeFromStorageFormat(GLenum internalformat) { switch (internalformat) { case GL_RGB565: return GL_UNSIGNED_SHORT_5_6_5; case GL_RGBA4: return GL_UNSIGNED_SHORT_4_4_4_4; case GL_RGB5_A1: return GL_UNSIGNED_SHORT_5_5_5_1; case GL_RGB8_OES: return GL_UNSIGNED_BYTE; case GL_RGBA8_OES: return GL_UNSIGNED_BYTE; case GL_LUMINANCE8_ALPHA8_EXT: return GL_UNSIGNED_BYTE; case GL_LUMINANCE8_EXT: return GL_UNSIGNED_BYTE; case GL_ALPHA8_EXT: return GL_UNSIGNED_BYTE; case GL_RGBA32F_EXT: return GL_FLOAT; case GL_RGB32F_EXT: return GL_FLOAT; case GL_ALPHA32F_EXT: return GL_FLOAT; case GL_LUMINANCE32F_EXT: return GL_FLOAT; case GL_LUMINANCE_ALPHA32F_EXT: return GL_FLOAT; case GL_RGBA16F_EXT: return GL_HALF_FLOAT_OES; case GL_RGB16F_EXT: return GL_HALF_FLOAT_OES; case GL_ALPHA16F_EXT: return GL_HALF_FLOAT_OES; case GL_LUMINANCE16F_EXT: return GL_HALF_FLOAT_OES; case GL_LUMINANCE_ALPHA16F_EXT: return GL_HALF_FLOAT_OES; case GL_BGRA8_EXT: return GL_UNSIGNED_BYTE; default: return GL_NONE; } } void GLES2DecoderImpl::DoTexStorage2DEXT( GLenum target, GLint levels, GLenum internal_format, GLsizei width, GLsizei height) { TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage2DEXT", "width", width, "height", height); if (!texture_manager()->ValidForTarget(target, 0, width, height, 1) || TextureManager::ComputeMipMapCount(target, width, height, 1) < levels) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glTexStorage2DEXT", "dimensions out of range"); return; } TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glTexStorage2DEXT", "unknown texture for target"); return; } Texture* texture = texture_ref->texture(); if (texture->IsAttachedToFramebuffer()) { framebuffer_state_.clear_state_dirty = true; } if (texture->IsImmutable()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glTexStorage2DEXT", "texture is immutable"); return; } GLenum format = ExtractFormatFromStorageFormat(internal_format); GLenum type = ExtractTypeFromStorageFormat(internal_format); { GLsizei level_width = width; GLsizei level_height = height; uint32 estimated_size = 0; for (int ii = 0; ii < levels; ++ii) { uint32 level_size = 0; if (!GLES2Util::ComputeImageDataSizes( level_width, level_height, 1, format, type, state_.unpack_alignment, &estimated_size, NULL, NULL) || !SafeAddUint32(estimated_size, level_size, &estimated_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glTexStorage2DEXT", "dimensions too large"); return; } level_width = std::max(1, level_width >> 1); level_height = std::max(1, level_height >> 1); } if (!EnsureGPUMemoryAvailable(estimated_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glTexStorage2DEXT", "out of memory"); return; } } LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glTexStorage2DEXT"); glTexStorage2DEXT(target, levels, internal_format, width, height); GLenum error = LOCAL_PEEK_GL_ERROR("glTexStorage2DEXT"); if (error == GL_NO_ERROR) { GLsizei level_width = width; GLsizei level_height = height; for (int ii = 0; ii < levels; ++ii) { texture_manager()->SetLevelInfo( texture_ref, target, ii, format, level_width, level_height, 1, 0, format, type, false); level_width = std::max(1, level_width >> 1); level_height = std::max(1, level_height >> 1); } texture->SetImmutable(true); } } error::Error GLES2DecoderImpl::HandleGenMailboxCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { return error::kUnknownCommand; } void GLES2DecoderImpl::DoProduceTextureCHROMIUM(GLenum target, const GLbyte* data) { TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoProduceTextureCHROMIUM", "context", logger_.GetLogPrefix(), "mailbox[0]", static_cast(data[0])); TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); ProduceTextureRef("glProduceTextureCHROMIUM", texture_ref, target, data); } void GLES2DecoderImpl::DoProduceTextureDirectCHROMIUM(GLuint client_id, GLenum target, const GLbyte* data) { TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoProduceTextureDirectCHROMIUM", "context", logger_.GetLogPrefix(), "mailbox[0]", static_cast(data[0])); ProduceTextureRef("glProduceTextureDirectCHROMIUM", GetTexture(client_id), target, data); } void GLES2DecoderImpl::ProduceTextureRef(std::string func_name, TextureRef* texture_ref, GLenum target, const GLbyte* data) { const Mailbox& mailbox = *reinterpret_cast(data); DLOG_IF(ERROR, !mailbox.Verify()) << func_name << " was passed a " "mailbox that was not generated by " "GenMailboxCHROMIUM."; if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, func_name.c_str(), "unknown texture for target"); return; } Texture* produced = texture_manager()->Produce(texture_ref); if (!produced) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, func_name.c_str(), "invalid texture"); return; } if (produced->target() != target) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, func_name.c_str(), "invalid target"); return; } group_->mailbox_manager()->ProduceTexture(mailbox, produced); } void GLES2DecoderImpl::DoConsumeTextureCHROMIUM(GLenum target, const GLbyte* data) { TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoConsumeTextureCHROMIUM", "context", logger_.GetLogPrefix(), "mailbox[0]", static_cast(data[0])); const Mailbox& mailbox = *reinterpret_cast(data); DLOG_IF(ERROR, !mailbox.Verify()) << "ConsumeTextureCHROMIUM was passed a " "mailbox that was not generated by " "GenMailboxCHROMIUM."; scoped_refptr texture_ref = texture_manager()->GetTextureInfoForTargetUnlessDefault(&state_, target); if (!texture_ref.get()) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glConsumeTextureCHROMIUM", "unknown texture for target"); return; } GLuint client_id = texture_ref->client_id(); if (!client_id) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glConsumeTextureCHROMIUM", "unknown texture for target"); return; } Texture* texture = group_->mailbox_manager()->ConsumeTexture(mailbox); if (!texture) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glConsumeTextureCHROMIUM", "invalid mailbox name"); return; } if (texture->target() != target) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glConsumeTextureCHROMIUM", "invalid target"); return; } DeleteTexturesHelper(1, &client_id); texture_ref = texture_manager()->Consume(client_id, texture); glBindTexture(target, texture_ref->service_id()); TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; unit.bind_target = target; switch (target) { case GL_TEXTURE_2D: unit.bound_texture_2d = texture_ref; break; case GL_TEXTURE_CUBE_MAP: unit.bound_texture_cube_map = texture_ref; break; case GL_TEXTURE_EXTERNAL_OES: unit.bound_texture_external_oes = texture_ref; break; case GL_TEXTURE_RECTANGLE_ARB: unit.bound_texture_rectangle_arb = texture_ref; break; default: NOTREACHED(); // Validation should prevent us getting here. break; } } void GLES2DecoderImpl::EnsureTextureForClientId( GLenum target, GLuint client_id) { TextureRef* texture_ref = GetTexture(client_id); if (!texture_ref) { GLuint service_id; glGenTextures(1, &service_id); DCHECK_NE(0u, service_id); texture_ref = CreateTexture(client_id, service_id); texture_manager()->SetTarget(texture_ref, target); glBindTexture(target, service_id); RestoreCurrentTextureBindings(&state_, target); } } // If CreateAndConsumeTexture fails we still need to ensure that the client_id // provided is associated with a service_id/TextureRef for consistency, even if // the resulting texture is incomplete. error::Error GLES2DecoderImpl::HandleCreateAndConsumeTextureCHROMIUMImmediate( uint32_t immediate_data_size, const void* cmd_data) { const gles2::cmds::CreateAndConsumeTextureCHROMIUMImmediate& c = *static_cast< const gles2::cmds::CreateAndConsumeTextureCHROMIUMImmediate*>( cmd_data); GLenum target = static_cast(c.target); uint32_t data_size; if (!ComputeDataSize(1, sizeof(GLbyte), 64, &data_size)) { return error::kOutOfBounds; } if (data_size > immediate_data_size) { return error::kOutOfBounds; } const GLbyte* mailbox = GetImmediateDataAs(c, data_size, immediate_data_size); if (!validators_->texture_bind_target.IsValid(target)) { LOCAL_SET_GL_ERROR_INVALID_ENUM( "glCreateAndConsumeTextureCHROMIUM", target, "target"); return error::kNoError; } if (mailbox == NULL) { return error::kOutOfBounds; } uint32_t client_id = c.client_id; DoCreateAndConsumeTextureCHROMIUM(target, mailbox, client_id); return error::kNoError; } void GLES2DecoderImpl::DoCreateAndConsumeTextureCHROMIUM(GLenum target, const GLbyte* data, GLuint client_id) { TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoCreateAndConsumeTextureCHROMIUM", "context", logger_.GetLogPrefix(), "mailbox[0]", static_cast(data[0])); const Mailbox& mailbox = *reinterpret_cast(data); DLOG_IF(ERROR, !mailbox.Verify()) << "CreateAndConsumeTextureCHROMIUM was " "passed a mailbox that was not " "generated by GenMailboxCHROMIUM."; TextureRef* texture_ref = GetTexture(client_id); if (texture_ref) { // No need to call EnsureTextureForClientId here, the client_id already has // an associated texture. LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCreateAndConsumeTextureCHROMIUM", "client id already in use"); return; } Texture* texture = group_->mailbox_manager()->ConsumeTexture(mailbox); if (!texture) { EnsureTextureForClientId(target, client_id); LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCreateAndConsumeTextureCHROMIUM", "invalid mailbox name"); return; } if (texture->target() != target) { EnsureTextureForClientId(target, client_id); LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glCreateAndConsumeTextureCHROMIUM", "invalid target"); return; } texture_ref = texture_manager()->Consume(client_id, texture); } bool GLES2DecoderImpl::DoIsValuebufferCHROMIUM(GLuint client_id) { const Valuebuffer* valuebuffer = GetValuebuffer(client_id); return valuebuffer && valuebuffer->IsValid(); } void GLES2DecoderImpl::DoBindValueBufferCHROMIUM(GLenum target, GLuint client_id) { Valuebuffer* valuebuffer = NULL; if (client_id != 0) { valuebuffer = GetValuebuffer(client_id); if (!valuebuffer) { if (!group_->bind_generates_resource()) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindValuebufferCHROMIUM", "id not generated by glBindValuebufferCHROMIUM"); return; } // It's a new id so make a valuebuffer for it. CreateValuebuffer(client_id); valuebuffer = GetValuebuffer(client_id); } valuebuffer->MarkAsValid(); } state_.bound_valuebuffer = valuebuffer; } void GLES2DecoderImpl::DoSubscribeValueCHROMIUM(GLenum target, GLenum subscription) { if (!CheckCurrentValuebuffer("glSubscribeValueCHROMIUM")) { return; } state_.bound_valuebuffer.get()->AddSubscription(subscription); } void GLES2DecoderImpl::DoPopulateSubscribedValuesCHROMIUM(GLenum target) { if (!CheckCurrentValuebuffer("glPopulateSubscribedValuesCHROMIUM")) { return; } valuebuffer_manager()->UpdateValuebufferState(state_.bound_valuebuffer.get()); } void GLES2DecoderImpl::DoUniformValueBufferCHROMIUM(GLint location, GLenum target, GLenum subscription) { if (!CheckCurrentValuebufferForSubscription( subscription, "glPopulateSubscribedValuesCHROMIUM")) { return; } if (!CheckSubscriptionTarget(location, subscription, "glPopulateSubscribedValuesCHROMIUM")) { return; } const ValueState* state = state_.bound_valuebuffer.get()->GetState(subscription); if (state) { switch (subscription) { case GL_MOUSE_POSITION_CHROMIUM: DoUniform2iv(location, 1, state->int_value); break; default: NOTREACHED() << "Unhandled uniform subscription target " << subscription; break; } } } void GLES2DecoderImpl::DoInsertEventMarkerEXT( GLsizei length, const GLchar* marker) { if (!marker) { marker = ""; } debug_marker_manager_.SetMarker( length ? std::string(marker, length) : std::string(marker)); } void GLES2DecoderImpl::DoPushGroupMarkerEXT( GLsizei length, const GLchar* marker) { if (!marker) { marker = ""; } std::string name = length ? std::string(marker, length) : std::string(marker); debug_marker_manager_.PushGroup(name); gpu_tracer_->Begin(TRACE_DISABLED_BY_DEFAULT("gpu_group_marker"), name, kTraceGroupMarker); } void GLES2DecoderImpl::DoPopGroupMarkerEXT(void) { debug_marker_manager_.PopGroup(); gpu_tracer_->End(kTraceGroupMarker); } void GLES2DecoderImpl::DoBindTexImage2DCHROMIUM( GLenum target, GLint image_id) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoBindTexImage2DCHROMIUM"); if (target == GL_TEXTURE_CUBE_MAP) { LOCAL_SET_GL_ERROR( GL_INVALID_ENUM, "glBindTexImage2DCHROMIUM", "invalid target"); return; } // Default target might be conceptually valid, but disallow it to avoid // accidents. TextureRef* texture_ref = texture_manager()->GetTextureInfoForTargetUnlessDefault(&state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBindTexImage2DCHROMIUM", "no texture bound"); return; } gfx::GLImage* gl_image = image_manager()->LookupImage(image_id); if (!gl_image) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBindTexImage2DCHROMIUM", "no image found with the given ID"); return; } { ScopedGLErrorSuppressor suppressor( "GLES2DecoderImpl::DoBindTexImage2DCHROMIUM", GetErrorState()); if (!gl_image->BindTexImage(target)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBindTexImage2DCHROMIUM", "fail to bind image with the given ID"); return; } } gfx::Size size = gl_image->GetSize(); texture_manager()->SetLevelInfo( texture_ref, target, 0, gl_image->GetInternalFormat(), size.width(), size.height(), 1, 0, gl_image->GetInternalFormat(), GL_UNSIGNED_BYTE, true); texture_manager()->SetLevelImage(texture_ref, target, 0, gl_image); } void GLES2DecoderImpl::DoReleaseTexImage2DCHROMIUM( GLenum target, GLint image_id) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoReleaseTexImage2DCHROMIUM"); // Default target might be conceptually valid, but disallow it to avoid // accidents. TextureRef* texture_ref = texture_manager()->GetTextureInfoForTargetUnlessDefault(&state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glReleaseTexImage2DCHROMIUM", "no texture bound"); return; } gfx::GLImage* gl_image = image_manager()->LookupImage(image_id); if (!gl_image) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glReleaseTexImage2DCHROMIUM", "no image found with the given ID"); return; } // Do nothing when image is not currently bound. if (texture_ref->texture()->GetLevelImage(target, 0) != gl_image) return; { ScopedGLErrorSuppressor suppressor( "GLES2DecoderImpl::DoReleaseTexImage2DCHROMIUM", GetErrorState()); gl_image->ReleaseTexImage(target); } texture_manager()->SetLevelInfo( texture_ref, target, 0, gl_image->GetInternalFormat(), 0, 0, 1, 0, gl_image->GetInternalFormat(), GL_UNSIGNED_BYTE, false); } error::Error GLES2DecoderImpl::HandleTraceBeginCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::TraceBeginCHROMIUM& c = *static_cast(cmd_data); Bucket* category_bucket = GetBucket(c.category_bucket_id); Bucket* name_bucket = GetBucket(c.name_bucket_id); if (!category_bucket || category_bucket->size() == 0 || !name_bucket || name_bucket->size() == 0) { return error::kInvalidArguments; } std::string category_name; std::string trace_name; if (!category_bucket->GetAsString(&category_name) || !name_bucket->GetAsString(&trace_name)) { return error::kInvalidArguments; } if (!gpu_tracer_->Begin(category_name, trace_name, kTraceCHROMIUM)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glTraceBeginCHROMIUM", "unable to create begin trace"); return error::kNoError; } return error::kNoError; } void GLES2DecoderImpl::DoTraceEndCHROMIUM() { if (!gpu_tracer_->End(kTraceCHROMIUM)) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTraceEndCHROMIUM", "no trace begin found"); return; } } void GLES2DecoderImpl::DoDrawBuffersEXT( GLsizei count, const GLenum* bufs) { if (count > static_cast(group_->max_draw_buffers())) { LOCAL_SET_GL_ERROR( GL_INVALID_VALUE, "glDrawBuffersEXT", "greater than GL_MAX_DRAW_BUFFERS_EXT"); return; } Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_FRAMEBUFFER); if (framebuffer) { for (GLsizei i = 0; i < count; ++i) { if (bufs[i] != static_cast(GL_COLOR_ATTACHMENT0 + i) && bufs[i] != GL_NONE) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glDrawBuffersEXT", "bufs[i] not GL_NONE or GL_COLOR_ATTACHMENTi_EXT"); return; } } glDrawBuffersARB(count, bufs); framebuffer->SetDrawBuffers(count, bufs); } else { // backbuffer if (count > 1 || (bufs[0] != GL_BACK && bufs[0] != GL_NONE)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glDrawBuffersEXT", "more than one buffer or bufs not GL_NONE or GL_BACK"); return; } GLenum mapped_buf = bufs[0]; if (GetBackbufferServiceId() != 0 && // emulated backbuffer bufs[0] == GL_BACK) { mapped_buf = GL_COLOR_ATTACHMENT0; } glDrawBuffersARB(count, &mapped_buf); group_->set_draw_buffer(bufs[0]); } } void GLES2DecoderImpl::DoLoseContextCHROMIUM(GLenum current, GLenum other) { MarkContextLost(GetContextLostReasonFromResetStatus(current)); group_->LoseContexts(GetContextLostReasonFromResetStatus(other)); reset_by_robustness_extension_ = true; } void GLES2DecoderImpl::DoMatrixLoadfCHROMIUM(GLenum matrix_mode, const GLfloat* matrix) { DCHECK(matrix_mode == GL_PATH_PROJECTION_CHROMIUM || matrix_mode == GL_PATH_MODELVIEW_CHROMIUM); if (!features().chromium_path_rendering) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glMatrixLoadfCHROMIUM", "function not available"); return; } GLfloat* target_matrix = matrix_mode == GL_PATH_PROJECTION_CHROMIUM ? state_.projection_matrix : state_.modelview_matrix; memcpy(target_matrix, matrix, sizeof(GLfloat) * 16); // The matrix_mode is either GL_PATH_MODELVIEW_NV or GL_PATH_PROJECTION_NV // since the values of the _NV and _CHROMIUM tokens match. glMatrixLoadfEXT(matrix_mode, matrix); } void GLES2DecoderImpl::DoMatrixLoadIdentityCHROMIUM(GLenum matrix_mode) { DCHECK(matrix_mode == GL_PATH_PROJECTION_CHROMIUM || matrix_mode == GL_PATH_MODELVIEW_CHROMIUM); if (!features().chromium_path_rendering) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glMatrixLoadIdentityCHROMIUM", "function not available"); return; } GLfloat* target_matrix = matrix_mode == GL_PATH_PROJECTION_CHROMIUM ? state_.projection_matrix : state_.modelview_matrix; memcpy(target_matrix, kIdentityMatrix, sizeof(kIdentityMatrix)); // The matrix_mode is either GL_PATH_MODELVIEW_NV or GL_PATH_PROJECTION_NV // since the values of the _NV and _CHROMIUM tokens match. glMatrixLoadIdentityEXT(matrix_mode); } bool GLES2DecoderImpl::ValidateAsyncTransfer( const char* function_name, TextureRef* texture_ref, GLenum target, GLint level, const void * data) { // We only support async uploads to 2D textures for now. if (GL_TEXTURE_2D != target) { LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, target, "target"); return false; } // We only support uploads to level zero for now. if (level != 0) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "level != 0"); return false; } // A transfer buffer must be bound, even for asyncTexImage2D. if (data == NULL) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "buffer == 0"); return false; } // We only support one async transfer in progress. if (!texture_ref || async_pixel_transfer_manager_->AsyncTransferIsInProgress(texture_ref)) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, function_name, "transfer already in progress"); return false; } return true; } base::Closure GLES2DecoderImpl::AsyncUploadTokenCompletionClosure( uint32 async_upload_token, uint32 sync_data_shm_id, uint32 sync_data_shm_offset) { scoped_refptr buffer = GetSharedMemoryBuffer(sync_data_shm_id); if (!buffer.get() || !buffer->GetDataAddress(sync_data_shm_offset, sizeof(AsyncUploadSync))) return base::Closure(); AsyncMemoryParams mem_params(buffer, sync_data_shm_offset, sizeof(AsyncUploadSync)); scoped_refptr observer( new AsyncUploadTokenCompletionObserver(async_upload_token)); return base::Bind( &AsyncPixelTransferManager::AsyncNotifyCompletion, base::Unretained(GetAsyncPixelTransferManager()), mem_params, observer); } error::Error GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::AsyncTexImage2DCHROMIUM& c = *static_cast(cmd_data); TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandleAsyncTexImage2DCHROMIUM"); GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLenum internal_format = static_cast(c.internalformat); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLint border = static_cast(c.border); GLenum format = static_cast(c.format); GLenum type = static_cast(c.type); uint32 pixels_shm_id = static_cast(c.pixels_shm_id); uint32 pixels_shm_offset = static_cast(c.pixels_shm_offset); uint32 pixels_size; uint32 async_upload_token = static_cast(c.async_upload_token); uint32 sync_data_shm_id = static_cast(c.sync_data_shm_id); uint32 sync_data_shm_offset = static_cast(c.sync_data_shm_offset); base::ScopedClosureRunner scoped_completion_callback; if (async_upload_token) { base::Closure completion_closure = AsyncUploadTokenCompletionClosure(async_upload_token, sync_data_shm_id, sync_data_shm_offset); if (completion_closure.is_null()) return error::kInvalidArguments; scoped_completion_callback.Reset(completion_closure); } // TODO(epenner): Move this and copies of this memory validation // into ValidateTexImage2D step. if (!GLES2Util::ComputeImageDataSizes( width, height, 1, format, type, state_.unpack_alignment, &pixels_size, NULL, NULL)) { return error::kOutOfBounds; } const void* pixels = NULL; if (pixels_shm_id != 0 || pixels_shm_offset != 0) { pixels = GetSharedMemoryAs( pixels_shm_id, pixels_shm_offset, pixels_size); if (!pixels) { return error::kOutOfBounds; } } TextureManager::DoTexImageArguments args = { target, level, internal_format, width, height, 1, border, format, type, pixels, pixels_size, TextureManager::DoTexImageArguments::kTexImage2D }; TextureRef* texture_ref; // All the normal glTexSubImage2D validation. if (!texture_manager()->ValidateTexImage( &state_, "glAsyncTexImage2DCHROMIUM", args, &texture_ref)) { return error::kNoError; } // Extra async validation. Texture* texture = texture_ref->texture(); if (!ValidateAsyncTransfer( "glAsyncTexImage2DCHROMIUM", texture_ref, target, level, pixels)) return error::kNoError; // Don't allow async redefinition of a textures. if (texture->IsDefined()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glAsyncTexImage2DCHROMIUM", "already defined"); return error::kNoError; } if (!EnsureGPUMemoryAvailable(pixels_size)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glAsyncTexImage2DCHROMIUM", "out of memory"); return error::kNoError; } // Setup the parameters. AsyncTexImage2DParams tex_params = { target, level, static_cast(internal_format), width, height, border, format, type}; AsyncMemoryParams mem_params( GetSharedMemoryBuffer(c.pixels_shm_id), c.pixels_shm_offset, pixels_size); // Set up the async state if needed, and make the texture // immutable so the async state stays valid. The level info // is set up lazily when the transfer completes. AsyncPixelTransferDelegate* delegate = async_pixel_transfer_manager_->CreatePixelTransferDelegate(texture_ref, tex_params); texture->SetImmutable(true); delegate->AsyncTexImage2D( tex_params, mem_params, base::Bind(&TextureManager::SetLevelInfoFromParams, // The callback is only invoked if the transfer delegate still // exists, which implies through manager->texture_ref->state // ownership that both of these pointers are valid. base::Unretained(texture_manager()), base::Unretained(texture_ref), tex_params)); return error::kNoError; } error::Error GLES2DecoderImpl::HandleAsyncTexSubImage2DCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::AsyncTexSubImage2DCHROMIUM& c = *static_cast(cmd_data); TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandleAsyncTexSubImage2DCHROMIUM"); GLenum target = static_cast(c.target); GLint level = static_cast(c.level); GLint xoffset = static_cast(c.xoffset); GLint yoffset = static_cast(c.yoffset); GLsizei width = static_cast(c.width); GLsizei height = static_cast(c.height); GLenum format = static_cast(c.format); GLenum type = static_cast(c.type); uint32 async_upload_token = static_cast(c.async_upload_token); uint32 sync_data_shm_id = static_cast(c.sync_data_shm_id); uint32 sync_data_shm_offset = static_cast(c.sync_data_shm_offset); base::ScopedClosureRunner scoped_completion_callback; if (async_upload_token) { base::Closure completion_closure = AsyncUploadTokenCompletionClosure(async_upload_token, sync_data_shm_id, sync_data_shm_offset); if (completion_closure.is_null()) return error::kInvalidArguments; scoped_completion_callback.Reset(completion_closure); } // TODO(epenner): Move this and copies of this memory validation // into ValidateTexSubImage2D step. uint32 data_size; if (!GLES2Util::ComputeImageDataSizes( width, height, 1, format, type, state_.unpack_alignment, &data_size, NULL, NULL)) { return error::kOutOfBounds; } const void* pixels = GetSharedMemoryAs( c.data_shm_id, c.data_shm_offset, data_size); // All the normal glTexSubImage2D validation. error::Error error = error::kNoError; if (!ValidateTexSubImage2D(&error, "glAsyncTexSubImage2DCHROMIUM", target, level, xoffset, yoffset, width, height, format, type, pixels)) { return error; } // Extra async validation. TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); Texture* texture = texture_ref->texture(); if (!ValidateAsyncTransfer( "glAsyncTexSubImage2DCHROMIUM", texture_ref, target, level, pixels)) return error::kNoError; // Guarantee async textures are always 'cleared' as follows: // - AsyncTexImage2D can not redefine an existing texture // - AsyncTexImage2D must initialize the entire image via non-null buffer. // - AsyncTexSubImage2D clears synchronously if not already cleared. // - Textures become immutable after an async call. // This way we know in all cases that an async texture is always clear. if (!texture->SafeToRenderFrom()) { if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, level)) { LOCAL_SET_GL_ERROR( GL_OUT_OF_MEMORY, "glAsyncTexSubImage2DCHROMIUM", "dimensions too big"); return error::kNoError; } } // Setup the parameters. AsyncTexSubImage2DParams tex_params = {target, level, xoffset, yoffset, width, height, format, type}; AsyncMemoryParams mem_params( GetSharedMemoryBuffer(c.data_shm_id), c.data_shm_offset, data_size); AsyncPixelTransferDelegate* delegate = async_pixel_transfer_manager_->GetPixelTransferDelegate(texture_ref); if (!delegate) { // TODO(epenner): We may want to enforce exclusive use // of async APIs in which case this should become an error, // (the texture should have been async defined). AsyncTexImage2DParams define_params = {target, level, 0, 0, 0, 0, 0, 0}; texture->GetLevelSize( target, level, &define_params.width, &define_params.height, nullptr); texture->GetLevelType( target, level, &define_params.type, &define_params.internal_format); // Set up the async state if needed, and make the texture // immutable so the async state stays valid. delegate = async_pixel_transfer_manager_->CreatePixelTransferDelegate( texture_ref, define_params); texture->SetImmutable(true); } delegate->AsyncTexSubImage2D(tex_params, mem_params); return error::kNoError; } error::Error GLES2DecoderImpl::HandleWaitAsyncTexImage2DCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { const gles2::cmds::WaitAsyncTexImage2DCHROMIUM& c = *static_cast(cmd_data); TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandleWaitAsyncTexImage2DCHROMIUM"); GLenum target = static_cast(c.target); if (GL_TEXTURE_2D != target) { LOCAL_SET_GL_ERROR( GL_INVALID_ENUM, "glWaitAsyncTexImage2DCHROMIUM", "target"); return error::kNoError; } TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( &state_, target); if (!texture_ref) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glWaitAsyncTexImage2DCHROMIUM", "unknown texture"); return error::kNoError; } AsyncPixelTransferDelegate* delegate = async_pixel_transfer_manager_->GetPixelTransferDelegate(texture_ref); if (!delegate) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glWaitAsyncTexImage2DCHROMIUM", "No async transfer started"); return error::kNoError; } delegate->WaitForTransferCompletion(); ProcessFinishedAsyncTransfers(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleWaitAllAsyncTexImage2DCHROMIUM( uint32 immediate_data_size, const void* data) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandleWaitAsyncTexImage2DCHROMIUM"); GetAsyncPixelTransferManager()->WaitAllAsyncTexImage2D(); ProcessFinishedAsyncTransfers(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleUniformBlockBinding( uint32_t immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::UniformBlockBinding& c = *static_cast(cmd_data); GLuint client_id = c.program; GLuint index = static_cast(c.index); GLuint binding = static_cast(c.binding); Program* program = GetProgramInfoNotShader( client_id, "glUniformBlockBinding"); if (!program) { return error::kNoError; } GLuint service_id = program->service_id(); glUniformBlockBinding(service_id, index, binding); return error::kNoError; } error::Error GLES2DecoderImpl::HandleClientWaitSync( uint32_t immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::ClientWaitSync& c = *static_cast(cmd_data); GLuint sync = static_cast(c.sync); GLbitfield flags = static_cast(c.flags); GLuint64 timeout = GLES2Util::MapTwoUint32ToUint64(c.timeout_0, c.timeout_1); typedef cmds::ClientWaitSync::Result Result; Result* result_dst = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result_dst)); if (!result_dst) { return error::kOutOfBounds; } if (*result_dst != GL_WAIT_FAILED) { return error::kInvalidArguments; } GLsync service_sync = 0; if (!group_->GetSyncServiceId(sync, &service_sync)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "ClientWaitSync", "invalid sync"); return error::kNoError; } *result_dst = glClientWaitSync(service_sync, flags, timeout); return error::kNoError; } error::Error GLES2DecoderImpl::HandleWaitSync( uint32_t immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) return error::kUnknownCommand; const gles2::cmds::WaitSync& c = *static_cast(cmd_data); GLuint sync = static_cast(c.sync); GLbitfield flags = static_cast(c.flags); GLuint64 timeout = GLES2Util::MapTwoUint32ToUint64(c.timeout_0, c.timeout_1); GLsync service_sync = 0; if (!group_->GetSyncServiceId(sync, &service_sync)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "WaitSync", "invalid sync"); return error::kNoError; } glWaitSync(service_sync, flags, timeout); return error::kNoError; } error::Error GLES2DecoderImpl::HandleMapBufferRange( uint32_t immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) { return error::kUnknownCommand; } const gles2::cmds::MapBufferRange& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); GLbitfield access = static_cast(c.access); GLintptr offset = static_cast(c.offset); GLsizeiptr size = static_cast(c.size); typedef cmds::MapBufferRange::Result Result; Result* result = GetSharedMemoryAs( c.result_shm_id, c.result_shm_offset, sizeof(*result)); if (!result) { return error::kOutOfBounds; } if (*result != 0) { *result = 0; return error::kInvalidArguments; } int8_t* mem = GetSharedMemoryAs(c.data_shm_id, c.data_shm_offset, size); if (!mem) { return error::kOutOfBounds; } GLbitfield mask = GL_MAP_INVALIDATE_BUFFER_BIT; if ((access & mask) == mask) { // TODO(zmo): To be on the safe side, always map // GL_MAP_INVALIDATE_BUFFER_BIT to GL_MAP_INVALIDATE_RANGE_BIT. access = (access & ~GL_MAP_INVALIDATE_BUFFER_BIT); access = (access | GL_MAP_INVALIDATE_RANGE_BIT); } // TODO(zmo): Always filter out GL_MAP_UNSYNCHRONIZED_BIT to get rid of // undefined behaviors. mask = GL_MAP_READ_BIT | GL_MAP_UNSYNCHRONIZED_BIT; if ((access & mask) == mask) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "MapBufferRange", "incompatible access bits"); return error::kNoError; } access = (access & ~GL_MAP_UNSYNCHRONIZED_BIT); if ((access & GL_MAP_WRITE_BIT) == GL_MAP_WRITE_BIT && (access & GL_MAP_INVALIDATE_RANGE_BIT) == 0) { access = (access | GL_MAP_READ_BIT); } void* ptr = glMapBufferRange(target, offset, size, access); if (ptr == nullptr) { return error::kNoError; } Buffer* buffer = buffer_manager()->GetBufferInfoForTarget(&state_, target); DCHECK(buffer); buffer->SetMappedRange(offset, size, access, ptr, GetSharedMemoryBuffer(c.data_shm_id)); if ((access & GL_MAP_INVALIDATE_RANGE_BIT) == 0) { memcpy(mem, ptr, size); } *result = 1; return error::kNoError; } error::Error GLES2DecoderImpl::HandleUnmapBuffer( uint32_t immediate_data_size, const void* cmd_data) { if (!unsafe_es3_apis_enabled()) { return error::kUnknownCommand; } const gles2::cmds::UnmapBuffer& c = *static_cast(cmd_data); GLenum target = static_cast(c.target); Buffer* buffer = buffer_manager()->GetBufferInfoForTarget(&state_, target); if (!buffer) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "UnmapBuffer", "no buffer bound"); return error::kNoError; } const Buffer::MappedRange* mapped_range = buffer->GetMappedRange(); if (!mapped_range) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "UnmapBuffer", "buffer is unmapped"); return error::kNoError; } if ((mapped_range->access & GL_MAP_WRITE_BIT) == 0 || (mapped_range->access & GL_MAP_FLUSH_EXPLICIT_BIT) == GL_MAP_FLUSH_EXPLICIT_BIT) { // If we don't need to write back, or explict flush is required, no copying // back is needed. } else { void* mem = mapped_range->GetShmPointer(); if (!mem) { return error::kOutOfBounds; } DCHECK(mapped_range->pointer); memcpy(mapped_range->pointer, mem, mapped_range->size); } buffer->RemoveMappedRange(); GLboolean rt = glUnmapBuffer(target); if (rt == GL_FALSE) { // At this point, we have already done the necessary validation, so // GL_FALSE indicates data corruption. // TODO(zmo): We could redo the map / copy data / unmap to recover, but // the second unmap could still return GL_FALSE. For now, we simply lose // the contexts in the share group. LOG(ERROR) << "glUnmapBuffer unexpectedly returned GL_FALSE"; // Need to lose current context before broadcasting! MarkContextLost(error::kGuilty); group_->LoseContexts(error::kInnocent); return error::kLostContext; } return error::kNoError; } void GLES2DecoderImpl::OnTextureRefDetachedFromFramebuffer( TextureRef* texture_ref) { Texture* texture = texture_ref->texture(); DoDidUseTexImageIfNeeded(texture, texture->target()); } // Note that GL_LOST_CONTEXT is specific to GLES. // For desktop GL we have to query the reset status proactively. void GLES2DecoderImpl::OnContextLostError() { if (!WasContextLost()) { // Need to lose current context before broadcasting! CheckResetStatus(); group_->LoseContexts(error::kUnknown); reset_by_robustness_extension_ = true; } } void GLES2DecoderImpl::OnOutOfMemoryError() { if (lose_context_when_out_of_memory_ && !WasContextLost()) { error::ContextLostReason other = error::kOutOfMemory; if (CheckResetStatus()) { other = error::kUnknown; } else { // Need to lose current context before broadcasting! MarkContextLost(error::kOutOfMemory); } group_->LoseContexts(other); } } // 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. #include "gpu/command_buffer/service/gles2_cmd_decoder_autogen.h" } // namespace gles2 } // namespace gpu