| // 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 <limits.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <limits> |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <utility> |
| |
| #include "base/callback.h" |
| #include "base/callback_helpers.h" |
| #include "base/containers/flat_set.h" |
| #include "base/containers/queue.h" |
| #include "base/containers/span.h" |
| #include "base/debug/alias.h" |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/numerics/ranges.h" |
| #include "base/numerics/safe_math.h" |
| #include "base/optional.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/trace_event/trace_event.h" |
| #include "build/build_config.h" |
| #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/gpu_memory_buffer_support.h" |
| #include "gpu/command_buffer/common/mailbox.h" |
| #include "gpu/command_buffer/common/sync_token.h" |
| #include "gpu/command_buffer/service/buffer_manager.h" |
| #include "gpu/command_buffer/service/command_buffer_service.h" |
| #include "gpu/command_buffer/service/context_group.h" |
| #include "gpu/command_buffer/service/context_state.h" |
| #include "gpu/command_buffer/service/decoder_client.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_stream_texture_image.h" |
| #include "gpu/command_buffer/service/gl_utils.h" |
| #include "gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h" |
| #include "gpu/command_buffer/service/gles2_cmd_clear_framebuffer.h" |
| #include "gpu/command_buffer/service/gles2_cmd_copy_tex_image.h" |
| #include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h" |
| #include "gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h" |
| #include "gpu/command_buffer/service/gles2_cmd_srgb_converter.h" |
| #include "gpu/command_buffer/service/gles2_cmd_validation.h" |
| #include "gpu/command_buffer/service/gles2_query_manager.h" |
| #include "gpu/command_buffer/service/gpu_fence_manager.h" |
| #include "gpu/command_buffer/service/gpu_state_tracer.h" |
| #include "gpu/command_buffer/service/gpu_tracer.h" |
| #include "gpu/command_buffer/service/image_factory.h" |
| #include "gpu/command_buffer/service/image_manager.h" |
| #include "gpu/command_buffer/service/logger.h" |
| #include "gpu/command_buffer/service/mailbox_manager.h" |
| #include "gpu/command_buffer/service/memory_tracking.h" |
| #include "gpu/command_buffer/service/path_manager.h" |
| #include "gpu/command_buffer/service/program_manager.h" |
| #include "gpu/command_buffer/service/renderbuffer_manager.h" |
| #include "gpu/command_buffer/service/sampler_manager.h" |
| #include "gpu/command_buffer/service/service_discardable_manager.h" |
| #include "gpu/command_buffer/service/shader_manager.h" |
| #include "gpu/command_buffer/service/shader_translator.h" |
| #include "gpu/command_buffer/service/shared_image_factory.h" |
| #include "gpu/command_buffer/service/shared_image_representation.h" |
| #include "gpu/command_buffer/service/texture_manager.h" |
| #include "gpu/command_buffer/service/transform_feedback_manager.h" |
| #include "gpu/command_buffer/service/validating_abstract_texture_impl.h" |
| #include "gpu/command_buffer/service/vertex_array_manager.h" |
| #include "gpu/command_buffer/service/vertex_attrib_manager.h" |
| #include "gpu/config/gpu_preferences.h" |
| #include "third_party/angle/src/image_util/loadimage.h" |
| #include "third_party/smhasher/src/City.h" |
| #include "ui/gfx/buffer_types.h" |
| #include "ui/gfx/color_space.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/gpu_fence.h" |
| #include "ui/gfx/gpu_memory_buffer.h" |
| #include "ui/gfx/ipc/color/gfx_param_traits.h" |
| #include "ui/gfx/overlay_transform.h" |
| #include "ui/gfx/transform.h" |
| #include "ui/gl/ca_renderer_layer_params.h" |
| #include "ui/gl/dc_renderer_layer_params.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_fence.h" |
| #include "ui/gl/gl_gl_api_implementation.h" |
| #include "ui/gl/gl_image.h" |
| #include "ui/gl/gl_implementation.h" |
| #include "ui/gl/gl_surface.h" |
| #include "ui/gl/gl_version_info.h" |
| #include "ui/gl/gpu_timing.h" |
| #include "ui/gl/init/create_gr_gl_interface.h" |
| #include "ui/gl/scoped_make_current.h" |
| |
| #if defined(OS_MACOSX) |
| #include <IOSurface/IOSurface.h> |
| // Note that this must be included after gl_bindings.h to avoid conflicts. |
| #include <OpenGL/CGLIOSurface.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"; |
| |
| 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 <typename MANAGER_TYPE, typename OBJECT_TYPE> |
| 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; |
| } |
| |
| template <typename OBJECT_TYPE> |
| GLuint GetServiceId(const OBJECT_TYPE* object) { |
| return object ? object->service_id() : 0; |
| } |
| |
| struct Vec4f { |
| explicit Vec4f(const Vec4& data) { |
| data.GetValues(v); |
| } |
| |
| GLfloat v[4]; |
| }; |
| |
| struct TexSubCoord3D { |
| TexSubCoord3D(int _xoffset, int _yoffset, int _zoffset, |
| int _width, int _height, int _depth) |
| : xoffset(_xoffset), |
| yoffset(_yoffset), |
| zoffset(_zoffset), |
| width(_width), |
| height(_height), |
| depth(_depth) {} |
| |
| int xoffset; |
| int yoffset; |
| int zoffset; |
| int width; |
| int height; |
| int depth; |
| }; |
| |
| // Check if all |ref| bits are set in |bits|. |
| bool AllBitsSet(GLbitfield bits, GLbitfield ref) { |
| DCHECK_NE(0u, ref); |
| return ((bits & ref) == ref); |
| } |
| |
| // Check if any of |ref| bits are set in |bits|. |
| bool AnyBitsSet(GLbitfield bits, GLbitfield ref) { |
| DCHECK_NE(0u, ref); |
| return ((bits & ref) != 0); |
| } |
| |
| // Check if any bits are set in |bits| other than the bits in |ref|. |
| bool AnyOtherBitsSet(GLbitfield bits, GLbitfield ref) { |
| DCHECK_NE(0u, ref); |
| GLbitfield mask = ~ref; |
| return ((bits & mask) != 0); |
| } |
| |
| void APIENTRY GLDebugMessageCallback(GLenum source, |
| GLenum type, |
| GLuint id, |
| GLenum severity, |
| GLsizei length, |
| const GLchar* message, |
| const GLvoid* user_param) { |
| Logger* error_logger = static_cast<Logger*>(const_cast<void*>(user_param)); |
| LogGLDebugMessage(source, type, id, severity, length, message, error_logger); |
| } |
| |
| } // 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, \ |
| static_cast<uint32_t>(value), label) |
| #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_t), // NOLINT |
| "GLint should be the same size as uint32_t"); |
| static_assert(sizeof(GLsizei) == sizeof(uint32_t), // NOLINT |
| "GLsizei should be the same size as uint32_t"); |
| 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 { |
| |
| // 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 std::string& str) { |
| return str.length() == 0 || |
| std::find_if_not(str.begin(), str.end(), CharacterIsValidForGLES) == |
| str.end(); |
| } |
| |
| // 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); |
| }; |
| |
| // Temporarily create and bind a single sample copy of the currently bound |
| // framebuffer using CopyTexImage2D. This is useful as a workaround for drivers |
| // that have broken implementations of ReadPixels on multisampled framebuffers. |
| // See http://crbug.com/890002 |
| class ScopedFramebufferCopyBinder { |
| public: |
| explicit ScopedFramebufferCopyBinder(GLES2DecoderImpl* decoder, |
| GLint x = 0, |
| GLint y = 0, |
| GLint width = 0, |
| GLint height = 0); |
| |
| ~ScopedFramebufferCopyBinder(); |
| |
| private: |
| GLES2DecoderImpl* decoder_; |
| std::unique_ptr<ScopedFramebufferBinder> framebuffer_binder_; |
| GLuint temp_texture_; |
| GLuint temp_framebuffer_; |
| DISALLOW_COPY_AND_ASSIGN(ScopedFramebufferCopyBinder); |
| }; |
| |
| // Temporarily changes a decoder's PIXEL_UNPACK_BUFFER to 0 and set pixel unpack |
| // params to default, and restore them when this object goes out of scope. |
| class ScopedPixelUnpackState { |
| public: |
| explicit ScopedPixelUnpackState(ContextState* state); |
| ~ScopedPixelUnpackState(); |
| |
| private: |
| ContextState* state_; |
| DISALLOW_COPY_AND_ASSIGN(ScopedPixelUnpackState); |
| }; |
| |
| // Encapsulates an OpenGL texture. |
| class BackTexture { |
| public: |
| explicit BackTexture(GLES2DecoderImpl* decoder); |
| ~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(); |
| |
| // 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(); |
| |
| // The bind point for the texture. |
| GLenum Target(); |
| |
| scoped_refptr<TextureRef> texture_ref() { return texture_ref_; } |
| |
| GLuint id() const { |
| return texture_ref_ ? texture_ref_->service_id() : 0; |
| } |
| |
| gfx::Size size() const { |
| return size_; |
| } |
| |
| gl::GLApi* api() const; |
| |
| private: |
| // The texture must be bound to Target() before calling this method. |
| // Returns whether the operation was successful. |
| bool AllocateNativeGpuMemoryBuffer(const gfx::Size& size, |
| GLenum format, |
| bool zero); |
| |
| // The texture must be bound to Target() before calling this method. |
| void DestroyNativeGpuMemoryBuffer(bool have_context); |
| |
| MemoryTypeTracker memory_tracker_; |
| size_t bytes_allocated_; |
| gfx::Size size_; |
| GLES2DecoderImpl* decoder_; |
| |
| scoped_refptr<TextureRef> texture_ref_; |
| |
| // The image that backs the texture, if its backed by a native |
| // GpuMemoryBuffer. |
| scoped_refptr<gl::GLImage> image_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BackTexture); |
| }; |
| |
| // Encapsulates an OpenGL render buffer of any format. |
| class BackRenderbuffer { |
| public: |
| explicit BackRenderbuffer(GLES2DecoderImpl* decoder); |
| ~BackRenderbuffer(); |
| |
| // Create a new render buffer. |
| void Create(); |
| |
| // Set the initial size and format of a render buffer or resize it. |
| bool AllocateStorage(const gfx::Size& size, GLenum format, GLsizei samples); |
| |
| // Destroy the render buffer. This must be explicitly called before destroying |
| // this object. |
| void Destroy(); |
| |
| // Invalidate the render buffer. This can be used when a context is lost and |
| // it is not possible to make it current in order to free the resource. |
| void Invalidate(); |
| |
| GLuint id() const { |
| return id_; |
| } |
| |
| gl::GLApi* api() const; |
| |
| private: |
| GLES2DecoderImpl* decoder_; |
| MemoryTypeTracker memory_tracker_; |
| 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_; |
| } |
| |
| gl::GLApi* api() const; |
| |
| private: |
| GLES2DecoderImpl* decoder_; |
| GLuint id_; |
| DISALLOW_COPY_AND_ASSIGN(BackFramebuffer); |
| }; |
| |
| struct FenceCallback { |
| FenceCallback() : fence(gl::GLFence::Create()) { DCHECK(fence); } |
| FenceCallback(FenceCallback&&) = default; |
| FenceCallback& operator=(FenceCallback&&) = default; |
| std::vector<base::OnceClosure> callbacks; |
| std::unique_ptr<gl::GLFence> fence; |
| }; |
| |
| // For ensuring that client-side glRenderbufferStorageMultisampleEXT |
| // calls go down the correct code path. |
| enum ForcedMultisampleMode { |
| kForceExtMultisampledRenderToTexture, |
| kDoNotForce |
| }; |
| |
| // } // anonymous namespace. |
| |
| // static |
| const unsigned int GLES2Decoder::kDefaultStencilMask = |
| static_cast<unsigned int>(-1); |
| |
| bool GLES2Decoder::GetServiceTextureId(uint32_t client_texture_id, |
| uint32_t* service_texture_id) { |
| return false; |
| } |
| |
| uint32_t GLES2Decoder::GetAndClearBackbufferClearBitsForTest() { |
| return 0; |
| } |
| |
| GLES2Decoder::GLES2Decoder(CommandBufferServiceBase* command_buffer_service, |
| Outputter* outputter) |
| : CommonDecoder(command_buffer_service), outputter_(outputter) { |
| DCHECK(outputter_); |
| } |
| |
| GLES2Decoder::~GLES2Decoder() = default; |
| |
| bool GLES2Decoder::initialized() const { |
| return initialized_; |
| } |
| |
| TextureBase* GLES2Decoder::GetTextureBase(uint32_t client_id) { |
| return nullptr; |
| } |
| |
| void GLES2Decoder::SetLevelInfo(uint32_t client_id, |
| int level, |
| unsigned internal_format, |
| unsigned width, |
| unsigned height, |
| unsigned depth, |
| unsigned format, |
| unsigned type, |
| const gfx::Rect& cleared_rect) {} |
| |
| void GLES2Decoder::BeginDecoding() {} |
| |
| void GLES2Decoder::EndDecoding() {} |
| |
| base::StringPiece GLES2Decoder::GetLogPrefix() { |
| return GetLogger()->GetLogPrefix(); |
| } |
| |
| void GLES2Decoder::SetLogCommands(bool log_commands) { |
| log_commands_ = log_commands; |
| } |
| |
| Outputter* GLES2Decoder::outputter() const { |
| return outputter_; |
| } |
| |
| // 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 ErrorStateClient { |
| public: |
| GLES2DecoderImpl(DecoderClient* client, |
| CommandBufferServiceBase* command_buffer_service, |
| Outputter* outputter, |
| ContextGroup* group); |
| ~GLES2DecoderImpl() override; |
| |
| error::Error DoCommands(unsigned int num_commands, |
| const volatile void* buffer, |
| int num_entries, |
| int* entries_processed) override; |
| |
| template <bool DebugImpl> |
| error::Error DoCommandsImpl(unsigned int num_commands, |
| const volatile void* buffer, |
| int num_entries, |
| int* entries_processed); |
| |
| // Overridden from GLES2Decoder. |
| base::WeakPtr<DecoderContext> AsWeakPtr() override; |
| gpu::ContextResult Initialize( |
| const scoped_refptr<gl::GLSurface>& surface, |
| const scoped_refptr<gl::GLContext>& context, |
| bool offscreen, |
| const DisallowedFeatures& disallowed_features, |
| const ContextCreationAttribs& attrib_helper) override; |
| void Destroy(bool have_context) override; |
| void SetSurface(const scoped_refptr<gl::GLSurface>& surface) override; |
| void ReleaseSurface() override; |
| void TakeFrontBuffer(const Mailbox& mailbox) override; |
| void ReturnFrontBuffer(const Mailbox& mailbox, bool is_lost) override; |
| bool ResizeOffscreenFramebuffer(const gfx::Size& size) override; |
| bool MakeCurrent() override; |
| gl::GLApi* api() const { return state_.api(); } |
| GLES2Util* GetGLES2Util() override { return &util_; } |
| gl::GLContext* GetGLContext() override { return context_.get(); } |
| gl::GLSurface* GetGLSurface() override { return surface_.get(); } |
| ContextGroup* GetContextGroup() override { return group_.get(); } |
| const FeatureInfo* GetFeatureInfo() const override { |
| return feature_info_.get(); |
| } |
| Capabilities GetCapabilities() override; |
| void RestoreState(const ContextState* prev_state) override; |
| |
| void RestoreActiveTexture() const override { state_.RestoreActiveTexture(); } |
| void RestoreAllTextureUnitAndSamplerBindings( |
| const ContextState* prev_state) const override { |
| state_.RestoreAllTextureUnitAndSamplerBindings(prev_state); |
| } |
| void RestoreActiveTextureUnitBinding(unsigned int target) const override { |
| state_.RestoreActiveTextureUnitBinding(target); |
| } |
| void RestoreBufferBindings() const override { |
| state_.RestoreBufferBindings(); |
| } |
| void RestoreGlobalState() const override { |
| state_.RestoreGlobalState(nullptr); |
| } |
| void RestoreProgramBindings() const override { |
| state_.RestoreProgramSettings(nullptr, false); |
| } |
| void RestoreTextureUnitBindings(unsigned unit) const override { |
| state_.RestoreTextureUnitBindings(unit, nullptr); |
| } |
| void RestoreVertexAttribArray(unsigned index) override { |
| RestoreStateForAttrib(index, true); |
| } |
| void RestoreBufferBinding(unsigned int target) override; |
| void RestoreFramebufferBindings() const override; |
| void RestoreRenderbufferBindings() override; |
| void RestoreTextureState(unsigned service_id) const override; |
| |
| void ClearDeviceWindowRectangles() const; |
| void RestoreDeviceWindowRectangles() const override; |
| |
| void ClearAllAttributes() const override; |
| void RestoreAllAttributes() const override; |
| |
| QueryManager* GetQueryManager() override { return query_manager_.get(); } |
| void SetQueryCallback(unsigned int query_client_id, |
| base::OnceClosure callback) override; |
| |
| GpuFenceManager* GetGpuFenceManager() override { |
| return gpu_fence_manager_.get(); |
| } |
| FramebufferManager* GetFramebufferManager() override { |
| return framebuffer_manager_.get(); |
| } |
| TransformFeedbackManager* GetTransformFeedbackManager() override { |
| return transform_feedback_manager_.get(); |
| } |
| VertexArrayManager* GetVertexArrayManager() override { |
| return vertex_array_manager_.get(); |
| } |
| ImageManager* GetImageManagerForTest() override { |
| return group_->image_manager(); |
| } |
| |
| bool HasPendingQueries() const override; |
| void ProcessPendingQueries(bool did_finish) override; |
| |
| bool HasMoreIdleWork() const override; |
| void PerformIdleWork() override; |
| |
| bool HasPollingWork() const override; |
| void PerformPollingWork() override; |
| |
| void WaitForReadPixels(base::OnceClosure callback) override; |
| |
| Logger* GetLogger() override; |
| |
| void BeginDecoding() override; |
| void EndDecoding() override; |
| |
| ErrorState* GetErrorState() override; |
| const ContextState* GetContextState() override { return &state_; } |
| std::unique_ptr<AbstractTexture> CreateAbstractTexture(GLenum target, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type) override; |
| |
| scoped_refptr<ShaderTranslatorInterface> GetTranslator(GLenum type) override; |
| scoped_refptr<ShaderTranslatorInterface> GetOrCreateTranslator(GLenum type); |
| |
| void SetIgnoreCachedStateForTest(bool ignore) override; |
| void SetForceShaderNameHashingForTest(bool force) override; |
| uint32_t GetAndClearBackbufferClearBitsForTest() override; |
| void ProcessFinishedAsyncTransfers(); |
| |
| bool GetServiceTextureId(uint32_t client_texture_id, |
| uint32_t* service_texture_id) override; |
| TextureBase* GetTextureBase(uint32_t client_id) override; |
| void SetLevelInfo(uint32_t client_id, |
| int level, |
| unsigned internal_format, |
| unsigned width, |
| unsigned height, |
| unsigned depth, |
| unsigned format, |
| unsigned type, |
| const gfx::Rect& cleared_rect) 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. |
| // Check with all attached and enabled color attachments. |
| bool BoundFramebufferAllowsChangesToAlphaChannel(); |
| bool BoundFramebufferHasDepthAttachment(); |
| bool BoundFramebufferHasStencilAttachment(); |
| |
| // 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. |
| void RenderbufferStorageMultisampleWithWorkaround(GLenum target, |
| GLsizei samples, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| ForcedMultisampleMode mode); |
| void RenderbufferStorageMultisampleHelper(GLenum target, |
| GLsizei samples, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| ForcedMultisampleMode mode); |
| bool RegenerateRenderbufferIfNeeded(Renderbuffer* renderbuffer); |
| |
| PathManager* path_manager() { return group_->path_manager(); } |
| |
| void SetCopyTextureResourceManagerForTest( |
| CopyTextureCHROMIUMResourceManager* copy_texture_resource_manager) |
| override { |
| copy_texture_chromium_.reset(copy_texture_resource_manager); |
| } |
| |
| void SetCopyTexImageBlitterForTest( |
| CopyTexImageResourceManager* copy_tex_image_blit) override { |
| copy_tex_image_blit_.reset(copy_tex_image_blit); |
| } |
| |
| // ServiceFontManager::Client implementation. |
| scoped_refptr<gpu::Buffer> GetShmBuffer(uint32_t shm_id); |
| |
| private: |
| friend class ScopedFramebufferBinder; |
| friend class ScopedResolvedFramebufferBinder; |
| friend class ScopedFramebufferCopyBinder; |
| friend class BackFramebuffer; |
| friend class BackRenderbuffer; |
| friend class BackTexture; |
| |
| enum FramebufferOperation { |
| kFramebufferDiscard, |
| kFramebufferInvalidate, |
| kFramebufferInvalidateSub |
| }; |
| |
| enum BindIndexedBufferFunctionType { |
| kBindBufferBase, |
| kBindBufferRange |
| }; |
| |
| const char* GetCommandName(unsigned int command_id) const; |
| |
| // Initialize or re-initialize the shader translator. |
| bool InitializeShaderTranslator(); |
| void DestroyShaderTranslator(); |
| |
| void UpdateCapabilities(); |
| |
| // Helpers for the glGen and glDelete functions. |
| bool GenTexturesHelper(GLsizei n, const GLuint* client_ids); |
| void DeleteTexturesHelper(GLsizei n, const volatile GLuint* client_ids); |
| bool GenBuffersHelper(GLsizei n, const GLuint* client_ids); |
| void DeleteBuffersHelper(GLsizei n, const volatile GLuint* client_ids); |
| bool GenFramebuffersHelper(GLsizei n, const GLuint* client_ids); |
| void DeleteFramebuffersHelper(GLsizei n, const volatile GLuint* client_ids); |
| bool GenRenderbuffersHelper(GLsizei n, const GLuint* client_ids); |
| void DeleteRenderbuffersHelper(GLsizei n, const volatile GLuint* client_ids); |
| bool GenQueriesEXTHelper(GLsizei n, const GLuint* client_ids); |
| void DeleteQueriesEXTHelper(GLsizei n, const volatile GLuint* client_ids); |
| bool GenVertexArraysOESHelper(GLsizei n, const GLuint* client_ids); |
| void DeleteVertexArraysOESHelper(GLsizei n, |
| const volatile GLuint* client_ids); |
| bool GenPathsCHROMIUMHelper(GLuint first_client_id, GLsizei range); |
| bool DeletePathsCHROMIUMHelper(GLuint first_client_id, GLsizei range); |
| bool GenSamplersHelper(GLsizei n, const GLuint* client_ids); |
| void DeleteSamplersHelper(GLsizei n, const volatile GLuint* client_ids); |
| bool GenTransformFeedbacksHelper(GLsizei n, const GLuint* client_ids); |
| void DeleteTransformFeedbacksHelper(GLsizei n, |
| const volatile GLuint* client_ids); |
| void DeleteSyncHelper(GLuint sync); |
| |
| bool UnmapBufferHelper(Buffer* buffer, GLenum target); |
| |
| // Workarounds |
| void OnFboChanged() const; |
| void OnUseFramebuffer() const; |
| void UpdateFramebufferSRGB(Framebuffer* framebuffer); |
| |
| // TODO(gman): Cache these pointers? |
| BufferManager* buffer_manager() { |
| return group_->buffer_manager(); |
| } |
| |
| RenderbufferManager* renderbuffer_manager() { |
| return group_->renderbuffer_manager(); |
| } |
| |
| FramebufferManager* framebuffer_manager() { |
| return framebuffer_manager_.get(); |
| } |
| |
| ProgramManager* program_manager() { |
| return group_->program_manager(); |
| } |
| |
| SamplerManager* sampler_manager() { |
| return group_->sampler_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 group_->image_manager(); } |
| |
| VertexArrayManager* vertex_array_manager() { |
| return vertex_array_manager_.get(); |
| } |
| |
| MemoryTracker* memory_tracker() { |
| return group_->memory_tracker(); |
| } |
| |
| const gl::GLVersionInfo& gl_version_info() { |
| return feature_info_->gl_version_info(); |
| } |
| |
| bool IsOffscreenBufferMultisampled() const { |
| return offscreen_target_samples_ > 0; |
| } |
| |
| // 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 nullptr 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); |
| } |
| |
| // Creates a Sampler for the given sampler. |
| Sampler* CreateSampler( |
| GLuint client_id, GLuint service_id) { |
| return sampler_manager()->CreateSampler(client_id, service_id); |
| } |
| |
| // Gets the sampler info for the given sampler. Returns nullptr if none |
| // exists. |
| Sampler* GetSampler(GLuint client_id) { |
| return sampler_manager()->GetSampler(client_id); |
| } |
| |
| // Deletes the sampler info for the given sampler. |
| void RemoveSampler(GLuint client_id) { |
| sampler_manager()->RemoveSampler(client_id); |
| } |
| |
| // Creates a TransformFeedback for the given transformfeedback. |
| TransformFeedback* CreateTransformFeedback( |
| GLuint client_id, GLuint service_id) { |
| return transform_feedback_manager_->CreateTransformFeedback( |
| client_id, service_id); |
| } |
| |
| // Gets the TransformFeedback info for the given transformfeedback. |
| // Returns nullptr if none exists. |
| TransformFeedback* GetTransformFeedback(GLuint client_id) { |
| return transform_feedback_manager_->GetTransformFeedback(client_id); |
| } |
| |
| // Deletes the TransformFeedback info for the given transformfeedback. |
| void RemoveTransformFeedback(GLuint client_id) { |
| transform_feedback_manager_->RemoveTransformFeedback(client_id); |
| } |
| |
| // Get the size (in pixels) of the currently bound frame buffer (either FBO |
| // or regular back buffer). |
| gfx::Size GetBoundReadFramebufferSize(); |
| |
| // Get the service side ID for the bound read framebuffer. |
| // If it's back buffer, 0 is returned. |
| GLuint GetBoundReadFramebufferServiceId(); |
| |
| // Get the service side ID for the bound draw framebuffer. |
| // If it's back buffer, 0 is returned. |
| GLuint GetBoundDrawFramebufferServiceId() const; |
| |
| // Get the format/type of the currently bound frame buffer (either FBO or |
| // regular back buffer). |
| // If the color image is a renderbuffer, returns 0 for type. |
| GLenum GetBoundReadFramebufferTextureType(); |
| GLenum GetBoundReadFramebufferInternalFormat(); |
| |
| // Get the i-th draw buffer's internal format/type from the bound framebuffer. |
| // If no framebuffer is bound, or no image is attached, or the DrawBuffers |
| // setting for that image is GL_NONE, return 0. |
| GLenum GetBoundColorDrawBufferType(GLint drawbuffer_i); |
| GLenum GetBoundColorDrawBufferInternalFormat(GLint drawbuffer_i); |
| |
| GLsizei GetBoundFramebufferSamples(GLenum target); |
| |
| // Return 0 if no depth attachment. |
| GLenum GetBoundFramebufferDepthFormat(GLenum target); |
| // Return 0 if no stencil attachment. |
| GLenum GetBoundFramebufferStencilFormat(GLenum target); |
| |
| gfx::Vector2d GetBoundFramebufferDrawOffset() const { |
| if (GetBoundDrawFramebuffer() || offscreen_target_frame_buffer_.get()) |
| return gfx::Vector2d(); |
| return surface_->GetDrawOffset(); |
| } |
| |
| void MarkDrawBufferAsCleared(GLenum buffer, GLint drawbuffer_i); |
| |
| // Wrapper for CompressedTexImage{2|3}D commands. |
| error::Error DoCompressedTexImage( |
| GLenum target, |
| GLint level, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLsizei image_size, |
| const void* data, |
| ContextState::Dimension dimension); |
| |
| // Wrapper for CompressedTexSubImage{2|3}D. |
| error::Error DoCompressedTexSubImage( |
| GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint zoffset, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLenum format, |
| GLsizei imageSize, |
| const void* data, |
| ContextState::Dimension dimension); |
| |
| // Validate if |format| is valid for CopyTex{Sub}Image functions. |
| // If not, generate a GL error and return false. |
| bool ValidateCopyTexFormat(const char* func_name, GLenum internal_format, |
| GLenum read_format, GLenum read_type); |
| |
| // 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(uint64_t swap_id, GLbitfield flags); |
| |
| // Wrapper for SwapBuffersWithBoundsCHROMIUM. |
| void DoSwapBuffersWithBoundsCHROMIUM(uint64_t swap_id, |
| GLsizei count, |
| const volatile GLint* rects, |
| GLbitfield flags); |
| |
| // Callback for async SwapBuffers. |
| void FinishAsyncSwapBuffers(uint64_t swap_id, |
| gfx::SwapResult result, |
| std::unique_ptr<gfx::GpuFence>); |
| void FinishSwapBuffers(gfx::SwapResult result); |
| |
| void DoCommitOverlayPlanes(uint64_t swap_id, GLbitfield flags); |
| |
| // Wrapper for CopyTexSubImage2D. |
| void DoCopyTexSubImage2D( |
| GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height); |
| |
| // Wrapper for CopyTexSubImage3D. |
| void DoCopyTexSubImage3D( |
| GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint zoffset, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height); |
| |
| // Wrapper for glCopyBufferSubData. |
| void DoCopyBufferSubData(GLenum readtarget, |
| GLenum writetarget, |
| GLintptr readoffset, |
| GLintptr writeoffset, |
| GLsizeiptr size); |
| |
| void DoCopyTextureCHROMIUM(GLuint source_id, |
| GLint source_level, |
| GLenum dest_target, |
| GLuint dest_id, |
| GLint dest_level, |
| GLenum internal_format, |
| GLenum dest_type, |
| GLboolean unpack_flip_y, |
| GLboolean unpack_premultiply_alpha, |
| GLboolean unpack_unmultiply_alpha); |
| |
| void DoCopySubTextureCHROMIUM(GLuint source_id, |
| GLint source_level, |
| GLenum dest_target, |
| GLuint dest_id, |
| GLint dest_level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| GLboolean unpack_flip_y, |
| GLboolean unpack_premultiply_alpha, |
| GLboolean unpack_unmultiply_alpha); |
| |
| // Helper for DoTexStorage2DEXT and DoTexStorage3D. |
| void TexStorageImpl(GLenum target, |
| GLsizei levels, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| ContextState::Dimension dimension, |
| const char* function_name); |
| |
| // Wrapper for TexStorage2DEXT. |
| void DoTexStorage2DEXT(GLenum target, |
| GLsizei levels, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height); |
| |
| // Wrapper for TexStorage3D. |
| void DoTexStorage3D(GLenum target, |
| GLsizei levels, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth); |
| |
| void DoTexStorage2DImageCHROMIUM(GLenum target, |
| GLenum internal_format, |
| GLenum buffer_usage, |
| GLsizei width, |
| GLsizei height); |
| |
| void DoProduceTextureDirectCHROMIUM(GLuint texture, |
| const volatile GLbyte* key); |
| |
| void DoCreateAndConsumeTextureINTERNAL(GLuint client_id, |
| const volatile GLbyte* key); |
| void DoCreateAndTexStorage2DSharedImageINTERNAL(GLuint client_id, |
| GLenum internal_format, |
| const volatile GLbyte* data); |
| void DoBeginSharedImageAccessDirectCHROMIUM(GLuint client_id, GLenum mode); |
| void DoEndSharedImageAccessDirectCHROMIUM(GLuint client_id); |
| void DoApplyScreenSpaceAntialiasingCHROMIUM(); |
| |
| void BindImage(uint32_t client_texture_id, |
| uint32_t texture_target, |
| gl::GLImage* image, |
| bool can_bind_to_sampler) override; |
| void DoBindTexImage2DCHROMIUM( |
| GLenum target, |
| GLint image_id); |
| void DoBindTexImage2DWithInternalformatCHROMIUM(GLenum target, |
| GLenum internalformat, |
| GLint image_id); |
| // Common implementation of DoBindTexImage2DCHROMIUM entry points. |
| void BindTexImage2DCHROMIUMImpl(const char* function_name, |
| GLenum target, |
| GLenum internalformat, |
| GLint image_id); |
| void DoReleaseTexImage2DCHROMIUM( |
| GLenum target, |
| GLint image_id); |
| |
| void DoTraceEndCHROMIUM(void); |
| |
| void DoDrawBuffersEXT(GLsizei count, const volatile GLenum* bufs); |
| |
| void DoLoseContextCHROMIUM(GLenum current, GLenum other); |
| |
| void DoFlushDriverCachesCHROMIUM(void); |
| |
| void DoMatrixLoadfCHROMIUM(GLenum matrix_mode, |
| const volatile GLfloat* matrix); |
| void DoMatrixLoadIdentityCHROMIUM(GLenum matrix_mode); |
| void DoScheduleCALayerInUseQueryCHROMIUM(GLsizei count, |
| const volatile GLuint* textures); |
| |
| void DoFlushMappedBufferRange( |
| GLenum target, GLintptr offset, GLsizeiptr size); |
| |
| // 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 nullptr 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<typename T> |
| 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<typename T> |
| 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 nullptr 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 nullptr 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 nullptr 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; |
| } |
| |
| // 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); |
| } |
| |
| // 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<VertexAttribManager> 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, |
| feature_info_->IsWebGL2OrES3Context()); |
| } |
| |
| void DoBindAttribLocation(GLuint client_id, |
| GLuint index, |
| const std::string& name); |
| |
| error::Error DoBindFragDataLocation(GLuint program_id, |
| GLuint colorName, |
| const std::string& name); |
| |
| error::Error DoBindFragDataLocationIndexed(GLuint program_id, |
| GLuint colorName, |
| GLuint index, |
| const std::string& name); |
| |
| void DoBindUniformLocationCHROMIUM(GLuint client_id, |
| GLint location, |
| const std::string& name); |
| |
| error::Error GetAttribLocationHelper(GLuint client_id, |
| uint32_t location_shm_id, |
| uint32_t location_shm_offset, |
| const std::string& name_str); |
| |
| error::Error GetUniformLocationHelper(GLuint client_id, |
| uint32_t location_shm_id, |
| uint32_t location_shm_offset, |
| const std::string& name_str); |
| |
| error::Error GetFragDataLocationHelper(GLuint client_id, |
| uint32_t location_shm_id, |
| uint32_t location_shm_offset, |
| const std::string& name_str); |
| |
| error::Error GetFragDataIndexHelper(GLuint program_id, |
| uint32_t index_shm_id, |
| uint32_t index_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 format, |
| unsigned type, |
| int xoffset, |
| int yoffset, |
| int width, |
| int height) override; |
| |
| // Helper function for ClearLevel that attempts to clear using a glClear call |
| // to a temporary FBO, rather than using glTexSubImage2D. |
| bool ClearLevelUsingGL(Texture* texture, |
| uint32_t channels, |
| unsigned target, |
| int level, |
| int xoffset, |
| int yoffset, |
| int width, |
| int height); |
| |
| // overridden from GLES2Decoder |
| bool ClearCompressedTextureLevel(Texture* texture, |
| unsigned target, |
| int level, |
| unsigned format, |
| int width, |
| int height) override; |
| bool IsCompressedTextureFormat(unsigned format) override; |
| |
| // overridden from GLES2Decoder |
| bool ClearLevel3D(Texture* texture, |
| unsigned target, |
| int level, |
| unsigned format, |
| unsigned type, |
| int width, |
| int height, |
| int depth) 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 read framebuffer's color image |
| // isn't the target texture of the glCopyTex{Sub}Image{2D|3D}. |
| bool FormsTextureCopyingFeedbackLoop( |
| TextureRef* texture, |
| GLint level, |
| GLint layer); |
| |
| // Check if a framebuffer meets our requirements. |
| // Generates |gl_error| if the framebuffer is incomplete. |
| bool CheckFramebufferValid( |
| Framebuffer* framebuffer, |
| GLenum target, |
| GLenum gl_error, |
| const char* func_name); |
| |
| bool CheckBoundDrawFramebufferValid(const char* func_name); |
| // Generates |gl_error| if the bound read fbo is incomplete. |
| bool CheckBoundReadFramebufferValid(const char* func_name, GLenum gl_error); |
| // This is only used by DoBlitFramebufferCHROMIUM which operates read/draw |
| // framebuffer at the same time. |
| bool CheckBoundFramebufferValid(const char* func_name); |
| |
| // Checks if the current program exists and is valid. If not generates the |
| // appropriate GL error. Returns true if the current program is in a usable |
| // state. |
| bool CheckCurrentProgram(const char* function_name); |
| |
| // Checks if the current program exists and is valid and that location is not |
| // -1. If the current program is not valid generates the appropriate GL |
| // error. Returns true if the current program is in a usable state and |
| // location is not -1. |
| bool CheckCurrentProgramForUniform(GLint location, const char* function_name); |
| |
| // 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(); |
| |
| bool SupportsDrawBuffers() const; |
| |
| // Checks if a draw buffer's format and its corresponding fragment shader |
| // output's type are compatible, i.e., a signed integer typed variable is |
| // incompatible with a float or unsigned integer buffer. |
| // If incompaticle, generates an INVALID_OPERATION to avoid undefined buffer |
| // contents and return false. |
| // Otherwise, filter out the draw buffers that are not written to but are not |
| // NONE through DrawBuffers, to be on the safe side. Return true. |
| bool ValidateAndAdjustDrawBuffers(const char* function_name); |
| |
| // Filter out the draw buffers that have no images attached but are not NONE |
| // through DrawBuffers, to be on the safe side. |
| void AdjustDrawBuffers(); |
| |
| // Checks if all active uniform blocks in the current program are backed by |
| // a buffer of sufficient size. |
| // If not, generates an INVALID_OPERATION to avoid undefined behavior in |
| // shader execution and return false. |
| bool ValidateUniformBlockBackings(const char* function_name); |
| |
| // 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. Returns |
| // false if pname is unhandled. |
| 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 glBindBufferBase since we need to track the current targets. |
| void DoBindBufferBase(GLenum target, GLuint index, GLuint buffer); |
| |
| // Wrapper for glBindBufferRange since we need to track the current targets. |
| void DoBindBufferRange(GLenum target, GLuint index, GLuint buffer, |
| GLintptr offset, GLsizeiptr size); |
| |
| // Helper for DoBindBufferBase and DoBindBufferRange. |
| void BindIndexedBufferImpl(GLenum target, GLuint index, GLuint buffer, |
| GLintptr offset, GLsizeiptr size, |
| BindIndexedBufferFunctionType function_type, |
| const char* function_name); |
| |
| // 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 glBindSampler since we need to track the current targets. |
| void DoBindSampler(GLuint unit, GLuint sampler); |
| |
| // Wrapper for glBindTransformFeedback since we need to emulate ES3 behaviors |
| // for BindBufferRange on Desktop GL lower than 4.2. |
| void DoBindTransformFeedback(GLenum target, GLuint transform_feedback); |
| |
| // Wrapper for glBeginTransformFeedback. |
| void DoBeginTransformFeedback(GLenum primitive_mode); |
| |
| // Wrapper for glEndTransformFeedback. |
| void DoEndTransformFeedback(); |
| |
| // Wrapper for glPauseTransformFeedback. |
| void DoPauseTransformFeedback(); |
| |
| // Wrapper for glResumeTransformFeedback. |
| void DoResumeTransformFeedback(); |
| |
| // 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); |
| void DoClearBufferiv(GLenum buffer, |
| GLint drawbuffer, |
| const volatile GLint* value); |
| void DoClearBufferuiv(GLenum buffer, |
| GLint drawbuffer, |
| const volatile GLuint* value); |
| void DoClearBufferfv(GLenum buffer, |
| GLint drawbuffer, |
| const volatile GLfloat* value); |
| void DoClearBufferfi( |
| GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); |
| |
| // 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 count, |
| const volatile GLenum* attachments); |
| |
| void DoInvalidateFramebuffer(GLenum target, |
| GLsizei count, |
| const volatile GLenum* attachments); |
| void DoInvalidateSubFramebuffer(GLenum target, |
| GLsizei count, |
| const volatile GLenum* attachments, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height); |
| |
| // Helper for DoDiscardFramebufferEXT, DoInvalidate{Sub}Framebuffer. |
| void InvalidateFramebufferImpl(GLenum target, |
| GLsizei count, |
| const volatile GLenum* attachments, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| const char* function_name, |
| FramebufferOperation op); |
| |
| // 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 glFramebufferParameteri. |
| void DoFramebufferParameteri(GLenum target, GLenum pname, GLint param); |
| |
| // 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 glFramebufferTextureLayer. |
| void DoFramebufferTextureMultiviewLayeredANGLE(GLenum target, |
| GLenum attachment, |
| GLuint texture, |
| GLint level, |
| GLint base_view_index, |
| GLsizei num_views); |
| |
| // 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, GLsizei params_size); |
| |
| // Wrapper for DoGetFloatv. |
| void DoGetFloatv(GLenum pname, GLfloat* params, GLsizei params_size); |
| |
| // Wrapper for glGetFramebufferAttachmentParameteriv. |
| void DoGetFramebufferAttachmentParameteriv(GLenum target, |
| GLenum attachment, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| |
| // Wrapper for glGetInteger64v. |
| void DoGetInteger64v(GLenum pname, GLint64* params, GLsizei params_size); |
| |
| // Wrapper for glGetIntegerv. |
| void DoGetIntegerv(GLenum pname, GLint* params, GLsizei params_size); |
| |
| // Helper for DoGetIntegeri_v and DoGetInteger64i_v. |
| template <typename TYPE> |
| void GetIndexedIntegerImpl( |
| const char* function_name, GLenum target, GLuint index, TYPE* data); |
| |
| // Wrapper for glGetIntegeri_v. |
| void DoGetIntegeri_v(GLenum target, |
| GLuint index, |
| GLint* params, |
| GLsizei params_size); |
| |
| // Wrapper for glGetInteger64i_v. |
| void DoGetInteger64i_v(GLenum target, |
| GLuint index, |
| GLint64* params, |
| GLsizei params_size); |
| |
| // Gets the max value in a range in a buffer. |
| GLuint DoGetMaxValueInBufferCHROMIUM( |
| GLuint buffer_id, GLsizei count, GLenum type, GLuint offset); |
| |
| // Wrapper for glGetBufferParameteri64v. |
| void DoGetBufferParameteri64v(GLenum target, |
| GLenum pname, |
| GLint64* params, |
| GLsizei params_size); |
| |
| // Wrapper for glGetBufferParameteriv. |
| void DoGetBufferParameteriv(GLenum target, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| |
| // Wrapper for glGetProgramiv. |
| void DoGetProgramiv(GLuint program_id, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| |
| // Wrapper for glRenderbufferParameteriv. |
| void DoGetRenderbufferParameteriv(GLenum target, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| |
| // Wrappers for glGetSamplerParameter. |
| void DoGetSamplerParameterfv(GLuint client_id, |
| GLenum pname, |
| GLfloat* params, |
| GLsizei params_size); |
| void DoGetSamplerParameteriv(GLuint client_id, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| |
| // Wrapper for glGetShaderiv |
| void DoGetShaderiv(GLuint shader, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| |
| // Wrapper for glGetSynciv. |
| void DoGetSynciv(GLuint sync_id, |
| GLenum pname, |
| GLsizei num_values, |
| GLsizei* length, |
| GLint* values); |
| |
| // Helper for DoGetTexParameter{f|i}v. |
| void GetTexParameterImpl( |
| GLenum target, GLenum pname, GLfloat* fparams, GLint* iparams, |
| const char* function_name); |
| |
| // Wrappers for glGetTexParameter. |
| void DoGetTexParameterfv(GLenum target, |
| GLenum pname, |
| GLfloat* params, |
| GLsizei params_size); |
| void DoGetTexParameteriv(GLenum target, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| |
| // Wrappers for glGetVertexAttrib. |
| template <typename T> |
| void DoGetVertexAttribImpl(GLuint index, GLenum pname, T* params); |
| void DoGetVertexAttribfv(GLuint index, |
| GLenum pname, |
| GLfloat* params, |
| GLsizei params_size); |
| void DoGetVertexAttribiv(GLuint index, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| void DoGetVertexAttribIiv(GLuint index, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size); |
| void DoGetVertexAttribIuiv(GLuint index, |
| GLenum pname, |
| GLuint* params, |
| GLsizei params_size); |
| |
| // 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 DoIsSampler(GLuint client_id); |
| bool DoIsTransformFeedback(GLuint client_id); |
| bool DoIsVertexArrayOES(GLuint client_id); |
| bool DoIsPathCHROMIUM(GLuint client_id); |
| bool DoIsSync(GLuint client_id); |
| |
| void DoLineWidth(GLfloat width); |
| |
| // Wrapper for glLinkProgram |
| void DoLinkProgram(GLuint program); |
| |
| // Wrapper for glOverlayPromotionHintCHROMIUIM |
| void DoOverlayPromotionHintCHROMIUM(GLuint client_id, |
| GLboolean promotion_hint, |
| GLint display_x, |
| GLint display_y, |
| GLint display_width, |
| GLint display_height); |
| |
| // Wrapper for glSetDrawRectangleCHROMIUM |
| void DoSetDrawRectangleCHROMIUM(GLint x, GLint y, GLint width, GLint height); |
| |
| void DoSetEnableDCLayersCHROMIUM(GLboolean enable); |
| |
| // Wrapper for glReadBuffer |
| void DoReadBuffer(GLenum src); |
| |
| // 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); |
| |
| // Wrapper for glFenceSync. |
| GLsync DoFenceSync(GLenum condition, GLbitfield flags); |
| |
| // 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 functions. |
| void DoSamplerParameterf(GLuint client_id, GLenum pname, GLfloat param); |
| void DoSamplerParameteri(GLuint client_id, GLenum pname, GLint param); |
| void DoSamplerParameterfv(GLuint client_id, |
| GLenum pname, |
| const volatile GLfloat* params); |
| void DoSamplerParameteriv(GLuint client_id, |
| GLenum pname, |
| const volatile 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 volatile GLfloat* params); |
| void DoTexParameteriv(GLenum target, |
| GLenum pname, |
| const volatile 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 volatile GLint* value); |
| void DoUniform2iv(GLint fake_location, |
| GLsizei count, |
| const volatile GLint* value); |
| void DoUniform3iv(GLint fake_location, |
| GLsizei count, |
| const volatile GLint* value); |
| void DoUniform4iv(GLint fake_location, |
| GLsizei count, |
| const volatile GLint* value); |
| |
| void DoUniform1ui(GLint fake_location, GLuint v0); |
| void DoUniform1uiv(GLint fake_location, |
| GLsizei count, |
| const volatile GLuint* value); |
| void DoUniform2uiv(GLint fake_location, |
| GLsizei count, |
| const volatile GLuint* value); |
| void DoUniform3uiv(GLint fake_location, |
| GLsizei count, |
| const volatile GLuint* value); |
| void DoUniform4uiv(GLint fake_location, |
| GLsizei count, |
| const volatile GLuint* value); |
| |
| // Wrappers for glUniformfv because some drivers don't correctly accept |
| // bool uniforms. |
| void DoUniform1fv(GLint fake_location, |
| GLsizei count, |
| const volatile GLfloat* value); |
| void DoUniform2fv(GLint fake_location, |
| GLsizei count, |
| const volatile GLfloat* value); |
| void DoUniform3fv(GLint fake_location, |
| GLsizei count, |
| const volatile GLfloat* value); |
| void DoUniform4fv(GLint fake_location, |
| GLsizei count, |
| const volatile GLfloat* value); |
| |
| void DoUniformMatrix2fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| void DoUniformMatrix3fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| void DoUniformMatrix4fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| void DoUniformMatrix4fvStreamTextureMatrixCHROMIUM( |
| GLint fake_location, |
| GLboolean transpose, |
| const volatile GLfloat* default_value); |
| void DoUniformMatrix2x3fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| void DoUniformMatrix2x4fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| void DoUniformMatrix3x2fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| void DoUniformMatrix3x4fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| void DoUniformMatrix4x2fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| void DoUniformMatrix4x3fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value); |
| |
| template <typename T> |
| 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 volatile GLfloat* v); |
| void DoVertexAttrib2fv(GLuint index, const volatile GLfloat* v); |
| void DoVertexAttrib3fv(GLuint index, const volatile GLfloat* v); |
| void DoVertexAttrib4fv(GLuint index, const volatile GLfloat* v); |
| void DoVertexAttribI4i(GLuint index, GLint v0, GLint v1, GLint v2, GLint v3); |
| void DoVertexAttribI4iv(GLuint index, const volatile GLint* v); |
| void DoVertexAttribI4ui( |
| GLuint index, GLuint v0, GLuint v1, GLuint v2, GLuint v3); |
| void DoVertexAttribI4uiv(GLuint index, const volatile GLuint* v); |
| |
| // Wrapper for glViewport |
| void DoViewport(GLint x, GLint y, GLsizei width, GLsizei height); |
| |
| // Wrapper for glScissor |
| void DoScissor(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 every attribute's type set by vertexAttrib API match |
| // the type of corresponding attribute in vertex shader. |
| bool AttribsTypeMatch(); |
| |
| // Verifies that front/back stencil settings match, per WebGL specification: |
| // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.11 |
| bool ValidateStencilStateForDraw(const char* function_name); |
| |
| // 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); |
| |
| // Copies the image to the texture currently bound to |textarget|. The image |
| // state of |texture| is updated to reflect the new state. |
| void DoCopyTexImage(Texture* texture, GLenum textarget, gl::GLImage* image); |
| |
| // If the texture has an image but that image is not bound or copied to the |
| // texture, this will first attempt to bind it, and if that fails |
| // DoCopyTexImage on it. texture_unit is the texture unit it should be bound |
| // to, or 0 if it doesn't matter - setting it to 0 will cause the previous |
| // binding to be restored after the operation. This returns true if a copy |
| // or bind happened and the caller needs to restore the previous texture |
| // binding. |
| bool DoBindOrCopyTexImageIfNeeded(Texture* texture, |
| GLenum textarget, |
| GLuint texture_unit); |
| |
| void DoUnpremultiplyAndDitherCopyCHROMIUM(GLuint source_id, |
| GLuint dest_id, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height); |
| |
| void DoWindowRectanglesEXT(GLenum mode, GLsizei n, const volatile GLint* box); |
| |
| void DoSetReadbackBufferShadowAllocationINTERNAL(GLuint buffer_id, |
| GLuint shm_id, |
| GLuint shm_offset, |
| GLuint size); |
| |
| // 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_t offset, |
| GLsizei primcount); |
| |
| GLenum GetBindTargetForSamplerType(GLenum type) { |
| switch (type) { |
| case GL_SAMPLER_2D: |
| case GL_SAMPLER_2D_SHADOW: |
| case GL_INT_SAMPLER_2D: |
| case GL_UNSIGNED_INT_SAMPLER_2D: |
| return GL_TEXTURE_2D; |
| case GL_SAMPLER_CUBE: |
| case GL_SAMPLER_CUBE_SHADOW: |
| case GL_INT_SAMPLER_CUBE: |
| case GL_UNSIGNED_INT_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; |
| case GL_SAMPLER_3D: |
| case GL_INT_SAMPLER_3D: |
| case GL_UNSIGNED_INT_SAMPLER_3D: |
| return GL_TEXTURE_3D; |
| case GL_SAMPLER_2D_ARRAY: |
| case GL_SAMPLER_2D_ARRAY_SHADOW: |
| case GL_INT_SAMPLER_2D_ARRAY: |
| case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: |
| return GL_TEXTURE_2D_ARRAY; |
| default: |
| NOTREACHED(); |
| return 0; |
| } |
| } |
| |
| // Gets the framebuffer info for a particular target. |
| Framebuffer* GetFramebufferInfoForTarget(GLenum target) const { |
| Framebuffer* framebuffer = nullptr; |
| 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 = nullptr; |
| 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 <class T> |
| bool GetUniformSetup(GLuint program, |
| GLint fake_location, |
| uint32_t shm_id, |
| uint32_t shm_offset, |
| error::Error* error, |
| GLint* real_location, |
| GLuint* service_id, |
| SizedResult<T>** result, |
| GLenum* result_type, |
| GLsizei* result_size); |
| |
| bool WasContextLost() const override; |
| bool WasContextLostByRobustnessExtension() const override; |
| void MarkContextLost(error::ContextLostReason reason) override; |
| bool CheckResetStatus() override; |
| |
| 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, |
| const GLvoid* data); |
| 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 ValidateCopyTextureCHROMIUMTextures(const char* function_name, |
| GLenum dest_target, |
| TextureRef* source_texture_ref, |
| TextureRef* dest_texture_ref); |
| bool CanUseCopyTextureCHROMIUMInternalFormat(GLenum dest_internal_format); |
| void CopySubTextureHelper(const char* function_name, |
| GLuint source_id, |
| GLint source_level, |
| GLenum dest_target, |
| GLuint dest_id, |
| GLint dest_level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| GLboolean unpack_flip_y, |
| GLboolean unpack_premultiply_alpha, |
| GLboolean unpack_unmultiply_alpha, |
| GLboolean dither); |
| |
| 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 GpuDriverBugWorkarounds& workarounds() const { |
| return feature_info_->workarounds(); |
| } |
| |
| bool ShouldDeferDraws() { |
| return !offscreen_target_frame_buffer_.get() && |
| framebuffer_state_.bound_draw_framebuffer.get() == nullptr && |
| surface_->DeferDraws(); |
| } |
| |
| bool ShouldDeferReads() { |
| return !offscreen_target_frame_buffer_.get() && |
| framebuffer_state_.bound_read_framebuffer.get() == nullptr && |
| 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; |
| } |
| |
| // Whether the back buffer exposed to the client has an alpha channel. Note |
| // that this is potentially different from whether the implementation of the |
| // back buffer has an alpha channel. |
| bool ClientExposedBackBufferHasAlpha() const { |
| if (back_buffer_draw_buffer_ == GL_NONE) |
| return false; |
| if (offscreen_target_frame_buffer_.get()) { |
| return offscreen_buffer_should_have_alpha_; |
| } |
| return (back_buffer_color_format_ == GL_RGBA || |
| back_buffer_color_format_ == GL_RGBA8); |
| } |
| |
| // If the back buffer has a non-emulated alpha channel, the clear color should |
| // be 0. Otherwise, the clear color should be 1. |
| GLfloat BackBufferAlphaClearColor() const { |
| return offscreen_buffer_should_have_alpha_ ? 0.f : 1.f; |
| } |
| |
| // Set remaining commands to process to 0 to force DoCommands to return |
| // and allow context preemption and GPU watchdog checks in CommandExecutor(). |
| void ExitCommandProcessingEarly() { commands_to_process_ = 0; } |
| |
| void ProcessPendingReadPixels(bool did_finish); |
| void FinishReadPixels(GLsizei width, |
| GLsizei height, |
| GLsizei format, |
| GLsizei type, |
| uint32_t pixels_shm_id, |
| uint32_t pixels_shm_offset, |
| uint32_t result_shm_id, |
| uint32_t result_shm_offset, |
| GLint pack_alignment, |
| GLenum read_format, |
| GLuint buffer); |
| |
| // Checks to see if the inserted fence has completed. |
| void ProcessDescheduleUntilFinished(); |
| |
| void DoBindFragmentInputLocationCHROMIUM(GLuint program_id, |
| GLint location, |
| const std::string& name); |
| |
| // If |texture_manager_version_| doesn't match the current version, then this |
| // will rebind all external textures to match their current service_id. |
| void RestoreAllExternalTextureBindingsIfNeeded() override; |
| |
| const SamplerState& GetSamplerStateForTextureUnit(GLenum target, GLuint unit); |
| |
| // copyTexImage2D doesn't work on OSX under very specific conditions. |
| // Returns whether those conditions have been met. If this method returns |
| // true, |source_texture_service_id| and |source_texture_target| are also |
| // populated, since they are needed to implement the workaround. |
| bool NeedsCopyTextureImageWorkaround(GLenum internal_format, |
| int32_t channels_exist, |
| GLuint* source_texture_service_id, |
| GLenum* source_texture_target); |
| |
| // Whether a texture backed by a Chromium Image needs to emulate GL_RGB format |
| // using GL_RGBA and glColorMask. |
| bool ChromiumImageNeedsRGBEmulation(); |
| |
| // The GL_CHROMIUM_schedule_ca_layer extension requires that SwapBuffers and |
| // equivalent functions reset shared state. |
| void ClearScheduleCALayerState(); |
| void ClearScheduleDCLayerState(); |
| |
| // Helper method to call glClear workaround. |
| void ClearFramebufferForWorkaround(GLbitfield mask); |
| |
| bool SupportsSeparateFramebufferBinds() const { |
| return (feature_info_->feature_flags().chromium_framebuffer_multisample || |
| feature_info_->IsWebGL2OrES3Context()); |
| } |
| |
| GLenum GetDrawFramebufferTarget() const { |
| return SupportsSeparateFramebufferBinds() ? |
| GL_DRAW_FRAMEBUFFER : GL_FRAMEBUFFER; |
| } |
| |
| GLenum GetReadFramebufferTarget() const { |
| return SupportsSeparateFramebufferBinds() ? |
| GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER; |
| } |
| |
| Framebuffer* GetBoundDrawFramebuffer() const { |
| return framebuffer_state_.bound_draw_framebuffer.get(); |
| } |
| |
| Framebuffer* GetBoundReadFramebuffer() const { |
| GLenum target = GetReadFramebufferTarget(); |
| return GetFramebufferInfoForTarget(target); |
| } |
| |
| bool InitializeCopyTexImageBlitter(const char* function_name); |
| bool InitializeCopyTextureCHROMIUM(const char* function_name); |
| bool InitializeSRGBConverter(const char* function_name); |
| |
| void UnbindTexture(TextureRef* texture_ref, |
| bool supports_separate_framebuffer_binds); |
| |
| void OnAbstractTextureDestroyed(ValidatingAbstractTextureImpl* texture, |
| scoped_refptr<TextureRef> texture_ref); |
| |
| void ReadBackBuffersIntoShadowCopies( |
| base::flat_set<scoped_refptr<Buffer>> buffers_to_shadow_copy); |
| |
| // Compiles the given shader and exits command processing early. |
| void CompileShaderAndExitCommandProcessingEarly(Shader* shader); |
| |
| // Notify the watchdog thread of progress, preventing time-outs when a |
| // command takes a long time. May be no-op when using in-process command |
| // buffer. |
| void ReportProgress(); |
| |
| // Generate a member function prototype for each command in an automated and |
| // typesafe way. |
| #define GLES2_CMD_OP(name) \ |
| Error Handle##name(uint32_t immediate_data_size, const volatile void* data); |
| |
| GLES2_COMMAND_LIST(GLES2_CMD_OP) |
| |
| #undef GLES2_CMD_OP |
| |
| DecoderClient* client_; |
| |
| // The GL context this decoder renders to on behalf of the client. |
| scoped_refptr<gl::GLSurface> surface_; |
| scoped_refptr<gl::GLContext> context_; |
| |
| // The ContextGroup for this decoder uses to track resources. |
| scoped_refptr<ContextGroup> group_; |
| |
| DebugMarkerManager debug_marker_manager_; |
| Logger logger_; |
| |
| // All the state for this context. |
| ContextState state_; |
| |
| std::unique_ptr<TransformFeedbackManager> transform_feedback_manager_; |
| |
| // Current width and height of the offscreen frame buffer. |
| gfx::Size offscreen_size_; |
| |
| // Util to help with GL. |
| GLES2Util util_; |
| |
| // 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. |
| std::unique_ptr<BackFramebuffer> offscreen_target_frame_buffer_; |
| std::unique_ptr<BackTexture> offscreen_target_color_texture_; |
| std::unique_ptr<BackRenderbuffer> offscreen_target_color_render_buffer_; |
| std::unique_ptr<BackRenderbuffer> offscreen_target_depth_render_buffer_; |
| std::unique_ptr<BackRenderbuffer> offscreen_target_stencil_render_buffer_; |
| |
| // The format of the texture or renderbuffer backing the offscreen |
| // framebuffer. Also the format of the texture backing the saved offscreen |
| // framebuffer. |
| GLenum offscreen_target_color_format_; |
| |
| GLenum offscreen_target_depth_format_; |
| GLenum offscreen_target_stencil_format_; |
| GLsizei offscreen_target_samples_; |
| GLboolean offscreen_target_buffer_preserved_; |
| |
| GLint max_offscreen_framebuffer_size_; |
| |
| // Whether or not offscreen color buffers exist in front/back pairs that |
| // can be swapped. |
| GLboolean offscreen_single_buffer_; |
| |
| // The saved copy of the backbuffer after a call to SwapBuffers. |
| std::unique_ptr<BackTexture> offscreen_saved_color_texture_; |
| |
| // For simplicity, |offscreen_saved_color_texture_| is always bound to |
| // |offscreen_saved_frame_buffer_|. |
| std::unique_ptr<BackFramebuffer> offscreen_saved_frame_buffer_; |
| |
| // When a client requests ownership of the swapped front buffer, all |
| // information is saved in this structure, and |in_use| is set to true. When a |
| // client releases ownership, |in_use| is set to false. |
| // |
| // An instance of this struct, with |in_use| = false may be reused instead of |
| // making a new BackTexture. |
| struct SavedBackTexture { |
| std::unique_ptr<BackTexture> back_texture; |
| bool in_use; |
| }; |
| std::vector<SavedBackTexture> saved_back_textures_; |
| |
| // If there's a SavedBackTexture that's not in use, takes that. Otherwise, |
| // generates a new back texture. |
| void CreateBackTexture(); |
| size_t create_back_texture_count_for_test_ = 0; |
| |
| // Releases all saved BackTextures that are not in use by a client. |
| void ReleaseNotInUseBackTextures(); |
| |
| // Releases all saved BackTextures. |
| void ReleaseAllBackTextures(bool have_context); |
| |
| size_t GetSavedBackTextureCountForTest() override; |
| size_t GetCreatedBackTextureCountForTest() override; |
| |
| // The copy that is used as the destination for multi-sample resolves. |
| std::unique_ptr<BackFramebuffer> offscreen_resolved_frame_buffer_; |
| std::unique_ptr<BackTexture> offscreen_resolved_color_texture_; |
| GLenum offscreen_saved_color_format_; |
| |
| // Whether the client requested an offscreen buffer with an alpha channel. |
| bool offscreen_buffer_should_have_alpha_; |
| |
| std::unique_ptr<FramebufferManager> framebuffer_manager_; |
| |
| std::unique_ptr<GLES2QueryManager> query_manager_; |
| |
| std::unique_ptr<GpuFenceManager> gpu_fence_manager_; |
| |
| std::unique_ptr<VertexArrayManager> vertex_array_manager_; |
| |
| base::flat_set<scoped_refptr<Buffer>> writes_submitted_but_not_completed_; |
| |
| // The format of the back buffer_ |
| GLenum back_buffer_color_format_; |
| bool back_buffer_has_depth_; |
| bool back_buffer_has_stencil_; |
| |
| // Tracks read buffer and draw buffer for backbuffer, whether it's onscreen |
| // or offscreen. |
| // TODO(zmo): when ES3 APIs are exposed to Nacl, make sure read_buffer_ |
| // setting is set correctly when SwapBuffers(). |
| GLenum back_buffer_read_buffer_; |
| GLenum back_buffer_draw_buffer_; |
| |
| bool surfaceless_; |
| |
| // Backbuffer attachments that are currently undefined. |
| uint32_t backbuffer_needs_clear_bits_; |
| |
| uint64_t swaps_since_resize_; |
| |
| // 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 has_fragment_precision_high_ = false; |
| scoped_refptr<ShaderTranslatorInterface> vertex_translator_; |
| scoped_refptr<ShaderTranslatorInterface> fragment_translator_; |
| |
| // Cached from ContextGroup |
| const Validators* validators_; |
| scoped_refptr<FeatureInfo> feature_info_; |
| |
| int frame_number_; |
| |
| // Number of commands remaining to be processed in DoCommands(). |
| int commands_to_process_; |
| |
| bool has_robustness_extension_; |
| bool context_was_lost_; |
| bool reset_by_robustness_extension_; |
| bool supports_post_sub_buffer_; |
| bool supports_swap_buffers_with_bounds_; |
| bool supports_commit_overlay_planes_; |
| bool supports_async_swap_; |
| bool supports_dc_layers_ = false; |
| |
| // 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 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_; |
| |
| // Forces the backbuffer to use native GMBs rather than a TEXTURE_2D texture. |
| bool should_use_native_gmb_for_backbuffer_; |
| |
| // Log extra info. |
| bool service_logging_; |
| |
| std::unique_ptr<ApplyFramebufferAttachmentCMAAINTELResourceManager> |
| apply_framebuffer_attachment_cmaa_intel_; |
| std::unique_ptr<CopyTexImageResourceManager> copy_tex_image_blit_; |
| std::unique_ptr<CopyTextureCHROMIUMResourceManager> copy_texture_chromium_; |
| std::unique_ptr<SRGBConverter> srgb_converter_; |
| std::unique_ptr<ClearFramebufferResourceManager> clear_framebuffer_blit_; |
| |
| // Cached values of the currently assigned viewport dimensions. |
| GLsizei viewport_max_width_; |
| GLsizei viewport_max_height_; |
| |
| // Cached value for the number of stencil bits for the default framebuffer. |
| GLint num_stencil_bits_; |
| |
| // States related to each manager. |
| DecoderTextureState texture_state_; |
| DecoderFramebufferState framebuffer_state_; |
| |
| std::unique_ptr<GPUTracer> gpu_tracer_; |
| std::unique_ptr<GPUStateTracer> gpu_state_tracer_; |
| const unsigned char* gpu_decoder_category_; |
| int gpu_trace_level_; |
| bool gpu_trace_commands_; |
| bool gpu_debug_commands_; |
| |
| base::queue<FenceCallback> pending_readpixel_fences_; |
| |
| // After a second fence is inserted, both the GpuChannelMessageQueue and |
| // CommandExecutor are descheduled. Once the first fence has completed, both |
| // get rescheduled. |
| std::vector<std::unique_ptr<gl::GLFence>> deschedule_until_finished_fences_; |
| |
| // Used to validate multisample renderbuffers if needed |
| typedef base::hash_map<GLenum, GLuint> TextureMap; |
| TextureMap validation_textures_; |
| GLuint validation_fbo_multisample_; |
| GLuint validation_fbo_; |
| |
| typedef gpu::gles2::GLES2Decoder::Error (GLES2DecoderImpl::*CmdHandler)( |
| uint32_t immediate_data_size, |
| const volatile void* data); |
| |
| // A struct to hold info about each command. |
| struct CommandInfo { |
| CmdHandler cmd_handler; |
| uint8_t arg_flags; // How to handle the arguments for this command |
| uint8_t cmd_flags; // How to handle this command |
| uint16_t arg_count; // How many arguments are expected for this command. |
| }; |
| |
| // A table of CommandInfo for all the commands. |
| static const CommandInfo command_info[kNumCommands - kFirstGLES2Command]; |
| |
| // Most recent generation of the TextureManager. If this no longer matches |
| // the current generation when our context becomes current, then we'll rebind |
| // all the textures to stay up to date with Texture::service_id() changes. |
| uint32_t texture_manager_service_id_generation_; |
| |
| bool force_shader_name_hashing_for_test; |
| |
| GLfloat line_width_range_[2]; |
| |
| SamplerState default_sampler_state_; |
| |
| std::unique_ptr<CALayerSharedState> ca_layer_shared_state_; |
| std::unique_ptr<DCLayerSharedState> dc_layer_shared_state_; |
| |
| // All currently outstanding AbstractTextures that we've created. |
| std::set<ValidatingAbstractTextureImpl*> abstract_textures_; |
| |
| // Set of texture refs that are pending destruction, at some point in the |
| // future when our context is current. |
| std::set<scoped_refptr<TextureRef>> texture_refs_pending_destruction_; |
| |
| base::WeakPtrFactory<GLES2DecoderImpl> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GLES2DecoderImpl); |
| }; |
| |
| constexpr 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, |
| GLuint texture_unit) { |
| DCHECK(!state->texture_units.empty()); |
| DCHECK_LT(texture_unit, state->texture_units.size()); |
| TextureUnit& info = state->texture_units[texture_unit]; |
| GLuint last_id; |
| TextureRef* texture_ref = info.GetInfoForTarget(target); |
| if (texture_ref) { |
| last_id = texture_ref->service_id(); |
| } else { |
| last_id = 0; |
| } |
| |
| state->api()->glBindTextureFn(target, last_id); |
| } |
| |
| 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. |
| auto* api = state->api(); |
| api->glActiveTextureFn(GL_TEXTURE0); |
| api->glBindTextureFn(target, id); |
| } |
| |
| ScopedTextureBinder::~ScopedTextureBinder() { |
| ScopedGLErrorSuppressor suppressor( |
| "ScopedTextureBinder::dtor", state_->GetErrorState()); |
| RestoreCurrentTextureBindings(state_, target_, 0); |
| state_->RestoreActiveTexture(); |
| } |
| |
| ScopedRenderBufferBinder::ScopedRenderBufferBinder(ContextState* state, |
| GLuint id) |
| : state_(state) { |
| ScopedGLErrorSuppressor suppressor( |
| "ScopedRenderBufferBinder::ctor", state_->GetErrorState()); |
| state->api()->glBindRenderbufferEXTFn(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()); |
| decoder->api()->glBindFramebufferEXTFn(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; |
| auto* api = decoder_->api(); |
| ScopedGLErrorSuppressor suppressor( |
| "ScopedResolvedFramebufferBinder::ctor", decoder_->GetErrorState()); |
| |
| // On old AMD GPUs on macOS, glColorMask doesn't work correctly for |
| // multisampled renderbuffers and the alpha channel can be overwritten. This |
| // workaround clears the alpha channel before resolving. |
| bool alpha_channel_needs_clear = |
| decoder_->should_use_native_gmb_for_backbuffer_ && |
| !decoder_->offscreen_buffer_should_have_alpha_ && |
| decoder_->ChromiumImageNeedsRGBEmulation() && |
| decoder_->workarounds() |
| .disable_multisampling_color_mask_usage; |
| if (alpha_channel_needs_clear) { |
| api->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER_EXT, |
| decoder_->offscreen_target_frame_buffer_->id()); |
| decoder_->state_.SetDeviceColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); |
| decoder->state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| decoder->ClearDeviceWindowRectangles(); |
| api->glClearColorFn(0, 0, 0, 1); |
| api->glClearFn(GL_COLOR_BUFFER_BIT); |
| decoder_->RestoreClearState(); |
| } |
| |
| api->glBindFramebufferEXTFn(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)); |
| 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(); |
| } |
| api->glBindFramebufferEXTFn(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->ClearDeviceWindowRectangles(); |
| decoder->api()->glBlitFramebufferFn(0, 0, width, height, 0, 0, width, height, |
| GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| api->glBindFramebufferEXTFn(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); |
| decoder_->RestoreDeviceWindowRectangles(); |
| } |
| } |
| |
| ScopedFramebufferCopyBinder::ScopedFramebufferCopyBinder( |
| GLES2DecoderImpl* decoder, |
| GLint x, |
| GLint y, |
| GLint width, |
| GLint height) |
| : decoder_(decoder) { |
| const Framebuffer::Attachment* attachment = |
| decoder->framebuffer_state_.bound_read_framebuffer.get() |
| ->GetReadBufferAttachment(); |
| DCHECK(attachment); |
| auto* api = decoder_->api(); |
| api->glGenTexturesFn(1, &temp_texture_); |
| |
| ScopedTextureBinder texture_binder(&decoder->state_, temp_texture_, |
| GL_TEXTURE_2D); |
| if (width == 0 || height == 0) { |
| // Copy the whole framebuffer if a rectangle isn't specified. |
| api->glCopyTexImage2DFn(GL_TEXTURE_2D, 0, attachment->internal_format(), 0, |
| 0, attachment->width(), attachment->height(), 0); |
| } else { |
| api->glCopyTexImage2DFn(GL_TEXTURE_2D, 0, attachment->internal_format(), x, |
| y, width, height, 0); |
| } |
| |
| api->glGenFramebuffersEXTFn(1, &temp_framebuffer_); |
| framebuffer_binder_ = |
| std::make_unique<ScopedFramebufferBinder>(decoder, temp_framebuffer_); |
| api->glFramebufferTexture2DEXTFn(GL_READ_FRAMEBUFFER_EXT, |
| GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| temp_texture_, 0); |
| api->glReadBufferFn(GL_COLOR_ATTACHMENT0); |
| } |
| |
| ScopedFramebufferCopyBinder::~ScopedFramebufferCopyBinder() { |
| auto* api = decoder_->api(); |
| framebuffer_binder_.reset(); |
| api->glDeleteFramebuffersEXTFn(1, &temp_framebuffer_); |
| api->glDeleteTexturesFn(1, &temp_texture_); |
| api->glReadBufferFn( |
| decoder_->framebuffer_state_.bound_read_framebuffer.get()->read_buffer()); |
| } |
| |
| ScopedPixelUnpackState::ScopedPixelUnpackState(ContextState* state) |
| : state_(state) { |
| DCHECK(state_); |
| state_->PushTextureUnpackState(); |
| } |
| |
| ScopedPixelUnpackState::~ScopedPixelUnpackState() { |
| state_->RestoreUnpackState(); |
| } |
| |
| BackTexture::BackTexture(GLES2DecoderImpl* decoder) |
| : memory_tracker_(decoder->memory_tracker()), |
| bytes_allocated_(0), |
| decoder_(decoder) { |
| DCHECK(!decoder_->should_use_native_gmb_for_backbuffer_ || |
| decoder_->GetContextGroup()->image_factory()); |
| } |
| |
| 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); |
| DCHECK(!image_.get()); |
| } |
| |
| inline gl::GLApi* BackTexture::api() const { |
| return decoder_->api(); |
| } |
| |
| void BackTexture::Create() { |
| DCHECK_EQ(id(), 0u); |
| ScopedGLErrorSuppressor suppressor("BackTexture::Create", |
| decoder_->state_.GetErrorState()); |
| GLuint id; |
| api()->glGenTexturesFn(1, &id); |
| |
| GLenum target = Target(); |
| ScopedTextureBinder binder(&decoder_->state_, id, target); |
| |
| // No client id is necessary because this texture will never be directly |
| // accessed by a client, only indirectly via a mailbox. |
| texture_ref_ = TextureRef::Create(decoder_->texture_manager(), 0, id); |
| decoder_->texture_manager()->SetTarget(texture_ref_.get(), target); |
| decoder_->texture_manager()->SetParameteri( |
| "BackTexture::Create", |
| decoder_->GetErrorState(), |
| texture_ref_.get(), |
| GL_TEXTURE_MAG_FILTER, |
| GL_LINEAR); |
| decoder_->texture_manager()->SetParameteri( |
| "BackTexture::Create", |
| decoder_->GetErrorState(), |
| texture_ref_.get(), |
| GL_TEXTURE_MIN_FILTER, |
| GL_LINEAR); |
| decoder_->texture_manager()->SetParameteri( |
| "BackTexture::Create", |
| decoder_->GetErrorState(), |
| texture_ref_.get(), |
| GL_TEXTURE_WRAP_S, |
| GL_CLAMP_TO_EDGE); |
| decoder_->texture_manager()->SetParameteri( |
| "BackTexture::Create", |
| decoder_->GetErrorState(), |
| texture_ref_.get(), |
| GL_TEXTURE_WRAP_T, |
| GL_CLAMP_TO_EDGE); |
| } |
| |
| bool BackTexture::AllocateStorage( |
| const gfx::Size& size, GLenum format, bool zero) { |
| DCHECK_NE(id(), 0u); |
| ScopedGLErrorSuppressor suppressor("BackTexture::AllocateStorage", |
| decoder_->state_.GetErrorState()); |
| ScopedTextureBinder binder(&decoder_->state_, id(), Target()); |
| uint32_t image_size = 0; |
| GLES2Util::ComputeImageDataSizes(size.width(), size.height(), 1, format, |
| GL_UNSIGNED_BYTE, 8, &image_size, nullptr, |
| nullptr); |
| |
| bool success = false; |
| size_ = size; |
| if (decoder_->should_use_native_gmb_for_backbuffer_) { |
| DestroyNativeGpuMemoryBuffer(true); |
| success = AllocateNativeGpuMemoryBuffer(size, format, zero); |
| } else { |
| { |
| // Add extra scope to destroy zero_data and the object it owns right |
| // after its usage. |
| std::unique_ptr<char[]> zero_data; |
| if (zero) { |
| zero_data.reset(new char[image_size]); |
| memset(zero_data.get(), 0, image_size); |
| } |
| |
| api()->glTexImage2DFn(Target(), |
| 0, // mip level |
| format, size.width(), size.height(), |
| 0, // border |
| format, GL_UNSIGNED_BYTE, zero_data.get()); |
| } |
| |
| decoder_->texture_manager()->SetLevelInfo( |
| texture_ref_.get(), Target(), |
| 0, // level |
| GL_RGBA, size.width(), size.height(), |
| 1, // depth |
| 0, // border |
| GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(size)); |
| success = api()->glGetErrorFn() == GL_NO_ERROR; |
| } |
| |
| if (success) { |
| memory_tracker_.TrackMemFree(bytes_allocated_); |
| bytes_allocated_ = image_size; |
| memory_tracker_.TrackMemAlloc(bytes_allocated_); |
| } |
| return success; |
| } |
| |
| void BackTexture::Copy() { |
| DCHECK_NE(id(), 0u); |
| ScopedGLErrorSuppressor suppressor("BackTexture::Copy", |
| decoder_->state_.GetErrorState()); |
| ScopedTextureBinder binder(&decoder_->state_, id(), Target()); |
| api()->glCopyTexSubImage2DFn(Target(), |
| 0, // level |
| 0, 0, 0, 0, size_.width(), size_.height()); |
| } |
| |
| void BackTexture::Destroy() { |
| if (image_) { |
| DCHECK(texture_ref_); |
| ScopedTextureBinder binder(&decoder_->state_, id(), Target()); |
| DestroyNativeGpuMemoryBuffer(true); |
| } |
| |
| if (texture_ref_) { |
| ScopedGLErrorSuppressor suppressor("BackTexture::Destroy", |
| decoder_->state_.GetErrorState()); |
| texture_ref_ = nullptr; |
| } |
| memory_tracker_.TrackMemFree(bytes_allocated_); |
| bytes_allocated_ = 0; |
| } |
| |
| void BackTexture::Invalidate() { |
| if (image_) { |
| DestroyNativeGpuMemoryBuffer(false); |
| image_ = nullptr; |
| } |
| if (texture_ref_) { |
| texture_ref_->ForceContextLost(); |
| texture_ref_ = nullptr; |
| } |
| } |
| |
| GLenum BackTexture::Target() { |
| return decoder_->should_use_native_gmb_for_backbuffer_ |
| ? decoder_->GetContextGroup() |
| ->image_factory() |
| ->RequiredTextureType() |
| : GL_TEXTURE_2D; |
| } |
| |
| bool BackTexture::AllocateNativeGpuMemoryBuffer(const gfx::Size& size, |
| GLenum format, |
| bool zero) { |
| if (!decoder_->GetContextGroup() |
| ->image_factory() |
| ->SupportsCreateAnonymousImage()) |
| return false; |
| |
| DCHECK(format == GL_RGB || format == GL_RGBA); |
| bool is_cleared = false; |
| gfx::BufferFormat buffer_format = gfx::BufferFormat::RGBA_8888; |
| if (format == GL_RGB) { |
| #if defined(USE_OZONE) |
| // BGRX format is preferred for Ozone as it matches the format used by the |
| // buffer queue and is as a result guaranteed to work on all devices. |
| // TODO(reveman): Define this format in one place instead of having to |
| // duplicate BGRX_8888. |
| buffer_format = gfx::BufferFormat::BGRX_8888; |
| #else |
| buffer_format = gfx::BufferFormat::RGBX_8888; |
| #endif |
| } |
| DCHECK_EQ(format, gpu::InternalFormatForGpuMemoryBufferFormat(buffer_format)); |
| scoped_refptr<gl::GLImage> image = |
| decoder_->GetContextGroup()->image_factory()->CreateAnonymousImage( |
| size, buffer_format, gfx::BufferUsage::SCANOUT, &is_cleared); |
| if (!image || !image->BindTexImage(Target())) |
| return false; |
| |
| image_ = image; |
| decoder_->texture_manager()->SetLevelInfo( |
| texture_ref_.get(), Target(), 0, image_->GetInternalFormat(), |
| size.width(), size.height(), 1, 0, image_->GetInternalFormat(), |
| GL_UNSIGNED_BYTE, gfx::Rect(size)); |
| decoder_->texture_manager()->SetLevelImage(texture_ref_.get(), Target(), 0, |
| image_.get(), Texture::BOUND); |
| |
| // Ignore the zero flag if the alpha channel needs to be cleared for RGB |
| // emulation. |
| bool needs_clear_for_rgb_emulation = |
| !decoder_->offscreen_buffer_should_have_alpha_ && |
| decoder_->ChromiumImageNeedsRGBEmulation(); |
| if (!is_cleared || zero || needs_clear_for_rgb_emulation) { |
| GLuint fbo; |
| api()->glGenFramebuffersEXTFn(1, &fbo); |
| { |
| ScopedFramebufferBinder binder(decoder_, fbo); |
| api()->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| Target(), id(), 0); |
| api()->glClearColorFn(0, 0, 0, decoder_->BackBufferAlphaClearColor()); |
| decoder_->state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| decoder_->state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| decoder_->ClearDeviceWindowRectangles(); |
| api()->glClearFn(GL_COLOR_BUFFER_BIT); |
| decoder_->RestoreClearState(); |
| } |
| api()->glDeleteFramebuffersEXTFn(1, &fbo); |
| } |
| return true; |
| } |
| |
| void BackTexture::DestroyNativeGpuMemoryBuffer(bool have_context) { |
| if (image_) { |
| ScopedGLErrorSuppressor suppressor( |
| "BackTexture::DestroyNativeGpuMemoryBuffer", |
| decoder_->state_.GetErrorState()); |
| |
| image_->ReleaseTexImage(Target()); |
| |
| decoder_->texture_manager()->SetLevelImage(texture_ref_.get(), Target(), 0, |
| nullptr, Texture::UNBOUND); |
| image_ = nullptr; |
| } |
| } |
| |
| BackRenderbuffer::BackRenderbuffer(GLES2DecoderImpl* decoder) |
| : decoder_(decoder), |
| memory_tracker_(decoder->memory_tracker()), |
| 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); |
| } |
| |
| inline gl::GLApi* BackRenderbuffer::api() const { |
| return decoder_->api(); |
| } |
| |
| void BackRenderbuffer::Create() { |
| ScopedGLErrorSuppressor suppressor("BackRenderbuffer::Create", |
| decoder_->state_.GetErrorState()); |
| Destroy(); |
| api()->glGenRenderbuffersEXTFn(1, &id_); |
| } |
| |
| bool BackRenderbuffer::AllocateStorage(const gfx::Size& size, |
| GLenum format, |
| GLsizei samples) { |
| ScopedGLErrorSuppressor suppressor("BackRenderbuffer::AllocateStorage", |
| decoder_->state_.GetErrorState()); |
| ScopedRenderBufferBinder binder(&decoder_->state_, id_); |
| |
| uint32_t estimated_size = 0; |
| if (!decoder_->renderbuffer_manager()->ComputeEstimatedRenderbufferSize( |
| size.width(), size.height(), samples, format, &estimated_size)) { |
| return false; |
| } |
| |
| // TODO(kainino): This path will not perform RegenerateRenderbufferIfNeeded on |
| // devices where multisample_renderbuffer_resize_emulation or |
| // depth_stencil_renderbuffer_resize_emulation is needed. Thus any code using |
| // this path (nacl/ppapi?) could encounter issues on those |
| // devices. RenderbufferStorageMultisampleWithWorkaround should be used |
| // instead, but can only be used if BackRenderbuffer tracks its renderbuffers |
| // in the renderbuffer manager instead of manually. http://crbug.com/731287 |
| decoder_->RenderbufferStorageMultisampleHelper(GL_RENDERBUFFER, samples, |
| format, size.width(), |
| size.height(), kDoNotForce); |
| |
| bool alpha_channel_needs_clear = |
| (format == GL_RGBA || format == GL_RGBA8) && |
| !decoder_->offscreen_buffer_should_have_alpha_; |
| if (alpha_channel_needs_clear) { |
| GLuint fbo; |
| api()->glGenFramebuffersEXTFn(1, &fbo); |
| { |
| ScopedFramebufferBinder binder(decoder_, fbo); |
| api()->glFramebufferRenderbufferEXTFn( |
| GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, id_); |
| api()->glClearColorFn(0, 0, 0, decoder_->BackBufferAlphaClearColor()); |
| decoder_->state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| decoder_->state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| decoder_->ClearDeviceWindowRectangles(); |
| api()->glClearFn(GL_COLOR_BUFFER_BIT); |
| decoder_->RestoreClearState(); |
| } |
| api()->glDeleteFramebuffersEXTFn(1, &fbo); |
| } |
| |
| bool success = api()->glGetErrorFn() == 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", |
| decoder_->state_.GetErrorState()); |
| api()->glDeleteRenderbuffersEXTFn(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); |
| } |
| |
| inline gl::GLApi* BackFramebuffer::api() const { |
| return decoder_->api(); |
| } |
| |
| void BackFramebuffer::Create() { |
| ScopedGLErrorSuppressor suppressor("BackFramebuffer::Create", |
| decoder_->GetErrorState()); |
| Destroy(); |
| api()->glGenFramebuffersEXTFn(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; |
| api()->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| texture->Target(), 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; |
| api()->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, target, GL_RENDERBUFFER, |
| attach_id); |
| } |
| |
| void BackFramebuffer::Destroy() { |
| if (id_ != 0) { |
| ScopedGLErrorSuppressor suppressor("BackFramebuffer::Destroy", |
| decoder_->GetErrorState()); |
| api()->glDeleteFramebuffersEXTFn(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 api()->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER); |
| } |
| |
| GLES2Decoder* GLES2Decoder::Create( |
| DecoderClient* client, |
| CommandBufferServiceBase* command_buffer_service, |
| Outputter* outputter, |
| ContextGroup* group) { |
| if (group->use_passthrough_cmd_decoder()) { |
| return new GLES2DecoderPassthroughImpl(client, command_buffer_service, |
| outputter, group); |
| } |
| return new GLES2DecoderImpl(client, command_buffer_service, outputter, group); |
| } |
| |
| GLES2DecoderImpl::GLES2DecoderImpl( |
| DecoderClient* client, |
| CommandBufferServiceBase* command_buffer_service, |
| Outputter* outputter, |
| ContextGroup* group) |
| : GLES2Decoder(command_buffer_service, outputter), |
| client_(client), |
| group_(group), |
| logger_(&debug_marker_manager_, |
| base::BindRepeating(&DecoderClient::OnConsoleMessage, |
| base::Unretained(client_), |
| 0)), |
| state_(group_->feature_info(), this, &logger_), |
| 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), |
| max_offscreen_framebuffer_size_(0), |
| offscreen_single_buffer_(false), |
| offscreen_saved_color_format_(0), |
| offscreen_buffer_should_have_alpha_(false), |
| back_buffer_color_format_(0), |
| back_buffer_has_depth_(false), |
| back_buffer_has_stencil_(false), |
| back_buffer_read_buffer_(GL_BACK), |
| back_buffer_draw_buffer_(GL_BACK), |
| surfaceless_(false), |
| backbuffer_needs_clear_bits_(0), |
| swaps_since_resize_(0), |
| current_decoder_error_(error::kNoError), |
| validators_(group_->feature_info()->validators()), |
| feature_info_(group_->feature_info()), |
| frame_number_(0), |
| has_robustness_extension_(false), |
| context_was_lost_(false), |
| reset_by_robustness_extension_(false), |
| supports_post_sub_buffer_(false), |
| supports_swap_buffers_with_bounds_(false), |
| supports_commit_overlay_planes_(false), |
| supports_async_swap_(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), |
| should_use_native_gmb_for_backbuffer_(false), |
| service_logging_( |
| group_->gpu_preferences().enable_gpu_service_logging_gpu), |
| viewport_max_width_(0), |
| viewport_max_height_(0), |
| num_stencil_bits_(0), |
| texture_state_(group_->feature_info()->workarounds()), |
| 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_fbo_multisample_(0), |
| validation_fbo_(0), |
| texture_manager_service_id_generation_(0), |
| force_shader_name_hashing_for_test(false), |
| weak_ptr_factory_(this) { |
| DCHECK(client); |
| DCHECK(group); |
| } |
| |
| GLES2DecoderImpl::~GLES2DecoderImpl() = default; |
| |
| base::WeakPtr<DecoderContext> GLES2DecoderImpl::AsWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| gpu::ContextResult GLES2DecoderImpl::Initialize( |
| const scoped_refptr<gl::GLSurface>& surface, |
| const scoped_refptr<gl::GLContext>& context, |
| bool offscreen, |
| const DisallowedFeatures& disallowed_features, |
| const ContextCreationAttribs& attrib_helper) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::Initialize"); |
| DCHECK(context->IsCurrent(surface.get())); |
| DCHECK(!context_.get()); |
| state_.set_api(gl::g_current_gl_context); |
| |
| surfaceless_ = surface->IsSurfaceless() && !offscreen; |
| |
| set_initialized(); |
| gpu_state_tracer_ = GPUStateTracer::Create(&state_); |
| |
| if (group_->gpu_preferences().enable_gpu_debugging) |
| set_debug(true); |
| |
| if (group_->gpu_preferences().enable_gpu_command_logging) |
| SetLogCommands(true); |
| |
| compile_shader_always_succeeds_ = |
| group_->gpu_preferences().compile_shader_always_succeeds; |
| |
| // Take ownership of the context and surface. The surface can be replaced with |
| // SetSurface. |
| context_ = context; |
| surface_ = surface; |
| |
| // Set workarounds for the surface. |
| if (workarounds().rely_on_implicit_sync_for_swap_buffers) |
| surface_->SetRelyOnImplicitSync(); |
| |
| // Create GPU Tracer for timing values. |
| gpu_tracer_.reset(new GPUTracer(this)); |
| |
| if (workarounds().disable_timestamp_queries) { |
| // Forcing time elapsed query for any GPU Timing Client forces it for all |
| // clients in the context. |
| GetGLContext()->CreateGPUTimingClient()->ForceTimeElapsedQuery(); |
| } |
| |
| // Save the loseContextWhenOutOfMemory context creation attribute. |
| lose_context_when_out_of_memory_ = |
| attrib_helper.lose_context_when_out_of_memory; |
| |
| // If the failIfMajorPerformanceCaveat context creation attribute was true |
| // and we are using a software renderer, fail. |
| if (attrib_helper.fail_if_major_perf_caveat && |
| feature_info_->feature_flags().is_swiftshader_for_webgl) { |
| // Must not destroy ContextGroup if it is not initialized. |
| group_ = nullptr; |
| Destroy(true); |
| LOG(ERROR) << "ContextResult::kFatalFailure: " |
| "fail_if_major_perf_caveat + swiftshader"; |
| return gpu::ContextResult::kFatalFailure; |
| } |
| |
| // Only create webgl2-compute for passthrough cmd decoder. |
| if (attrib_helper.context_type == CONTEXT_TYPE_WEBGL2_COMPUTE) { |
| // Must not destroy ContextGroup if it is not initialized. |
| group_ = nullptr; |
| Destroy(true); |
| LOG(ERROR) |
| << "ContextResult::kFatalFailure: " |
| "webgl2-compute is not supported on validating command decoder."; |
| return gpu::ContextResult::kFatalFailure; |
| } |
| |
| auto result = |
| group_->Initialize(this, attrib_helper.context_type, disallowed_features); |
| if (result != gpu::ContextResult::kSuccess) { |
| // Must not destroy ContextGroup if it is not initialized. |
| group_ = nullptr; |
| Destroy(true); |
| return result; |
| } |
| CHECK_GL_ERROR(); |
| |
| should_use_native_gmb_for_backbuffer_ = |
| attrib_helper.should_use_native_gmb_for_backbuffer; |
| if (should_use_native_gmb_for_backbuffer_) { |
| gpu::ImageFactory* image_factory = group_->image_factory(); |
| bool supported = false; |
| if (image_factory) { |
| switch (image_factory->RequiredTextureType()) { |
| case GL_TEXTURE_RECTANGLE_ARB: |
| supported = feature_info_->feature_flags().arb_texture_rectangle; |
| break; |
| case GL_TEXTURE_EXTERNAL_OES: |
| supported = feature_info_->feature_flags().oes_egl_image_external; |
| break; |
| case GL_TEXTURE_2D: |
| supported = true; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (!supported) { |
| Destroy(true); |
| LOG(ERROR) << "ContextResult::kFatalFailure: " |
| "native gmb format not supported"; |
| return gpu::ContextResult::kFatalFailure; |
| } |
| } |
| |
| // Support for CHROMIUM_texture_storage_image depends on the underlying |
| // ImageFactory's ability to create anonymous images. |
| gpu::ImageFactory* image_factory = group_->image_factory(); |
| if (image_factory && image_factory->SupportsCreateAnonymousImage()) |
| feature_info_->EnableCHROMIUMTextureStorageImage(); |
| |
| // In theory |needs_emulation| needs to be true on Desktop GL 4.1 or lower. |
| // However, we set it to true everywhere, not to trust drivers to handle |
| // out-of-bounds buffer accesses. |
| bool needs_emulation = true; |
| transform_feedback_manager_.reset(new TransformFeedbackManager( |
| group_->max_transform_feedback_separate_attribs(), needs_emulation)); |
| |
| if (feature_info_->IsWebGL2OrES3Context()) { |
| if (!feature_info_->IsES3Capable()) { |
| Destroy(true); |
| LOG(ERROR) << "ContextResult::kFatalFailure: " |
| "ES3 is blacklisted/disabled/unsupported by driver."; |
| return gpu::ContextResult::kFatalFailure; |
| } |
| feature_info_->EnableES3Validators(); |
| |
| frag_depth_explicitly_enabled_ = true; |
| draw_buffers_explicitly_enabled_ = true; |
| // TODO(zmo): Look into shader_texture_lod_explicitly_enabled_ situation. |
| |
| // Create a fake default transform feedback and bind to it. |
| GLuint default_transform_feedback = 0; |
| api()->glGenTransformFeedbacksFn(1, &default_transform_feedback); |
| state_.default_transform_feedback = |
| transform_feedback_manager_->CreateTransformFeedback( |
| 0, default_transform_feedback); |
| api()->glBindTransformFeedbackFn(GL_TRANSFORM_FEEDBACK, |
| default_transform_feedback); |
| state_.bound_transform_feedback = state_.default_transform_feedback.get(); |
| state_.bound_transform_feedback->SetIsBound(true); |
| } |
| state_.indexed_uniform_buffer_bindings = |
| base::MakeRefCounted<gles2::IndexedBufferBindingHost>( |
| group_->max_uniform_buffer_bindings(), GL_UNIFORM_BUFFER, |
| needs_emulation); |
| state_.indexed_uniform_buffer_bindings->SetIsBound(true); |
| |
| state_.InitGenericAttribs(group_->max_vertex_attribs()); |
| vertex_array_manager_.reset(new VertexArrayManager()); |
| |
| GLuint default_vertex_attrib_service_id = 0; |
| if (features().native_vertex_array_object) { |
| api()->glGenVertexArraysOESFn(1, &default_vertex_attrib_service_id); |
| api()->glBindVertexArrayOESFn(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(), |
| workarounds().init_vertex_attributes); |
| |
| // vertex_attrib_manager is set to default_vertex_attrib_manager by this call |
| DoBindVertexArrayOES(0); |
| |
| framebuffer_manager_.reset(new FramebufferManager( |
| group_->max_draw_buffers(), group_->max_color_attachments(), |
| group_->framebuffer_completeness_cache())); |
| group_->texture_manager()->AddFramebufferManager(framebuffer_manager_.get()); |
| |
| query_manager_.reset(new GLES2QueryManager(this, feature_info_.get())); |
| |
| gpu_fence_manager_.reset(new GpuFenceManager()); |
| |
| util_.set_num_compressed_texture_formats( |
| validators_->compressed_texture_format.GetValues().size()); |
| |
| if (!gl_version_info().BehavesLikeGLES()) { |
| // We have to enable vertex array 0 on GL with compatibility profile or it |
| // won't render. Note that ES or GL with core profile does not have this |
| // issue. |
| state_.vertex_attrib_manager->SetDriverVertexAttribEnabled(0, true); |
| } |
| api()->glGenBuffersARBFn(1, &attrib_0_buffer_id_); |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, attrib_0_buffer_id_); |
| api()->glVertexAttribPointerFn(0, 1, GL_FLOAT, GL_FALSE, 0, nullptr); |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, 0); |
| api()->glGenBuffersARBFn(1, &fixed_attrib_buffer_id_); |
| |
| state_.texture_units.resize(group_->max_texture_units()); |
| state_.sampler_units.resize(group_->max_texture_units()); |
| for (uint32_t tt = 0; tt < state_.texture_units.size(); ++tt) { |
| api()->glActiveTextureFn(GL_TEXTURE0 + tt); |
| // We want the last bind to be 2D. |
| TextureRef* ref; |
| if (features().oes_egl_image_external || |
| features().nv_egl_stream_consumer_external) { |
| ref = texture_manager()->GetDefaultTextureInfo( |
| GL_TEXTURE_EXTERNAL_OES); |
| state_.texture_units[tt].bound_texture_external_oes = ref; |
| api()->glBindTextureFn(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; |
| api()->glBindTextureFn(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; |
| api()->glBindTextureFn(GL_TEXTURE_CUBE_MAP, ref ? ref->service_id() : 0); |
| ref = texture_manager()->GetDefaultTextureInfo(GL_TEXTURE_2D); |
| state_.texture_units[tt].bound_texture_2d = ref; |
| api()->glBindTextureFn(GL_TEXTURE_2D, ref ? ref->service_id() : 0); |
| } |
| api()->glActiveTextureFn(GL_TEXTURE0); |
| CHECK_GL_ERROR(); |
| |
| // cache ALPHA_BITS result for re-use with clear behaviour |
| GLint alpha_bits = 0; |
| |
| if (offscreen) { |
| offscreen_buffer_should_have_alpha_ = attrib_helper.alpha_size > 0; |
| |
| // Whether the offscreen buffer texture should have an alpha channel. Does |
| // not include logic from workarounds. |
| bool offscreen_buffer_texture_needs_alpha = |
| offscreen_buffer_should_have_alpha_ || |
| (ChromiumImageNeedsRGBEmulation() && |
| attrib_helper.should_use_native_gmb_for_backbuffer); |
| |
| if (attrib_helper.samples > 0 && attrib_helper.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 = 0; |
| api()->glGetIntegervFn(GL_MAX_SAMPLES_EXT, &max_sample_count); |
| offscreen_target_samples_ = std::min(attrib_helper.samples, |
| max_sample_count); |
| } else { |
| offscreen_target_samples_ = 0; |
| } |
| offscreen_target_buffer_preserved_ = attrib_helper.buffer_preserved; |
| offscreen_single_buffer_ = attrib_helper.single_buffer; |
| |
| if (gl_version_info().is_es) { |
| const bool rgb8_supported = features().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_ > 0) { |
| offscreen_target_color_format_ = |
| offscreen_buffer_texture_needs_alpha ? GL_RGBA8 : GL_RGB8; |
| } else { |
| offscreen_target_samples_ = 0; |
| offscreen_target_color_format_ = |
| offscreen_buffer_texture_needs_alpha || |
| 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_helper.depth_size > 0 || attrib_helper.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_helper.depth_size > 0 ? |
| GL_DEPTH_COMPONENT16 : 0; |
| offscreen_target_stencil_format_ = attrib_helper.stencil_size > 0 ? |
| GL_STENCIL_INDEX8 : 0; |
| } |
| } else { |
| offscreen_target_color_format_ = |
| offscreen_buffer_texture_needs_alpha || |
| 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_helper.depth_size > 0 || attrib_helper.stencil_size > 0) && |
| depth24_stencil8_supported) { |
| offscreen_target_depth_format_ = GL_DEPTH24_STENCIL8; |
| offscreen_target_stencil_format_ = 0; |
| } else { |
| offscreen_target_depth_format_ = attrib_helper.depth_size > 0 ? |
| GL_DEPTH_COMPONENT : 0; |
| offscreen_target_stencil_format_ = attrib_helper.stencil_size > 0 ? |
| GL_STENCIL_INDEX : 0; |
| } |
| } |
| |
| offscreen_saved_color_format_ = offscreen_buffer_texture_needs_alpha || |
| workarounds().disable_gl_rgb_format |
| ? GL_RGBA |
| : GL_RGB; |
| |
| max_offscreen_framebuffer_size_ = |
| std::min(renderbuffer_manager()->max_renderbuffer_size(), |
| texture_manager()->MaxSizeForTarget(GL_TEXTURE_2D)); |
| |
| gfx::Size initial_size = attrib_helper.offscreen_framebuffer_size; |
| if (initial_size.IsEmpty()) { |
| // If we're an offscreen surface with zero width and/or height, set to a |
| // non-zero size so that we have a complete framebuffer for operations |
| // like glClear. |
| // TODO(piman): allow empty framebuffers, similar to |
| // EGL_KHR_surfaceless_context / GL_OES_surfaceless_context. |
| initial_size = gfx::Size(1, 1); |
| } |
| |
| state_.viewport_width = initial_size.width(); |
| state_.viewport_height = initial_size.height(); |
| } else { |
| api()->glBindFramebufferEXTFn(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 (gl_version_info().is_desktop_core_profile) { |
| api()->glGetFramebufferAttachmentParameterivEXTFn( |
| GL_FRAMEBUFFER, default_fb ? GL_BACK_LEFT : GL_COLOR_ATTACHMENT0, |
| GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, &alpha_bits); |
| api()->glGetFramebufferAttachmentParameterivEXTFn( |
| GL_FRAMEBUFFER, default_fb ? GL_DEPTH : GL_DEPTH_ATTACHMENT, |
| GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &depth_bits); |
| api()->glGetFramebufferAttachmentParameterivEXTFn( |
| GL_FRAMEBUFFER, default_fb ? GL_STENCIL : GL_STENCIL_ATTACHMENT, |
| GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencil_bits); |
| } else { |
| api()->glGetIntegervFn(GL_ALPHA_BITS, &alpha_bits); |
| api()->glGetIntegervFn(GL_DEPTH_BITS, &depth_bits); |
| api()->glGetIntegervFn(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_helper.alpha_size != 0 && alpha_bits > 0) ? GL_RGBA : GL_RGB; |
| back_buffer_has_depth_ = attrib_helper.depth_size != 0 && depth_bits > 0; |
| back_buffer_has_stencil_ = |
| attrib_helper.stencil_size != 0 && stencil_bits > 0; |
| num_stencil_bits_ = stencil_bits; |
| } else { |
| num_stencil_bits_ = attrib_helper.stencil_size; |
| } |
| |
| 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 (!gl_version_info().BehavesLikeGLES()) { |
| api()->glEnableFn(GL_VERTEX_PROGRAM_POINT_SIZE); |
| api()->glEnableFn(GL_POINT_SPRITE); |
| } else if (gl_version_info().is_desktop_core_profile) { |
| // The desktop core profile changed how program point size mode is |
| // enabled. |
| api()->glEnableFn(GL_PROGRAM_POINT_SIZE); |
| } |
| |
| // ES3 requires seamless cubemap. ES2 does not. |
| // However, when ES2 is implemented on top of DX11, seamless cubemap is |
| // always enabled and there is no way to disable it. |
| // Therefore, it seems OK to also always enable it on top of Desktop GL for |
| // both ES2 and ES3 contexts. |
| if (!workarounds().disable_texture_cube_map_seamless && |
| gl_version_info().IsAtLeastGL(3, 2)) { |
| api()->glEnableFn(GL_TEXTURE_CUBE_MAP_SEAMLESS); |
| } |
| |
| has_robustness_extension_ = features().arb_robustness || |
| features().khr_robustness || |
| features().ext_robustness; |
| |
| GLint range[2] = {0, 0}; |
| GLint precision = 0; |
| QueryShaderPrecisionFormat(gl_version_info(), GL_FRAGMENT_SHADER, |
| GL_HIGH_FLOAT, range, &precision); |
| has_fragment_precision_high_ = |
| PrecisionMeetsSpecForHighpFloat(range[0], range[1], precision); |
| |
| GLint viewport_params[4] = { 0 }; |
| api()->glGetIntegervFn(GL_MAX_VIEWPORT_DIMS, viewport_params); |
| viewport_max_width_ = viewport_params[0]; |
| viewport_max_height_ = viewport_params[1]; |
| |
| api()->glGetFloatvFn(GL_ALIASED_LINE_WIDTH_RANGE, line_width_range_); |
| state_.SetLineWidthBounds(line_width_range_[0], line_width_range_[1]); |
| |
| if (feature_info_->feature_flags().ext_window_rectangles) { |
| GLint max_window_rectangles = -1; |
| api()->glGetIntegervFn(GL_MAX_WINDOW_RECTANGLES_EXT, |
| &max_window_rectangles); |
| state_.SetMaxWindowRectangles(max_window_rectangles); |
| } |
| |
| 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(nullptr); |
| state_.InitState(nullptr); |
| |
| // Default state must be set before offscreen resources can be created. |
| if (offscreen) { |
| // 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(this)); |
| offscreen_target_color_render_buffer_->Create(); |
| } else { |
| offscreen_target_color_texture_.reset(new BackTexture(this)); |
| offscreen_target_color_texture_->Create(); |
| } |
| offscreen_target_depth_render_buffer_.reset(new BackRenderbuffer(this)); |
| offscreen_target_depth_render_buffer_->Create(); |
| offscreen_target_stencil_render_buffer_.reset(new BackRenderbuffer(this)); |
| offscreen_target_stencil_render_buffer_->Create(); |
| |
| if (!offscreen_single_buffer_) { |
| // 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(this)); |
| offscreen_saved_color_texture_->Create(); |
| } |
| |
| // Allocate the render buffers at their initial size and check the status |
| // of the frame buffers is okay. |
| if (!ResizeOffscreenFramebuffer( |
| gfx::Size(state_.viewport_width, state_.viewport_height))) { |
| Destroy(true); |
| LOG(ERROR) << "ContextResult::kFatalFailure: " |
| "Could not allocate offscreen buffer storage."; |
| return gpu::ContextResult::kFatalFailure; |
| } |
| if (!offscreen_single_buffer_) { |
| // 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) { |
| bool was_lost = CheckResetStatus(); |
| Destroy(true); |
| LOG(ERROR) << (was_lost ? "ContextResult::kTransientFailure: " |
| : "ContextResult::kFatalFailure: ") |
| << "Offscreen saved FBO was incomplete."; |
| return was_lost ? gpu::ContextResult::kTransientFailure |
| : gpu::ContextResult::kFatalFailure; |
| } |
| } |
| } |
| |
| api()->glActiveTextureFn(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); |
| UpdateFramebufferSRGB(nullptr); |
| |
| bool call_gl_clear = !surfaceless_ && !offscreen; |
| #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) { |
| api()->glClearColorFn(0.0f, 0.0f, 0.0f, 1.0f); |
| } |
| |
| // Clear the backbuffer. |
| api()->glClearFn(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | |
| GL_STENCIL_BUFFER_BIT); |
| |
| // Restore alpha clear value if we changed it. |
| if (clear_alpha) { |
| api()->glClearColorFn(0.0f, 0.0f, 0.0f, 0.0f); |
| } |
| } |
| |
| supports_post_sub_buffer_ = surface->SupportsPostSubBuffer(); |
| if (workarounds() |
| .disable_post_sub_buffers_for_onscreen_surfaces && |
| !surface->IsOffscreen()) |
| supports_post_sub_buffer_ = false; |
| |
| supports_swap_buffers_with_bounds_ = surface->SupportsSwapBuffersWithBounds(); |
| |
| supports_commit_overlay_planes_ = surface->SupportsCommitOverlayPlanes(); |
| |
| supports_async_swap_ = surface->SupportsAsyncSwap(); |
| |
| supports_dc_layers_ = !offscreen && surface->SupportsDCLayers(); |
| |
| if (workarounds().unbind_fbo_on_context_switch) { |
| context_->SetUnbindFboOnMakeCurrent(); |
| } |
| |
| if (workarounds().gl_clear_broken) { |
| DCHECK(!clear_framebuffer_blit_.get()); |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glClearWorkaroundInit"); |
| clear_framebuffer_blit_ = |
| std::make_unique<ClearFramebufferResourceManager>(this); |
| if (LOCAL_PEEK_GL_ERROR("glClearWorkaroundInit") != GL_NO_ERROR) { |
| LOG(ERROR) << "ContextResult::kFatalFailure: " |
| "glClearWorkaroundInit failed"; |
| return gpu::ContextResult::kFatalFailure; |
| } |
| } |
| |
| if (group_->gpu_preferences().enable_gpu_driver_debug_logging && |
| feature_info_->feature_flags().khr_debug) { |
| InitializeGLDebugLogging(true, GLDebugMessageCallback, &logger_); |
| } |
| |
| if (feature_info_->feature_flags().chromium_texture_filtering_hint && |
| feature_info_->feature_flags().is_swiftshader) { |
| api()->glHintFn(GL_TEXTURE_FILTERING_HINT_CHROMIUM, GL_NICEST); |
| } |
| |
| return gpu::ContextResult::kSuccess; |
| } |
| |
| Capabilities GLES2DecoderImpl::GetCapabilities() { |
| DCHECK(initialized()); |
| Capabilities caps; |
| const gl::GLVersionInfo& version_info = gl_version_info(); |
| caps.VisitPrecisions([&version_info]( |
| GLenum shader, GLenum type, |
| Capabilities::ShaderPrecision* shader_precision) { |
| GLint range[2] = {0, 0}; |
| GLint precision = 0; |
| QueryShaderPrecisionFormat(version_info, 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, 1); |
| DoGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &caps.max_cube_map_texture_size, |
| 1); |
| DoGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, |
| &caps.max_fragment_uniform_vectors, 1); |
| DoGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &caps.max_renderbuffer_size, 1); |
| DoGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &caps.max_texture_image_units, 1); |
| DoGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.max_texture_size, 1); |
| DoGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.max_varying_vectors, 1); |
| DoGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.max_vertex_attribs, 1); |
| DoGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, |
| &caps.max_vertex_texture_image_units, 1); |
| DoGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &caps.max_vertex_uniform_vectors, |
| 1); |
| { |
| GLint dims[2] = {0, 0}; |
| DoGetIntegerv(GL_MAX_VIEWPORT_DIMS, dims, 2); |
| caps.max_viewport_width = dims[0]; |
| caps.max_viewport_height = dims[1]; |
| } |
| DoGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, |
| &caps.num_compressed_texture_formats, 1); |
| DoGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &caps.num_shader_binary_formats, |
| 1); |
| DoGetIntegerv(GL_BIND_GENERATES_RESOURCE_CHROMIUM, |
| &caps.bind_generates_resource_chromium, 1); |
| if (feature_info_->IsWebGL2OrES3Context()) { |
| // 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, 1); |
| DoGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &caps.max_array_texture_layers, |
| 1); |
| DoGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &caps.max_color_attachments, 1); |
| DoGetInteger64v(GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, |
| &caps.max_combined_fragment_uniform_components, 1); |
| DoGetIntegerv(GL_MAX_COMBINED_UNIFORM_BLOCKS, |
| &caps.max_combined_uniform_blocks, 1); |
| DoGetInteger64v(GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, |
| &caps.max_combined_vertex_uniform_components, 1); |
| DoGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.max_draw_buffers, 1); |
| DoGetInteger64v(GL_MAX_ELEMENT_INDEX, &caps.max_element_index, 1); |
| DoGetIntegerv(GL_MAX_ELEMENTS_INDICES, &caps.max_elements_indices, 1); |
| DoGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &caps.max_elements_vertices, 1); |
| DoGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, |
| &caps.max_fragment_input_components, 1); |
| DoGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, |
| &caps.max_fragment_uniform_blocks, 1); |
| DoGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, |
| &caps.max_fragment_uniform_components, 1); |
| DoGetIntegerv(GL_MAX_PROGRAM_TEXEL_OFFSET, &caps.max_program_texel_offset, |
| 1); |
| DoGetInteger64v(GL_MAX_SERVER_WAIT_TIMEOUT, &caps.max_server_wait_timeout, |
| 1); |
| // 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, 1); |
| DoGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, |
| &caps.max_transform_feedback_interleaved_components, 1); |
| DoGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, |
| &caps.max_transform_feedback_separate_attribs, 1); |
| DoGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, |
| &caps.max_transform_feedback_separate_components, 1); |
| DoGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &caps.max_uniform_block_size, 1); |
| DoGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, |
| &caps.max_uniform_buffer_bindings, 1); |
| DoGetIntegerv(GL_MAX_VARYING_COMPONENTS, &caps.max_varying_components, 1); |
| DoGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, |
| &caps.max_vertex_output_components, 1); |
| DoGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &caps.max_vertex_uniform_blocks, |
| 1); |
| DoGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, |
| &caps.max_vertex_uniform_components, 1); |
| DoGetIntegerv(GL_MIN_PROGRAM_TEXEL_OFFSET, &caps.min_program_texel_offset, |
| 1); |
| DoGetIntegerv(GL_NUM_EXTENSIONS, &caps.num_extensions, 1); |
| // TODO(vmiura): Remove GL_NUM_PROGRAM_BINARY_FORMATS. |
| DoGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, |
| &caps.num_program_binary_formats, 1); |
| DoGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, |
| &caps.uniform_buffer_offset_alignment, 1); |
| // 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 || |
| feature_info_->IsWebGL2OrES3Context()) { |
| DoGetIntegerv(GL_MAX_SAMPLES, &caps.max_samples, 1); |
| } |
| |
| caps.num_stencil_bits = num_stencil_bits_; |
| |
| caps.egl_image_external = |
| feature_info_->feature_flags().oes_egl_image_external; |
| caps.texture_format_astc = |
| feature_info_->feature_flags().ext_texture_format_astc; |
| 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; |
| // Whether or not a texture will be bound to an EGLImage is |
| // dependent on whether we are using the sync mailbox manager. |
| caps.disable_one_component_textures = |
| mailbox_manager()->UsesSync() && |
| workarounds().avoid_one_component_egl_images; |
| 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; |
| |
| caps.chromium_image_rgb_emulation = ChromiumImageNeedsRGBEmulation(); |
| #if defined(OS_MACOSX) |
| // This is unconditionally true on mac, no need to test for it at runtime. |
| caps.iosurface = true; |
| #endif |
| caps.use_gpu_fences_for_overlay_planes = surface_->SupportsPlaneGpuFences(); |
| |
| caps.post_sub_buffer = supports_post_sub_buffer_; |
| caps.swap_buffers_with_bounds = supports_swap_buffers_with_bounds_; |
| caps.commit_overlay_planes = supports_commit_overlay_planes_; |
| caps.surfaceless = surfaceless_; |
| bool is_offscreen = !!offscreen_target_frame_buffer_.get(); |
| caps.flips_vertically = !is_offscreen && surface_->FlipsVertically(); |
| caps.msaa_is_slow = workarounds().msaa_is_slow; |
| caps.avoid_stencil_buffers = workarounds().avoid_stencil_buffers; |
| caps.multisample_compatibility = |
| feature_info_->feature_flags().ext_multisample_compatibility; |
| caps.dc_layers = supports_dc_layers_; |
| caps.use_dc_overlays_for_video = surface_->UseOverlaysForVideo(); |
| caps.protected_video_swap_chain = surface_->SupportsProtectedVideo(); |
| |
| 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; |
| caps.texture_norm16 = feature_info_->feature_flags().ext_texture_norm16; |
| caps.texture_half_float_linear = |
| feature_info_->oes_texture_half_float_linear_available(); |
| caps.color_buffer_half_float_rgba = |
| feature_info_->ext_color_buffer_float_available() || |
| feature_info_->ext_color_buffer_half_float_available(); |
| caps.image_ycbcr_422 = |
| feature_info_->feature_flags().chromium_image_ycbcr_422; |
| caps.image_ycbcr_420v = |
| feature_info_->feature_flags().chromium_image_ycbcr_420v; |
| caps.image_ycbcr_420v_disabled_for_video_frames = |
| group_->gpu_preferences() |
| .disable_biplanar_gpu_memory_buffers_for_video_frames; |
| caps.image_xr30 = feature_info_->feature_flags().chromium_image_xr30; |
| caps.image_xb30 = feature_info_->feature_flags().chromium_image_xb30; |
| caps.max_copy_texture_chromium_size = |
| workarounds().max_copy_texture_chromium_size; |
| caps.render_buffer_format_bgra8888 = |
| feature_info_->feature_flags().ext_render_buffer_format_bgra8888; |
| caps.occlusion_query = feature_info_->feature_flags().occlusion_query; |
| caps.occlusion_query_boolean = |
| feature_info_->feature_flags().occlusion_query_boolean; |
| caps.timer_queries = query_manager_->GPUTimingAvailable(); |
| caps.gpu_rasterization = |
| group_->gpu_feature_info() |
| .status_values[GPU_FEATURE_TYPE_GPU_RASTERIZATION] == |
| kGpuFeatureStatusEnabled; |
| if (workarounds().disable_non_empty_post_sub_buffers_for_onscreen_surfaces && |
| !surface_->IsOffscreen()) { |
| caps.disable_non_empty_post_sub_buffers = true; |
| } |
| if (workarounds().broken_egl_image_ref_counting && |
| group_->gpu_preferences().enable_threaded_texture_mailboxes) { |
| caps.disable_2d_canvas_copy_on_write = true; |
| } |
| caps.texture_npot = feature_info_->feature_flags().npot_ok; |
| caps.texture_storage_image = |
| feature_info_->feature_flags().chromium_texture_storage_image; |
| caps.supports_oop_raster = false; |
| caps.chromium_gpu_fence = feature_info_->feature_flags().chromium_gpu_fence; |
| caps.unpremultiply_and_dither_copy = |
| feature_info_->feature_flags().unpremultiply_and_dither_copy; |
| caps.separate_stencil_ref_mask_writemask = |
| feature_info_->feature_flags().separate_stencil_ref_mask_writemask; |
| caps.chromium_nonblocking_readback = |
| feature_info_->context_type() == CONTEXT_TYPE_WEBGL2; |
| caps.num_surface_buffers = surface_->GetBufferCount(); |
| caps.mesa_framebuffer_flip_y = |
| feature_info_->feature_flags().mesa_framebuffer_flip_y; |
| |
| caps.gpu_memory_buffer_formats = |
| feature_info_->feature_flags().gpu_memory_buffer_formats; |
| caps.texture_target_exception_list = |
| group_->gpu_preferences().texture_target_exception_list; |
| |
| 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 (feature_info_->disable_shader_translator()) { |
| return true; |
| } |
| if (vertex_translator_ || fragment_translator_) { |
| DCHECK(vertex_translator_ && fragment_translator_); |
| return true; |
| } |
| ShBuiltInResources resources; |
| sh::InitBuiltInResources(&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; |
| resources.MaxDualSourceDrawBuffers = group_->max_dual_source_draw_buffers(); |
| |
| if (!feature_info_->IsWebGL1OrES2Context()) { |
| resources.MaxVertexOutputVectors = |
| group_->max_vertex_output_components() / 4; |
| resources.MaxFragmentInputVectors = |
| group_->max_fragment_input_components() / 4; |
| resources.MaxProgramTexelOffset = group_->max_program_texel_offset(); |
| resources.MinProgramTexelOffset = group_->min_program_texel_offset(); |
| } |
| |
| resources.FragmentPrecisionHigh = has_fragment_precision_high_; |
| |
| ShShaderSpec shader_spec; |
| switch (feature_info_->context_type()) { |
| case CONTEXT_TYPE_WEBGL1: |
| shader_spec = SH_WEBGL_SPEC; |
| 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; |
| break; |
| case CONTEXT_TYPE_WEBGL2: |
| shader_spec = SH_WEBGL2_SPEC; |
| break; |
| case CONTEXT_TYPE_OPENGLES2: |
| shader_spec = SH_GLES2_SPEC; |
| 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.NV_EGL_stream_consumer_external = |
| features().nv_egl_stream_consumer_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; |
| resources.EXT_blend_func_extended = |
| features().ext_blend_func_extended ? 1 : 0; |
| break; |
| case CONTEXT_TYPE_OPENGLES3: |
| shader_spec = SH_GLES3_SPEC; |
| resources.ARB_texture_rectangle = |
| features().arb_texture_rectangle ? 1 : 0; |
| resources.OES_EGL_image_external = |
| features().oes_egl_image_external ? 1 : 0; |
| resources.NV_EGL_stream_consumer_external = |
| features().nv_egl_stream_consumer_external ? 1 : 0; |
| resources.EXT_blend_func_extended = |
| features().ext_blend_func_extended ? 1 : 0; |
| break; |
| default: |
| NOTREACHED(); |
| shader_spec = SH_GLES2_SPEC; |
| break; |
| } |
| |
| if (((shader_spec == SH_WEBGL_SPEC || shader_spec == SH_WEBGL2_SPEC) && |
| features().enable_shader_name_hashing) || |
| force_shader_name_hashing_for_test) |
| resources.HashFunction = &CityHash64; |
| else |
| resources.HashFunction = nullptr; |
| |
| ShCompileOptions driver_bug_workarounds = 0; |
| 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().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 (workarounds().remove_pow_with_constant_exponent) |
| driver_bug_workarounds |= SH_REMOVE_POW_WITH_CONSTANT_EXPONENT; |
| if (workarounds().emulate_abs_int_function) |
| driver_bug_workarounds |= SH_EMULATE_ABS_INT_FUNCTION; |
| if (workarounds().rewrite_texelfetchoffset_to_texelfetch) |
| driver_bug_workarounds |= SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH; |
| if (workarounds().add_and_true_to_loop_condition) |
| driver_bug_workarounds |= SH_ADD_AND_TRUE_TO_LOOP_CONDITION; |
| if (workarounds().rewrite_do_while_loops) |
| driver_bug_workarounds |= SH_REWRITE_DO_WHILE_LOOPS; |
| if (workarounds().emulate_isnan_on_float) |
| driver_bug_workarounds |= SH_EMULATE_ISNAN_FLOAT_FUNCTION; |
| if (workarounds().use_unused_standard_shared_blocks) |
| driver_bug_workarounds |= SH_USE_UNUSED_STANDARD_SHARED_BLOCKS; |
| if (workarounds().dont_remove_invariant_for_fragment_input) |
| driver_bug_workarounds |= SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT; |
| if (workarounds().remove_invariant_and_centroid_for_essl3) |
| driver_bug_workarounds |= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3; |
| if (workarounds().rewrite_float_unary_minus_operator) |
| driver_bug_workarounds |= SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR; |
| if (workarounds().dont_use_loops_to_initialize_variables) |
| driver_bug_workarounds |= SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES; |
| |
| // Initialize uninitialized locals by default |
| if (!workarounds().dont_initialize_uninitialized_locals) |
| driver_bug_workarounds |= SH_INITIALIZE_UNINITIALIZED_LOCALS; |
| |
| resources.WEBGL_debug_shader_precision = |
| group_->gpu_preferences().emulate_shader_precision; |
| |
| ShShaderOutput shader_output_language = |
| ShaderTranslator::GetShaderOutputLanguageForContext(gl_version_info()); |
| |
| vertex_translator_ = shader_translator_cache()->GetTranslator( |
| GL_VERTEX_SHADER, shader_spec, &resources, shader_output_language, |
| 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, shader_output_language, |
| driver_bug_workarounds); |
| if (!fragment_translator_.get()) { |
| LOG(ERROR) << "Could not initialize fragment shader translator."; |
| Destroy(true); |
| return false; |
| } |
| return true; |
| } |
| |
| void GLES2DecoderImpl::DestroyShaderTranslator() { |
| vertex_translator_ = nullptr; |
| fragment_translator_ = nullptr; |
| } |
| |
| bool GLES2DecoderImpl::GenBuffersHelper(GLsizei n, const GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| if (GetBuffer(client_ids[ii])) { |
| return false; |
| } |
| } |
| std::unique_ptr<GLuint[]> service_ids(new GLuint[n]); |
| api()->glGenBuffersARBFn(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; |
| } |
| } |
| std::unique_ptr<GLuint[]> service_ids(new GLuint[n]); |
| api()->glGenFramebuffersEXTFn(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; |
| } |
| } |
| std::unique_ptr<GLuint[]> service_ids(new GLuint[n]); |
| api()->glGenRenderbuffersEXTFn(n, service_ids.get()); |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| CreateRenderbuffer(client_ids[ii], service_ids[ii]); |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::GenTexturesHelper(GLsizei n, const GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| if (GetTexture(client_ids[ii])) { |
| return false; |
| } |
| } |
| std::unique_ptr<GLuint[]> service_ids(new GLuint[n]); |
| api()->glGenTexturesFn(n, service_ids.get()); |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| CreateTexture(client_ids[ii], service_ids[ii]); |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::GenSamplersHelper(GLsizei n, const GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| if (GetSampler(client_ids[ii])) { |
| return false; |
| } |
| } |
| std::unique_ptr<GLuint[]> service_ids(new GLuint[n]); |
| api()->glGenSamplersFn(n, service_ids.get()); |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| CreateSampler(client_ids[ii], service_ids[ii]); |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::GenTransformFeedbacksHelper( |
| GLsizei n, const GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| if (GetTransformFeedback(client_ids[ii])) { |
| return false; |
| } |
| } |
| std::unique_ptr<GLuint[]> service_ids(new GLuint[n]); |
| api()->glGenTransformFeedbacksFn(n, service_ids.get()); |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| CreateTransformFeedback(client_ids[ii], service_ids[ii]); |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::GenPathsCHROMIUMHelper(GLuint first_client_id, |
| GLsizei range) { |
| GLuint last_client_id; |
| if (!SafeAddUint32(first_client_id, range - 1, &last_client_id)) |
| return false; |
| |
| if (path_manager()->HasPathsInRange(first_client_id, last_client_id)) |
| return false; |
| |
| GLuint first_service_id = api()->glGenPathsNVFn(range); |
| if (first_service_id == 0) { |
| // We have to fail the connection here, because client has already |
| // succeeded in allocating the ids. This happens if we allocate |
| // the whole path id space (two allocations of 0x7FFFFFFF paths, for |
| // example). |
| return false; |
| } |
| // GenPathsNV does not wrap. |
| DCHECK(first_service_id + range - 1 >= first_service_id); |
| |
| path_manager()->CreatePathRange(first_client_id, last_client_id, |
| first_service_id); |
| |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::DeletePathsCHROMIUMHelper(GLuint first_client_id, |
| GLsizei range) { |
| GLuint last_client_id; |
| if (!SafeAddUint32(first_client_id, range - 1, &last_client_id)) |
| return false; |
| |
| path_manager()->RemovePaths(first_client_id, last_client_id); |
| return true; |
| } |
| |
| void GLES2DecoderImpl::DeleteBuffersHelper(GLsizei n, |
| const volatile GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| GLuint client_id = client_ids[ii]; |
| Buffer* buffer = GetBuffer(client_id); |
| if (buffer && !buffer->IsDeleted()) { |
| if (buffer->GetMappedRange()) { |
| // The buffer is not guaranteed to still be bound to any binding point, |
| // even though it is mapped. If it is not bound, we will need to bind |
| // it temporarily in order to unmap it. |
| GLenum target = buffer->initial_target(); |
| Buffer* currently_bound = |
| buffer_manager()->GetBufferInfoForTarget(&state_, target); |
| if (currently_bound != buffer) { |
| api()->glBindBufferFn(target, buffer->service_id()); |
| } |
| UnmapBufferHelper(buffer, target); |
| if (currently_bound != buffer) { |
| api()->glBindBufferFn( |
| target, currently_bound ? currently_bound->service_id() : 0); |
| } |
| } |
| state_.RemoveBoundBuffer(buffer); |
| buffer_manager()->RemoveBuffer(client_id); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DeleteFramebuffersHelper( |
| GLsizei n, |
| const volatile GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| GLuint client_id = client_ids[ii]; |
| Framebuffer* framebuffer = GetFramebuffer(client_id); |
| if (framebuffer && !framebuffer->IsDeleted()) { |
| if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) { |
| GLenum target = GetDrawFramebufferTarget(); |
| |
| // Unbind attachments on FBO before deletion. |
| if (workarounds().unbind_attachments_on_bound_render_fbo_delete) |
| framebuffer->DoUnbindGLAttachmentsForWorkaround(target); |
| |
| api()->glBindFramebufferEXTFn(target, GetBackbufferServiceId()); |
| state_.UpdateWindowRectanglesForBoundDrawFramebufferClientID(0); |
| framebuffer_state_.bound_draw_framebuffer = nullptr; |
| framebuffer_state_.clear_state_dirty = true; |
| } |
| if (framebuffer == framebuffer_state_.bound_read_framebuffer.get()) { |
| framebuffer_state_.bound_read_framebuffer = nullptr; |
| GLenum target = GetReadFramebufferTarget(); |
| api()->glBindFramebufferEXTFn(target, GetBackbufferServiceId()); |
| } |
| OnFboChanged(); |
| RemoveFramebuffer(client_id); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DeleteRenderbuffersHelper( |
| GLsizei n, |
| const volatile GLuint* client_ids) { |
| bool supports_separate_framebuffer_binds = |
| features().chromium_framebuffer_multisample; |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| GLuint client_id = client_ids[ii]; |
| Renderbuffer* renderbuffer = GetRenderbuffer(client_id); |
| if (renderbuffer && !renderbuffer->IsDeleted()) { |
| if (state_.bound_renderbuffer.get() == renderbuffer) { |
| state_.bound_renderbuffer = nullptr; |
| } |
| // 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_id); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DeleteTexturesHelper(GLsizei n, |
| const volatile GLuint* client_ids) { |
| bool supports_separate_framebuffer_binds = SupportsSeparateFramebufferBinds(); |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| GLuint client_id = client_ids[ii]; |
| TextureRef* texture_ref = GetTexture(client_id); |
| if (texture_ref) { |
| UnbindTexture(texture_ref, supports_separate_framebuffer_binds); |
| RemoveTexture(client_id); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DeleteSamplersHelper(GLsizei n, |
| const volatile GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| GLuint client_id = client_ids[ii]; |
| Sampler* sampler = GetSampler(client_id); |
| if (sampler && !sampler->IsDeleted()) { |
| // Unbind from current sampler units. |
| state_.UnbindSampler(sampler); |
| |
| RemoveSampler(client_id); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DeleteTransformFeedbacksHelper( |
| GLsizei n, |
| const volatile GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| GLuint client_id = client_ids[ii]; |
| TransformFeedback* transform_feedback = GetTransformFeedback(client_id); |
| if (transform_feedback) { |
| if (transform_feedback->active()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glDeleteTransformFeedbacks", |
| "Deleting transform feedback is active"); |
| return; |
| } |
| if (state_.bound_transform_feedback.get() == transform_feedback) { |
| // Bind to the default transform feedback. |
| DCHECK(state_.default_transform_feedback.get()); |
| state_.default_transform_feedback->DoBindTransformFeedback( |
| GL_TRANSFORM_FEEDBACK, state_.bound_transform_feedback.get(), |
| state_.bound_transform_feedback_buffer.get()); |
| state_.bound_transform_feedback = |
| state_.default_transform_feedback.get(); |
| } |
| RemoveTransformFeedback(client_id); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DeleteSyncHelper(GLuint sync) { |
| GLsync service_id = 0; |
| if (group_->GetSyncServiceId(sync, &service_id)) { |
| api()->glDeleteSyncFn(service_id); |
| group_->RemoveSyncId(sync); |
| } else if (sync != 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glDeleteSync", "unknown sync"); |
| } |
| } |
| |
| bool GLES2DecoderImpl::MakeCurrent() { |
| DCHECK(surface_); |
| 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; |
| } |
| DCHECK_EQ(api(), gl::g_current_gl_context); |
| |
| 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; |
| state_.stencil_state_changed_since_validation = true; |
| |
| // Rebind textures if the service ids may have changed. |
| RestoreAllExternalTextureBindingsIfNeeded(); |
| |
| // Since we have a context now, take the opportunity to drop any TextureRefs |
| // that are pending destruction. |
| texture_refs_pending_destruction_.clear(); |
| |
| return true; |
| } |
| |
| void GLES2DecoderImpl::ProcessFinishedAsyncTransfers() { |
| ProcessPendingReadPixels(false); |
| } |
| |
| static void RebindCurrentFramebuffer(gl::GLApi* api, |
| 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; |
| } |
| |
| api->glBindFramebufferEXTFn(target, framebuffer_id); |
| } |
| |
| void GLES2DecoderImpl::RestoreCurrentFramebufferBindings() { |
| framebuffer_state_.clear_state_dirty = true; |
| |
| if (!SupportsSeparateFramebufferBinds()) { |
| RebindCurrentFramebuffer(api(), GL_FRAMEBUFFER, |
| framebuffer_state_.bound_draw_framebuffer.get(), |
| GetBackbufferServiceId()); |
| } else { |
| RebindCurrentFramebuffer(api(), GL_READ_FRAMEBUFFER_EXT, |
| framebuffer_state_.bound_read_framebuffer.get(), |
| GetBackbufferServiceId()); |
| RebindCurrentFramebuffer(api(), GL_DRAW_FRAMEBUFFER_EXT, |
| framebuffer_state_.bound_draw_framebuffer.get(), |
| GetBackbufferServiceId()); |
| } |
| OnFboChanged(); |
| } |
| |
| bool GLES2DecoderImpl::CheckFramebufferValid( |
| Framebuffer* framebuffer, |
| GLenum target, |
| GLenum gl_error, |
| const char* func_name) { |
| if (!framebuffer) { |
| if (surfaceless_) |
| return false; |
| if (backbuffer_needs_clear_bits_) { |
| api()->glClearColorFn(0, 0, 0, BackBufferAlphaClearColor()); |
| state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| api()->glClearStencilFn(0); |
| state_.SetDeviceStencilMaskSeparate(GL_FRONT, kDefaultStencilMask); |
| state_.SetDeviceStencilMaskSeparate(GL_BACK, kDefaultStencilMask); |
| api()->glClearDepthFn(1.0f); |
| state_.SetDeviceDepthMask(GL_TRUE); |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| ClearDeviceWindowRectangles(); |
| bool reset_draw_buffer = false; |
| if ((backbuffer_needs_clear_bits_ & GL_COLOR_BUFFER_BIT) != 0 && |
| back_buffer_draw_buffer_ == GL_NONE) { |
| reset_draw_buffer = true; |
| GLenum buf = GL_BACK; |
| if (GetBackbufferServiceId() != 0) // emulated backbuffer |
| buf = GL_COLOR_ATTACHMENT0; |
| api()->glDrawBuffersARBFn(1, &buf); |
| } |
| if (workarounds().gl_clear_broken) { |
| ClearFramebufferForWorkaround(backbuffer_needs_clear_bits_); |
| } else { |
| api()->glClearFn(backbuffer_needs_clear_bits_); |
| } |
| if (reset_draw_buffer) { |
| GLenum buf = GL_NONE; |
| api()->glDrawBuffersARBFn(1, &buf); |
| } |
| backbuffer_needs_clear_bits_ = 0; |
| RestoreClearState(); |
| } |
| return true; |
| } |
| |
| if (!framebuffer_manager()->IsComplete(framebuffer)) { |
| GLenum completeness = framebuffer->IsPossiblyComplete(feature_info_.get()); |
| if (completeness != GL_FRAMEBUFFER_COMPLETE) { |
| LOCAL_SET_GL_ERROR(gl_error, func_name, "framebuffer incomplete"); |
| return false; |
| } |
| |
| if (framebuffer->GetStatus(texture_manager(), target) != |
| GL_FRAMEBUFFER_COMPLETE) { |
| LOCAL_SET_GL_ERROR( |
| gl_error, func_name, "framebuffer incomplete (check)"); |
| return false; |
| } |
| framebuffer_manager()->MarkAsComplete(framebuffer); |
| } |
| |
| // Are all the attachments cleared? |
| if (renderbuffer_manager()->HaveUnclearedRenderbuffers() || |
| texture_manager()->HaveUnclearedMips()) { |
| if (!framebuffer->IsCleared()) { |
| ClearUnclearedAttachments(target, framebuffer); |
| } |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::CheckBoundDrawFramebufferValid(const char* func_name) { |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| bool valid = CheckFramebufferValid( |
| framebuffer, GetDrawFramebufferTarget(), |
| GL_INVALID_FRAMEBUFFER_OPERATION, func_name); |
| if (!valid) |
| return false; |
| |
| if (!SupportsSeparateFramebufferBinds()) |
| OnUseFramebuffer(); |
| |
| UpdateFramebufferSRGB(framebuffer); |
| return true; |
| } |
| |
| void GLES2DecoderImpl::UpdateFramebufferSRGB(Framebuffer* framebuffer) { |
| // Manually set the value of FRAMEBUFFER_SRGB based on the state that was set |
| // by the client. |
| bool needs_enable_disable_framebuffer_srgb = false; |
| bool enable_framebuffer_srgb = true; |
| if (feature_info_->feature_flags().ext_srgb_write_control) { |
| needs_enable_disable_framebuffer_srgb = true; |
| enable_framebuffer_srgb &= state_.GetEnabled(GL_FRAMEBUFFER_SRGB); |
| } |
| // On desktop, enable FRAMEBUFFER_SRGB only if the framebuffer contains sRGB |
| // attachments. In theory, we can just leave FRAMEBUFFER_SRGB enabled, |
| // however, |
| // many drivers behave incorrectly when no attachments are sRGB. When at |
| // least one attachment is sRGB, then they behave correctly. |
| if (feature_info_->feature_flags().desktop_srgb_support) { |
| needs_enable_disable_framebuffer_srgb = true; |
| // Assume that the default fbo does not have an sRGB image. |
| enable_framebuffer_srgb &= framebuffer && framebuffer->HasSRGBAttachments(); |
| } |
| if (needs_enable_disable_framebuffer_srgb) |
| state_.EnableDisableFramebufferSRGB(enable_framebuffer_srgb); |
| } |
| |
| bool GLES2DecoderImpl::CheckBoundReadFramebufferValid( |
| const char* func_name, GLenum gl_error) { |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| bool valid = CheckFramebufferValid( |
| framebuffer, GetReadFramebufferTarget(), gl_error, func_name); |
| return valid; |
| } |
| |
| bool GLES2DecoderImpl::CheckBoundFramebufferValid(const char* func_name) { |
| GLenum gl_error = GL_INVALID_FRAMEBUFFER_OPERATION; |
| Framebuffer* draw_framebuffer = GetBoundDrawFramebuffer(); |
| bool valid = CheckFramebufferValid( |
| draw_framebuffer, GetDrawFramebufferTarget(), gl_error, func_name); |
| |
| Framebuffer* read_framebuffer = GetBoundReadFramebuffer(); |
| valid = valid && CheckFramebufferValid( |
| read_framebuffer, GetReadFramebufferTarget(), gl_error, func_name); |
| return valid; |
| } |
| |
| bool GLES2DecoderImpl::FormsTextureCopyingFeedbackLoop( |
| TextureRef* texture, GLint level, GLint layer) { |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| if (!framebuffer) |
| return false; |
| const Framebuffer::Attachment* attachment = |
| framebuffer->GetReadBufferAttachment(); |
| if (!attachment) |
| return false; |
| return attachment->FormsFeedbackLoop(texture, level, layer); |
| } |
| |
| gfx::Size GLES2DecoderImpl::GetBoundReadFramebufferSize() { |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| if (framebuffer) { |
| return framebuffer->GetFramebufferValidSize(); |
| } else if (offscreen_target_frame_buffer_.get()) { |
| return offscreen_size_; |
| } else { |
| return surface_->GetSize(); |
| } |
| } |
| |
| GLuint GLES2DecoderImpl::GetBoundReadFramebufferServiceId() { |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| if (framebuffer) { |
| return framebuffer->service_id(); |
| } |
| if (offscreen_resolved_frame_buffer_.get()) { |
| return offscreen_resolved_frame_buffer_->id(); |
| } |
| if (offscreen_target_frame_buffer_.get()) { |
| return offscreen_target_frame_buffer_->id(); |
| } |
| if (surface_.get()) { |
| return surface_->GetBackingFramebufferObject(); |
| } |
| return 0; |
| } |
| |
| GLuint GLES2DecoderImpl::GetBoundDrawFramebufferServiceId() const { |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (framebuffer) { |
| return framebuffer->service_id(); |
| } |
| if (offscreen_target_frame_buffer_.get()) { |
| return offscreen_target_frame_buffer_->id(); |
| } |
| if (surface_.get()) { |
| return surface_->GetBackingFramebufferObject(); |
| } |
| return 0; |
| } |
| |
| GLenum GLES2DecoderImpl::GetBoundReadFramebufferTextureType() { |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| if (framebuffer) { |
| return framebuffer->GetReadBufferTextureType(); |
| } else { // Back buffer. |
| if (back_buffer_read_buffer_ == GL_NONE) |
| return 0; |
| return GL_UNSIGNED_BYTE; |
| } |
| } |
| |
| GLenum GLES2DecoderImpl::GetBoundReadFramebufferInternalFormat() { |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| if (framebuffer) { |
| return framebuffer->GetReadBufferInternalFormat(); |
| } else { // Back buffer. |
| if (back_buffer_read_buffer_ == GL_NONE) |
| return 0; |
| if (offscreen_target_frame_buffer_.get()) { |
| return offscreen_target_color_format_; |
| } |
| return back_buffer_color_format_; |
| } |
| } |
| |
| GLenum GLES2DecoderImpl::GetBoundColorDrawBufferType(GLint drawbuffer_i) { |
| DCHECK(drawbuffer_i >= 0 && |
| drawbuffer_i < static_cast<GLint>(group_->max_draw_buffers())); |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (!framebuffer) { |
| return 0; |
| } |
| GLenum drawbuffer = static_cast<GLenum>(GL_DRAW_BUFFER0 + drawbuffer_i); |
| if (framebuffer->GetDrawBuffer(drawbuffer) == GL_NONE) { |
| return 0; |
| } |
| GLenum attachment = static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + drawbuffer_i); |
| const Framebuffer::Attachment* buffer = |
| framebuffer->GetAttachment(attachment); |
| if (!buffer) { |
| return 0; |
| } |
| return buffer->texture_type(); |
| } |
| |
| GLenum GLES2DecoderImpl::GetBoundColorDrawBufferInternalFormat( |
| GLint drawbuffer_i) { |
| DCHECK(drawbuffer_i >= 0 && |
| drawbuffer_i < static_cast<GLint>(group_->max_draw_buffers())); |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (!framebuffer) { |
| return 0; |
| } |
| GLenum drawbuffer = static_cast<GLenum>(GL_DRAW_BUFFER0 + drawbuffer_i); |
| if (framebuffer->GetDrawBuffer(drawbuffer) == GL_NONE) { |
| return 0; |
| } |
| GLenum attachment = static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + drawbuffer_i); |
| const Framebuffer::Attachment* buffer = |
| framebuffer->GetAttachment(attachment); |
| if (!buffer) { |
| return 0; |
| } |
| return buffer->internal_format(); |
| } |
| |
| GLsizei GLES2DecoderImpl::GetBoundFramebufferSamples(GLenum target) { |
| DCHECK(target == GL_DRAW_FRAMEBUFFER || target == GL_READ_FRAMEBUFFER || |
| target == GL_FRAMEBUFFER); |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); |
| if (framebuffer) { |
| return framebuffer->GetSamples(); |
| } else { // Back buffer. |
| if (offscreen_target_frame_buffer_.get()) { |
| return offscreen_target_samples_; |
| } |
| return 0; |
| } |
| } |
| |
| GLenum GLES2DecoderImpl::GetBoundFramebufferDepthFormat( |
| GLenum target) { |
| DCHECK(target == GL_DRAW_FRAMEBUFFER || target == GL_READ_FRAMEBUFFER || |
| target == GL_FRAMEBUFFER); |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); |
| if (framebuffer) { |
| return framebuffer->GetDepthFormat(); |
| } else { // Back buffer. |
| if (offscreen_target_frame_buffer_.get()) { |
| return offscreen_target_depth_format_; |
| } |
| if (back_buffer_has_depth_) |
| return GL_DEPTH; |
| return 0; |
| } |
| } |
| |
| GLenum GLES2DecoderImpl::GetBoundFramebufferStencilFormat( |
| GLenum target) { |
| DCHECK(target == GL_DRAW_FRAMEBUFFER || target == GL_READ_FRAMEBUFFER || |
| target == GL_FRAMEBUFFER); |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); |
| if (framebuffer) { |
| return framebuffer->GetStencilFormat(); |
| } else { // Back buffer. |
| if (offscreen_target_frame_buffer_.get()) { |
| return offscreen_target_stencil_format_; |
| } |
| if (back_buffer_has_stencil_) |
| return GL_STENCIL; |
| return 0; |
| } |
| } |
| |
| void GLES2DecoderImpl::MarkDrawBufferAsCleared( |
| GLenum buffer, GLint drawbuffer_i) { |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (!framebuffer) |
| return; |
| GLenum attachment = 0; |
| switch (buffer) { |
| case GL_COLOR: |
| DCHECK(drawbuffer_i >= 0 && |
| drawbuffer_i < static_cast<GLint>(group_->max_draw_buffers())); |
| attachment = static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + drawbuffer_i); |
| break; |
| case GL_DEPTH: |
| attachment = GL_DEPTH_ATTACHMENT; |
| break; |
| case GL_STENCIL: |
| attachment = GL_STENCIL_ATTACHMENT; |
| break; |
| default: |
| // Caller is responsible for breaking GL_DEPTH_STENCIL into GL_DEPTH and |
| // GL_STENCIL. |
| NOTREACHED(); |
| } |
| framebuffer->MarkAttachmentAsCleared( |
| renderbuffer_manager(), texture_manager(), attachment, true); |
| } |
| |
| 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_; |
| query_manager_->ProcessFrameBeginUpdates(); |
| query_manager_->BeginProcessingCommands(); |
| } |
| |
| void GLES2DecoderImpl::EndDecoding() { |
| gpu_tracer_->EndDecoding(); |
| query_manager_->EndProcessingCommands(); |
| } |
| |
| ErrorState* GLES2DecoderImpl::GetErrorState() { |
| return state_.GetErrorState(); |
| } |
| |
| bool GLES2DecoderImpl::GetServiceTextureId(uint32_t client_texture_id, |
| uint32_t* 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; |
| } |
| |
| TextureBase* GLES2DecoderImpl::GetTextureBase(uint32_t client_id) { |
| TextureRef* texture_ref = texture_manager()->GetTexture(client_id); |
| return texture_ref ? texture_ref->texture() : nullptr; |
| } |
| |
| void GLES2DecoderImpl::SetLevelInfo(uint32_t client_id, |
| int level, |
| unsigned internal_format, |
| unsigned width, |
| unsigned height, |
| unsigned depth, |
| unsigned format, |
| unsigned type, |
| const gfx::Rect& cleared_rect) { |
| TextureRef* texture_ref = texture_manager()->GetTexture(client_id); |
| texture_manager()->SetLevelInfo(texture_ref, texture_ref->texture()->target(), |
| level, internal_format, width, height, depth, |
| 0 /* border */, format, type, cleared_rect); |
| } |
| |
| void GLES2DecoderImpl::Destroy(bool have_context) { |
| if (!initialized()) |
| return; |
| |
| DCHECK(!have_context || context_->IsCurrent(nullptr)); |
| |
| // Prepare to destroy the surface while the context is still current, because |
| // some surface destructors make GL calls. |
| if (surface_) |
| surface_->PrepareToDestroy(have_context); |
| |
| // Destroy any textures that are pending destruction. |
| if (!have_context) { |
| for (auto iter : texture_refs_pending_destruction_) |
| iter->ForceContextLost(); |
| } |
| texture_refs_pending_destruction_.clear(); |
| |
| // Notify any AbstractTextures to drop their texture ref. |
| for (auto* iter : abstract_textures_) |
| iter->OnDecoderWillDestroy(have_context); |
| abstract_textures_.clear(); |
| |
| ReleaseAllBackTextures(have_context); |
| if (have_context) { |
| if (apply_framebuffer_attachment_cmaa_intel_.get()) { |
| apply_framebuffer_attachment_cmaa_intel_->Destroy(); |
| apply_framebuffer_attachment_cmaa_intel_.reset(); |
| } |
| |
| if (copy_tex_image_blit_.get()) { |
| copy_tex_image_blit_->Destroy(); |
| copy_tex_image_blit_.reset(); |
| } |
| |
| if (copy_texture_chromium_.get()) { |
| copy_texture_chromium_->Destroy(); |
| copy_texture_chromium_.reset(); |
| } |
| |
| if (srgb_converter_.get()) { |
| srgb_converter_->Destroy(); |
| srgb_converter_.reset(); |
| } |
| |
| clear_framebuffer_blit_.reset(); |
| |
| if (state_.current_program.get()) { |
| program_manager()->UnuseProgram(shader_manager(), |
| state_.current_program.get()); |
| } |
| |
| if (attrib_0_buffer_id_) { |
| api()->glDeleteBuffersARBFn(1, &attrib_0_buffer_id_); |
| } |
| if (fixed_attrib_buffer_id_) { |
| api()->glDeleteBuffersARBFn(1, &fixed_attrib_buffer_id_); |
| } |
| |
| if (validation_fbo_) { |
| api()->glDeleteFramebuffersEXTFn(1, &validation_fbo_multisample_); |
| api()->glDeleteFramebuffersEXTFn(1, &validation_fbo_); |
| } |
| while (!validation_textures_.empty()) { |
| GLuint tex; |
| tex = validation_textures_.begin()->second; |
| api()->glDeleteTexturesFn(1, &tex); |
| validation_textures_.erase(validation_textures_.begin()); |
| } |
| |
| 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(); |
| for (auto& fence : deschedule_until_finished_fences_) { |
| fence->Invalidate(); |
| } |
| |
| if (group_ && group_->texture_manager()) |
| group_->texture_manager()->MarkContextLost(); |
| state_.MarkContextLost(); |
| } |
| deschedule_until_finished_fences_.clear(); |
| |
| ReportProgress(); |
| |
| // Unbind everything. |
| state_.vertex_attrib_manager = nullptr; |
| state_.default_vertex_attrib_manager = nullptr; |
| state_.texture_units.clear(); |
| state_.sampler_units.clear(); |
| state_.bound_array_buffer = nullptr; |
| state_.bound_copy_read_buffer = nullptr; |
| state_.bound_copy_write_buffer = nullptr; |
| state_.bound_pixel_pack_buffer = nullptr; |
| state_.bound_pixel_unpack_buffer = nullptr; |
| state_.bound_transform_feedback_buffer = nullptr; |
| state_.bound_uniform_buffer = nullptr; |
| framebuffer_state_.bound_read_framebuffer = nullptr; |
| framebuffer_state_.bound_draw_framebuffer = nullptr; |
| state_.current_draw_framebuffer_client_id = 0; |
| state_.bound_renderbuffer = nullptr; |
| state_.bound_transform_feedback = nullptr; |
| state_.default_transform_feedback = nullptr; |
| state_.indexed_uniform_buffer_bindings = nullptr; |
| |
| // 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 = nullptr; |
| |
| apply_framebuffer_attachment_cmaa_intel_.reset(); |
| copy_tex_image_blit_.reset(); |
| copy_texture_chromium_.reset(); |
| srgb_converter_.reset(); |
| clear_framebuffer_blit_.reset(); |
| |
| ReportProgress(); |
| |
| if (framebuffer_manager_.get()) { |
| framebuffer_manager_->Destroy(have_context); |
| if (group_->texture_manager()) |
| group_->texture_manager()->RemoveFramebufferManager( |
| framebuffer_manager_.get()); |
| framebuffer_manager_.reset(); |
| } |
| |
| if (query_manager_.get()) { |
| query_manager_->Destroy(have_context); |
| query_manager_.reset(); |
| } |
| |
| if (gpu_fence_manager_.get()) { |
| gpu_fence_manager_->Destroy(have_context); |
| gpu_fence_manager_.reset(); |
| } |
| |
| if (vertex_array_manager_ .get()) { |
| vertex_array_manager_->Destroy(have_context); |
| vertex_array_manager_.reset(); |
| } |
| |
| if (transform_feedback_manager_.get()) { |
| if (!have_context) { |
| transform_feedback_manager_->MarkContextLost(); |
| } |
| transform_feedback_manager_->Destroy(); |
| transform_feedback_manager_.reset(); |
| } |
| |
| ReportProgress(); |
| |
| 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(); |
| |
| // Release all fences now, because some fence types need the context to be |
| // current on destruction. |
| pending_readpixel_fences_ = base::queue<FenceCallback>(); |
| |
| // Need to release these before releasing |group_| which may own the |
| // ShaderTranslatorCache. |
| DestroyShaderTranslator(); |
| |
| ReportProgress(); |
| |
| // 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()) { |
| group_->Destroy(this, have_context); |
| group_ = nullptr; |
| } |
| |
| if (context_.get()) { |
| context_->ReleaseCurrent(nullptr); |
| context_ = nullptr; |
| } |
| surface_ = nullptr; |
| } |
| |
| void GLES2DecoderImpl::SetSurface(const scoped_refptr<gl::GLSurface>& surface) { |
| DCHECK(context_->IsCurrent(nullptr)); |
| DCHECK(surface); |
| surface_ = surface; |
| RestoreCurrentFramebufferBindings(); |
| } |
| |
| void GLES2DecoderImpl::ReleaseSurface() { |
| if (!context_.get()) |
| return; |
| if (WasContextLost()) { |
| DLOG(ERROR) << " GLES2DecoderImpl: Trying to release lost context."; |
| return; |
| } |
| context_->ReleaseCurrent(surface_.get()); |
| surface_ = nullptr; |
| } |
| |
| void GLES2DecoderImpl::TakeFrontBuffer(const Mailbox& mailbox) { |
| if (offscreen_single_buffer_) { |
| mailbox_manager()->ProduceTexture( |
| mailbox, offscreen_target_color_texture_->texture_ref()->texture()); |
| return; |
| } |
| |
| if (!offscreen_saved_color_texture_.get()) { |
| DLOG(ERROR) << "Called TakeFrontBuffer on a non-offscreen context"; |
| return; |
| } |
| |
| mailbox_manager()->ProduceTexture( |
| mailbox, offscreen_saved_color_texture_->texture_ref()->texture()); |
| |
| // Save the BackTexture and TextureRef. There's no need to update |
| // |offscreen_saved_frame_buffer_| since CreateBackTexture() will take care of |
| // that. |
| SavedBackTexture save; |
| save.back_texture.swap(offscreen_saved_color_texture_); |
| save.in_use = true; |
| saved_back_textures_.push_back(std::move(save)); |
| |
| CreateBackTexture(); |
| } |
| |
| void GLES2DecoderImpl::ReturnFrontBuffer(const Mailbox& mailbox, bool is_lost) { |
| TextureBase* texture = mailbox_manager()->ConsumeTexture(mailbox); |
| mailbox_manager()->TextureDeleted(texture); |
| |
| if (offscreen_single_buffer_) |
| return; |
| |
| for (auto it = saved_back_textures_.begin(); it != saved_back_textures_.end(); |
| ++it) { |
| if (texture != it->back_texture->texture_ref()->texture()) |
| continue; |
| |
| if (is_lost || it->back_texture->size() != offscreen_size_) { |
| if (is_lost) |
| it->back_texture->Invalidate(); |
| else |
| it->back_texture->Destroy(); |
| saved_back_textures_.erase(it); |
| return; |
| } |
| |
| it->in_use = false; |
| return; |
| } |
| |
| DLOG(ERROR) << "Attempting to return a frontbuffer that was not saved."; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCreateGpuFenceINTERNAL( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::CreateGpuFenceINTERNAL& c = |
| *static_cast<const volatile gles2::cmds::CreateGpuFenceINTERNAL*>( |
| cmd_data); |
| if (!features().chromium_gpu_fence) { |
| return error::kUnknownCommand; |
| } |
| GLuint gpu_fence_id = static_cast<GLuint>(c.gpu_fence_id); |
| if (!GetGpuFenceManager()->CreateGpuFence(gpu_fence_id)) |
| return error::kInvalidArguments; |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleWaitGpuFenceCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::WaitGpuFenceCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::WaitGpuFenceCHROMIUM*>(cmd_data); |
| if (!features().chromium_gpu_fence) { |
| return error::kUnknownCommand; |
| } |
| GLuint gpu_fence_id = static_cast<GLuint>(c.gpu_fence_id); |
| if (!GetGpuFenceManager()->GpuFenceServerWait(gpu_fence_id)) |
| return error::kInvalidArguments; |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleDestroyGpuFenceCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::DestroyGpuFenceCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::DestroyGpuFenceCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_gpu_fence) { |
| return error::kUnknownCommand; |
| } |
| GLuint gpu_fence_id = static_cast<GLuint>(c.gpu_fence_id); |
| if (!GetGpuFenceManager()->RemoveGpuFence(gpu_fence_id)) |
| return error::kInvalidArguments; |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderImpl::CreateBackTexture() { |
| for (auto it = saved_back_textures_.begin(); it != saved_back_textures_.end(); |
| ++it) { |
| if (it->in_use) |
| continue; |
| |
| if (it->back_texture->size() != offscreen_size_) |
| continue; |
| offscreen_saved_color_texture_ = std::move(it->back_texture); |
| offscreen_saved_frame_buffer_->AttachRenderTexture( |
| offscreen_saved_color_texture_.get()); |
| saved_back_textures_.erase(it); |
| return; |
| } |
| |
| ++create_back_texture_count_for_test_; |
| offscreen_saved_color_texture_.reset(new BackTexture(this)); |
| offscreen_saved_color_texture_->Create(); |
| offscreen_saved_color_texture_->AllocateStorage( |
| offscreen_size_, offscreen_saved_color_format_, false); |
| offscreen_saved_frame_buffer_->AttachRenderTexture( |
| offscreen_saved_color_texture_.get()); |
| } |
| |
| void GLES2DecoderImpl::ReleaseNotInUseBackTextures() { |
| for (auto& saved_back_texture : saved_back_textures_) { |
| if (!saved_back_texture.in_use) |
| saved_back_texture.back_texture->Destroy(); |
| } |
| |
| base::EraseIf(saved_back_textures_, |
| [](const SavedBackTexture& saved_back_texture) { |
| return !saved_back_texture.in_use; |
| }); |
| } |
| |
| void GLES2DecoderImpl::ReleaseAllBackTextures(bool have_context) { |
| for (auto& saved_back_texture : saved_back_textures_) { |
| if (have_context) |
| saved_back_texture.back_texture->Destroy(); |
| else |
| saved_back_texture.back_texture->Invalidate(); |
| } |
| saved_back_textures_.clear(); |
| } |
| |
| size_t GLES2DecoderImpl::GetSavedBackTextureCountForTest() { |
| return saved_back_textures_.size(); |
| } |
| |
| size_t GLES2DecoderImpl::GetCreatedBackTextureCountForTest() { |
| return create_back_texture_count_for_test_; |
| } |
| |
| 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 || w > max_offscreen_framebuffer_size_ || |
| h > max_offscreen_framebuffer_size_) { |
| 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( |
| 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( |
| 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( |
| 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()); |
| api()->glClearColorFn(0, 0, 0, BackBufferAlphaClearColor()); |
| state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| api()->glClearStencilFn(0); |
| state_.SetDeviceStencilMaskSeparate(GL_FRONT, kDefaultStencilMask); |
| state_.SetDeviceStencilMaskSeparate(GL_BACK, kDefaultStencilMask); |
| api()->glClearDepthFn(0); |
| state_.SetDeviceDepthMask(GL_TRUE); |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| ClearDeviceWindowRectangles(); |
| api()->glClearFn(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::ResizeCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::ResizeCHROMIUM*>(cmd_data); |
| if (!offscreen_target_frame_buffer_.get() && surface_->DeferDraws()) |
| return error::kDeferCommandUntilLater; |
| |
| GLuint width = static_cast<GLuint>(c.width); |
| GLuint height = static_cast<GLuint>(c.height); |
| GLfloat scale_factor = c.scale_factor; |
| GLenum color_space = c.color_space; |
| GLboolean has_alpha = c.alpha; |
| TRACE_EVENT2("gpu", "glResizeChromium", "width", width, "height", height); |
| |
| // gfx::Size uses integers, make sure width and height do not overflow |
| static_assert(sizeof(GLuint) >= sizeof(int), "Unexpected GLuint size."); |
| static const GLuint kMaxDimension = |
| static_cast<GLuint>(std::numeric_limits<int>::max()); |
| width = std::min(std::max(1U, width), kMaxDimension); |
| height = std::min(std::max(1U, height), kMaxDimension); |
| |
| gl::GLSurface::ColorSpace surface_color_space = |
| gl::GLSurface::ColorSpace::UNSPECIFIED; |
| switch (color_space) { |
| case GL_COLOR_SPACE_UNSPECIFIED_CHROMIUM: |
| surface_color_space = gl::GLSurface::ColorSpace::UNSPECIFIED; |
| break; |
| case GL_COLOR_SPACE_SCRGB_LINEAR_CHROMIUM: |
| surface_color_space = gl::GLSurface::ColorSpace::SCRGB_LINEAR; |
| break; |
| case GL_COLOR_SPACE_SRGB_CHROMIUM: |
| surface_color_space = gl::GLSurface::ColorSpace::SRGB; |
| break; |
| case GL_COLOR_SPACE_DISPLAY_P3_CHROMIUM: |
| surface_color_space = gl::GLSurface::ColorSpace::DISPLAY_P3; |
| break; |
| default: |
| LOG(ERROR) << "GLES2DecoderImpl: Context lost because specified color" |
| << "space was invalid."; |
| return error::kLostContext; |
| } |
| 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; |
| } |
| } else { |
| if (!surface_->Resize(gfx::Size(width, height), scale_factor, |
| surface_color_space, !!has_alpha)) { |
| LOG(ERROR) << "GLES2DecoderImpl: Context lost because resize failed."; |
| return error::kLostContext; |
| } |
| 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; |
| } |
| if (surface_->BuffersFlipped()) { |
| backbuffer_needs_clear_bits_ |= GL_COLOR_BUFFER_BIT; |
| } |
| } |
| |
| swaps_since_resize_ = 0; |
| |
| return error::kNoError; |
| } |
| |
| const char* GLES2DecoderImpl::GetCommandName(unsigned int command_id) const { |
| if (command_id >= kFirstGLES2Command && command_id < kNumCommands) { |
| return gles2::GetCommandName(static_cast<CommandId>(command_id)); |
| } |
| return GetCommonCommandName(static_cast<cmd::CommandId>(command_id)); |
| } |
| |
| // 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 <bool DebugImpl> |
| error::Error GLES2DecoderImpl::DoCommandsImpl(unsigned int num_commands, |
| const volatile void* buffer, |
| int num_entries, |
| int* entries_processed) { |
| DCHECK(entries_processed); |
| commands_to_process_ = num_commands; |
| error::Error result = error::kNoError; |
| const volatile CommandBufferEntry* cmd_data = |
| static_cast<const volatile CommandBufferEntry*>(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<int>(size) + process_pos > num_entries) { |
| result = error::kOutOfBounds; |
| break; |
| } |
| |
| if (DebugImpl && log_commands()) { |
| LOG(ERROR) << "[" << logger_.GetLogPrefix() << "]" |
| << "cmd: " << GetCommandName(command); |
| } |
| |
| const unsigned int arg_count = size - 1; |
| unsigned int command_index = command - kFirstGLES2Command; |
| if (command_index < arraysize(command_info)) { |
| const CommandInfo& info = command_info[command_index]; |
| unsigned int info_arg_count = static_cast<unsigned int>(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_t 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() && !WasContextLost()) { |
| GLenum error; |
| while ((error = api()->glGetErrorFn()) != 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 (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 defined(OS_MACOSX) |
| // Aggressively call glFlush on macOS. This is the only fix that has been |
| // found so far to avoid crashes on Intel drivers. The workaround |
| // isn't needed for WebGL contexts, though. |
| // https://crbug.com/863817 |
| if (!feature_info_->IsWebGLContext()) |
| context_->FlushForDriverCrashWorkaround(); |
| #endif |
| |
| *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 volatile void* buffer, |
| int num_entries, |
| int* entries_processed) { |
| if (gpu_debug_commands_) { |
| return DoCommandsImpl<true>( |
| num_commands, buffer, num_entries, entries_processed); |
| } else { |
| return DoCommandsImpl<false>( |
| num_commands, buffer, num_entries, entries_processed); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoFinish() { |
| api()->glFinishFn(); |
| ProcessPendingReadPixels(true); |
| ProcessPendingQueries(true); |
| } |
| |
| void GLES2DecoderImpl::DoFlush() { |
| api()->glFlushFn(); |
| 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; |
| api()->glActiveTextureFn(texture_unit); |
| } |
| |
| void GLES2DecoderImpl::DoBindBuffer(GLenum target, GLuint client_id) { |
| Buffer* buffer = nullptr; |
| 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. |
| api()->glGenBuffersARBFn(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); |
| api()->glBindBufferFn(target, service_id); |
| } |
| |
| void GLES2DecoderImpl::BindIndexedBufferImpl( |
| GLenum target, GLuint index, GLuint client_id, |
| GLintptr offset, GLsizeiptr size, |
| BindIndexedBufferFunctionType function_type, const char* function_name) { |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER: { |
| if (index >= group_->max_transform_feedback_separate_attribs()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "index out of range"); |
| return; |
| } |
| DCHECK(state_.bound_transform_feedback.get()); |
| if (state_.bound_transform_feedback->active()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "bound transform feedback is active"); |
| return; |
| } |
| break; |
| } |
| case GL_UNIFORM_BUFFER: { |
| if (index >= group_->max_uniform_buffer_bindings()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "index out of range"); |
| return; |
| } |
| break; |
| } |
| default: |
| NOTREACHED(); |
| break; |
| } |
| |
| if (function_type == kBindBufferRange) { |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| if ((size % 4 != 0) || (offset % 4 != 0)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "size or offset are not multiples of 4"); |
| return; |
| } |
| break; |
| case GL_UNIFORM_BUFFER: { |
| if (offset % group_->uniform_buffer_offset_alignment() != 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "offset is not a multiple of UNIFORM_BUFFER_OFFSET_ALIGNMENT"); |
| return; |
| } |
| break; |
| } |
| default: |
| NOTREACHED(); |
| break; |
| } |
| |
| if (client_id != 0) { |
| if (size <= 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "size <= 0"); |
| return; |
| } |
| if (offset < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "offset < 0"); |
| return; |
| } |
| } |
| } |
| |
| Buffer* buffer = nullptr; |
| 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, function_name, |
| "id not generated by glGenBuffers"); |
| return; |
| } |
| |
| // It's a new id so make a buffer for it. |
| api()->glGenBuffersARBFn(1, &service_id); |
| CreateBuffer(client_id, service_id); |
| buffer = GetBuffer(client_id); |
| DCHECK(buffer); |
| } |
| if (!buffer_manager()->SetTarget(buffer, target)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "buffer bound to more than 1 target"); |
| return; |
| } |
| service_id = buffer->service_id(); |
| } |
| LogClientServiceForInfo(buffer, client_id, function_name); |
| |
| scoped_refptr<IndexedBufferBindingHost> bindings; |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| bindings = state_.bound_transform_feedback.get(); |
| break; |
| case GL_UNIFORM_BUFFER: |
| bindings = state_.indexed_uniform_buffer_bindings.get(); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| DCHECK(bindings); |
| switch (function_type) { |
| case kBindBufferBase: |
| bindings->DoBindBufferBase(index, buffer); |
| break; |
| case kBindBufferRange: |
| bindings->DoBindBufferRange(index, buffer, offset, size); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| state_.SetBoundBuffer(target, buffer); |
| } |
| |
| void GLES2DecoderImpl::DoBindBufferBase(GLenum target, GLuint index, |
| GLuint client_id) { |
| BindIndexedBufferImpl(target, index, client_id, 0, 0, |
| kBindBufferBase, "glBindBufferBase"); |
| } |
| |
| void GLES2DecoderImpl::DoBindBufferRange(GLenum target, GLuint index, |
| GLuint client_id, |
| GLintptr offset, |
| GLsizeiptr size) { |
| BindIndexedBufferImpl(target, index, client_id, offset, size, |
| kBindBufferRange, "glBindBufferRange"); |
| } |
| |
| bool GLES2DecoderImpl::BoundFramebufferAllowsChangesToAlphaChannel() { |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (framebuffer) |
| return framebuffer->HasAlphaMRT(); |
| if (back_buffer_draw_buffer_ == GL_NONE) |
| return false; |
| if (offscreen_target_frame_buffer_.get()) { |
| GLenum format = offscreen_target_color_format_; |
| return (format == GL_RGBA || format == GL_RGBA8) && |
| offscreen_buffer_should_have_alpha_; |
| } |
| return (back_buffer_color_format_ == GL_RGBA || |
| back_buffer_color_format_ == GL_RGBA8); |
| } |
| |
| bool GLES2DecoderImpl::BoundFramebufferHasDepthAttachment() { |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| 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 = GetBoundDrawFramebuffer(); |
| 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 allows_alpha_change = BoundFramebufferAllowsChangesToAlphaChannel(); |
| state_.SetDeviceColorMask(state_.color_mask_red, state_.color_mask_green, |
| state_.color_mask_blue, |
| state_.color_mask_alpha && allows_alpha_change); |
| |
| 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::RestoreBufferBinding(unsigned int target) { |
| if (target == GL_PIXEL_PACK_BUFFER) { |
| state_.UpdatePackParameters(); |
| } else if (target == GL_PIXEL_UNPACK_BUFFER) { |
| state_.UpdateUnpackParameters(); |
| } |
| Buffer* bound_buffer = |
| buffer_manager()->GetBufferInfoForTarget(&state_, target); |
| api()->glBindBufferFn(target, bound_buffer ? bound_buffer->service_id() : 0); |
| } |
| |
| void GLES2DecoderImpl::RestoreFramebufferBindings() const { |
| GLuint service_id = |
| framebuffer_state_.bound_draw_framebuffer.get() |
| ? framebuffer_state_.bound_draw_framebuffer->service_id() |
| : GetBackbufferServiceId(); |
| if (!SupportsSeparateFramebufferBinds()) { |
| api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, service_id); |
| } else { |
| api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, service_id); |
| service_id = framebuffer_state_.bound_read_framebuffer.get() |
| ? framebuffer_state_.bound_read_framebuffer->service_id() |
| : GetBackbufferServiceId(); |
| api()->glBindFramebufferEXTFn(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(); |
| api()->glBindTextureFn(target, service_id); |
| api()->glTexParameteriFn(target, GL_TEXTURE_WRAP_S, texture->wrap_s()); |
| api()->glTexParameteriFn(target, GL_TEXTURE_WRAP_T, texture->wrap_t()); |
| api()->glTexParameteriFn(target, GL_TEXTURE_MIN_FILTER, |
| texture->min_filter()); |
| api()->glTexParameteriFn(target, GL_TEXTURE_MAG_FILTER, |
| texture->mag_filter()); |
| if (feature_info_->IsWebGL2OrES3Context()) { |
| api()->glTexParameteriFn(target, GL_TEXTURE_BASE_LEVEL, |
| texture->base_level()); |
| } |
| RestoreTextureUnitBindings(state_.active_texture_unit); |
| } |
| } |
| |
| void GLES2DecoderImpl::ClearDeviceWindowRectangles() const { |
| if (!feature_info_->feature_flags().ext_window_rectangles) { |
| return; |
| } |
| api()->glWindowRectanglesEXTFn(GL_EXCLUSIVE_EXT, 0, nullptr); |
| } |
| |
| void GLES2DecoderImpl::RestoreDeviceWindowRectangles() const { |
| state_.UpdateWindowRectangles(); |
| } |
| |
| 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) |
| api()->glBindVertexArrayOESFn(0); |
| |
| for (uint32_t i = 0; i < group_->max_vertex_attribs(); ++i) { |
| if (i != 0) // Never disable attribute 0 |
| state_.vertex_attrib_manager->SetDriverVertexAttribEnabled(i, false); |
| if (features().angle_instanced_arrays) |
| api()->glVertexAttribDivisorANGLEFn(i, 0); |
| } |
| } |
| |
| void GLES2DecoderImpl::RestoreAllAttributes() const { |
| state_.RestoreVertexAttribs(nullptr); |
| } |
| |
| void GLES2DecoderImpl::SetIgnoreCachedStateForTest(bool ignore) { |
| state_.SetIgnoreCachedStateForTest(ignore); |
| } |
| |
| void GLES2DecoderImpl::SetForceShaderNameHashingForTest(bool force) { |
| force_shader_name_hashing_for_test = force; |
| } |
| |
| // Added specifically for testing backbuffer_needs_clear_bits unittests. |
| uint32_t GLES2DecoderImpl::GetAndClearBackbufferClearBitsForTest() { |
| uint32_t clear_bits = backbuffer_needs_clear_bits_; |
| backbuffer_needs_clear_bits_ = 0; |
| return clear_bits; |
| } |
| |
| void GLES2DecoderImpl::OnFboChanged() const { |
| state_.fbo_binding_for_scissor_workaround_dirty = true; |
| state_.stencil_state_changed_since_validation = true; |
| |
| if (workarounds().flush_on_framebuffer_change) |
| api()->glFlushFn(); |
| } |
| |
| // Called after the FBO is checked for completeness. |
| void GLES2DecoderImpl::OnUseFramebuffer() const { |
| if (!state_.fbo_binding_for_scissor_workaround_dirty) |
| return; |
| state_.fbo_binding_for_scissor_workaround_dirty = false; |
| |
| if (supports_dc_layers_) { |
| gfx::Vector2d draw_offset = GetBoundFramebufferDrawOffset(); |
| api()->glViewportFn(state_.viewport_x + draw_offset.x(), |
| state_.viewport_y + draw_offset.y(), |
| state_.viewport_width, state_.viewport_height); |
| } |
| |
| if (workarounds().restore_scissor_on_fbo_change || supports_dc_layers_) { |
| // The driver forgets the correct scissor when modifying the FBO binding. |
| gfx::Vector2d scissor_offset = GetBoundFramebufferDrawOffset(); |
| api()->glScissorFn(state_.scissor_x + scissor_offset.x(), |
| state_.scissor_y + scissor_offset.y(), |
| state_.scissor_width, state_.scissor_height); |
| } |
| |
| if (workarounds().restore_scissor_on_fbo_change) { |
| // crbug.com/222018 - Also on QualComm, the flush here avoids flicker, |
| // it's unclear how this bug works. |
| api()->glFlushFn(); |
| } |
| |
| if (workarounds().force_update_scissor_state_when_binding_fbo0 && |
| GetBoundDrawFramebufferServiceId() == 0) { |
| // The theory is that FBO0 keeps some internal (in HW regs maybe?) scissor |
| // test state, but the driver forgets to update it with GL_SCISSOR_TEST |
| // when FBO0 gets bound. (So it stuck with whatever state we last switched |
| // from it.) |
| // If the internal scissor test state was enabled, it does update its |
| // internal scissor rect with GL_SCISSOR_BOX though. |
| if (state_.enable_flags.cached_scissor_test) { |
| // The driver early outs if the new state matches previous state so some |
| // shake up is needed. |
| api()->glDisableFn(GL_SCISSOR_TEST); |
| api()->glEnableFn(GL_SCISSOR_TEST); |
| } else { |
| // Ditto. |
| api()->glEnableFn(GL_SCISSOR_TEST); |
| api()->glDisableFn(GL_SCISSOR_TEST); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DoBindFramebuffer(GLenum target, GLuint client_id) { |
| Framebuffer* framebuffer = nullptr; |
| 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. |
| api()->glGenFramebuffersEXTFn(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; |
| state_.UpdateWindowRectanglesForBoundDrawFramebufferClientID(client_id); |
| } |
| |
| // 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 == nullptr) { |
| service_id = GetBackbufferServiceId(); |
| } |
| |
| api()->glBindFramebufferEXTFn(target, service_id); |
| OnFboChanged(); |
| } |
| |
| void GLES2DecoderImpl::DoBindRenderbuffer(GLenum target, GLuint client_id) { |
| DCHECK_EQ(target, (GLenum)GL_RENDERBUFFER); |
| Renderbuffer* renderbuffer = nullptr; |
| 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. |
| api()->glGenRenderbuffersEXTFn(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; |
| api()->glBindRenderbufferEXTFn(GL_RENDERBUFFER, service_id); |
| } |
| |
| void GLES2DecoderImpl::DoBindTexture(GLenum target, GLuint client_id) { |
| TextureRef* texture_ref = nullptr; |
| 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. |
| api()->glGenTexturesFn(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"); |
| api()->glBindTextureFn(target, texture->service_id()); |
| if (texture->target() == 0) { |
| texture_manager()->SetTarget(texture_ref, target); |
| if (!gl_version_info().BehavesLikeGLES() && |
| gl_version_info().IsAtLeastGL(3, 2)) { |
| // In Desktop GL core profile and GL ES, depth textures are always |
| // sampled to the RED channel, whereas on Desktop GL compatibility |
| // proifle, they are sampled to RED, LUMINANCE, INTENSITY, or ALPHA |
| // channel, depending on the DEPTH_TEXTURE_MODE value. |
| // In theory we only need to apply this for depth textures, but it is |
| // simpler to apply to all textures. |
| api()->glTexParameteriFn(target, GL_DEPTH_TEXTURE_MODE, GL_RED); |
| } |
| } |
| } else { |
| api()->glBindTextureFn(target, 0); |
| } |
| |
| TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; |
| unit.bind_target = target; |
| unit.SetInfoForTarget(target, texture_ref); |
| } |
| |
| void GLES2DecoderImpl::DoBindSampler(GLuint unit, GLuint client_id) { |
| if (unit >= group_->max_texture_units()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glBindSampler", "unit out of bounds"); |
| return; |
| } |
| Sampler* sampler = nullptr; |
| if (client_id != 0) { |
| sampler = GetSampler(client_id); |
| if (!sampler) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "glBindSampler", |
| "id not generated by glGenSamplers"); |
| return; |
| } |
| } |
| |
| // Check the sampler exists |
| if (sampler) { |
| LogClientServiceForInfo(sampler, client_id, "glBindSampler"); |
| api()->glBindSamplerFn(unit, sampler->service_id()); |
| } else { |
| api()->glBindSamplerFn(unit, 0); |
| } |
| |
| state_.sampler_units[unit] = sampler; |
| } |
| |
| void GLES2DecoderImpl::DoBindTransformFeedback( |
| GLenum target, GLuint client_id) { |
| const char* function_name = "glBindTransformFeedback"; |
| |
| TransformFeedback* transform_feedback = nullptr; |
| if (client_id != 0) { |
| transform_feedback = GetTransformFeedback(client_id); |
| if (!transform_feedback) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "id not generated by glGenTransformFeedbacks"); |
| return; |
| } |
| } else { |
| transform_feedback = state_.default_transform_feedback.get(); |
| } |
| DCHECK(transform_feedback); |
| if (transform_feedback == state_.bound_transform_feedback.get()) |
| return; |
| if (state_.bound_transform_feedback->active() && |
| !state_.bound_transform_feedback->paused()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "currently bound transform feedback is active"); |
| return; |
| } |
| LogClientServiceForInfo(transform_feedback, client_id, function_name); |
| transform_feedback->DoBindTransformFeedback( |
| target, state_.bound_transform_feedback.get(), |
| state_.bound_transform_feedback_buffer.get()); |
| state_.bound_transform_feedback = transform_feedback; |
| } |
| |
| void GLES2DecoderImpl::DoBeginTransformFeedback(GLenum primitive_mode) { |
| const char* function_name = "glBeginTransformFeedback"; |
| TransformFeedback* transform_feedback = state_.bound_transform_feedback.get(); |
| DCHECK(transform_feedback); |
| if (transform_feedback->active()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "transform feedback is already active"); |
| return; |
| } |
| if (!CheckCurrentProgram(function_name)) { |
| return; |
| } |
| Program* program = state_.current_program.get(); |
| DCHECK(program); |
| size_t required_buffer_count = |
| program->effective_transform_feedback_varyings().size(); |
| if (required_buffer_count == 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "no active transform feedback varyings"); |
| return; |
| } |
| if (required_buffer_count > 1 && |
| GL_INTERLEAVED_ATTRIBS == |
| program->effective_transform_feedback_buffer_mode()) { |
| required_buffer_count = 1; |
| } |
| for (size_t ii = 0; ii < required_buffer_count; ++ii) { |
| Buffer* buffer = transform_feedback->GetBufferBinding(ii); |
| if (!buffer) { |
| std::string msg = base::StringPrintf("missing buffer bound at index %i", |
| static_cast<int>(ii)); |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, msg.c_str()); |
| return; |
| } |
| if (buffer->GetMappedRange()) { |
| std::string msg = base::StringPrintf( |
| "bound buffer bound at index %i is mapped", static_cast<int>(ii)); |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, msg.c_str()); |
| return; |
| } |
| } |
| transform_feedback->DoBeginTransformFeedback(primitive_mode); |
| DCHECK(transform_feedback->active()); |
| } |
| |
| void GLES2DecoderImpl::DoEndTransformFeedback() { |
| const char* function_name = "glEndTransformFeedback"; |
| DCHECK(state_.bound_transform_feedback.get()); |
| if (!state_.bound_transform_feedback->active()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "transform feedback is not active"); |
| return; |
| } |
| // TODO(zmo): Validate binding points. |
| state_.bound_transform_feedback->DoEndTransformFeedback(); |
| } |
| |
| void GLES2DecoderImpl::DoPauseTransformFeedback() { |
| DCHECK(state_.bound_transform_feedback.get()); |
| if (!state_.bound_transform_feedback->active() || |
| state_.bound_transform_feedback->paused()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glPauseTransformFeedback", |
| "transform feedback is not active or already paused"); |
| return; |
| } |
| state_.bound_transform_feedback->DoPauseTransformFeedback(); |
| } |
| |
| void GLES2DecoderImpl::DoResumeTransformFeedback() { |
| DCHECK(state_.bound_transform_feedback.get()); |
| if (!state_.bound_transform_feedback->active() || |
| !state_.bound_transform_feedback->paused()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glResumeTransformFeedback", |
| "transform feedback is not active or not paused"); |
| return; |
| } |
| if (workarounds().rebind_transform_feedback_before_resume) { |
| api()->glBindTransformFeedbackFn(GL_TRANSFORM_FEEDBACK, 0); |
| api()->glBindTransformFeedbackFn( |
| GL_TRANSFORM_FEEDBACK, state_.bound_transform_feedback->service_id()); |
| } |
| state_.bound_transform_feedback->DoResumeTransformFeedback(); |
| } |
| |
| void GLES2DecoderImpl::DoDisableVertexAttribArray(GLuint index) { |
| if (state_.vertex_attrib_manager->Enable(index, false)) { |
| if (index != 0 || gl_version_info().BehavesLikeGLES()) { |
| state_.vertex_attrib_manager->SetDriverVertexAttribEnabled(index, false); |
| } |
| } else { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, |
| "glDisableVertexAttribArray", "index out of range"); |
| } |
| } |
| |
| void GLES2DecoderImpl::InvalidateFramebufferImpl( |
| GLenum target, |
| GLsizei count, |
| const volatile GLenum* attachments, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| const char* function_name, |
| FramebufferOperation op) { |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); |
| |
| // Because of performance issues, no-op if the format of the attachment is |
| // DEPTH_STENCIL and only one part is intended to be invalidated. |
| bool has_depth_stencil_format = framebuffer && |
| framebuffer->HasDepthStencilFormatAttachment(); |
| bool invalidate_depth = false; |
| bool invalidate_stencil = false; |
| std::unique_ptr<GLenum[]> validated_attachments(new GLenum[count+1]); |
| GLsizei validated_count = 0; |
| |
| // Validates the attachments. If one of them fails, the whole command fails. |
| GLenum thresh0 = GL_COLOR_ATTACHMENT0 + group_->max_color_attachments(); |
| GLenum thresh1 = GL_COLOR_ATTACHMENT15; |
| for (GLsizei i = 0; i < count; ++i) { |
| GLenum attachment = attachments[i]; |
| if (framebuffer) { |
| if (attachment >= thresh0 && attachment <= thresh1) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, function_name, "invalid attachment"); |
| return; |
| } |
| if (!validators_->attachment.IsValid(attachment)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, attachment, |
| "attachments"); |
| return; |
| } |
| if (has_depth_stencil_format) { |
| switch (attachment) { |
| case GL_DEPTH_ATTACHMENT: |
| invalidate_depth = true; |
| continue; |
| case GL_STENCIL_ATTACHMENT: |
| invalidate_stencil = true; |
| continue; |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| invalidate_depth = true; |
| invalidate_stencil = true; |
| continue; |
| } |
| } |
| } else { |
| if (!validators_->backbuffer_attachment.IsValid(attachment)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, attachment, |
| "attachments"); |
| return; |
| } |
| } |
| validated_attachments[validated_count++] = attachment; |
| } |
| if (invalidate_depth && invalidate_stencil) { |
| // We do not use GL_DEPTH_STENCIL_ATTACHMENT here because |
| // it is not a valid token for glDiscardFramebufferEXT. |
| validated_attachments[validated_count++] = GL_DEPTH_ATTACHMENT; |
| validated_attachments[validated_count++] = GL_STENCIL_ATTACHMENT; |
| } |
| |
| // 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. |
| std::unique_ptr<GLenum[]> translated_attachments(new GLenum[validated_count]); |
| for (GLsizei i = 0; i < validated_count; ++i) { |
| GLenum attachment = validated_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; |
| } |
| |
| bool dirty = false; |
| switch (op) { |
| case kFramebufferDiscard: |
| if (gl_version_info().is_es3) { |
| api()->glInvalidateFramebufferFn(target, validated_count, |
| translated_attachments.get()); |
| } else { |
| api()->glDiscardFramebufferEXTFn(target, validated_count, |
| translated_attachments.get()); |
| } |
| dirty = true; |
| break; |
| case kFramebufferInvalidate: |
| if (gl_version_info().IsLowerThanGL(4, 3)) { |
| // no-op since the function isn't supported. |
| } else { |
| api()->glInvalidateFramebufferFn(target, validated_count, |
| translated_attachments.get()); |
| dirty = true; |
| } |
| break; |
| case kFramebufferInvalidateSub: |
| // Make it an no-op because we don't have a mechanism to mark partial |
| // pixels uncleared yet. |
| // TODO(zmo): Revisit this. |
| break; |
| } |
| |
| if (!dirty) |
| return; |
| |
| // Marks each one of them as not cleared. |
| for (GLsizei i = 0; i < validated_count; ++i) { |
| if (framebuffer) { |
| if (validated_attachments[i] == GL_DEPTH_STENCIL_ATTACHMENT) { |
| framebuffer->MarkAttachmentAsCleared(renderbuffer_manager(), |
| texture_manager(), |
| GL_DEPTH_ATTACHMENT, |
| false); |
| framebuffer->MarkAttachmentAsCleared(renderbuffer_manager(), |
| texture_manager(), |
| GL_STENCIL_ATTACHMENT, |
| false); |
| } else { |
| framebuffer->MarkAttachmentAsCleared(renderbuffer_manager(), |
| texture_manager(), |
| validated_attachments[i], |
| false); |
| } |
| } else { |
| switch (validated_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; |
| break; |
| case GL_STENCIL_EXT: |
| backbuffer_needs_clear_bits_ |= GL_STENCIL_BUFFER_BIT; |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DoDiscardFramebufferEXT( |
| GLenum target, |
| GLsizei count, |
| const volatile GLenum* attachments) { |
| if (workarounds().disable_discard_framebuffer) |
| return; |
| |
| const GLsizei kWidthNotUsed = 1; |
| const GLsizei kHeightNotUsed = 1; |
| InvalidateFramebufferImpl( |
| target, count, attachments, 0, 0, kWidthNotUsed, kHeightNotUsed, |
| "glDiscardFramebufferEXT", kFramebufferDiscard); |
| } |
| |
| void GLES2DecoderImpl::DoInvalidateFramebuffer( |
| GLenum target, |
| GLsizei count, |
| const volatile GLenum* attachments) { |
| const GLsizei kWidthNotUsed = 1; |
| const GLsizei kHeightNotUsed = 1; |
| InvalidateFramebufferImpl( |
| target, count, attachments, 0, 0, kWidthNotUsed, kHeightNotUsed, |
| "glInvalidateFramebuffer", kFramebufferInvalidate); |
| } |
| |
| void GLES2DecoderImpl::DoInvalidateSubFramebuffer( |
| GLenum target, |
| GLsizei count, |
| const volatile GLenum* attachments, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height) { |
| InvalidateFramebufferImpl( |
| target, count, attachments, x, y, width, height, |
| "glInvalidateSubFramebuffer", kFramebufferInvalidateSub); |
| } |
| |
| void GLES2DecoderImpl::DoEnableVertexAttribArray(GLuint index) { |
| if (state_.vertex_attrib_manager->Enable(index, true)) { |
| state_.vertex_attrib_manager->SetDriverVertexAttribEnabled(index, true); |
| } 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; |
| } |
| Texture* tex = texture_ref->texture(); |
| GLint base_level = tex->base_level(); |
| |
| 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, |
| base_level)) { |
| LOCAL_SET_GL_ERROR( |
| GL_OUT_OF_MEMORY, "glGenerateMipmap", "dimensions too big"); |
| return; |
| } |
| } |
| } else { |
| if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, |
| base_level)) { |
| 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. If the base level is non-zero but the zero |
| // level of a texture has not been set glGenerateMipmaps sets the entire mip |
| // chain to opaque black. If the zero level is set at all, however, the mip |
| // chain is properly generated from the base level. |
| bool texture_zero_level_set = false; |
| GLenum type = 0; |
| GLenum internal_format = 0; |
| GLenum format = 0; |
| if (workarounds().set_zero_level_before_generating_mipmap && |
| target == GL_TEXTURE_2D) { |
| if (base_level != 0 && |
| !tex->GetLevelType(target, 0, &type, &internal_format) && |
| tex->GetLevelType(target, tex->base_level(), &type, &internal_format)) { |
| format = TextureManager::ExtractFormatFromStorageFormat(internal_format); |
| ScopedPixelUnpackState reset_restore(&state_); |
| api()->glTexImage2DFn(target, 0, internal_format, 1, 1, 0, format, type, |
| nullptr); |
| texture_zero_level_set = true; |
| } |
| } |
| |
| bool enable_srgb = 0; |
| if (target == GL_TEXTURE_2D) { |
| tex->GetLevelType(target, tex->base_level(), &type, &internal_format); |
| enable_srgb = GLES2Util::GetColorEncodingFromInternalFormat( |
| internal_format) == GL_SRGB; |
| } |
| if (enable_srgb && feature_info_->feature_flags().desktop_srgb_support) { |
| state_.EnableDisableFramebufferSRGB(enable_srgb); |
| } |
| if (enable_srgb && workarounds().decode_encode_srgb_for_generatemipmap) { |
| if (target == GL_TEXTURE_2D) { |
| if (!InitializeSRGBConverter("generateMipmap")) { |
| return; |
| } |
| srgb_converter_->GenerateMipmap(this, tex, target); |
| } else { |
| // TODO(yizhou): If the target is GL_TEXTURE_3D ,GL_TEXTURE_2D_ARRAY, |
| // GL_TEXTURE_CUBE_MAP, |
| // this change can not generate correct mipmap. |
| api()->glGenerateMipmapEXTFn(target); |
| } |
| } else { |
| api()->glGenerateMipmapEXTFn(target); |
| } |
| |
| if (texture_zero_level_set) { |
| // This may have some unwanted side effects, but we expect command buffer |
| // validation to prevent you from doing anything weird with the texture |
| // after this, like calling texSubImage2D sucessfully. |
| ScopedPixelUnpackState reset_restore(&state_); |
| api()->glTexImage2DFn(target, 0, internal_format, 0, 0, 0, format, type, |
| nullptr); |
| } |
| |
| 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); |
| switch (pname) { |
| case GL_IMPLEMENTATION_COLOR_READ_FORMAT: |
| case GL_IMPLEMENTATION_COLOR_READ_TYPE: |
| *num_written = 1; |
| { |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| if (framebuffer && |
| framebuffer->IsPossiblyComplete(feature_info_.get()) != |
| GL_FRAMEBUFFER_COMPLETE) { |
| // Here we avoid querying the driver framebuffer status because the |
| // above should cover most cases. This is an effort to reduce crashes |
| // on MacOSX. See crbug.com/662802. |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glGetIntegerv", "incomplete framebuffer"); |
| if (params) { |
| *params = 0; |
| } |
| return true; |
| } |
| } |
| if (params) { |
| if (feature_info_->gl_version_info().is_es) { |
| api()->glGetIntegervFn(pname, params); |
| } else { |
| // On Desktop GL where these two enums can be queried, instead of |
| // returning the second pair of read format/type, the preferred pair |
| // is returned. So this semantic is different from GL ES. |
| if (pname == GL_IMPLEMENTATION_COLOR_READ_FORMAT) { |
| *params = GLES2Util::GetGLReadPixelsImplementationFormat( |
| GetBoundReadFramebufferInternalFormat(), |
| GetBoundReadFramebufferTextureType(), |
| feature_info_->feature_flags().ext_read_format_bgra); |
| } else { |
| *params = GLES2Util::GetGLReadPixelsImplementationType( |
| GetBoundReadFramebufferInternalFormat(), |
| GetBoundReadFramebufferTextureType()); |
| } |
| } |
| if (*params == GL_HALF_FLOAT && feature_info_->IsWebGL1OrES2Context()) { |
| *params = GL_HALF_FLOAT_OES; |
| } |
| if (*params == GL_SRGB_ALPHA_EXT) { |
| *params = GL_RGBA; |
| } |
| if (*params == GL_SRGB_EXT) { |
| *params = GL_RGB; |
| } |
| } |
| return true; |
| default: |
| break; |
| } |
| |
| if (!gl_version_info().is_es) { |
| switch (pname) { |
| 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 (feature_info_->IsWebGL2OrES3Context()) { |
| switch (pname) { |
| case GL_MAX_VARYING_COMPONENTS: { |
| if (gl_version_info().is_es) { |
| // We can just delegate this query to the driver. |
| *num_written = 1; |
| break; |
| } |
| |
| // 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; |
| api()->glGetIntegervFn(GL_MAX_VARYING_VECTORS, &max_varying_vectors); |
| *num_written = 1; |
| if (params) { |
| *params = max_varying_vectors * 4; |
| } |
| return true; |
| } |
| case GL_READ_BUFFER: |
| *num_written = 1; |
| if (params) { |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| GLenum read_buffer; |
| if (framebuffer) { |
| read_buffer = framebuffer->read_buffer(); |
| } else { |
| read_buffer = back_buffer_read_buffer_; |
| } |
| *params = static_cast<GLint>(read_buffer); |
| } |
| return true; |
| case GL_TRANSFORM_FEEDBACK_ACTIVE: |
| *num_written = 1; |
| if (params) { |
| *params = |
| static_cast<GLint>(state_.bound_transform_feedback->active()); |
| } |
| return true; |
| case GL_TRANSFORM_FEEDBACK_PAUSED: |
| *num_written = 1; |
| if (params) { |
| *params = |
| static_cast<GLint>(state_.bound_transform_feedback->paused()); |
| } |
| return true; |
| case GL_WINDOW_RECTANGLE_EXT: |
| *num_written = 4; |
| // This is only used for glGetIntegeri_v and similar, so params will |
| // always be null - the only path here is through |
| // GetNumValuesReturnedForGLGet. |
| DCHECK(!params); |
| return true; |
| } |
| } |
| switch (pname) { |
| case GL_MAX_VIEWPORT_DIMS: |
| *num_written = 2; |
| if (offscreen_target_frame_buffer_.get()) { |
| if (params) { |
| params[0] = renderbuffer_manager()->max_renderbuffer_size(); |
| params[1] = renderbuffer_manager()->max_renderbuffer_size(); |
| } |
| return true; |
| } |
| break; |
| 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; |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (framebuffer) { |
| if (framebuffer->HasAlphaMRT() && |
| framebuffer->HasSameInternalFormatsMRT()) { |
| if (gl_version_info().is_desktop_core_profile) { |
| for (uint32_t i = 0; i < group_->max_draw_buffers(); i++) { |
| if (framebuffer->HasColorAttachment(i)) { |
| api()->glGetFramebufferAttachmentParameterivEXTFn( |
| GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, |
| GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, &v); |
| break; |
| } |
| } |
| } else { |
| api()->glGetIntegervFn(GL_ALPHA_BITS, &v); |
| } |
| } |
| } else { |
| v = (ClientExposedBackBufferHasAlpha() ? 8 : 0); |
| } |
| params[0] = v; |
| } |
| return true; |
| case GL_DEPTH_BITS: |
| *num_written = 1; |
| if (params) { |
| GLint v = 0; |
| if (gl_version_info().is_desktop_core_profile) { |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (framebuffer) { |
| if (framebuffer->HasDepthAttachment()) { |
| api()->glGetFramebufferAttachmentParameterivEXTFn( |
| GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
| GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &v); |
| } |
| } else { |
| v = (back_buffer_has_depth_ ? 24 : 0); |
| } |
| } else { |
| api()->glGetIntegervFn(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 (gl_version_info().is_desktop_core_profile) { |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (framebuffer) { |
| if (framebuffer->HasSameInternalFormatsMRT()) { |
| 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; |
| } |
| for (uint32_t i = 0; i < group_->max_draw_buffers(); i++) { |
| if (framebuffer->HasColorAttachment(i)) { |
| api()->glGetFramebufferAttachmentParameterivEXTFn( |
| GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, |
| framebuffer_enum, &v); |
| break; |
| } |
| } |
| } |
| } else { |
| v = 8; |
| } |
| } else { |
| api()->glGetIntegervFn(pname, &v); |
| } |
| params[0] = v; |
| } |
| return true; |
| case GL_STENCIL_BITS: |
| *num_written = 1; |
| if (params) { |
| GLint v = 0; |
| if (gl_version_info().is_desktop_core_profile) { |
| Framebuffer* framebuffer = GetBoundDrawFramebuffer(); |
| if (framebuffer) { |
| if (framebuffer->HasStencilAttachment()) { |
| api()->glGetFramebufferAttachmentParameterivEXTFn( |
| GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
| GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &v); |
| } |
| } else { |
| v = (back_buffer_has_stencil_ ? 8 : 0); |
| } |
| } else { |
| api()->glGetIntegervFn(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_BIND_GENERATES_RESOURCE_CHROMIUM: |
| *num_written = 1; |
| if (params) { |
| params[0] = group_->bind_generates_resource() ? 1 : 0; |
| } |
| return true; |
| case GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT: |
| *num_written = 1; |
| if (params) { |
| params[0] = group_->max_dual_source_draw_buffers(); |
| } |
| return true; |
| |
| case GL_MAJOR_VERSION: |
| *num_written = 1; |
| if (params) { |
| // TODO(zmo): once we switch to MANGLE, we should query version numbers. |
| params[0] = 3; |
| } |
| return true; |
| case GL_MINOR_VERSION: |
| // TODO(zmo): once we switch to MANGLE, we should query version numbers. |
| *num_written = 1; |
| if (params) { |
| params[0] = 0; |
| } |
| return true; |
| |
| case GL_NUM_EXTENSIONS: |
| // TODO(vmiura): Should the command buffer support this? |
| *num_written = 1; |
| if (params) { |
| params[0] = 0; |
| } |
| return true; |
| case GL_GPU_DISJOINT_EXT: |
| // TODO(vmiura): Should the command buffer support this? |
| *num_written = 1; |
| if (params) { |
| params[0] = 0; |
| } |
| return true; |
| case GL_TIMESTAMP_EXT: |
| // TODO(vmiura): Should the command buffer support this? |
| *num_written = 1; |
| if (params) { |
| params[0] = 0; |
| } |
| return true; |
| case GL_TEXTURE_BINDING_2D_ARRAY: |
| *num_written = 1; |
| if (params) { |
| TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; |
| if (unit.bound_texture_2d_array.get()) { |
| *params = unit.bound_texture_2d_array->client_id(); |
| } else { |
| *params = 0; |
| } |
| } |
| return true; |
| case GL_TEXTURE_BINDING_3D: |
| *num_written = 1; |
| if (params) { |
| TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; |
| if (unit.bound_texture_3d.get()) { |
| *params = unit.bound_texture_3d->client_id(); |
| } else { |
| *params = 0; |
| } |
| } |
| return true; |
| case GL_SAMPLER_BINDING: |
| *num_written = 1; |
| if (params) { |
| DCHECK_LT(state_.active_texture_unit, state_.sampler_units.size()); |
| Sampler* sampler = |
| state_.sampler_units[state_.active_texture_unit].get(); |
| *params = sampler ? sampler->client_id() : 0; |
| } |
| return true; |
| case GL_TRANSFORM_FEEDBACK_BINDING: |
| *num_written = 1; |
| if (params) { |
| *params = state_.bound_transform_feedback->client_id(); |
| } |
| return true; |
| case GL_NUM_PROGRAM_BINARY_FORMATS: |
| *num_written = 1; |
| if (params) { |
| *params = 0; |
| } |
| return true; |
| case GL_PROGRAM_BINARY_FORMATS: |
| *num_written = 0; |
| return true; |
| default: |
| if (pname >= GL_DRAW_BUFFER0_ARB && pname <= GL_DRAW_BUFFER15_ARB) { |
| *num_written = 1; |
| if (params) { |
| if (pname < GL_DRAW_BUFFER0_ARB + group_->max_draw_buffers()) { |
| Framebuffer* framebuffer = |
| GetFramebufferInfoForTarget(GL_FRAMEBUFFER); |
| if (framebuffer) { |
| *params = framebuffer->GetDrawBuffer(pname); |
| } else { // backbuffer |
| if (pname == GL_DRAW_BUFFER0_ARB) |
| *params = back_buffer_draw_buffer_; |
| else |
| *params = GL_NONE; |
| } |
| } else { |
| *params = GL_NONE; |
| } |
| } |
| return true; |
| } |
| |
| *num_written = util_.GLGetNumValuesReturned(pname); |
| if (*num_written) |
| break; |
| |
| return false; |
| } |
| |
| if (params) { |
| DCHECK(*num_written); |
| pname = AdjustGetPname(pname); |
| api()->glGetIntegervFn(pname, params); |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::GetNumValuesReturnedForGLGet( |
| GLenum pname, GLsizei* num_values) { |
| *num_values = 0; |
| if (state_.GetStateAsGLint(pname, nullptr, num_values)) { |
| return true; |
| } |
| return GetHelper(pname, nullptr, 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; |
| } |
| if (GL_ALIASED_POINT_SIZE_RANGE == pname && |
| gl_version_info().is_desktop_core_profile) { |
| return GL_POINT_SIZE_RANGE; |
| } |
| return pname; |
| } |
| |
| void GLES2DecoderImpl::DoGetBooleanv(GLenum pname, |
| GLboolean* params, |
| GLsizei params_size) { |
| DCHECK(params); |
| std::unique_ptr<GLint[]> values(new GLint[params_size]); |
| memset(values.get(), 0, params_size * sizeof(GLint)); |
| DoGetIntegerv(pname, values.get(), params_size); |
| for (GLsizei ii = 0; ii < params_size; ++ii) { |
| params[ii] = static_cast<GLboolean>(values[ii]); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoGetFloatv(GLenum pname, |
| GLfloat* params, |
| GLsizei params_size) { |
| DCHECK(params); |
| GLsizei num_written = 0; |
| if (state_.GetStateAsGLfloat(pname, params, &num_written)) { |
| DCHECK_EQ(num_written, params_size); |
| return; |
| } |
| |
| switch (pname) { |
| case GL_ALIASED_POINT_SIZE_RANGE: |
| case GL_ALIASED_LINE_WIDTH_RANGE: |
| case GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: |
| DCHECK_EQ(params_size, util_.GLGetNumValuesReturned(pname)); |
| pname = AdjustGetPname(pname); |
| api()->glGetFloatvFn(pname, params); |
| return; |
| } |
| |
| std::unique_ptr<GLint[]> values(new GLint[params_size]); |
| memset(values.get(), 0, params_size * sizeof(GLint)); |
| DoGetIntegerv(pname, values.get(), params_size); |
| for (GLsizei ii = 0; ii < params_size; ++ii) { |
| params[ii] = static_cast<GLfloat>(values[ii]); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoGetInteger64v(GLenum pname, |
| GLint64* params, |
| GLsizei params_size) { |
| DCHECK(params); |
| if (feature_info_->IsWebGL2OrES3Context()) { |
| switch (pname) { |
| case GL_MAX_ELEMENT_INDEX: { |
| DCHECK_EQ(params_size, 1); |
| if (gl_version_info().IsAtLeastGLES(3, 0) || |
| gl_version_info().IsAtLeastGL(4, 3)) { |
| api()->glGetInteger64vFn(GL_MAX_ELEMENT_INDEX, params); |
| } else { |
| // Assume that desktop GL implementations can generally support |
| // 32-bit indices. |
| if (params) { |
| *params = std::numeric_limits<unsigned int>::max(); |
| } |
| } |
| return; |
| } |
| } |
| } |
| |
| std::unique_ptr<GLint[]> values(new GLint[params_size]); |
| memset(values.get(), 0, params_size * sizeof(GLint)); |
| DoGetIntegerv(pname, values.get(), params_size); |
| for (GLsizei ii = 0; ii < params_size; ++ii) { |
| params[ii] = static_cast<GLint64>(values[ii]); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoGetIntegerv(GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| DCHECK(params); |
| GLsizei num_written = 0; |
| if (state_.GetStateAsGLint(pname, params, &num_written) || |
| GetHelper(pname, params, &num_written)) { |
| DCHECK_EQ(num_written, params_size); |
| return; |
| } |
| NOTREACHED() << "Unhandled enum " << pname; |
| } |
| |
| template <typename TYPE> |
| void GLES2DecoderImpl::GetIndexedIntegerImpl( |
| const char* function_name, GLenum target, GLuint index, TYPE* data) { |
| DCHECK(data); |
| |
| if (features().ext_window_rectangles && target == GL_WINDOW_RECTANGLE_EXT) { |
| if (index >= state_.GetMaxWindowRectangles()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "window rectangle index out of bounds"); |
| } |
| // This must be queried from the state tracker because the driver state is |
| // "wrong" if the bound framebuffer is 0 (backbuffer). |
| state_.GetWindowRectangle(index, data); |
| return; |
| } |
| scoped_refptr<IndexedBufferBindingHost> bindings; |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: |
| case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: |
| case GL_TRANSFORM_FEEDBACK_BUFFER_START: |
| if (index >= group_->max_transform_feedback_separate_attribs()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid index"); |
| return; |
| } |
| bindings = state_.bound_transform_feedback.get(); |
| break; |
| case GL_UNIFORM_BUFFER_BINDING: |
| case GL_UNIFORM_BUFFER_SIZE: |
| case GL_UNIFORM_BUFFER_START: |
| if (index >= group_->max_uniform_buffer_bindings()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid index"); |
| return; |
| } |
| bindings = state_.indexed_uniform_buffer_bindings.get(); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| DCHECK(bindings); |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: |
| case GL_UNIFORM_BUFFER_BINDING: |
| { |
| Buffer* buffer = bindings->GetBufferBinding(index); |
| *data = static_cast<TYPE>(buffer ? buffer->service_id() : 0); |
| } |
| break; |
| case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: |
| case GL_UNIFORM_BUFFER_SIZE: |
| *data = static_cast<TYPE>(bindings->GetBufferSize(index)); |
| break; |
| case GL_TRANSFORM_FEEDBACK_BUFFER_START: |
| case GL_UNIFORM_BUFFER_START: |
| *data = static_cast<TYPE>(bindings->GetBufferStart(index)); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void GLES2DecoderImpl::DoGetIntegeri_v(GLenum target, |
| GLuint index, |
| GLint* params, |
| GLsizei params_size) { |
| GetIndexedIntegerImpl<GLint>("glGetIntegeri_v", target, index, params); |
| } |
| |
| void GLES2DecoderImpl::DoGetInteger64i_v(GLenum target, |
| GLuint index, |
| GLint64* params, |
| GLsizei params_size) { |
| GetIndexedIntegerImpl<GLint64>("glGetInteger64i_v", target, index, params); |
| } |
| |
| void GLES2DecoderImpl::DoGetProgramiv(GLuint program_id, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| Program* program = GetProgramInfoNotShader(program_id, "glGetProgramiv"); |
| if (!program) { |
| return; |
| } |
| program->GetProgramiv(pname, params); |
| } |
| |
| void GLES2DecoderImpl::DoGetSynciv(GLuint sync_id, |
| GLenum pname, |
| GLsizei num_values, |
| GLsizei* length, |
| GLint* values) { |
| GLsync service_sync = 0; |
| if (!group_->GetSyncServiceId(sync_id, &service_sync)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glGetSynciv", "invalid sync id"); |
| return; |
| } |
| api()->glGetSyncivFn(service_sync, pname, num_values, nullptr, values); |
| } |
| |
| void GLES2DecoderImpl::DoGetBufferParameteri64v(GLenum target, |
| GLenum pname, |
| GLint64* params, |
| GLsizei params_size) { |
| // Just delegate it. Some validation is actually done before this. |
| buffer_manager()->ValidateAndDoGetBufferParameteri64v( |
| &state_, target, pname, params); |
| } |
| |
| void GLES2DecoderImpl::DoGetBufferParameteriv(GLenum target, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| // 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 std::string& name) { |
| if (!StringIsValidForGLES(name)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glBindAttribLocation", "Invalid character"); |
| return; |
| } |
| if (ProgramManager::HasBuiltInPrefix(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 simply cache the binding, and then call |
| // Program::ExecuteBindAttribLocationCalls() right before link. |
| program->SetAttribLocationBinding(name, static_cast<GLint>(index)); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleBindAttribLocationBucket( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::BindAttribLocationBucket& c = |
| *static_cast<const volatile gles2::cmds::BindAttribLocationBucket*>( |
| cmd_data); |
| GLuint program = static_cast<GLuint>(c.program); |
| GLuint index = static_cast<GLuint>(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); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::DoBindFragDataLocation(GLuint program_id, |
| GLuint colorName, |
| const std::string& name) { |
| const char kFunctionName[] = "glBindFragDataLocationEXT"; |
| if (!StringIsValidForGLES(name)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "invalid character"); |
| return error::kNoError; |
| } |
| if (ProgramManager::HasBuiltInPrefix(name)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "reserved prefix"); |
| return error::kNoError; |
| } |
| if (colorName >= group_->max_draw_buffers()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, |
| "colorName out of range"); |
| return error::kNoError; |
| } |
| Program* program = GetProgramInfoNotShader(program_id, kFunctionName); |
| if (!program) { |
| return error::kNoError; |
| } |
| program->SetProgramOutputLocationBinding(name, colorName); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleBindFragDataLocationEXTBucket( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!features().ext_blend_func_extended) { |
| return error::kUnknownCommand; |
| } |
| const volatile gles2::cmds::BindFragDataLocationEXTBucket& c = |
| *static_cast<const volatile gles2::cmds::BindFragDataLocationEXTBucket*>( |
| cmd_data); |
| GLuint program = static_cast<GLuint>(c.program); |
| GLuint colorNumber = static_cast<GLuint>(c.colorNumber); |
| 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; |
| } |
| return DoBindFragDataLocation(program, colorNumber, name_str); |
| } |
| |
| error::Error GLES2DecoderImpl::DoBindFragDataLocationIndexed( |
| GLuint program_id, |
| GLuint colorName, |
| GLuint index, |
| const std::string& name) { |
| const char kFunctionName[] = "glBindFragDataLocationIndexEXT"; |
| if (!StringIsValidForGLES(name)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "invalid character"); |
| return error::kNoError; |
| } |
| if (ProgramManager::HasBuiltInPrefix(name)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "reserved prefix"); |
| return error::kNoError; |
| } |
| if (index != 0 && index != 1) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "index out of range"); |
| return error::kNoError; |
| } |
| if ((index == 0 && colorName >= group_->max_draw_buffers()) || |
| (index == 1 && colorName >= group_->max_dual_source_draw_buffers())) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, |
| "colorName out of range for the color index"); |
| return error::kNoError; |
| } |
| Program* program = GetProgramInfoNotShader(program_id, kFunctionName); |
| if (!program) { |
| return error::kNoError; |
| } |
| program->SetProgramOutputLocationIndexedBinding(name, colorName, index); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleBindFragDataLocationIndexedEXTBucket( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!features().ext_blend_func_extended) { |
| return error::kUnknownCommand; |
| } |
| const volatile gles2::cmds::BindFragDataLocationIndexedEXTBucket& c = |
| *static_cast< |
| const volatile gles2::cmds::BindFragDataLocationIndexedEXTBucket*>( |
| cmd_data); |
| GLuint program = static_cast<GLuint>(c.program); |
| GLuint colorNumber = static_cast<GLuint>(c.colorNumber); |
| GLuint index = static_cast<GLuint>(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; |
| } |
| return DoBindFragDataLocationIndexed(program, colorNumber, index, name_str); |
| } |
| |
| void GLES2DecoderImpl::DoBindUniformLocationCHROMIUM(GLuint program_id, |
| GLint location, |
| const std::string& name) { |
| if (!StringIsValidForGLES(name)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, |
| "glBindUniformLocationCHROMIUM", "Invalid character"); |
| return; |
| } |
| if (ProgramManager::HasBuiltInPrefix(name)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glBindUniformLocationCHROMIUM", "reserved prefix"); |
| return; |
| } |
| if (location < 0 || |
| static_cast<uint32_t>(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::BindUniformLocationCHROMIUMBucket& c = |
| *static_cast< |
| const volatile gles2::cmds::BindUniformLocationCHROMIUMBucket*>( |
| cmd_data); |
| GLuint program = static_cast<GLuint>(c.program); |
| GLint location = static_cast<GLint>(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); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleDeleteShader( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::DeleteShader& c = |
| *static_cast<const volatile gles2::cmds::DeleteShader*>(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::DeleteProgram& c = |
| *static_cast<const volatile gles2::cmds::DeleteProgram*>(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) { |
| const char* func_name = "glClear"; |
| DCHECK(!ShouldDeferDraws()); |
| if (mask & |
| ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "invalid mask"); |
| return error::kNoError; |
| } |
| if (CheckBoundDrawFramebufferValid(func_name)) { |
| ApplyDirtyState(); |
| if (workarounds().gl_clear_broken) { |
| if (!BoundFramebufferHasDepthAttachment()) |
| mask &= ~GL_DEPTH_BUFFER_BIT; |
| if (!BoundFramebufferHasStencilAttachment()) |
| mask &= ~GL_STENCIL_BUFFER_BIT; |
| ClearFramebufferForWorkaround(mask); |
| return error::kNoError; |
| } |
| if (mask & GL_COLOR_BUFFER_BIT) { |
| Framebuffer* framebuffer = |
| framebuffer_state_.bound_draw_framebuffer.get(); |
| if (framebuffer && framebuffer->ContainsActiveIntegerAttachments()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "can't be called on integer buffers"); |
| return error::kNoError; |
| } |
| } |
| AdjustDrawBuffers(); |
| api()->glClearFn(mask); |
| } |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderImpl::DoClearBufferiv(GLenum buffer, |
| GLint drawbuffer, |
| const volatile GLint* value) { |
| const char* func_name = "glClearBufferiv"; |
| if (!CheckBoundDrawFramebufferValid(func_name)) |
| return; |
| ApplyDirtyState(); |
| |
| if (buffer == GL_COLOR) { |
| if (drawbuffer < 0 || |
| drawbuffer >= static_cast<GLint>(group_->max_draw_buffers())) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "invalid drawBuffer"); |
| return; |
| } |
| GLenum internal_format = |
| GetBoundColorDrawBufferInternalFormat(drawbuffer); |
| if (!GLES2Util::IsSignedIntegerFormat(internal_format)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "can only be called on signed integer buffers"); |
| return; |
| } |
| } else { |
| DCHECK(buffer == GL_STENCIL); |
| if (drawbuffer != 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "invalid drawBuffer"); |
| return; |
| } |
| if (!BoundFramebufferHasStencilAttachment()) { |
| return; |
| } |
| } |
| MarkDrawBufferAsCleared(buffer, drawbuffer); |
| api()->glClearBufferivFn(buffer, drawbuffer, const_cast<const GLint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoClearBufferuiv(GLenum buffer, |
| GLint drawbuffer, |
| const volatile GLuint* value) { |
| const char* func_name = "glClearBufferuiv"; |
| if (!CheckBoundDrawFramebufferValid(func_name)) |
| return; |
| ApplyDirtyState(); |
| |
| if (drawbuffer < 0 || |
| drawbuffer >= static_cast<GLint>(group_->max_draw_buffers())) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "invalid drawBuffer"); |
| return; |
| } |
| GLenum internal_format = |
| GetBoundColorDrawBufferInternalFormat(drawbuffer); |
| if (!GLES2Util::IsUnsignedIntegerFormat(internal_format)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "can only be called on unsigned integer buffers"); |
| return; |
| } |
| MarkDrawBufferAsCleared(buffer, drawbuffer); |
| api()->glClearBufferuivFn(buffer, drawbuffer, |
| const_cast<const GLuint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoClearBufferfv(GLenum buffer, |
| GLint drawbuffer, |
| const volatile GLfloat* value) { |
| const char* func_name = "glClearBufferfv"; |
| if (!CheckBoundDrawFramebufferValid(func_name)) |
| return; |
| ApplyDirtyState(); |
| |
| if (buffer == GL_COLOR) { |
| if (drawbuffer < 0 || |
| drawbuffer >= static_cast<GLint>(group_->max_draw_buffers())) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "invalid drawBuffer"); |
| return; |
| } |
| GLenum internal_format = |
| GetBoundColorDrawBufferInternalFormat(drawbuffer); |
| if (GLES2Util::IsIntegerFormat(internal_format)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "can only be called on float buffers"); |
| return; |
| } |
| } else { |
| DCHECK(buffer == GL_DEPTH); |
| if (drawbuffer != 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "invalid drawBuffer"); |
| return; |
| } |
| if (!BoundFramebufferHasDepthAttachment()) { |
| return; |
| } |
| } |
| MarkDrawBufferAsCleared(buffer, drawbuffer); |
| api()->glClearBufferfvFn(buffer, drawbuffer, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoClearBufferfi( |
| GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { |
| const char* func_name = "glClearBufferfi"; |
| if (!CheckBoundDrawFramebufferValid(func_name)) |
| return; |
| ApplyDirtyState(); |
| |
| if (drawbuffer != 0) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, func_name, "invalid drawBuffer"); |
| return; |
| } |
| if (!BoundFramebufferHasDepthAttachment() && |
| !BoundFramebufferHasStencilAttachment()) { |
| return; |
| } |
| MarkDrawBufferAsCleared(GL_DEPTH, drawbuffer); |
| MarkDrawBufferAsCleared(GL_STENCIL, drawbuffer); |
| api()->glClearBufferfiFn(buffer, drawbuffer, depth, stencil); |
| } |
| |
| void GLES2DecoderImpl::DoFramebufferParameteri(GLenum target, |
| GLenum pname, |
| GLint param) { |
| const char* func_name = "glFramebufferParameteri"; |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); |
| if (!framebuffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "no framebuffer bound"); |
| return; |
| } |
| api()->glFramebufferParameteriFn(target, pname, param); |
| } |
| |
| void GLES2DecoderImpl::DoFramebufferRenderbuffer( |
| GLenum target, GLenum attachment, GLenum renderbuffertarget, |
| GLuint client_renderbuffer_id) { |
| const char* func_name = "glFramebufferRenderbuffer"; |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); |
| if (!framebuffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "no framebuffer bound"); |
| return; |
| } |
| GLuint service_id = 0; |
| Renderbuffer* renderbuffer = nullptr; |
| if (client_renderbuffer_id) { |
| renderbuffer = GetRenderbuffer(client_renderbuffer_id); |
| if (!renderbuffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "unknown renderbuffer"); |
| return; |
| } |
| if (!renderbuffer->IsValid()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "renderbuffer never bound or deleted"); |
| return; |
| } |
| service_id = renderbuffer->service_id(); |
| } |
| std::vector<GLenum> attachments; |
| if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { |
| attachments.push_back(GL_DEPTH_ATTACHMENT); |
| attachments.push_back(GL_STENCIL_ATTACHMENT); |
| } else { |
| attachments.push_back(attachment); |
| } |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(func_name); |
| for (GLenum attachment_point : attachments) { |
| api()->glFramebufferRenderbufferEXTFn(target, attachment_point, |
| renderbuffertarget, service_id); |
| GLenum error = LOCAL_PEEK_GL_ERROR(func_name); |
| if (error == GL_NO_ERROR) { |
| framebuffer->AttachRenderbuffer(attachment_point, 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)) { |
| if (cap == GL_PRIMITIVE_RESTART_FIXED_INDEX && |
| feature_info_->feature_flags().emulate_primitive_restart_fixed_index) { |
| // Enable and Disable PRIMITIVE_RESTART only before and after |
| // DrawElements* for old desktop GL. |
| return; |
| } |
| if (cap == GL_FRAMEBUFFER_SRGB) { |
| // Enable and Disable GL_FRAMEBUFFER_SRGB is done manually in |
| // CheckBoundDrawFramebufferValid. |
| return; |
| } |
| api()->glDisableFn(cap); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoEnable(GLenum cap) { |
| if (SetCapabilityState(cap, true)) { |
| if (cap == GL_PRIMITIVE_RESTART_FIXED_INDEX && |
| feature_info_->feature_flags().emulate_primitive_restart_fixed_index) { |
| // Enable and Disable PRIMITIVE_RESTART only before and after |
| // DrawElements* for old desktop GL. |
| return; |
| } |
| if (cap == GL_FRAMEBUFFER_SRGB) { |
| // Enable and Disable GL_FRAMEBUFFER_SRGB is done manually in |
| // CheckBoundDrawFramebufferValid. |
| return; |
| } |
| api()->glEnableFn(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)); |
| api()->glDepthRangeFn(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); |
| api()->glSampleCoverageFn(state_.sample_coverage_value, invert); |
| } |
| |
| // Assumes framebuffer is complete. |
| void GLES2DecoderImpl::ClearUnclearedAttachments( |
| GLenum target, Framebuffer* framebuffer) { |
| // Clear textures that we can't use glClear first. These textures will be |
| // marked as cleared after the call and no longer be part of the following |
| // code. |
| framebuffer->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures( |
| this, texture_manager()); |
| |
| bool cleared_int_renderbuffers = false; |
| Framebuffer* draw_framebuffer = GetBoundDrawFramebuffer(); |
| if (framebuffer->HasUnclearedIntRenderbufferAttachments()) { |
| if (target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) { |
| // TODO(zmo): There is no guarantee that an FBO that is complete on the |
| // READ attachment will be complete as a DRAW attachment. |
| api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, |
| framebuffer->service_id()); |
| } |
| state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| ClearDeviceWindowRectangles(); |
| |
| // TODO(zmo): Assume DrawBuffers() does not affect ClearBuffer(). |
| framebuffer->ClearUnclearedIntRenderbufferAttachments( |
| renderbuffer_manager()); |
| |
| cleared_int_renderbuffers = true; |
| } |
| |
| GLbitfield clear_bits = 0; |
| bool reset_draw_buffers = false; |
| if (framebuffer->HasUnclearedColorAttachments()) { |
| // We should always use alpha == 0 here, because 1) some draw buffers may |
| // have alpha and some may not; 2) we won't have the same situation as the |
| // back buffer where alpha channel exists but is not requested. |
| api()->glClearColorFn(0.0f, 0.0f, 0.0f, 0.0f); |
| state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| clear_bits |= GL_COLOR_BUFFER_BIT; |
| |
| if (SupportsDrawBuffers()) { |
| reset_draw_buffers = |
| framebuffer->PrepareDrawBuffersForClearingUninitializedAttachments(); |
| } |
| } |
| |
| if (framebuffer->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT)) { |
| api()->glClearStencilFn(0); |
| state_.SetDeviceStencilMaskSeparate(GL_FRONT, kDefaultStencilMask); |
| state_.SetDeviceStencilMaskSeparate(GL_BACK, kDefaultStencilMask); |
| clear_bits |= GL_STENCIL_BUFFER_BIT; |
| } |
| |
| if (framebuffer->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT)) { |
| api()->glClearDepthFn(1.0f); |
| state_.SetDeviceDepthMask(GL_TRUE); |
| clear_bits |= GL_DEPTH_BUFFER_BIT; |
| } |
| |
| if (clear_bits) { |
| if (!cleared_int_renderbuffers && |
| target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) { |
| // TODO(zmo): There is no guarantee that an FBO that is complete on the |
| // READ attachment will be complete as a DRAW attachment. |
| api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, |
| framebuffer->service_id()); |
| } |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| ClearDeviceWindowRectangles(); |
| if (workarounds().gl_clear_broken) { |
| ClearFramebufferForWorkaround(clear_bits); |
| } else { |
| api()->glClearFn(clear_bits); |
| } |
| } |
| |
| if (cleared_int_renderbuffers || clear_bits) { |
| if (reset_draw_buffers) |
| framebuffer->RestoreDrawBuffers(); |
| RestoreClearState(); |
| if (target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) { |
| GLuint service_id = draw_framebuffer ? draw_framebuffer->service_id() : |
| GetBackbufferServiceId(); |
| api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, service_id); |
| } |
| } |
| |
| framebuffer_manager()->MarkAttachmentsAsCleared( |
| framebuffer, renderbuffer_manager(), texture_manager()); |
| } |
| |
| void GLES2DecoderImpl::RestoreClearState() { |
| framebuffer_state_.clear_state_dirty = true; |
| api()->glClearColorFn(state_.color_clear_red, state_.color_clear_green, |
| state_.color_clear_blue, state_.color_clear_alpha); |
| api()->glClearStencilFn(state_.stencil_clear); |
| api()->glClearDepthFn(state_.depth_clear); |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, |
| state_.enable_flags.scissor_test); |
| RestoreDeviceWindowRectangles(); |
| gfx::Vector2d scissor_offset = GetBoundFramebufferDrawOffset(); |
| api()->glScissorFn(state_.scissor_x + scissor_offset.x(), |
| state_.scissor_y + scissor_offset.y(), |
| state_.scissor_width, state_.scissor_height); |
| } |
| |
| GLenum GLES2DecoderImpl::DoCheckFramebufferStatus(GLenum target) { |
| Framebuffer* framebuffer = |
| GetFramebufferInfoForTarget(target); |
| if (!framebuffer) { |
| return GL_FRAMEBUFFER_COMPLETE; |
| } |
| GLenum completeness = framebuffer->IsPossiblyComplete(feature_info_.get()); |
| 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 = nullptr; |
| 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; |
| } |
| GLenum texture_target = texture_ref->texture()->target(); |
| if (texture_target != GLES2Util::GLFaceTargetToTextureTarget(textarget)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| name, "Attachment textarget doesn't match texture target"); |
| return; |
| } |
| service_id = texture_ref->service_id(); |
| } |
| |
| if ((level > 0 && !feature_info_->IsWebGL2OrES3Context()) || |
| !texture_manager()->ValidForTarget(textarget, level, 0, 0, 1)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, |
| name, "level out of range"); |
| return; |
| } |
| |
| if (texture_ref) |
| DoBindOrCopyTexImageIfNeeded(texture_ref->texture(), textarget, 0); |
| |
| std::vector<GLenum> attachments; |
| if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { |
| attachments.push_back(GL_DEPTH_ATTACHMENT); |
| attachments.push_back(GL_STENCIL_ATTACHMENT); |
| } else { |
| attachments.push_back(attachment); |
| } |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(name); |
| for (size_t ii = 0; ii < attachments.size(); ++ii) { |
| if (0 == samples) { |
| api()->glFramebufferTexture2DEXTFn(target, attachments[ii], textarget, |
| service_id, level); |
| } else { |
| api()->glFramebufferTexture2DMultisampleEXTFn( |
| target, attachments[ii], textarget, service_id, level, samples); |
| } |
| GLenum error = LOCAL_PEEK_GL_ERROR(name); |
| if (error == GL_NO_ERROR) { |
| framebuffer->AttachTexture(attachments[ii], texture_ref, textarget, level, |
| samples); |
| } |
| } |
| if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) { |
| framebuffer_state_.clear_state_dirty = true; |
| } |
| |
| OnFboChanged(); |
| } |
| |
| void GLES2DecoderImpl::DoFramebufferTextureLayer( |
| GLenum target, GLenum attachment, GLuint client_texture_id, |
| GLint level, GLint layer) { |
| const char* function_name = "glFramebufferTextureLayer"; |
| |
| TextureRef* texture_ref = nullptr; |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); |
| if (!framebuffer) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, function_name, "no framebuffer bound."); |
| return; |
| } |
| GLuint service_id = 0; |
| GLenum texture_target = 0; |
| if (client_texture_id) { |
| texture_ref = GetTexture(client_texture_id); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, function_name, "unknown texture"); |
| return; |
| } |
| service_id = texture_ref->service_id(); |
| |
| texture_target = texture_ref->texture()->target(); |
| switch (texture_target) { |
| case GL_TEXTURE_3D: |
| case GL_TEXTURE_2D_ARRAY: |
| break; |
| default: |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "texture is neither TEXTURE_3D nor TEXTURE_2D_ARRAY"); |
| return; |
| } |
| if (!texture_manager()->ValidForTarget(texture_target, level, |
| 0, 0, layer)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, function_name, "invalid level or layer"); |
| return; |
| } |
| } |
| api()->glFramebufferTextureLayerFn(target, attachment, service_id, level, |
| layer); |
| if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { |
| framebuffer->AttachTextureLayer( |
| GL_DEPTH_ATTACHMENT, texture_ref, texture_target, level, layer); |
| framebuffer->AttachTextureLayer( |
| GL_STENCIL_ATTACHMENT, texture_ref, texture_target, level, layer); |
| } else { |
| framebuffer->AttachTextureLayer( |
| attachment, texture_ref, texture_target, level, layer); |
| } |
| if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) { |
| framebuffer_state_.clear_state_dirty = true; |
| } |
| } |
| |
| void GLES2DecoderImpl::DoFramebufferTextureMultiviewLayeredANGLE( |
| GLenum target, |
| GLenum attachment, |
| GLuint client_texture_id, |
| GLint level, |
| GLint base_view_index, |
| GLsizei num_views) { |
| // This is only supported in passthrough command buffer. |
| NOTREACHED(); |
| } |
| |
| void GLES2DecoderImpl::DoGetFramebufferAttachmentParameteriv( |
| GLenum target, |
| GLenum attachment, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| const char kFunctionName[] = "glGetFramebufferAttachmentParameteriv"; |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(target); |
| if (!framebuffer) { |
| if (!feature_info_->IsWebGL2OrES3Context()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "no framebuffer bound"); |
| return; |
| } |
| if (!validators_->backbuffer_attachment.IsValid(attachment)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "invalid attachment for backbuffer"); |
| return; |
| } |
| switch (pname) { |
| case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: |
| *params = static_cast<GLint>(GL_FRAMEBUFFER_DEFAULT); |
| return; |
| case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: |
| case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: |
| case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: |
| // Delegate to underlying driver. |
| break; |
| default: |
| LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, kFunctionName, |
| "invalid pname for backbuffer"); |
| return; |
| } |
| if (GetBackbufferServiceId() != 0) { // Emulated backbuffer. |
| switch (attachment) { |
| case GL_BACK: |
| attachment = GL_COLOR_ATTACHMENT0; |
| break; |
| case GL_DEPTH: |
| attachment = GL_DEPTH_ATTACHMENT; |
| break; |
| case GL_STENCIL: |
| attachment = GL_STENCIL_ATTACHMENT; |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| } else { |
| if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { |
| const Framebuffer::Attachment* depth = |
| framebuffer->GetAttachment(GL_DEPTH_ATTACHMENT); |
| const Framebuffer::Attachment* stencil = |
| framebuffer->GetAttachment(GL_STENCIL_ATTACHMENT); |
| if ((!depth && !stencil) || |
| (depth && stencil && depth->IsSameAttachment(stencil))) { |
| attachment = GL_DEPTH_ATTACHMENT; |
| } else { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "depth and stencil attachment mismatch"); |
| return; |
| } |
| } |
| } |
| if (pname == GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT && |
| features().use_img_for_multisampled_render_to_texture) { |
| pname = GL_TEXTURE_SAMPLES_IMG; |
| } |
| if (pname == GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) { |
| DCHECK(framebuffer); |
| // If we query from the driver, it will be service ID; however, we need to |
| // return the client ID here. |
| const Framebuffer::Attachment* attachment_object = |
| framebuffer->GetAttachment(attachment); |
| *params = attachment_object ? attachment_object->object_name() : 0; |
| return; |
| } |
| |
| api()->glGetFramebufferAttachmentParameterivEXTFn(target, attachment, pname, |
| params); |
| // We didn't perform a full error check before gl call. |
| LOCAL_PEEK_GL_ERROR(kFunctionName); |
| } |
| |
| void GLES2DecoderImpl::DoGetRenderbufferParameteriv(GLenum target, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| 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) { |
| api()->glGetRenderbufferParameterivEXTFn( |
| target, GL_RENDERBUFFER_SAMPLES_IMG, params); |
| } else { |
| api()->glGetRenderbufferParameterivEXTFn( |
| target, GL_RENDERBUFFER_SAMPLES_EXT, params); |
| } |
| break; |
| default: |
| api()->glGetRenderbufferParameterivEXTFn(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) { |
| const char* func_name = "glBlitFramebufferCHROMIUM"; |
| DCHECK(!ShouldDeferReads() && !ShouldDeferDraws()); |
| |
| if (!CheckBoundFramebufferValid(func_name)) { |
| return; |
| } |
| |
| if (GetBoundFramebufferSamples(GL_DRAW_FRAMEBUFFER) > 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "destination framebuffer is multisampled"); |
| return; |
| } |
| |
| GLsizei read_buffer_samples = GetBoundFramebufferSamples(GL_READ_FRAMEBUFFER); |
| if (read_buffer_samples > 0 && |
| (srcX0 != dstX0 || srcY0 != dstY0 || srcX1 != dstX1 || srcY1 != dstY1)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "src framebuffer is multisampled, but src/dst regions are different"); |
| return; |
| } |
| |
| // If color/depth/stencil buffer have no image, we can remove corresponding |
| // bitfield from mask and return early if mask equals to 0. |
| // But validations should be done against the original mask. |
| GLbitfield mask_blit = mask; |
| |
| // Detect that designated read/depth/stencil buffer in read framebuffer miss |
| // image, and the corresponding buffers in draw framebuffer have image. |
| bool read_framebuffer_miss_image = false; |
| |
| // Check whether read framebuffer and draw framebuffer have identical image |
| // TODO(yunchao): consider doing something like CheckFramebufferStatus(). |
| // We cache the validation results, and if read_framebuffer doesn't change, |
| // draw_framebuffer doesn't change, then use the cached status. |
| enum FeedbackLoopState { |
| FeedbackLoopTrue, |
| FeedbackLoopFalse, |
| FeedbackLoopUnknown |
| }; |
| |
| FeedbackLoopState is_feedback_loop = FeedbackLoopUnknown; |
| Framebuffer* read_framebuffer = |
| framebuffer_state_.bound_read_framebuffer.get(); |
| Framebuffer* draw_framebuffer = |
| framebuffer_state_.bound_draw_framebuffer.get(); |
| // If both read framebuffer and draw framebuffer are default framebuffer, |
| // They always have identical image. Otherwise, if one of read framebuffer |
| // and draw framebuffe is default framebuffer, but the other is fbo, they |
| // always have no identical image. |
| if (!read_framebuffer && !draw_framebuffer) { |
| is_feedback_loop = FeedbackLoopTrue; |
| } else if (!read_framebuffer || !draw_framebuffer) { |
| is_feedback_loop = FeedbackLoopFalse; |
| if (read_framebuffer) { |
| if (((mask & GL_COLOR_BUFFER_BIT) != 0 && |
| !GetBoundReadFramebufferInternalFormat()) || |
| ((mask & GL_DEPTH_BUFFER_BIT) != 0 && |
| !read_framebuffer->GetAttachment(GL_DEPTH_ATTACHMENT) && |
| BoundFramebufferHasDepthAttachment()) || |
| ((mask & GL_STENCIL_BUFFER_BIT) != 0 && |
| !read_framebuffer->GetAttachment(GL_STENCIL_ATTACHMENT) && |
| BoundFramebufferHasStencilAttachment())) { |
| read_framebuffer_miss_image = true; |
| } |
| } |
| } else { |
| DCHECK(read_framebuffer && draw_framebuffer); |
| if ((mask & GL_DEPTH_BUFFER_BIT) != 0) { |
| const Framebuffer::Attachment* depth_buffer_read = |
| read_framebuffer->GetAttachment(GL_DEPTH_ATTACHMENT); |
| const Framebuffer::Attachment* depth_buffer_draw = |
| draw_framebuffer->GetAttachment(GL_DEPTH_ATTACHMENT); |
| if (!depth_buffer_draw || !depth_buffer_read) { |
| mask_blit &= ~GL_DEPTH_BUFFER_BIT; |
| if (depth_buffer_draw) { |
| read_framebuffer_miss_image = true; |
| } |
| } else if (depth_buffer_draw->IsSameAttachment(depth_buffer_read)) { |
| is_feedback_loop = FeedbackLoopTrue; |
| } |
| } |
| if ((mask & GL_STENCIL_BUFFER_BIT) != 0) { |
| const Framebuffer::Attachment* stencil_buffer_read = |
| read_framebuffer->GetAttachment(GL_STENCIL_ATTACHMENT); |
| const Framebuffer::Attachment* stencil_buffer_draw = |
| draw_framebuffer->GetAttachment(GL_STENCIL_ATTACHMENT); |
| if (!stencil_buffer_draw || !stencil_buffer_read) { |
| mask_blit &= ~GL_STENCIL_BUFFER_BIT; |
| if (stencil_buffer_draw) { |
| read_framebuffer_miss_image = true; |
| } |
| } else if (stencil_buffer_draw->IsSameAttachment(stencil_buffer_read)) { |
| is_feedback_loop = FeedbackLoopTrue; |
| } |
| } |
| } |
| |
| GLenum src_internal_format = GetBoundReadFramebufferInternalFormat(); |
| GLenum src_type = GetBoundReadFramebufferTextureType(); |
| |
| bool read_buffer_has_srgb = GLES2Util::GetColorEncodingFromInternalFormat( |
| src_internal_format) == GL_SRGB; |
| bool draw_buffers_has_srgb = false; |
| if ((mask & GL_COLOR_BUFFER_BIT) != 0) { |
| bool is_src_signed_int = |
| GLES2Util::IsSignedIntegerFormat(src_internal_format); |
| bool is_src_unsigned_int = |
| GLES2Util::IsUnsignedIntegerFormat(src_internal_format); |
| DCHECK(!is_src_signed_int || !is_src_unsigned_int); |
| |
| if ((is_src_signed_int || is_src_unsigned_int) && filter == GL_LINEAR) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "invalid filter for integer format"); |
| return; |
| } |
| |
| GLenum src_sized_format = |
| GLES2Util::ConvertToSizedFormat(src_internal_format, src_type); |
| DCHECK(read_framebuffer || (is_feedback_loop != FeedbackLoopUnknown)); |
| const Framebuffer::Attachment* read_buffer = |
| is_feedback_loop == FeedbackLoopUnknown ? |
| read_framebuffer->GetReadBufferAttachment() : nullptr; |
| bool draw_buffer_has_image = false; |
| for (uint32_t ii = 0; ii < group_->max_draw_buffers(); ++ii) { |
| GLenum dst_format = GetBoundColorDrawBufferInternalFormat( |
| static_cast<GLint>(ii)); |
| GLenum dst_type = GetBoundColorDrawBufferType(static_cast<GLint>(ii)); |
| if (dst_format == 0) |
| continue; |
| draw_buffer_has_image = true; |
| if (!src_internal_format) { |
| read_framebuffer_miss_image = true; |
| } |
| if (GLES2Util::GetColorEncodingFromInternalFormat(dst_format) == GL_SRGB) |
| draw_buffers_has_srgb = true; |
| if (read_buffer_samples > 0 && |
| (src_sized_format != |
| GLES2Util::ConvertToSizedFormat(dst_format, dst_type))) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "src and dst formats differ for color"); |
| return; |
| } |
| bool is_dst_signed_int = GLES2Util::IsSignedIntegerFormat(dst_format); |
| bool is_dst_unsigned_int = GLES2Util::IsUnsignedIntegerFormat(dst_format); |
| DCHECK(!is_dst_signed_int || !is_dst_unsigned_int); |
| if (is_src_signed_int != is_dst_signed_int || |
| is_src_unsigned_int != is_dst_unsigned_int) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "incompatible src/dst color formats"); |
| return; |
| } |
| // Check whether draw buffers have identical color image with read buffer |
| if (is_feedback_loop == FeedbackLoopUnknown) { |
| GLenum attachment = static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + ii); |
| DCHECK(draw_framebuffer); |
| const Framebuffer::Attachment* draw_buffer = |
| draw_framebuffer->GetAttachment(attachment); |
| if (!draw_buffer || !read_buffer) { |
| continue; |
| } |
| if (draw_buffer->IsSameAttachment(read_buffer)) { |
| is_feedback_loop = FeedbackLoopTrue; |
| break; |
| } |
| } |
| } |
| if (draw_framebuffer && !draw_buffer_has_image) |
| mask_blit &= ~GL_COLOR_BUFFER_BIT; |
| } |
| if (is_feedback_loop == FeedbackLoopTrue) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "source buffer and destination buffers are identical"); |
| return; |
| } |
| if (read_framebuffer_miss_image == true) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "The designated attachment point(s) in read framebuffer miss image"); |
| return; |
| } |
| |
| if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) { |
| if (filter != GL_NEAREST) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "invalid filter for depth/stencil"); |
| return; |
| } |
| } |
| |
| mask = mask_blit; |
| if (!mask) |
| return; |
| |
| if (((mask & GL_DEPTH_BUFFER_BIT) != 0 && |
| (GetBoundFramebufferDepthFormat(GL_READ_FRAMEBUFFER) != |
| GetBoundFramebufferDepthFormat(GL_DRAW_FRAMEBUFFER))) || |
| ((mask & GL_STENCIL_BUFFER_BIT) != 0 && |
| ((GetBoundFramebufferStencilFormat(GL_READ_FRAMEBUFFER) != |
| GetBoundFramebufferStencilFormat(GL_DRAW_FRAMEBUFFER))))) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "src and dst formats differ for depth/stencil"); |
| return; |
| } |
| |
| base::CheckedNumeric<GLint> src_width_temp = srcX1; |
| src_width_temp -= srcX0; |
| base::CheckedNumeric<GLint> src_height_temp = srcY1; |
| src_height_temp -= srcY0; |
| base::CheckedNumeric<GLint> dst_width_temp = dstX1; |
| dst_width_temp -= dstX0; |
| base::CheckedNumeric<GLint> dst_height_temp = dstY1; |
| dst_height_temp -= dstY0; |
| if (!src_width_temp.Abs().IsValid() || !src_height_temp.Abs().IsValid() || |
| !dst_width_temp.Abs().IsValid() || !dst_height_temp.Abs().IsValid()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, |
| "the width or height of src or dst region overflowed"); |
| return; |
| } |
| |
| if (workarounds().adjust_src_dst_region_for_blitframebuffer) { |
| gfx::Size read_size = GetBoundReadFramebufferSize(); |
| gfx::Rect src_bounds(0, 0, read_size.width(), read_size.height()); |
| GLint src_x = srcX1 > srcX0 ? srcX0 : srcX1; |
| GLint src_y = srcY1 > srcY0 ? srcY0 : srcY1; |
| GLuint src_width = 0, src_height = 0; |
| if (!src_width_temp.Abs().AssignIfValid(&src_width)) |
| src_width = 0; |
| if (!src_height_temp.Abs().AssignIfValid(&src_height)) |
| src_height = 0; |
| |
| gfx::Rect src_region(src_x, src_y, src_width, src_height); |
| if (!src_bounds.Contains(src_region) && |
| (src_width != 0) && (src_height != 0)) { |
| // If pixels lying outside the read framebuffer, adjust src region |
| // and dst region to appropriate in-bounds regions respectively. |
| src_bounds.Intersect(src_region); |
| GLuint src_real_width = src_bounds.width(); |
| GLuint src_real_height = src_bounds.height(); |
| GLuint xoffset = src_bounds.x() - src_x; |
| GLuint yoffset = src_bounds.y() - src_y; |
| // if X/Y is reversed, use the top/right out-of-bounds region for mapping |
| // to dst region, instead of left/bottom out-of-bounds region for mapping. |
| if (((srcX1 > srcX0) && (dstX1 < dstX0)) || |
| ((srcX1 < srcX0) && (dstX1 > dstX0))) { |
| xoffset = src_x + src_width - src_bounds.x() - src_bounds.width(); |
| } |
| if (((srcY1 > srcY0) && (dstY1 < dstY0)) || |
| ((srcY1 < srcY0) && (dstY1 > dstY0))) { |
| yoffset = src_y + src_height - src_bounds.y() - src_bounds.height(); |
| } |
| |
| GLint dst_x = dstX1 > dstX0 ? dstX0 : dstX1; |
| GLint dst_y = dstY1 > dstY0 ? dstY0 : dstY1; |
| base::CheckedNumeric<GLint> dst_width_temp = dstX1; |
| dst_width_temp -= dstX0; |
| base::CheckedNumeric<GLint> dst_height_temp = dstY1; |
| dst_height_temp -= dstY0; |
| GLuint dst_width = 0, dst_height = 0; |
| if (!dst_width_temp.IsValid() || !dst_height_temp.IsValid()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "the width or height of dst region overflow"); |
| return; |
| } |
| if (!dst_width_temp.Abs().AssignIfValid(&dst_width)) |
| dst_width = 0; |
| if (!dst_height_temp.Abs().AssignIfValid(&dst_height)) |
| dst_height = 0; |
| |
| GLfloat dst_mapping_width = |
| static_cast<GLfloat>(src_real_width) * dst_width / src_width; |
| GLfloat dst_mapping_height = |
| static_cast<GLfloat>(src_real_height) * dst_height / src_height; |
| GLfloat dst_mapping_xoffset = |
| static_cast<GLfloat>(xoffset) * dst_width / src_width; |
| GLfloat dst_mapping_yoffset = |
| static_cast<GLfloat>(yoffset) * dst_height / src_height; |
| |
| GLuint dst_mapping_x0 = |
| std::round(dst_x + dst_mapping_xoffset); |
| GLuint dst_mapping_y0 = |
| std::round(dst_y + dst_mapping_yoffset); |
| |
| GLuint dst_mapping_x1 = |
| std::round(dst_x + dst_mapping_xoffset + dst_mapping_width); |
| GLuint dst_mapping_y1 = |
| std::round(dst_y + dst_mapping_yoffset + dst_mapping_height); |
| |
| // adjust the src region and dst region to fit the read framebuffer |
| srcX0 = srcX0 < srcX1 ? |
| src_bounds.x() : src_bounds.x() + src_bounds.width(); |
| srcY0 = srcY0 < srcY1 ? |
| src_bounds.y() : src_bounds.y() + src_bounds.height(); |
| srcX1 = srcX0 < srcX1 ? |
| src_bounds.x() + src_bounds.width() : src_bounds.x(); |
| srcY1 = srcY0 < srcY1 ? |
| src_bounds.y() + src_bounds.height() : src_bounds.y(); |
| |
| dstX0 = dstX0 < dstX1 ? dst_mapping_x0 : dst_mapping_x1; |
| dstY0 = dstY0 < dstY1 ? dst_mapping_y0 : dst_mapping_y1; |
| dstX1 = dstX0 < dstX1 ? dst_mapping_x1 : dst_mapping_x0; |
| dstY1 = dstY0 < dstY1 ? dst_mapping_y1 : dst_mapping_y0; |
| } |
| } |
| |
| bool enable_srgb = |
| (read_buffer_has_srgb || draw_buffers_has_srgb) && |
| ((mask & GL_COLOR_BUFFER_BIT) != 0); |
| bool encode_srgb_only = |
| (draw_buffers_has_srgb && !read_buffer_has_srgb) && |
| ((mask & GL_COLOR_BUFFER_BIT) != 0); |
| if (!enable_srgb || |
| read_buffer_samples > 0 || |
| !feature_info_->feature_flags().desktop_srgb_support || |
| gl_version_info().IsAtLeastGL(4, 4) || |
| (gl_version_info().IsAtLeastGL(4, 2) && encode_srgb_only)) { |
| if (enable_srgb && gl_version_info().IsAtLeastGL(4, 2)) { |
| state_.EnableDisableFramebufferSRGB(enable_srgb); |
| } |
| |
| api()->glBlitFramebufferFn(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, |
| dstY1, mask, filter); |
| return; |
| } |
| |
| // emulate srgb for desktop core profile when GL version < 4.4 |
| // TODO(yunchao): Need to handle this situation: |
| // There are multiple draw buffers. Some of them are srgb images. |
| // The others are not. |
| state_.EnableDisableFramebufferSRGB(true); |
| if (!InitializeSRGBConverter(func_name)) { |
| return; |
| } |
| GLenum src_format = |
| TextureManager::ExtractFormatFromStorageFormat(src_internal_format); |
| srgb_converter_->Blit(this, srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, filter, |
| GetBoundReadFramebufferSize(), |
| GetBoundReadFramebufferServiceId(), |
| src_internal_format, src_format, src_type, |
| GetBoundDrawFramebufferServiceId(), |
| read_buffer_has_srgb, draw_buffers_has_srgb, |
| state_.enable_flags.scissor_test); |
| } |
| |
| bool GLES2DecoderImpl::InitializeSRGBConverter( |
| const char* function_name) { |
| if (!srgb_converter_.get()) { |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(function_name); |
| srgb_converter_.reset( |
| new SRGBConverter(feature_info_.get())); |
| srgb_converter_->InitializeSRGBConverter(this); |
| if (LOCAL_PEEK_GL_ERROR(function_name) != GL_NO_ERROR) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void GLES2DecoderImpl::UnbindTexture(TextureRef* texture_ref, |
| bool supports_separate_framebuffer_binds) { |
| Texture* texture = texture_ref->texture(); |
| if (texture->IsAttachedToFramebuffer()) { |
| framebuffer_state_.clear_state_dirty = true; |
| } |
| // Unbind texture_ref from texture_ref units. |
| state_.UnbindTexture(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); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::EnsureRenderbufferBound() { |
| if (!state_.bound_renderbuffer_valid) { |
| state_.bound_renderbuffer_valid = true; |
| api()->glBindRenderbufferEXTFn(GL_RENDERBUFFER, |
| state_.bound_renderbuffer.get() |
| ? state_.bound_renderbuffer->service_id() |
| : 0); |
| } |
| } |
| |
| void GLES2DecoderImpl::RenderbufferStorageMultisampleWithWorkaround( |
| GLenum target, |
| GLsizei samples, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| ForcedMultisampleMode mode) { |
| RegenerateRenderbufferIfNeeded(state_.bound_renderbuffer.get()); |
| EnsureRenderbufferBound(); |
| RenderbufferStorageMultisampleHelper(target, samples, internal_format, width, |
| height, mode); |
| } |
| |
| void GLES2DecoderImpl::RenderbufferStorageMultisampleHelper( |
| GLenum target, |
| GLsizei samples, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| ForcedMultisampleMode mode) { |
| if (samples == 0) { |
| api()->glRenderbufferStorageEXTFn(target, internal_format, width, height); |
| return; |
| } |
| |
| // TODO(sievers): This could be resolved at the GL binding level, but the |
| // binding process is currently a bit too 'brute force'. |
| |
| // Note that when this is called via the |
| // RenderbufferStorageMultisampleEXT handler, |
| // kForceExtMultisampledRenderToTexture is passed in order to |
| // prevent the core ES 3.0 multisampling code path from being used. |
| if (mode == kForceExtMultisampledRenderToTexture) { |
| api()->glRenderbufferStorageMultisampleEXTFn( |
| target, samples, internal_format, width, height); |
| } else { |
| api()->glRenderbufferStorageMultisampleFn(target, samples, internal_format, |
| width, height); |
| } |
| } |
| |
| bool GLES2DecoderImpl::RegenerateRenderbufferIfNeeded( |
| Renderbuffer* renderbuffer) { |
| if (!renderbuffer->RegenerateAndBindBackingObjectIfNeeded(workarounds())) { |
| return false; |
| } |
| |
| if (renderbuffer != state_.bound_renderbuffer.get()) { |
| // The renderbuffer bound in the driver has changed to the new |
| // renderbuffer->service_id(). If that isn't state_.bound_renderbuffer, |
| // then state_.bound_renderbuffer is no longer bound in the driver. |
| state_.bound_renderbuffer_valid = false; |
| } |
| |
| return true; |
| } |
| |
| 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_t 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; |
| } |
| |
| 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; |
| } |
| |
| GLenum impl_format = |
| renderbuffer_manager()->InternalRenderbufferFormatToImplFormat( |
| internalformat); |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER( |
| "glRenderbufferStorageMultisampleCHROMIUM"); |
| RenderbufferStorageMultisampleWithWorkaround(target, samples, impl_format, |
| width, height, kDoNotForce); |
| 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; |
| } |
| } |
| |
| renderbuffer_manager()->SetInfoAndInvalidate(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; |
| } |
| |
| GLenum impl_format = |
| renderbuffer_manager()->InternalRenderbufferFormatToImplFormat( |
| internalformat); |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glRenderbufferStorageMultisampleEXT"); |
| RenderbufferStorageMultisampleWithWorkaround( |
| target, samples, impl_format, width, height, |
| kForceExtMultisampledRenderToTexture); |
| GLenum error = LOCAL_PEEK_GL_ERROR("glRenderbufferStorageMultisampleEXT"); |
| if (error == GL_NO_ERROR) { |
| renderbuffer_manager()->SetInfoAndInvalidate(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. |
| GLenum pixel_format = GL_RGBA; |
| GLenum pixel_type = GL_UNSIGNED_BYTE; |
| switch (format) { |
| case GL_RGB8: |
| pixel_format = GL_RGB; |
| break; |
| case GL_RGBA8: |
| break; |
| default: |
| return true; |
| } |
| |
| GLint draw_framebuffer, read_framebuffer; |
| |
| // Cache framebuffer and texture bindings. |
| api()->glGetIntegervFn(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer); |
| api()->glGetIntegervFn(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer); |
| |
| if (!validation_fbo_) { |
| api()->glGenFramebuffersEXTFn(1, &validation_fbo_multisample_); |
| api()->glGenFramebuffersEXTFn(1, &validation_fbo_); |
| } |
| |
| GLint bound_texture; |
| api()->glGetIntegervFn(GL_TEXTURE_BINDING_2D, &bound_texture); |
| GLuint validation_texture; |
| TextureMap::iterator iter = validation_textures_.find(format); |
| if (iter == validation_textures_.end()) { |
| // Create additional resources needed for the verification. |
| api()->glGenTexturesFn(1, &validation_texture); |
| validation_textures_.insert(std::make_pair(format, validation_texture)); |
| |
| // Texture only needs to be 1x1. |
| api()->glBindTextureFn(GL_TEXTURE_2D, validation_texture); |
| api()->glTexImage2DFn(GL_TEXTURE_2D, 0, format, 1, 1, 0, pixel_format, |
| pixel_type, nullptr); |
| } else { |
| validation_texture = iter->second; |
| } |
| api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, validation_fbo_); |
| api()->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_TEXTURE_2D, validation_texture, 0); |
| api()->glBindTextureFn(GL_TEXTURE_2D, bound_texture); |
| |
| api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, validation_fbo_multisample_); |
| api()->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_RENDERBUFFER, renderbuffer); |
| |
| // Cache current state and reset it to the values we require. |
| GLboolean scissor_enabled = false; |
| api()->glGetBooleanvFn(GL_SCISSOR_TEST, &scissor_enabled); |
| if (scissor_enabled) |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| ClearDeviceWindowRectangles(); |
| |
| GLboolean color_mask[4] = {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE}; |
| api()->glGetBooleanvFn(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}; |
| api()->glGetFloatvFn(GL_COLOR_CLEAR_VALUE, clear_color); |
| api()->glClearColorFn(1.0f, 0.0f, 1.0f, 1.0f); |
| |
| // Clear the buffer to the desired key color. |
| api()->glClearFn(GL_COLOR_BUFFER_BIT); |
| |
| // Blit from the multisample buffer to a standard texture. |
| api()->glBindFramebufferEXTFn(GL_READ_FRAMEBUFFER, |
| validation_fbo_multisample_); |
| api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, validation_fbo_); |
| |
| api()->glBlitFramebufferFn(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, |
| GL_NEAREST); |
| |
| // Read a pixel from the buffer. |
| api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, validation_fbo_); |
| |
| unsigned char pixel[3] = {0, 0, 0}; |
| api()->glReadPixelsFn(0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &pixel); |
| |
| // Detach the renderbuffer. |
| api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, validation_fbo_multisample_); |
| api()->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_RENDERBUFFER, 0); |
| |
| // Restore cached state. |
| if (scissor_enabled) |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, true); |
| RestoreDeviceWindowRectangles(); |
| |
| state_.SetDeviceColorMask( |
| color_mask[0], color_mask[1], color_mask[2], color_mask[3]); |
| api()->glClearColorFn(clear_color[0], clear_color[1], clear_color[2], |
| clear_color[3]); |
| api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, draw_framebuffer); |
| api()->glBindFramebufferEXTFn(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::DoReleaseShaderCompiler() { |
| DestroyShaderTranslator(); |
| } |
| |
| 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_t 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; |
| } |
| |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glRenderbufferStorage"); |
| RenderbufferStorageMultisampleWithWorkaround( |
| target, 0, |
| renderbuffer_manager()->InternalRenderbufferFormatToImplFormat( |
| internalformat), |
| width, height, kDoNotForce); |
| GLenum error = LOCAL_PEEK_GL_ERROR("glRenderbufferStorage"); |
| if (error == GL_NO_ERROR) { |
| renderbuffer_manager()->SetInfoAndInvalidate(renderbuffer, 0, |
| internalformat, width, height); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoLineWidth(GLfloat width) { |
| api()->glLineWidthFn( |
| std::min(std::max(width, line_width_range_[0]), line_width_range_[1])); |
| } |
| |
| void GLES2DecoderImpl::DoLinkProgram(GLuint program_id) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoLinkProgram"); |
| SCOPED_UMA_HISTOGRAM_TIMER("GPU.DoLinkProgramTime"); |
| 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, |
| client_)) { |
| if (program == state_.current_program.get()) { |
| 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::DoOverlayPromotionHintCHROMIUM(GLuint client_id, |
| GLboolean promotion_hint, |
| GLint display_x, |
| GLint display_y, |
| GLint display_width, |
| GLint display_height) { |
| if (client_id == 0) |
| return; |
| |
| TextureRef* texture_ref = GetTexture(client_id); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glOverlayPromotionHintCHROMIUM", |
| "invalid texture id"); |
| return; |
| } |
| GLStreamTextureImage* image = |
| texture_ref->texture()->GetLevelStreamTextureImage( |
| GL_TEXTURE_EXTERNAL_OES, 0); |
| if (!image) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glOverlayPromotionHintCHROMIUM", |
| "texture has no StreamTextureImage"); |
| return; |
| } |
| |
| image->NotifyPromotionHint(promotion_hint != GL_FALSE, display_x, display_y, |
| display_width, display_height); |
| } |
| |
| void GLES2DecoderImpl::DoSetDrawRectangleCHROMIUM(GLint x, |
| GLint y, |
| GLint width, |
| GLint height) { |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER); |
| if (framebuffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSetDrawRectangleCHROMIUM", |
| "framebuffer must not be bound"); |
| return; |
| } |
| if (!supports_dc_layers_) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSetDrawRectangleCHROMIUM", |
| "surface doesn't support SetDrawRectangle"); |
| return; |
| } |
| gfx::Rect rect(x, y, width, height); |
| if (!surface_->SetDrawRectangle(rect)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSetDrawRectangleCHROMIUM", |
| "failed on surface"); |
| LOG(ERROR) << "Context lost because SetDrawRectangleCHROMIUM failed."; |
| MarkContextLost(error::kUnknown); |
| group_->LoseContexts(error::kUnknown); |
| } |
| OnFboChanged(); |
| } |
| |
| void GLES2DecoderImpl::DoSetEnableDCLayersCHROMIUM(GLboolean enable) { |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER); |
| if (framebuffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSetEnableDCLayersCHROMIUM", |
| "framebuffer must not be bound"); |
| return; |
| } |
| if (!supports_dc_layers_) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSetEnableDCLayersCHROMIUM", |
| "surface doesn't support SetDrawRectangle"); |
| return; |
| } |
| if (!surface_->SetEnableDCLayers(!!enable)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSetEnableDCLayersCHROMIUM", |
| "failed on surface"); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoReadBuffer(GLenum src) { |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER); |
| if (framebuffer) { |
| if (src == GL_BACK) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_ENUM, "glReadBuffer", |
| "invalid src for a named framebuffer"); |
| return; |
| } |
| framebuffer->set_read_buffer(src); |
| } else { |
| if (src != GL_NONE && src != GL_BACK) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_ENUM, "glReadBuffer", |
| "invalid src for the default framebuffer"); |
| return; |
| } |
| back_buffer_read_buffer_ = src; |
| if (GetBackbufferServiceId() && src == GL_BACK) |
| src = GL_COLOR_ATTACHMENT0; |
| } |
| api()->glReadBufferFn(src); |
| } |
| |
| void GLES2DecoderImpl::DoSamplerParameterf( |
| GLuint client_id, GLenum pname, GLfloat param) { |
| Sampler* sampler = GetSampler(client_id); |
| if (!sampler) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glSamplerParameterf", "unknown sampler"); |
| return; |
| } |
| sampler_manager()->SetParameterf( |
| "glSamplerParameterf", GetErrorState(), sampler, pname, param); |
| } |
| |
| void GLES2DecoderImpl::DoSamplerParameteri( |
| GLuint client_id, GLenum pname, GLint param) { |
| Sampler* sampler = GetSampler(client_id); |
| if (!sampler) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glSamplerParameteri", "unknown sampler"); |
| return; |
| } |
| sampler_manager()->SetParameteri( |
| "glSamplerParameteri", GetErrorState(), sampler, pname, param); |
| } |
| |
| void GLES2DecoderImpl::DoSamplerParameterfv(GLuint client_id, |
| GLenum pname, |
| const volatile GLfloat* params) { |
| DCHECK(params); |
| Sampler* sampler = GetSampler(client_id); |
| if (!sampler) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glSamplerParameterfv", "unknown sampler"); |
| return; |
| } |
| sampler_manager()->SetParameterf( |
| "glSamplerParameterfv", GetErrorState(), sampler, pname, params[0]); |
| } |
| |
| void GLES2DecoderImpl::DoSamplerParameteriv(GLuint client_id, |
| GLenum pname, |
| const volatile GLint* params) { |
| DCHECK(params); |
| Sampler* sampler = GetSampler(client_id); |
| if (!sampler) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glSamplerParameteriv", "unknown sampler"); |
| return; |
| } |
| sampler_manager()->SetParameteri( |
| "glSamplerParameteriv", GetErrorState(), 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 volatile 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 volatile 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::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 !state_.current_program->IsInactiveUniformLocationByFakeLocation( |
| location); |
| } |
| |
| 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); |
| 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(uniform_info->type); |
| if (attachment->IsTexture(texture_ref)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool GLES2DecoderImpl::SupportsDrawBuffers() const { |
| return feature_info_->IsWebGL1OrES2Context() ? |
| feature_info_->feature_flags().ext_draw_buffers : true; |
| } |
| |
| bool GLES2DecoderImpl::ValidateAndAdjustDrawBuffers(const char* func_name) { |
| if (!SupportsDrawBuffers()) { |
| return true; |
| } |
| Framebuffer* framebuffer = framebuffer_state_.bound_draw_framebuffer.get(); |
| if (!state_.current_program.get() || !framebuffer) { |
| return true; |
| } |
| uint32_t fragment_output_type_mask = |
| state_.current_program->fragment_output_type_mask(); |
| uint32_t fragment_output_written_mask = |
| state_.current_program->fragment_output_written_mask(); |
| if (!framebuffer->ValidateAndAdjustDrawBuffers( |
| fragment_output_type_mask, fragment_output_written_mask)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "buffer format and fragment output variable type incompatible"); |
| return false; |
| } |
| return true; |
| } |
| |
| void GLES2DecoderImpl::AdjustDrawBuffers() { |
| if (!SupportsDrawBuffers()) { |
| return; |
| } |
| Framebuffer* framebuffer = framebuffer_state_.bound_draw_framebuffer.get(); |
| if (framebuffer) { |
| framebuffer->AdjustDrawBuffers(); |
| } |
| } |
| |
| bool GLES2DecoderImpl::ValidateUniformBlockBackings(const char* func_name) { |
| DCHECK(feature_info_->IsWebGL2OrES3Context()); |
| if (!state_.current_program.get()) |
| return true; |
| int32_t max_index = -1; |
| for (auto info : state_.current_program->uniform_block_size_info()) { |
| int32_t index = static_cast<int32_t>(info.binding); |
| if (index > max_index) |
| max_index = index; |
| } |
| if (max_index < 0) |
| return true; |
| std::vector<GLsizeiptr> uniform_block_sizes(max_index + 1); |
| for (int32_t ii = 0; ii <= max_index; ++ii) |
| uniform_block_sizes[ii] = 0; |
| for (auto info : state_.current_program->uniform_block_size_info()) { |
| uint32_t index = info.binding; |
| uniform_block_sizes[index] = static_cast<GLsizeiptr>(info.data_size); |
| } |
| return buffer_manager()->RequestBuffersAccess( |
| state_.GetErrorState(), state_.indexed_uniform_buffer_bindings.get(), |
| uniform_block_sizes, 1, func_name, "uniform buffers"); |
| } |
| |
| 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; |
| } |
| api()->glUniform1iFn(real_location, v0); |
| } |
| |
| void GLES2DecoderImpl::DoUniform1iv(GLint fake_location, |
| GLsizei count, |
| const volatile GLint* values) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniform1iv", |
| Program::kUniform1i, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| auto values_copy = std::make_unique<GLint[]>(count); |
| GLint* safe_values = values_copy.get(); |
| std::copy(values, values + count, safe_values); |
| 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, safe_values)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glUniform1iv", "texture unit out of range"); |
| return; |
| } |
| } |
| api()->glUniform1ivFn(real_location, count, safe_values); |
| } |
| |
| void GLES2DecoderImpl::DoUniform1uiv(GLint fake_location, |
| GLsizei count, |
| const volatile GLuint* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniform1uiv", |
| Program::kUniform1ui, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniform1uivFn(real_location, count, |
| const_cast<const GLuint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniform1fv(GLint fake_location, |
| GLsizei count, |
| const volatile 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) { |
| std::unique_ptr<GLint[]> temp(new GLint[count]); |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| temp[ii] = static_cast<GLint>(value[ii] != 0.0f); |
| } |
| api()->glUniform1ivFn(real_location, count, temp.get()); |
| } else { |
| api()->glUniform1fvFn(real_location, count, |
| const_cast<const GLfloat*>(value)); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoUniform2fv(GLint fake_location, |
| GLsizei count, |
| const volatile 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; |
| std::unique_ptr<GLint[]> temp(new GLint[num_values]); |
| for (GLsizei ii = 0; ii < num_values; ++ii) { |
| temp[ii] = static_cast<GLint>(value[ii] != 0.0f); |
| } |
| api()->glUniform2ivFn(real_location, count, temp.get()); |
| } else { |
| api()->glUniform2fvFn(real_location, count, |
| const_cast<const GLfloat*>(value)); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoUniform3fv(GLint fake_location, |
| GLsizei count, |
| const volatile 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; |
| std::unique_ptr<GLint[]> temp(new GLint[num_values]); |
| for (GLsizei ii = 0; ii < num_values; ++ii) { |
| temp[ii] = static_cast<GLint>(value[ii] != 0.0f); |
| } |
| api()->glUniform3ivFn(real_location, count, temp.get()); |
| } else { |
| api()->glUniform3fvFn(real_location, count, |
| const_cast<const GLfloat*>(value)); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoUniform4fv(GLint fake_location, |
| GLsizei count, |
| const volatile 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; |
| std::unique_ptr<GLint[]> temp(new GLint[num_values]); |
| for (GLsizei ii = 0; ii < num_values; ++ii) { |
| temp[ii] = static_cast<GLint>(value[ii] != 0.0f); |
| } |
| api()->glUniform4ivFn(real_location, count, temp.get()); |
| } else { |
| api()->glUniform4fvFn(real_location, count, |
| const_cast<const GLfloat*>(value)); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoUniform2iv(GLint fake_location, |
| GLsizei count, |
| const volatile GLint* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniform2iv", |
| Program::kUniform2i, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniform2ivFn(real_location, count, const_cast<const GLint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniform2uiv(GLint fake_location, |
| GLsizei count, |
| const volatile GLuint* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniform2uiv", |
| Program::kUniform2ui, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniform2uivFn(real_location, count, |
| const_cast<const GLuint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniform3iv(GLint fake_location, |
| GLsizei count, |
| const volatile GLint* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniform3iv", |
| Program::kUniform3i, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniform3ivFn(real_location, count, const_cast<const GLint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniform3uiv(GLint fake_location, |
| GLsizei count, |
| const volatile GLuint* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniform3uiv", |
| Program::kUniform3ui, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniform3uivFn(real_location, count, |
| const_cast<const GLuint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniform4iv(GLint fake_location, |
| GLsizei count, |
| const volatile GLint* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniform4iv", |
| Program::kUniform4i, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniform4ivFn(real_location, count, const_cast<const GLint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniform4uiv(GLint fake_location, |
| GLsizei count, |
| const volatile GLuint* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniform4uiv", |
| Program::kUniform4ui, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniform4uivFn(real_location, count, |
| const_cast<const GLuint*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix2fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (transpose && !feature_info_->IsWebGL2OrES3Context()) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glUniformMatrix2fv", "transpose not FALSE"); |
| return; |
| } |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix2fv", |
| Program::kUniformMatrix2f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix2fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix3fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (transpose && !feature_info_->IsWebGL2OrES3Context()) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glUniformMatrix3fv", "transpose not FALSE"); |
| return; |
| } |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix3fv", |
| Program::kUniformMatrix3f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix3fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix4fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (transpose && !feature_info_->IsWebGL2OrES3Context()) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glUniformMatrix4fv", "transpose not FALSE"); |
| return; |
| } |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix4fv", |
| Program::kUniformMatrix4f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix4fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix4fvStreamTextureMatrixCHROMIUM( |
| GLint fake_location, |
| GLboolean transpose, |
| const volatile GLfloat* transform) { |
| float gl_matrix[16]; |
| |
| // This refers to the bound external texture on the active unit. |
| TextureUnit& unit = state_.texture_units[state_.active_texture_unit]; |
| if (TextureRef* texture_ref = unit.bound_texture_external_oes.get()) { |
| if (GLStreamTextureImage* image = |
| texture_ref->texture()->GetLevelStreamTextureImage( |
| GL_TEXTURE_EXTERNAL_OES, 0)) { |
| gfx::Transform st_transform(gfx::Transform::kSkipInitialization); |
| gfx::Transform pre_transform(gfx::Transform::kSkipInitialization); |
| image->GetTextureMatrix(gl_matrix); |
| st_transform.matrix().setColMajorf(gl_matrix); |
| // const_cast is safe, because setColMajorf only does a memcpy. |
| // TODO(piman): can we remove this assumption without having to introduce |
| // an extra copy? |
| pre_transform.matrix().setColMajorf( |
| const_cast<const GLfloat*>(transform)); |
| gfx::Transform(pre_transform, st_transform) |
| .matrix() |
| .asColMajorf(gl_matrix); |
| } else { |
| // Missing stream texture. Treat matrix as identity. |
| memcpy(gl_matrix, const_cast<const GLfloat*>(transform), |
| sizeof(gl_matrix)); |
| } |
| } else { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "DoUniformMatrix4vStreamTextureMatrix", |
| "no texture bound"); |
| return; |
| } |
| |
| GLenum type = 0; |
| GLint real_location = -1; |
| GLsizei count = 1; |
| if (!PrepForSetUniformByLocation(fake_location, "glUniformMatrix4fv", |
| Program::kUniformMatrix4f, &real_location, |
| &type, &count)) { |
| return; |
| } |
| |
| api()->glUniformMatrix4fvFn(real_location, count, transpose, gl_matrix); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix2x3fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix2x3fv", |
| Program::kUniformMatrix2x3f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix2x3fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix2x4fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix2x4fv", |
| Program::kUniformMatrix2x4f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix2x4fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix3x2fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix3x2fv", |
| Program::kUniformMatrix3x2f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix3x2fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix3x4fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix3x4fv", |
| Program::kUniformMatrix3x4f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix3x4fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix4x2fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix4x2fv", |
| Program::kUniformMatrix4x2f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix4x2fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUniformMatrix4x3fv(GLint fake_location, |
| GLsizei count, |
| GLboolean transpose, |
| const volatile GLfloat* value) { |
| GLenum type = 0; |
| GLint real_location = -1; |
| if (!PrepForSetUniformByLocation(fake_location, |
| "glUniformMatrix4x3fv", |
| Program::kUniformMatrix4x3f, |
| &real_location, |
| &type, |
| &count)) { |
| return; |
| } |
| api()->glUniformMatrix4x3fvFn(real_location, count, transpose, |
| const_cast<const GLfloat*>(value)); |
| } |
| |
| void GLES2DecoderImpl::DoUseProgram(GLuint program_id) { |
| const char* function_name = "glUseProgram"; |
| GLuint service_id = 0; |
| Program* program = nullptr; |
| if (program_id) { |
| program = GetProgramInfoNotShader(program_id, function_name); |
| if (!program) { |
| return; |
| } |
| if (!program->IsValid()) { |
| // Program was not linked successfully. (ie, glLinkProgram) |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, function_name, "program not linked"); |
| return; |
| } |
| service_id = program->service_id(); |
| } |
| if (state_.bound_transform_feedback.get() && |
| state_.bound_transform_feedback->active() && |
| !state_.bound_transform_feedback->paused()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "transformfeedback is active and not paused"); |
| return; |
| } |
| if (program == state_.current_program.get()) |
| return; |
| if (state_.current_program.get()) { |
| program_manager()->UnuseProgram(shader_manager(), |
| state_.current_program.get()); |
| } |
| state_.current_program = program; |
| LogClientServiceMapping(function_name, program_id, service_id); |
| api()->glUseProgramFn(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::DoCopyTexImage(Texture* texture, |
| GLenum textarget, |
| gl::GLImage* image) { |
| // Note: We update the state to COPIED prior to calling CopyTexImage() |
| // as that allows the GLImage implemenatation to set it back to UNBOUND |
| // and ensure that CopyTexImage() is called each time the texture is |
| // used. |
| texture->SetLevelImageState(textarget, 0, Texture::COPIED); |
| bool rv = image->CopyTexImage(textarget); |
| DCHECK(rv) << "CopyTexImage() failed"; |
| } |
| |
| bool GLES2DecoderImpl::DoBindOrCopyTexImageIfNeeded(Texture* texture, |
| GLenum textarget, |
| GLuint texture_unit) { |
| // Image is already in use if texture is attached to a framebuffer. |
| if (texture && !texture->IsAttachedToFramebuffer()) { |
| Texture::ImageState image_state; |
| gl::GLImage* image = texture->GetLevelImage(textarget, 0, &image_state); |
| if (image && image_state == Texture::UNBOUND) { |
| ScopedGLErrorSuppressor suppressor( |
| "GLES2DecoderImpl::DoBindOrCopyTexImageIfNeeded", GetErrorState()); |
| if (texture_unit) |
| api()->glActiveTextureFn(texture_unit); |
| api()->glBindTextureFn(textarget, texture->service_id()); |
| if (image->BindTexImage(textarget)) { |
| image_state = Texture::BOUND; |
| } else { |
| DoCopyTexImage(texture, textarget, image); |
| } |
| if (!texture_unit) { |
| RestoreCurrentTextureBindings(&state_, textarget, |
| state_.active_texture_unit); |
| return false; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void GLES2DecoderImpl::DoCopyBufferSubData(GLenum readtarget, |
| GLenum writetarget, |
| GLintptr readoffset, |
| GLintptr writeoffset, |
| GLsizeiptr size) { |
| // Just delegate it. Some validation is actually done before this. |
| buffer_manager()->ValidateAndDoCopyBufferSubData( |
| &state_, readtarget, writetarget, readoffset, writeoffset, size); |
| } |
| |
| bool GLES2DecoderImpl::PrepareTexturesForRender() { |
| DCHECK(state_.current_program.get()); |
| 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); |
| GLenum textarget = GetBindTargetForSamplerType(uniform_info->type); |
| const SamplerState& sampler_state = GetSamplerStateForTextureUnit( |
| uniform_info->type, texture_unit_index); |
| if (!texture_ref || |
| !texture_manager()->CanRenderWithSampler( |
| texture_ref, sampler_state)) { |
| textures_set = true; |
| api()->glActiveTextureFn(GL_TEXTURE0 + texture_unit_index); |
| api()->glBindTextureFn(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::UintToString(texture_unit_index)); |
| } else { |
| LOCAL_RENDER_WARNING( |
| std::string("texture bound to texture unit ") + |
| base::UintToString(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(); |
| if (DoBindOrCopyTexImageIfNeeded(texture, textarget, |
| GL_TEXTURE0 + texture_unit_index)) { |
| textures_set = true; |
| 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); |
| const SamplerState& sampler_state = GetSamplerStateForTextureUnit( |
| uniform_info->type, texture_unit_index); |
| if (!texture_ref || |
| !texture_manager()->CanRenderWithSampler( |
| texture_ref, sampler_state)) { |
| api()->glActiveTextureFn(GL_TEXTURE0 + texture_unit_index); |
| // Get the texture_ref info that was previously bound here. |
| texture_ref = |
| texture_unit.GetInfoForTarget(texture_unit.bind_target); |
| api()->glBindTextureFn(texture_unit.bind_target, |
| texture_ref ? texture_ref->service_id() : 0); |
| continue; |
| } |
| } |
| } |
| } |
| // Set the active texture back to whatever the user had it as. |
| api()->glActiveTextureFn(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); |
| if (texture_ref && !texture_ref->texture()->SafeToRenderFrom()) { |
| if (!texture_manager()->ClearRenderableLevels(this, texture_ref)) { |
| return false; |
| } |
| } |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::ValidateStencilStateForDraw(const char* function_name) { |
| if (!state_.stencil_state_changed_since_validation) { |
| return true; |
| } |
| |
| GLenum stencil_format = GetBoundFramebufferStencilFormat(GL_DRAW_FRAMEBUFFER); |
| uint8_t stencil_bits = GLES2Util::StencilBitsPerPixel(stencil_format); |
| |
| if (state_.enable_flags.stencil_test && stencil_bits > 0) { |
| DCHECK_LE(stencil_bits, 8U); |
| |
| GLuint max_stencil_value = (1 << stencil_bits) - 1; |
| GLint max_stencil_ref = static_cast<GLint>(max_stencil_value); |
| bool different_refs = |
| base::ClampToRange(state_.stencil_front_ref, 0, max_stencil_ref) != |
| base::ClampToRange(state_.stencil_back_ref, 0, max_stencil_ref); |
| bool different_writemasks = |
| (state_.stencil_front_writemask & max_stencil_value) != |
| (state_.stencil_back_writemask & max_stencil_value); |
| bool different_value_masks = |
| (state_.stencil_front_mask & max_stencil_value) != |
| (state_.stencil_back_mask & max_stencil_value); |
| if (different_refs || different_writemasks || different_value_masks) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "Front/back stencil settings do not match."); |
| return false; |
| } |
| } |
| |
| state_.stencil_state_changed_since_validation = 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; |
| } |
| |
| // Perform extra stencil validation on ANGLE/D3D, and for all WebGL contexts. |
| if (!feature_info_->feature_flags().separate_stencil_ref_mask_writemask) { |
| if (!ValidateStencilStateForDraw(function_name)) { |
| 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; |
| } |
| |
| if (!state_.vertex_attrib_manager->ValidateBindings( |
| function_name, this, feature_info_.get(), buffer_manager(), |
| state_.current_program.get(), max_vertex_accessed, instanced, |
| primcount)) { |
| return false; |
| } |
| |
| if (workarounds().disallow_large_instanced_draw) { |
| const GLsizei kMaxInstancedDrawPrimitiveCount = 0x4000000; |
| if (primcount > kMaxInstancedDrawPrimitiveCount) { |
| LOCAL_SET_GL_ERROR( |
| GL_OUT_OF_MEMORY, function_name, |
| "Instanced draw primcount too large for this platform"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::SimulateAttrib0( |
| const char* function_name, GLuint max_vertex_accessed, bool* simulated) { |
| DCHECK(simulated); |
| *simulated = false; |
| |
| if (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) != nullptr; |
| 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_t 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 significant performance penalty"); |
| |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(function_name); |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, attrib_0_buffer_id_); |
| |
| bool new_buffer = static_cast<GLsizei>(size_needed) > attrib_0_size_; |
| if (new_buffer) { |
| api()->glBufferDataFn(GL_ARRAY_BUFFER, size_needed, nullptr, |
| GL_DYNAMIC_DRAW); |
| GLenum error = api()->glGetErrorFn(); |
| 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); |
| constexpr GLuint kMaxVerticesPerLoop = 32u << 10; |
| const GLuint vertices_per_loop = |
| std::min(num_vertices, kMaxVerticesPerLoop); |
| std::vector<Vec4f> temp(vertices_per_loop, fvalue); |
| for (GLuint offset = 0; offset < num_vertices;) { |
| size_t count = std::min(num_vertices - offset, vertices_per_loop); |
| api()->glBufferSubDataFn(GL_ARRAY_BUFFER, offset * sizeof(Vec4f), |
| count * sizeof(Vec4f), temp.data()); |
| offset += count; |
| } |
| attrib_0_buffer_matches_value_ = true; |
| attrib_0_value_ = value; |
| attrib_0_size_ = size_needed; |
| } |
| |
| api()->glVertexAttribPointerFn(0, 4, GL_FLOAT, GL_FALSE, 0, nullptr); |
| |
| if (feature_info_->feature_flags().angle_instanced_arrays) |
| api()->glVertexAttribDivisorANGLEFn(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<const void*>(attrib->offset()); |
| Buffer* buffer = attrib->buffer(); |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, buffer ? buffer->service_id() : 0); |
| api()->glVertexAttribPointerFn(attrib_index, attrib->size(), attrib->type(), |
| attrib->normalized(), attrib->gl_stride(), |
| ptr); |
| } |
| |
| // Attrib divisors should only be non-zero when the ANGLE_instanced_arrays |
| // extension is available |
| DCHECK(attrib->divisor() == 0 || |
| feature_info_->feature_flags().angle_instanced_arrays); |
| |
| if (feature_info_->feature_flags().angle_instanced_arrays) |
| api()->glVertexAttribDivisorANGLEFn(attrib_index, attrib->divisor()); |
| api()->glBindBufferFn(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 with compatibility profile because it will |
| // never be re-enabled. |
| if (attrib_index != 0 || gl_version_info().BehavesLikeGLES()) { |
| // Restore the vertex attrib array enable-state according to |
| // the VertexAttrib enabled_in_driver value (which really represents the |
| // state of the virtual context - not the driver - notably, above the |
| // vertex array object emulation layer). |
| if (attrib->enabled_in_driver()) { |
| api()->glEnableVertexAttribArrayFn(attrib_index); |
| } else { |
| api()->glDisableVertexAttribArrayFn(attrib_index); |
| } |
| } |
| } |
| |
| bool GLES2DecoderImpl::SimulateFixedAttribs( |
| const char* function_name, |
| GLuint max_vertex_accessed, bool* simulated, GLsizei primcount) { |
| DCHECK(simulated); |
| *simulated = false; |
| if (gl_version_info().SupportsFixedType()) |
| return true; |
| |
| if (!state_.vertex_attrib_manager->HaveFixedAttribs()) { |
| return true; |
| } |
| |
| LOCAL_PERFORMANCE_WARNING( |
| "GL_FIXED attributes have a significant 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_t 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_t kSizeOfFloat = sizeof(float); // NOLINT |
| uint32_t 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); |
| |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, fixed_attrib_buffer_id_); |
| if (static_cast<GLsizei>(size_needed) > fixed_attrib_buffer_size_) { |
| api()->glBufferDataFn(GL_ARRAY_BUFFER, size_needed, nullptr, |
| GL_DYNAMIC_DRAW); |
| GLenum error = api()->glGetErrorFn(); |
| 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_t); |
| const int dst_size = num_elements * sizeof(float); |
| std::unique_ptr<float[]> data(new float[num_elements]); |
| const int32_t* src = reinterpret_cast<const int32_t*>( |
| attrib->buffer()->GetRange(attrib->offset(), src_size)); |
| const int32_t* end = src + num_elements; |
| float* dst = data.get(); |
| while (src != end) { |
| *dst++ = static_cast<float>(*src++) / 65536.0f; |
| } |
| api()->glBufferSubDataFn(GL_ARRAY_BUFFER, offset, dst_size, data.get()); |
| api()->glVertexAttribPointerFn(attrib->index(), attrib->size(), GL_FLOAT, |
| false, 0, |
| reinterpret_cast<GLvoid*>(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. |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, |
| state_.bound_array_buffer.get() |
| ? state_.bound_array_buffer->service_id() |
| : 0); |
| } |
| |
| bool GLES2DecoderImpl::AttribsTypeMatch() { |
| if (!state_.current_program.get()) |
| return true; |
| const std::vector<uint32_t>& shader_attrib_active_mask = |
| state_.current_program->vertex_input_active_mask(); |
| const std::vector<uint32_t>& shader_attrib_type_mask = |
| state_.current_program->vertex_input_base_type_mask(); |
| const std::vector<uint32_t>& generic_vertex_attrib_type_mask = |
| state_.generic_attrib_base_type_mask(); |
| const std::vector<uint32_t>& vertex_attrib_array_enabled_mask = |
| state_.vertex_attrib_manager->attrib_enabled_mask(); |
| const std::vector<uint32_t>& vertex_attrib_array_type_mask = |
| state_.vertex_attrib_manager->attrib_base_type_mask(); |
| DCHECK_EQ(shader_attrib_active_mask.size(), |
| shader_attrib_type_mask.size()); |
| DCHECK_EQ(shader_attrib_active_mask.size(), |
| generic_vertex_attrib_type_mask.size()); |
| DCHECK_EQ(shader_attrib_active_mask.size(), |
| vertex_attrib_array_enabled_mask.size()); |
| DCHECK_EQ(shader_attrib_active_mask.size(), |
| vertex_attrib_array_type_mask.size()); |
| for (size_t ii = 0; ii < shader_attrib_active_mask.size(); ++ii) { |
| uint32_t vertex_attrib_source_type_mask = |
| (~vertex_attrib_array_enabled_mask[ii] & |
| generic_vertex_attrib_type_mask[ii]) | |
| (vertex_attrib_array_enabled_mask[ii] & |
| vertex_attrib_array_type_mask[ii]); |
| if ((shader_attrib_type_mask[ii] & shader_attrib_active_mask[ii]) != |
| (vertex_attrib_source_type_mask & shader_attrib_active_mask[ii])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| 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 (!CheckBoundDrawFramebufferValid(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 (feature_info_->IsWebGL2OrES3Context()) { |
| if (!AttribsTypeMatch()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "vertexAttrib function must match shader attrib type"); |
| return error::kNoError; |
| } |
| |
| DCHECK(state_.bound_transform_feedback.get()); |
| if (state_.bound_transform_feedback->active() && |
| !state_.bound_transform_feedback->paused()) { |
| if (mode != state_.bound_transform_feedback->primitive_mode()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "mode differs from active transformfeedback's primitiveMode"); |
| return error::kNoError; |
| } |
| GLsizei vertices = 0; |
| bool valid = state_.bound_transform_feedback->GetVerticesNeededForDraw( |
| mode, count, primcount, &vertices); |
| if (!valid) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "integer overflow calculating number of vertices " |
| "for transform feedback"); |
| return error::kNoError; |
| } |
| if (!buffer_manager()->RequestBuffersAccess( |
| state_.GetErrorState(), state_.bound_transform_feedback.get(), |
| state_.current_program->GetTransformFeedbackVaryingSizes(), |
| vertices, function_name, "transformfeedback buffers")) { |
| return error::kNoError; |
| } |
| } |
| |
| if (!ValidateUniformBlockBackings(function_name)) { |
| return error::kNoError; |
| } |
| } |
| |
| if (count == 0 || primcount == 0) { |
| LOCAL_RENDER_WARNING("Render count or primcount is 0."); |
| return error::kNoError; |
| } |
| |
| base::CheckedNumeric<GLuint> checked_max_vertex = first; |
| checked_max_vertex += count - 1; |
| // first and count-1 are both a non-negative int, so their sum fits an |
| // unsigned int. |
| if (!checked_max_vertex.IsValid()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "first + count overflow"); |
| return error::kNoError; |
| } |
| GLuint max_vertex_accessed = checked_max_vertex.ValueOrDefault(0); |
| 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(); |
| if (!ValidateAndAdjustDrawBuffers(function_name)) { |
| return error::kNoError; |
| } |
| if (!instanced) { |
| api()->glDrawArraysFn(mode, first, count); |
| } else { |
| api()->glDrawArraysInstancedANGLEFn(mode, first, count, primcount); |
| } |
| if (state_.bound_transform_feedback.get()) { |
| state_.bound_transform_feedback->OnVerticesDrawn(mode, 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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile cmds::DrawArrays& c = |
| *static_cast<const volatile cmds::DrawArrays*>(cmd_data); |
| return DoDrawArrays("glDrawArrays", |
| false, |
| static_cast<GLenum>(c.mode), |
| static_cast<GLint>(c.first), |
| static_cast<GLsizei>(c.count), |
| 1); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleDrawArraysInstancedANGLE( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::DrawArraysInstancedANGLE& c = |
| *static_cast<const volatile gles2::cmds::DrawArraysInstancedANGLE*>( |
| cmd_data); |
| if (!features().angle_instanced_arrays) |
| return error::kUnknownCommand; |
| |
| return DoDrawArrays("glDrawArraysInstancedANGLE", true, |
| static_cast<GLenum>(c.mode), static_cast<GLint>(c.first), |
| static_cast<GLsizei>(c.count), |
| static_cast<GLsizei>(c.primcount)); |
| } |
| |
| error::Error GLES2DecoderImpl::DoDrawElements(const char* function_name, |
| bool instanced, |
| GLenum mode, |
| GLsizei count, |
| GLenum type, |
| int32_t offset, |
| GLsizei primcount) { |
| error::Error error = WillAccessBoundFramebufferForDraw(); |
| if (error != error::kNoError) |
| return error; |
| |
| 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; |
| } |
| Buffer* element_array_buffer = buffer_manager()->RequestBufferAccess( |
| &state_, GL_ELEMENT_ARRAY_BUFFER, function_name); |
| if (!element_array_buffer) { |
| return error::kNoError; |
| } |
| |
| if (!CheckBoundDrawFramebufferValid(function_name)) { |
| return error::kNoError; |
| } |
| |
| if (state_.bound_transform_feedback.get() && |
| state_.bound_transform_feedback->active() && |
| !state_.bound_transform_feedback->paused()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "transformfeedback is active and not paused"); |
| return error::kNoError; |
| } |
| |
| if (feature_info_->IsWebGL2OrES3Context()) { |
| if (!AttribsTypeMatch()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "vertexAttrib function must match shader attrib type"); |
| return error::kNoError; |
| } |
| if (!ValidateUniformBlockBackings(function_name)) { |
| return error::kNoError; |
| } |
| } |
| |
| if (count == 0 || primcount == 0) { |
| return error::kNoError; |
| } |
| |
| GLuint max_vertex_accessed; |
| if (!element_array_buffer->GetMaxValueForRange( |
| offset, count, type, |
| state_.enable_flags.primitive_restart_fixed_index, |
| &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<const GLvoid*>(offset); |
| bool used_client_side_array = false; |
| if (element_array_buffer->IsClientSideArray()) { |
| used_client_side_array = true; |
| api()->glBindBufferFn(GL_ELEMENT_ARRAY_BUFFER, 0); |
| indices = element_array_buffer->GetRange(offset, 0); |
| } |
| if (!ValidateAndAdjustDrawBuffers(function_name)) { |
| return error::kNoError; |
| } |
| if (state_.enable_flags.primitive_restart_fixed_index && |
| feature_info_->feature_flags(). |
| emulate_primitive_restart_fixed_index) { |
| api()->glEnableFn(GL_PRIMITIVE_RESTART); |
| buffer_manager()->SetPrimitiveRestartFixedIndexIfNecessary(type); |
| } |
| if (!instanced) { |
| api()->glDrawElementsFn(mode, count, type, indices); |
| } else { |
| api()->glDrawElementsInstancedANGLEFn(mode, count, type, indices, |
| primcount); |
| } |
| if (state_.enable_flags.primitive_restart_fixed_index && |
| feature_info_->feature_flags(). |
| emulate_primitive_restart_fixed_index) { |
| api()->glDisableFn(GL_PRIMITIVE_RESTART); |
| } |
| if (used_client_side_array) { |
| api()->glBindBufferFn(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::DrawElements& c = |
| *static_cast<const volatile gles2::cmds::DrawElements*>(cmd_data); |
| return DoDrawElements("glDrawElements", false, static_cast<GLenum>(c.mode), |
| static_cast<GLsizei>(c.count), |
| static_cast<GLenum>(c.type), |
| static_cast<int32_t>(c.index_offset), 1); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleDrawElementsInstancedANGLE( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::DrawElementsInstancedANGLE& c = |
| *static_cast<const volatile gles2::cmds::DrawElementsInstancedANGLE*>( |
| cmd_data); |
| if (!features().angle_instanced_arrays) |
| return error::kUnknownCommand; |
| |
| return DoDrawElements( |
| "glDrawElementsInstancedANGLE", true, static_cast<GLenum>(c.mode), |
| static_cast<GLsizei>(c.count), static_cast<GLenum>(c.type), |
| static_cast<int32_t>(c.index_offset), static_cast<GLsizei>(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 { |
| // The max value is used here to emulate client-side vertex |
| // arrays, by uploading enough vertices into buffer objects to |
| // cover the DrawElements call. Baking the primitive restart bit |
| // into this result isn't strictly correct in all cases; the |
| // client side code should pass down the bit and decide how to use |
| // the result. However, the only caller makes the draw call |
| // immediately afterward, so the state won't change between this |
| // query and the draw call. |
| if (!buffer->GetMaxValueForRange( |
| offset, count, type, |
| state_.enable_flags.primitive_restart_fixed_index, |
| &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); |
| } |
| |
| scoped_refptr<ShaderTranslatorInterface> GLES2DecoderImpl::GetTranslator( |
| GLenum type) { |
| return type == GL_VERTEX_SHADER ? vertex_translator_ : fragment_translator_; |
| } |
| |
| scoped_refptr<ShaderTranslatorInterface> |
| GLES2DecoderImpl::GetOrCreateTranslator(GLenum type) { |
| if (!InitializeShaderTranslator()) { |
| return nullptr; |
| } |
| return GetTranslator(type); |
| } |
| |
| void GLES2DecoderImpl::DoCompileShader(GLuint client_id) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoCompileShader"); |
| Shader* shader = GetShaderInfoNotProgram(client_id, "glCompileShader"); |
| if (!shader) { |
| return; |
| } |
| |
| scoped_refptr<ShaderTranslatorInterface> translator; |
| if (!feature_info_->disable_shader_translator()) |
| translator = GetOrCreateTranslator(shader->shader_type()); |
| |
| 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, |
| GLsizei params_size) { |
| 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: |
| CompileShaderAndExitCommandProcessingEarly(shader); |
| 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; |
| } |
| api()->glGetShaderivFn(shader->service_id(), pname, params); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetShaderSource( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetShaderSource& c = |
| *static_cast<const volatile gles2::cmds::GetShaderSource*>(cmd_data); |
| GLuint shader_id = c.shader; |
| uint32_t bucket_id = static_cast<uint32_t>(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetTranslatedShaderSourceANGLE& c = |
| *static_cast<const volatile gles2::cmds::GetTranslatedShaderSourceANGLE*>( |
| cmd_data); |
| GLuint shader_id = c.shader; |
| uint32_t bucket_id = static_cast<uint32_t>(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. |
| CompileShaderAndExitCommandProcessingEarly(shader); |
| |
| bucket->SetFromString(shader->translated_source().c_str()); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetProgramInfoLog( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetProgramInfoLog& c = |
| *static_cast<const volatile gles2::cmds::GetProgramInfoLog*>(cmd_data); |
| GLuint program_id = c.program; |
| uint32_t bucket_id = static_cast<uint32_t>(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetShaderInfoLog& c = |
| *static_cast<const volatile gles2::cmds::GetShaderInfoLog*>(cmd_data); |
| GLuint shader_id = c.shader; |
| uint32_t bucket_id = static_cast<uint32_t>(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. |
| CompileShaderAndExitCommandProcessingEarly(shader); |
| |
| 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 != nullptr && !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 != nullptr && !shader->IsDeleted(); |
| } |
| |
| bool GLES2DecoderImpl::DoIsTexture(GLuint client_id) { |
| const TextureRef* texture_ref = GetTexture(client_id); |
| return texture_ref && texture_ref->texture()->IsValid(); |
| } |
| |
| bool GLES2DecoderImpl::DoIsSampler(GLuint client_id) { |
| const Sampler* sampler = GetSampler(client_id); |
| return sampler && !sampler->IsDeleted(); |
| } |
| |
| bool GLES2DecoderImpl::DoIsTransformFeedback(GLuint client_id) { |
| const TransformFeedback* transform_feedback = |
| GetTransformFeedback(client_id); |
| return transform_feedback && transform_feedback->has_been_bound(); |
| } |
| |
| 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; |
| } |
| api()->glAttachShaderFn(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->IsShaderAttached(shader)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glDetachShader", "shader not attached to program"); |
| return; |
| } |
| api()->glDetachShaderFn(program->service_id(), shader->service_id()); |
| program->DetachShader(shader_manager(), shader); |
| } |
| |
| 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::DoGetSamplerParameterfv(GLuint client_id, |
| GLenum pname, |
| GLfloat* params, |
| GLsizei params_size) { |
| Sampler* sampler = GetSampler(client_id); |
| if (!sampler) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glGetSamplerParamterfv", "unknown sampler"); |
| return; |
| } |
| api()->glGetSamplerParameterfvFn(sampler->service_id(), pname, params); |
| } |
| |
| void GLES2DecoderImpl::DoGetSamplerParameteriv(GLuint client_id, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| Sampler* sampler = GetSampler(client_id); |
| if (!sampler) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glGetSamplerParamteriv", "unknown sampler"); |
| return; |
| } |
| api()->glGetSamplerParameterivFn(sampler->service_id(), pname, params); |
| } |
| |
| void GLES2DecoderImpl::GetTexParameterImpl( |
| GLenum target, GLenum pname, GLfloat* fparams, GLint* iparams, |
| const char* function_name) { |
| 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; |
| } |
| Texture* texture = texture_ref->texture(); |
| switch (pname) { |
| case GL_TEXTURE_MAX_ANISOTROPY_EXT: |
| if (workarounds().init_texture_max_anisotropy) { |
| texture->InitTextureMaxAnisotropyIfNeeded(target); |
| } |
| break; |
| case GL_TEXTURE_IMMUTABLE_LEVELS: |
| if (gl_version_info().IsLowerThanGL(4, 2)) { |
| GLint levels = texture->GetImmutableLevels(); |
| if (fparams) { |
| fparams[0] = static_cast<GLfloat>(levels); |
| } else { |
| iparams[0] = levels; |
| } |
| return; |
| } |
| break; |
| case GL_TEXTURE_BASE_LEVEL: |
| // Use shadowed value in case it's clamped; also because older MacOSX |
| // stores the value on int16_t (see https://crbug.com/610153). |
| if (fparams) { |
| fparams[0] = static_cast<GLfloat>(texture->unclamped_base_level()); |
| } else { |
| iparams[0] = texture->unclamped_base_level(); |
| } |
| return; |
| case GL_TEXTURE_MAX_LEVEL: |
| // Use shadowed value in case it's clamped; also because older MacOSX |
| // stores the value on int16_t (see https://crbug.com/610153). |
| if (fparams) { |
| fparams[0] = static_cast<GLfloat>(texture->unclamped_max_level()); |
| } else { |
| iparams[0] = texture->unclamped_max_level(); |
| } |
| return; |
| case GL_TEXTURE_SWIZZLE_R: |
| if (fparams) { |
| fparams[0] = static_cast<GLfloat>(texture->swizzle_r()); |
| } else { |
| iparams[0] = texture->swizzle_r(); |
| } |
| return; |
| case GL_TEXTURE_SWIZZLE_G: |
| if (fparams) { |
| fparams[0] = static_cast<GLfloat>(texture->swizzle_g()); |
| } else { |
| iparams[0] = texture->swizzle_g(); |
| } |
| return; |
| case GL_TEXTURE_SWIZZLE_B: |
| if (fparams) { |
| fparams[0] = static_cast<GLfloat>(texture->swizzle_b()); |
| } else { |
| iparams[0] = texture->swizzle_b(); |
| } |
| return; |
| case GL_TEXTURE_SWIZZLE_A: |
| if (fparams) { |
| fparams[0] = static_cast<GLfloat>(texture->swizzle_a()); |
| } else { |
| iparams[0] = texture->swizzle_a(); |
| } |
| return; |
| default: |
| break; |
| } |
| if (fparams) { |
| api()->glGetTexParameterfvFn(target, pname, fparams); |
| } else { |
| api()->glGetTexParameterivFn(target, pname, iparams); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoGetTexParameterfv(GLenum target, |
| GLenum pname, |
| GLfloat* params, |
| GLsizei params_size) { |
| GetTexParameterImpl(target, pname, params, nullptr, "glGetTexParameterfv"); |
| } |
| |
| void GLES2DecoderImpl::DoGetTexParameteriv(GLenum target, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| GetTexParameterImpl(target, pname, nullptr, params, "glGetTexParameteriv"); |
| } |
| |
| template <typename T> |
| 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<T>(value); |
| break; |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DoGetVertexAttribfv(GLuint index, |
| GLenum pname, |
| GLfloat* params, |
| GLsizei params_size) { |
| DoGetVertexAttribImpl<GLfloat>(index, pname, params); |
| } |
| |
| void GLES2DecoderImpl::DoGetVertexAttribiv(GLuint index, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| DoGetVertexAttribImpl<GLint>(index, pname, params); |
| } |
| |
| void GLES2DecoderImpl::DoGetVertexAttribIiv(GLuint index, |
| GLenum pname, |
| GLint* params, |
| GLsizei params_size) { |
| DoGetVertexAttribImpl<GLint>(index, pname, params); |
| } |
| |
| void GLES2DecoderImpl::DoGetVertexAttribIuiv(GLuint index, |
| GLenum pname, |
| GLuint* params, |
| GLsizei params_size) { |
| DoGetVertexAttribImpl<GLuint>(index, pname, params); |
| } |
| |
| template <typename T> |
| 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)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_FLOAT); |
| api()->glVertexAttrib1fFn(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)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_FLOAT); |
| api()->glVertexAttrib2fFn(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)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_FLOAT); |
| api()->glVertexAttrib3fFn(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)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_FLOAT); |
| api()->glVertexAttrib4fFn(index, v0, v1, v2, v3); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoVertexAttrib1fv(GLuint index, |
| const volatile GLfloat* v) { |
| GLfloat t[4] = { v[0], 0.0f, 0.0f, 1.0f, }; |
| if (SetVertexAttribValue("glVertexAttrib1fv", index, t)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_FLOAT); |
| api()->glVertexAttrib1fvFn(index, t); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoVertexAttrib2fv(GLuint index, |
| const volatile GLfloat* v) { |
| GLfloat t[4] = { v[0], v[1], 0.0f, 1.0f, }; |
| if (SetVertexAttribValue("glVertexAttrib2fv", index, t)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_FLOAT); |
| api()->glVertexAttrib2fvFn(index, t); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoVertexAttrib3fv(GLuint index, |
| const volatile GLfloat* v) { |
| GLfloat t[4] = { v[0], v[1], v[2], 1.0f, }; |
| if (SetVertexAttribValue("glVertexAttrib3fv", index, t)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_FLOAT); |
| api()->glVertexAttrib3fvFn(index, t); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoVertexAttrib4fv(GLuint index, |
| const volatile GLfloat* v) { |
| GLfloat t[4] = {v[0], v[1], v[2], v[3]}; |
| if (SetVertexAttribValue("glVertexAttrib4fv", index, t)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_FLOAT); |
| api()->glVertexAttrib4fvFn(index, t); |
| } |
| } |
| |
| 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)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_INT); |
| api()->glVertexAttribI4iFn(index, v0, v1, v2, v3); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoVertexAttribI4iv(GLuint index, |
| const volatile GLint* v) { |
| GLint t[4] = {v[0], v[1], v[2], v[3]}; |
| if (SetVertexAttribValue("glVertexAttribI4iv", index, t)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_INT); |
| api()->glVertexAttribI4ivFn(index, t); |
| } |
| } |
| |
| 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)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_UINT); |
| api()->glVertexAttribI4uiFn(index, v0, v1, v2, v3); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoVertexAttribI4uiv(GLuint index, |
| const volatile GLuint* v) { |
| GLuint t[4] = {v[0], v[1], v[2], v[3]}; |
| if (SetVertexAttribValue("glVertexAttribI4uiv", index, t)) { |
| state_.SetGenericVertexAttribBaseType( |
| index, SHADER_VARIABLE_UINT); |
| api()->glVertexAttribI4uivFn(index, t); |
| } |
| } |
| |
| error::Error GLES2DecoderImpl::HandleVertexAttribIPointer( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::VertexAttribIPointer& c = |
| *static_cast<const volatile gles2::cmds::VertexAttribIPointer*>(cmd_data); |
| GLuint indx = c.indx; |
| GLint size = c.size; |
| GLenum type = c.type; |
| GLsizei stride = c.stride; |
| GLsizei offset = c.offset; |
| |
| if (!state_.bound_array_buffer.get() || |
| state_.bound_array_buffer->IsDeleted()) { |
| if (offset != 0) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glVertexAttribIPointer", "offset != 0"); |
| return error::kNoError; |
| } |
| } |
| |
| const void* ptr = reinterpret_cast<const void*>(offset); |
| if (!validators_->vertex_attrib_i_type.IsValid(type)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM("glVertexAttribIPointer", type, "type"); |
| return error::kNoError; |
| } |
| if (size < 1 || size > 4) { |
| 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 type_size = GLES2Util::GetGLTypeSizeForBuffers(type); |
| // type_size must be a power of two to use & as optimized modulo. |
| DCHECK(GLES2Util::IsPOT(type_size)); |
| if (offset & (type_size - 1)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glVertexAttribIPointer", "offset not valid for type"); |
| return error::kNoError; |
| } |
| if (stride & (type_size - 1)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glVertexAttribIPointer", "stride not valid for type"); |
| return error::kNoError; |
| } |
| |
| GLenum base_type = (type == GL_BYTE || type == GL_SHORT || type == GL_INT) ? |
| SHADER_VARIABLE_INT : SHADER_VARIABLE_UINT; |
| state_.vertex_attrib_manager->UpdateAttribBaseTypeAndMask(indx, base_type); |
| |
| GLsizei group_size = GLES2Util::GetGroupSizeForBufferType(size, type); |
| state_.vertex_attrib_manager |
| ->SetAttribInfo(indx, |
| state_.bound_array_buffer.get(), |
| size, |
| type, |
| GL_FALSE, |
| stride, |
| stride != 0 ? stride : group_size, |
| offset, |
| GL_TRUE); |
| api()->glVertexAttribIPointerFn(indx, size, type, stride, ptr); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleVertexAttribPointer( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::VertexAttribPointer& c = |
| *static_cast<const volatile gles2::cmds::VertexAttribPointer*>(cmd_data); |
| GLuint indx = c.indx; |
| GLint size = c.size; |
| GLenum type = c.type; |
| GLboolean normalized = static_cast<GLboolean>(c.normalized); |
| GLsizei stride = c.stride; |
| GLsizei offset = c.offset; |
| |
| if (!state_.bound_array_buffer.get() || |
| state_.bound_array_buffer->IsDeleted()) { |
| if (offset != 0) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glVertexAttribPointer", "offset != 0"); |
| return error::kNoError; |
| } |
| } |
| const void* ptr = reinterpret_cast<const void*>(offset); |
| if (!validators_->vertex_attrib_type.IsValid(type)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM("glVertexAttribPointer", type, "type"); |
| return error::kNoError; |
| } |
| if (size < 1 || size > 4) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glVertexAttribPointer", "size GL_INVALID_VALUE"); |
| return error::kNoError; |
| } |
| if ((type == GL_INT_2_10_10_10_REV || type == GL_UNSIGNED_INT_2_10_10_10_REV) |
| && size != 4) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glVertexAttribPointer", "size != 4"); |
| 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 type_size = GLES2Util::GetGLTypeSizeForBuffers(type); |
| // type_size must be a power of two to use & as optimized modulo. |
| DCHECK(GLES2Util::IsPOT(type_size)); |
| if (offset & (type_size - 1)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glVertexAttribPointer", "offset not valid for type"); |
| return error::kNoError; |
| } |
| if (stride & (type_size - 1)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glVertexAttribPointer", "stride not valid for type"); |
| return error::kNoError; |
| } |
| |
| state_.vertex_attrib_manager->UpdateAttribBaseTypeAndMask( |
| indx, SHADER_VARIABLE_FLOAT); |
| |
| GLsizei group_size = GLES2Util::GetGroupSizeForBufferType(size, type); |
| state_.vertex_attrib_manager |
| ->SetAttribInfo(indx, |
| state_.bound_array_buffer.get(), |
| size, |
| type, |
| normalized, |
| stride, |
| stride != 0 ? stride : group_size, |
| offset, |
| GL_FALSE); |
| if (type != GL_FIXED || gl_version_info().SupportsFixedType()) { |
| api()->glVertexAttribPointerFn(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_); |
| gfx::Vector2d viewport_offset = GetBoundFramebufferDrawOffset(); |
| api()->glViewportFn(x + viewport_offset.x(), y + viewport_offset.y(), width, |
| height); |
| } |
| |
| void GLES2DecoderImpl::DoScissor(GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height) { |
| gfx::Vector2d draw_offset = GetBoundFramebufferDrawOffset(); |
| api()->glScissorFn(x + draw_offset.x(), y + draw_offset.y(), width, height); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleVertexAttribDivisorANGLE( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::VertexAttribDivisorANGLE& c = |
| *static_cast<const volatile gles2::cmds::VertexAttribDivisorANGLE*>( |
| cmd_data); |
| if (!features().angle_instanced_arrays) |
| return error::kUnknownCommand; |
| |
| 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); |
| api()->glVertexAttribDivisorANGLEFn(index, divisor); |
| return error::kNoError; |
| } |
| |
| template <typename pixel_data_type> |
| static void WriteAlphaData(void* pixels, |
| uint32_t row_count, |
| uint32_t channel_count, |
| uint32_t alpha_channel_index, |
| uint32_t unpadded_row_size, |
| uint32_t padded_row_size, |
| pixel_data_type alpha_value) { |
| DCHECK_GT(channel_count, 0U); |
| DCHECK_EQ(unpadded_row_size % sizeof(pixel_data_type), 0U); |
| uint32_t unpadded_row_size_in_elements = |
| unpadded_row_size / sizeof(pixel_data_type); |
| DCHECK_EQ(padded_row_size % sizeof(pixel_data_type), 0U); |
| uint32_t padded_row_size_in_elements = |
| padded_row_size / sizeof(pixel_data_type); |
| pixel_data_type* dst = |
| static_cast<pixel_data_type*>(pixels) + alpha_channel_index; |
| for (uint32_t 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(GLsizei width, |
| GLsizei height, |
| GLsizei format, |
| GLsizei type, |
| uint32_t pixels_shm_id, |
| uint32_t pixels_shm_offset, |
| uint32_t result_shm_id, |
| uint32_t result_shm_offset, |
| GLint pack_alignment, |
| GLenum read_format, |
| GLuint buffer) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::FinishReadPixels"); |
| typedef cmds::ReadPixels::Result Result; |
| uint32_t pixels_size; |
| Result* result = nullptr; |
| if (result_shm_id != 0) { |
| result = GetSharedMemoryAs<Result*>(result_shm_id, result_shm_offset, |
| sizeof(*result)); |
| if (!result) { |
| if (buffer != 0) { |
| api()->glDeleteBuffersARBFn(1, &buffer); |
| } |
| return; |
| } |
| } |
| GLES2Util::ComputeImageDataSizes(width, height, 1, format, type, |
| pack_alignment, &pixels_size, nullptr, |
| nullptr); |
| void* pixels = |
| GetSharedMemoryAs<void*>(pixels_shm_id, pixels_shm_offset, pixels_size); |
| if (!pixels) { |
| if (buffer != 0) { |
| api()->glDeleteBuffersARBFn(1, &buffer); |
| } |
| return; |
| } |
| |
| if (buffer != 0) { |
| api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, buffer); |
| void* data; |
| if (features().map_buffer_range) { |
| data = api()->glMapBufferRangeFn(GL_PIXEL_PACK_BUFFER_ARB, 0, pixels_size, |
| GL_MAP_READ_BIT); |
| } else { |
| data = api()->glMapBufferFn(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); |
| api()->glUnmapBufferFn(GL_PIXEL_PACK_BUFFER_ARB); |
| api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, |
| GetServiceId(state_.bound_pixel_pack_buffer.get())); |
| api()->glDeleteBuffersARBFn(1, &buffer); |
| } |
| |
| if (result != nullptr) { |
| result->success = 1; |
| } |
| } |
| |
| error::Error GLES2DecoderImpl::HandleReadPixels(uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const char* func_name = "glReadPixels"; |
| const volatile gles2::cmds::ReadPixels& c = |
| *static_cast<const volatile gles2::cmds::ReadPixels*>(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; |
| uint32_t pixels_shm_id = c.pixels_shm_id; |
| uint32_t pixels_shm_offset = c.pixels_shm_offset; |
| uint32_t result_shm_id = c.result_shm_id; |
| uint32_t result_shm_offset = c.result_shm_offset; |
| GLboolean async = static_cast<GLboolean>(c.async); |
| if (width < 0 || height < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "dimensions < 0"); |
| return error::kNoError; |
| } |
| typedef cmds::ReadPixels::Result Result; |
| |
| PixelStoreParams params; |
| if (pixels_shm_id == 0) { |
| params = state_.GetPackParams(); |
| } else { |
| // When reading into client buffer, we actually set pack parameters to 0 |
| // (except for alignment) before calling glReadPixels. This makes sure we |
| // only send back meaningful pixel data to the command buffer client side, |
| // and the client side will take the responsibility to take the pixels and |
| // write to the client buffer according to the full ES3 pack parameters. |
| params.alignment = state_.pack_alignment; |
| } |
| uint32_t pixels_size = 0; |
| uint32_t unpadded_row_size = 0; |
| uint32_t padded_row_size = 0; |
| uint32_t skip_size = 0; |
| uint32_t padding = 0; |
| if (!GLES2Util::ComputeImageDataSizesES3(width, height, 1, |
| format, type, |
| params, |
| &pixels_size, |
| &unpadded_row_size, |
| &padded_row_size, |
| &skip_size, |
| &padding)) { |
| return error::kOutOfBounds; |
| } |
| |
| uint8_t* pixels = nullptr; |
| Buffer* buffer = state_.bound_pixel_pack_buffer.get(); |
| if (pixels_shm_id == 0) { |
| if (!buffer) { |
| return error::kInvalidArguments; |
| } |
| if (!buffer_manager()->RequestBufferAccess( |
| state_.GetErrorState(), buffer, func_name, "pixel pack buffer")) { |
| return error::kNoError; |
| } |
| uint32_t size = 0; |
| if (!SafeAddUint32(pixels_size + skip_size, pixels_shm_offset, &size)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "size + offset overflow"); |
| return error::kNoError; |
| } |
| if (static_cast<uint32_t>(buffer->size()) < size) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glReadPixels", |
| "pixel pack buffer is not large enough"); |
| return error::kNoError; |
| } |
| pixels = reinterpret_cast<uint8_t *>(pixels_shm_offset); |
| pixels += skip_size; |
| } else { |
| if (buffer) { |
| return error::kInvalidArguments; |
| } |
| DCHECK_EQ(0u, skip_size); |
| pixels = GetSharedMemoryAs<uint8_t*>( |
| pixels_shm_id, pixels_shm_offset, pixels_size); |
| if (!pixels) { |
| return error::kOutOfBounds; |
| } |
| } |
| |
| Result* result = nullptr; |
| if (result_shm_id != 0) { |
| result = GetSharedMemoryAs<Result*>( |
| result_shm_id, result_shm_offset, sizeof(*result)); |
| if (!result) { |
| return error::kOutOfBounds; |
| } |
| if (result->success != 0) { |
| return error::kInvalidArguments; |
| } |
| } |
| |
| if (!validators_->read_pixel_format.IsValid(format)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, format, "format"); |
| return error::kNoError; |
| } |
| if (!validators_->read_pixel_type.IsValid(type)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, type, "type"); |
| return error::kNoError; |
| } |
| |
| if (!CheckBoundReadFramebufferValid( |
| func_name, GL_INVALID_FRAMEBUFFER_OPERATION)) { |
| return error::kNoError; |
| } |
| GLenum src_internal_format = GetBoundReadFramebufferInternalFormat(); |
| if (src_internal_format == 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "no valid color image"); |
| return error::kNoError; |
| } |
| std::vector<GLenum> accepted_formats; |
| std::vector<GLenum> accepted_types; |
| switch (src_internal_format) { |
| case GL_R8UI: |
| case GL_R16UI: |
| case GL_R32UI: |
| case GL_RG8UI: |
| case GL_RG16UI: |
| case GL_RG32UI: |
| // All the RGB_INTEGER formats are not renderable. |
| case GL_RGBA8UI: |
| case GL_RGB10_A2UI: |
| case GL_RGBA16UI: |
| case GL_RGBA32UI: |
| accepted_formats.push_back(GL_RGBA_INTEGER); |
| accepted_types.push_back(GL_UNSIGNED_INT); |
| break; |
| case GL_R8I: |
| case GL_R16I: |
| case GL_R32I: |
| case GL_RG8I: |
| case GL_RG16I: |
| case GL_RG32I: |
| case GL_RGBA8I: |
| case GL_RGBA16I: |
| case GL_RGBA32I: |
| accepted_formats.push_back(GL_RGBA_INTEGER); |
| accepted_types.push_back(GL_INT); |
| break; |
| case GL_RGB10_A2: |
| accepted_formats.push_back(GL_RGBA); |
| accepted_types.push_back(GL_UNSIGNED_BYTE); |
| // Special case with an extra supported format/type. |
| accepted_formats.push_back(GL_RGBA); |
| accepted_types.push_back(GL_UNSIGNED_INT_2_10_10_10_REV); |
| break; |
| default: |
| accepted_formats.push_back(GL_RGBA); |
| { |
| GLenum src_type = GetBoundReadFramebufferTextureType(); |
| switch (src_type) { |
| case GL_HALF_FLOAT: |
| case GL_HALF_FLOAT_OES: |
| case GL_FLOAT: |
| case GL_UNSIGNED_INT_10F_11F_11F_REV: |
| accepted_types.push_back(GL_FLOAT); |
| break; |
| default: |
| accepted_types.push_back(GL_UNSIGNED_BYTE); |
| break; |
| } |
| } |
| break; |
| } |
| if (!feature_info_->IsWebGLContext()) { |
| accepted_formats.push_back(GL_BGRA_EXT); |
| accepted_types.push_back(GL_UNSIGNED_BYTE); |
| } |
| DCHECK_EQ(accepted_formats.size(), accepted_types.size()); |
| bool format_type_acceptable = false; |
| for (size_t ii = 0; ii < accepted_formats.size(); ++ii) { |
| if (format == accepted_formats[ii] && type == accepted_types[ii]) { |
| format_type_acceptable = true; |
| break; |
| } |
| } |
| if (!format_type_acceptable) { |
| // 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, 1); |
| GLint preferred_type = 0; |
| DoGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &preferred_type, 1); |
| if (format == static_cast<GLenum>(preferred_format) && |
| type == static_cast<GLenum>(preferred_type)) { |
| format_type_acceptable = true; |
| } |
| } |
| if (!format_type_acceptable) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "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_t max_x; |
| int32_t max_y; |
| if (!SafeAddInt32(x, width, &max_x) || !SafeAddInt32(y, height, &max_y)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "dimensions out of range"); |
| return error::kNoError; |
| } |
| |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(func_name); |
| |
| ScopedResolvedFramebufferBinder binder(this, false, true); |
| GLenum read_format = GetBoundReadFramebufferInternalFormat(); |
| |
| gfx::Rect rect(x, y, width, height); // Safe before we checked above. |
| gfx::Rect max_rect(max_size); |
| if (!max_rect.Contains(rect)) { |
| rect.Intersect(max_rect); |
| if (!rect.IsEmpty()) { |
| std::unique_ptr<ScopedFramebufferCopyBinder> binder; |
| if (workarounds() |
| .use_copyteximage2d_instead_of_readpixels_on_multisampled_textures && |
| framebuffer_state_.bound_read_framebuffer.get() && |
| framebuffer_state_.bound_read_framebuffer.get() |
| ->GetReadBufferIsMultisampledTexture()) { |
| binder = std::make_unique<ScopedFramebufferCopyBinder>(this); |
| } |
| if (y < 0) { |
| pixels += static_cast<uint32_t>(-y) * padded_row_size;; |
| } |
| if (x < 0) { |
| uint32_t group_size = GLES2Util::ComputeImageGroupSize(format, type); |
| uint32_t leading_bytes = static_cast<uint32_t>(-x) * group_size; |
| pixels += leading_bytes; |
| } |
| for (GLint iy = rect.y(); iy < rect.bottom(); ++iy) { |
| bool reset_row_length = false; |
| if (iy + 1 == max_y && pixels_shm_id == 0 && |
| workarounds().pack_parameters_workaround_with_pack_buffer && |
| state_.pack_row_length > 0 && state_.pack_row_length < width) { |
| // Some drivers (for example, Mac AMD) incorrecly limit the last |
| // row to ROW_LENGTH in this case. |
| api()->glPixelStoreiFn(GL_PACK_ROW_LENGTH, width); |
| reset_row_length = true; |
| } |
| api()->glReadPixelsFn(rect.x(), iy, rect.width(), 1, format, type, |
| pixels); |
| if (reset_row_length) { |
| api()->glPixelStoreiFn(GL_PACK_ROW_LENGTH, state_.pack_row_length); |
| } |
| pixels += padded_row_size; |
| } |
| } |
| } else { |
| if (async && features().use_async_readpixels && |
| !state_.bound_pixel_pack_buffer.get()) { |
| // This workaround isn't implemented in this code path yet. Hopefully it's |
| // unnecessary. |
| DCHECK( |
| !workarounds() |
| .use_copyteximage2d_instead_of_readpixels_on_multisampled_textures || |
| !framebuffer_state_.bound_read_framebuffer.get() || |
| !framebuffer_state_.bound_read_framebuffer.get() |
| ->GetReadBufferIsMultisampledTexture()); |
| // To simply the state tracking, we don't go down the async path if |
| // a PIXEL_PACK_BUFFER is bound (in which case the client can |
| // implement something similar on their own - all necessary functions |
| // should be exposed). |
| GLuint buffer = 0; |
| api()->glGenBuffersARBFn(1, &buffer); |
| api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, buffer); |
| // For ANGLE client version 2, GL_STREAM_READ is not available. |
| const GLenum usage_hint = |
| gl_version_info().is_angle ? GL_STATIC_DRAW : GL_STREAM_READ; |
| api()->glBufferDataFn(GL_PIXEL_PACK_BUFFER_ARB, pixels_size, nullptr, |
| usage_hint); |
| GLenum error = api()->glGetErrorFn(); |
| if (error == GL_NO_ERROR) { |
| // No need to worry about ES3 pixel pack parameters, because no |
| // PIXEL_PACK_BUFFER is bound, and all these settings haven't been |
| // sent to GL. |
| api()->glReadPixelsFn(x, y, width, height, format, type, 0); |
| pending_readpixel_fences_.push(FenceCallback()); |
| WaitForReadPixels(base::BindOnce( |
| &GLES2DecoderImpl::FinishReadPixels, weak_ptr_factory_.GetWeakPtr(), |
| width, height, format, type, pixels_shm_id, pixels_shm_offset, |
| result_shm_id, result_shm_offset, state_.pack_alignment, |
| read_format, buffer)); |
| api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, 0); |
| return error::kNoError; |
| } else { |
| // On error, unbind pack buffer and fall through to sync readpixels |
| api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, 0); |
| api()->glDeleteBuffersARBFn(1, &buffer); |
| } |
| } |
| if (pixels_shm_id == 0 && |
| workarounds().pack_parameters_workaround_with_pack_buffer) { |
| // If we ever have a device that needs both of these workarounds, we'll |
| // need to do some extra work to implement that correctly here. |
| DCHECK( |
| !workarounds() |
| .use_copyteximage2d_instead_of_readpixels_on_multisampled_textures); |
| if (state_.pack_row_length > 0 && state_.pack_row_length < width) { |
| // Some drivers (for example, NVidia Linux) reset in this case. |
| // Some drivers (for example, Mac AMD) incorrecly limit the last |
| // row to ROW_LENGTH in this case. |
| api()->glPixelStoreiFn(GL_PACK_ROW_LENGTH, width); |
| for (GLint iy = y; iy < max_y; ++iy) { |
| // Need to set PACK_ALIGNMENT for last row. See comment below. |
| if (iy + 1 == max_y && padding > 0) |
| api()->glPixelStoreiFn(GL_PACK_ALIGNMENT, 1); |
| api()->glReadPixelsFn(x, iy, width, 1, format, type, pixels); |
| if (iy + 1 == max_y && padding > 0) |
| api()->glPixelStoreiFn(GL_PACK_ALIGNMENT, state_.pack_alignment); |
| pixels += padded_row_size; |
| } |
| api()->glPixelStoreiFn(GL_PACK_ROW_LENGTH, state_.pack_row_length); |
| } else if (padding > 0) { |
| // Some drivers (for example, NVidia Linux) incorrectly require the |
| // pack buffer to have padding for the last row. |
| if (height > 1) |
| api()->glReadPixelsFn(x, y, width, height - 1, format, type, pixels); |
| api()->glPixelStoreiFn(GL_PACK_ALIGNMENT, 1); |
| pixels += padded_row_size * (height - 1); |
| api()->glReadPixelsFn(x, max_y - 1, width, 1, format, type, pixels); |
| api()->glPixelStoreiFn(GL_PACK_ALIGNMENT, state_.pack_alignment); |
| } else { |
| api()->glReadPixelsFn(x, y, width, height, format, type, pixels); |
| } |
| } else if ( |
| workarounds() |
| .use_copyteximage2d_instead_of_readpixels_on_multisampled_textures && |
| framebuffer_state_.bound_read_framebuffer.get() && |
| framebuffer_state_.bound_read_framebuffer.get() |
| ->GetReadBufferIsMultisampledTexture()) { |
| ScopedFramebufferCopyBinder binder(this, x, y, width, height); |
| api()->glReadPixelsFn(0, 0, width, height, format, type, pixels); |
| } else { |
| api()->glReadPixelsFn(x, y, width, height, format, type, pixels); |
| } |
| } |
| if (pixels_shm_id != 0) { |
| GLenum error = LOCAL_PEEK_GL_ERROR(func_name); |
| if (error == GL_NO_ERROR) { |
| if (result) { |
| result->success = 1; |
| result->row_length = static_cast<uint32_t>(rect.width()); |
| result->num_rows = static_cast<uint32_t>(rect.height()); |
| } |
| FinishReadPixels(width, height, format, type, pixels_shm_id, |
| pixels_shm_offset, result_shm_id, result_shm_offset, |
| state_.pack_alignment, read_format, 0); |
| } |
| } |
| |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandlePixelStorei( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::PixelStorei& c = |
| *static_cast<const volatile gles2::cmds::PixelStorei*>(cmd_data); |
| GLenum pname = c.pname; |
| GLint 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", "invalid param"); |
| return error::kNoError; |
| } |
| break; |
| case GL_PACK_ROW_LENGTH: |
| case GL_UNPACK_ROW_LENGTH: |
| case GL_UNPACK_IMAGE_HEIGHT: |
| if (param < 0) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, "glPixelStorei", "invalid param"); |
| return error::kNoError; |
| } |
| break; |
| case GL_PACK_SKIP_PIXELS: |
| case GL_PACK_SKIP_ROWS: |
| case GL_UNPACK_SKIP_PIXELS: |
| case GL_UNPACK_SKIP_ROWS: |
| case GL_UNPACK_SKIP_IMAGES: |
| // All SKIP parameters are handled on the client side and should never |
| // be passed to the service side. |
| return error::kInvalidArguments; |
| default: |
| break; |
| } |
| // For alignment parameters, we always apply them. |
| // For other parameters, we don't apply them if no buffer is bound at |
| // PIXEL_PACK or PIXEL_UNPACK. We will handle pack and unpack according to |
| // the user specified parameters on the client side. |
| switch (pname) { |
| case GL_PACK_ROW_LENGTH: |
| if (state_.bound_pixel_pack_buffer.get()) |
| api()->glPixelStoreiFn(pname, param); |
| break; |
| case GL_UNPACK_ROW_LENGTH: |
| case GL_UNPACK_IMAGE_HEIGHT: |
| if (state_.bound_pixel_unpack_buffer.get()) |
| api()->glPixelStoreiFn(pname, param); |
| break; |
| default: |
| api()->glPixelStoreiFn(pname, param); |
| break; |
| } |
| switch (pname) { |
| case GL_PACK_ALIGNMENT: |
| state_.pack_alignment = param; |
| break; |
| case GL_PACK_ROW_LENGTH: |
| state_.pack_row_length = param; |
| break; |
| case GL_UNPACK_ALIGNMENT: |
| state_.unpack_alignment = param; |
| break; |
| case GL_UNPACK_ROW_LENGTH: |
| state_.unpack_row_length = param; |
| break; |
| case GL_UNPACK_IMAGE_HEIGHT: |
| state_.unpack_image_height = param; |
| break; |
| default: |
| // Validation should have prevented us from getting here. |
| NOTREACHED(); |
| break; |
| } |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderImpl::DoSwapBuffersWithBoundsCHROMIUM( |
| uint64_t swap_id, |
| GLsizei count, |
| const volatile GLint* rects, |
| GLbitfield flags) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::SwapBuffersWithBoundsCHROMIUM"); |
| if (!supports_swap_buffers_with_bounds_) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glSwapBuffersWithBoundsCHROMIUM", |
| "command not supported by surface"); |
| return; |
| } |
| 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, GetBoundDrawFramebufferServiceId()); |
| gpu_state_tracer_->TakeSnapshotWithCurrentFramebuffer( |
| is_offscreen ? offscreen_size_ : surface_->GetSize()); |
| } |
| |
| ClearScheduleCALayerState(); |
| ClearScheduleDCLayerState(); |
| |
| std::vector<gfx::Rect> bounds(count); |
| for (GLsizei i = 0; i < count; ++i) { |
| bounds[i] = gfx::Rect(rects[i * 4 + 0], rects[i * 4 + 1], rects[i * 4 + 2], |
| rects[i * 4 + 3]); |
| } |
| client_->OnSwapBuffers(swap_id, flags); |
| FinishSwapBuffers(surface_->SwapBuffersWithBounds(bounds, base::DoNothing())); |
| } |
| |
| error::Error GLES2DecoderImpl::HandlePostSubBufferCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::PostSubBufferCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::PostSubBufferCHROMIUM*>( |
| cmd_data); |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::HandlePostSubBufferCHROMIUM"); |
| 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, GetBoundDrawFramebufferServiceId()); |
| gpu_state_tracer_->TakeSnapshotWithCurrentFramebuffer( |
| is_offscreen ? offscreen_size_ : surface_->GetSize()); |
| } |
| |
| ClearScheduleCALayerState(); |
| ClearScheduleDCLayerState(); |
| |
| if (supports_async_swap_) { |
| TRACE_EVENT_ASYNC_BEGIN0("gpu", "AsyncSwapBuffers", c.swap_id()); |
| |
| client_->OnSwapBuffers(c.swap_id(), c.flags); |
| surface_->PostSubBufferAsync( |
| c.x, c.y, c.width, c.height, |
| base::Bind(&GLES2DecoderImpl::FinishAsyncSwapBuffers, |
| weak_ptr_factory_.GetWeakPtr(), c.swap_id()), |
| base::DoNothing()); |
| } else { |
| client_->OnSwapBuffers(c.swap_id(), c.flags); |
| FinishSwapBuffers(surface_->PostSubBuffer(c.x, c.y, c.width, c.height, |
| base::DoNothing())); |
| } |
| |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleScheduleOverlayPlaneCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::ScheduleOverlayPlaneCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::ScheduleOverlayPlaneCHROMIUM*>( |
| 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; |
| } |
| Texture::ImageState image_state; |
| gl::GLImage* image = |
| ref->texture()->GetLevelImage(ref->texture()->target(), 0, &image_state); |
| 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; |
| } |
| GLuint gpu_fence_id = static_cast<GLuint>(c.gpu_fence_id); |
| std::unique_ptr<gfx::GpuFence> gpu_fence; |
| if (gpu_fence_id > 0) { |
| gpu_fence = GetGpuFenceManager()->GetGpuFence(gpu_fence_id); |
| if (!gpu_fence) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, "glScheduleOverlayPlaneCHROMIUM", |
| "unknown fence"); |
| 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), c.enable_blend, |
| std::move(gpu_fence))) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "glScheduleOverlayPlaneCHROMIUM", |
| "failed to schedule overlay"); |
| } |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleScheduleCALayerSharedStateCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::ScheduleCALayerSharedStateCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::ScheduleCALayerSharedStateCHROMIUM*>( |
| cmd_data); |
| |
| const GLfloat* mem = GetSharedMemoryAs<const GLfloat*>(c.shm_id, c.shm_offset, |
| 20 * sizeof(GLfloat)); |
| if (!mem) { |
| return error::kOutOfBounds; |
| } |
| gfx::RectF clip_rect(mem[0], mem[1], mem[2], mem[3]); |
| gfx::Transform transform(mem[4], mem[8], mem[12], mem[16], |
| mem[5], mem[9], mem[13], mem[17], |
| mem[6], mem[10], mem[14], mem[18], |
| mem[7], mem[11], mem[15], mem[19]); |
| ca_layer_shared_state_.reset(new CALayerSharedState); |
| ca_layer_shared_state_->opacity = c.opacity; |
| ca_layer_shared_state_->is_clipped = c.is_clipped ? true : false; |
| ca_layer_shared_state_->clip_rect = gfx::ToEnclosingRect(clip_rect); |
| ca_layer_shared_state_->sorting_context_id = c.sorting_context_id; |
| ca_layer_shared_state_->transform = transform; |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleScheduleCALayerCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::ScheduleCALayerCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::ScheduleCALayerCHROMIUM*>( |
| cmd_data); |
| GLuint filter = c.filter; |
| if (filter != GL_NEAREST && filter != GL_LINEAR) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glScheduleCALayerCHROMIUM", |
| "invalid filter"); |
| return error::kNoError; |
| } |
| |
| if (!ca_layer_shared_state_) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glScheduleCALayerCHROMIUM", |
| "glScheduleCALayerSharedStateCHROMIUM has not been called"); |
| return error::kNoError; |
| } |
| |
| gl::GLImage* image = nullptr; |
| GLuint contents_texture_id = c.contents_texture_id; |
| if (contents_texture_id) { |
| TextureRef* ref = texture_manager()->GetTexture(contents_texture_id); |
| if (!ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glScheduleCALayerCHROMIUM", |
| "unknown texture"); |
| return error::kNoError; |
| } |
| Texture::ImageState image_state; |
| image = ref->texture()->GetLevelImage(ref->texture()->target(), 0, |
| &image_state); |
| if (!image) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glScheduleCALayerCHROMIUM", |
| "unsupported texture format"); |
| return error::kNoError; |
| } |
| } |
| |
| const GLfloat* mem = GetSharedMemoryAs<const GLfloat*>(c.shm_id, c.shm_offset, |
| 8 * sizeof(GLfloat)); |
| if (!mem) { |
| return error::kOutOfBounds; |
| } |
| gfx::RectF contents_rect(mem[0], mem[1], mem[2], mem[3]); |
| gfx::RectF bounds_rect(mem[4], mem[5], mem[6], mem[7]); |
| |
| ui::CARendererLayerParams params = ui::CARendererLayerParams( |
| ca_layer_shared_state_->is_clipped, ca_layer_shared_state_->clip_rect, |
| ca_layer_shared_state_->sorting_context_id, |
| ca_layer_shared_state_->transform, image, contents_rect, |
| gfx::ToEnclosingRect(bounds_rect), c.background_color, c.edge_aa_mask, |
| ca_layer_shared_state_->opacity, filter); |
| if (!surface_->ScheduleCALayer(params)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glScheduleCALayerCHROMIUM", |
| "failed to schedule CALayer"); |
| } |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleScheduleDCLayerSharedStateCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::ScheduleDCLayerSharedStateCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::ScheduleDCLayerSharedStateCHROMIUM*>( |
| cmd_data); |
| |
| const GLfloat* mem = GetSharedMemoryAs<const GLfloat*>(c.shm_id, c.shm_offset, |
| 20 * sizeof(GLfloat)); |
| if (!mem) { |
| return error::kOutOfBounds; |
| } |
| gfx::RectF clip_rect(mem[0], mem[1], mem[2], mem[3]); |
| gfx::Transform transform(mem[4], mem[8], mem[12], mem[16], mem[5], mem[9], |
| mem[13], mem[17], mem[6], mem[10], mem[14], mem[18], |
| mem[7], mem[11], mem[15], mem[19]); |
| dc_layer_shared_state_.reset(new DCLayerSharedState); |
| dc_layer_shared_state_->opacity = c.opacity; |
| dc_layer_shared_state_->is_clipped = c.is_clipped ? true : false; |
| dc_layer_shared_state_->clip_rect = gfx::ToEnclosingRect(clip_rect); |
| dc_layer_shared_state_->z_order = c.z_order; |
| dc_layer_shared_state_->transform = transform; |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleScheduleDCLayerCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::ScheduleDCLayerCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::ScheduleDCLayerCHROMIUM*>( |
| cmd_data); |
| GLuint filter = c.filter; |
| if (filter != GL_NEAREST && filter != GL_LINEAR) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glScheduleDCLayerCHROMIUM", |
| "invalid filter"); |
| return error::kNoError; |
| } |
| |
| if (!dc_layer_shared_state_) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glScheduleDCLayerCHROMIUM", |
| "glScheduleDCLayerSharedStateCHROMIUM has not been called"); |
| return error::kNoError; |
| } |
| |
| GLsizei num_textures = c.num_textures; |
| if (num_textures < 0 || num_textures > 4) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glScheduleDCLayerCHROMIUM", |
| "number of textures greater than maximum of 4"); |
| return error::kNoError; |
| } |
| |
| size_t textures_size = num_textures * sizeof(GLuint); |
| |
| base::CheckedNumeric<uint32_t> data_size = textures_size; |
| const uint32_t kRectDataSize = 8 * sizeof(GLfloat); |
| data_size += kRectDataSize; |
| if (!data_size.IsValid()) |
| return error::kOutOfBounds; |
| const void* data = |
| GetAddressAndCheckSize(c.shm_id, c.shm_offset, data_size.ValueOrDie()); |
| if (!data) { |
| return error::kOutOfBounds; |
| } |
| const GLfloat* mem = reinterpret_cast<const GLfloat*>(data); |
| |
| gfx::RectF contents_rect(mem[0], mem[1], mem[2], mem[3]); |
| gfx::RectF bounds_rect(mem[4], mem[5], mem[6], mem[7]); |
| |
| const volatile GLuint* texture_ids = reinterpret_cast<const volatile GLuint*>( |
| static_cast<const volatile char*>(data) + kRectDataSize); |
| |
| std::vector<scoped_refptr<gl::GLImage>> images; |
| for (int i = 0; i < num_textures; ++i) { |
| GLuint contents_texture_id = texture_ids[i]; |
| scoped_refptr<gl::GLImage> image; |
| if (contents_texture_id) { |
| TextureRef* ref = texture_manager()->GetTexture(contents_texture_id); |
| if (!ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glScheduleDCLayerCHROMIUM", |
| "unknown texture"); |
| return error::kNoError; |
| } |
| Texture::ImageState image_state; |
| image = ref->texture()->GetLevelImage(ref->texture()->target(), 0, |
| &image_state); |
| if (!image) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glScheduleDCLayerCHROMIUM", |
| "unsupported texture format"); |
| return error::kNoError; |
| } |
| } |
| images.push_back(image); |
| } |
| |
| ui::DCRendererLayerParams params = ui::DCRendererLayerParams( |
| dc_layer_shared_state_->is_clipped, dc_layer_shared_state_->clip_rect, |
| dc_layer_shared_state_->z_order, dc_layer_shared_state_->transform, |
| images, contents_rect, gfx::ToEnclosingRect(bounds_rect), |
| c.background_color, c.edge_aa_mask, dc_layer_shared_state_->opacity, |
| filter, c.is_protected_video); |
| if (!surface_->ScheduleDCLayer(params)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glScheduleDCLayerCHROMIUM", |
| "failed to schedule DCLayer"); |
| } |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleSetColorSpaceMetadataCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::SetColorSpaceMetadataCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::SetColorSpaceMetadataCHROMIUM*>( |
| cmd_data); |
| |
| GLuint texture_id = c.texture_id; |
| GLsizei color_space_size = c.color_space_size; |
| const char* data = static_cast<const char*>( |
| GetAddressAndCheckSize(c.shm_id, c.shm_offset, color_space_size)); |
| if (!data) |
| return error::kOutOfBounds; |
| |
| // Make a copy to reduce the risk of a time of check to time of use attack. |
| std::vector<char> color_space_data(data, data + color_space_size); |
| base::Pickle color_space_pickle(color_space_data.data(), color_space_size); |
| base::PickleIterator iterator(color_space_pickle); |
| gfx::ColorSpace color_space; |
| if (!IPC::ParamTraits<gfx::ColorSpace>::Read(&color_space_pickle, &iterator, |
| &color_space)) |
| return error::kOutOfBounds; |
| |
| TextureRef* ref = texture_manager()->GetTexture(texture_id); |
| if (!ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glSetColorSpaceMetadataCHROMIUM", |
| "unknown texture"); |
| return error::kNoError; |
| } |
| |
| scoped_refptr<gl::GLImage> image = |
| ref->texture()->GetLevelImage(ref->texture()->target(), 0); |
| if (!image) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glSetColorSpaceMetadataCHROMIUM", |
| "no image associated with texture"); |
| return error::kNoError; |
| } |
| |
| image->SetColorSpace(color_space); |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderImpl::DoScheduleCALayerInUseQueryCHROMIUM( |
| GLsizei count, |
| const volatile GLuint* textures) { |
| std::vector<gl::GLSurface::CALayerInUseQuery> queries; |
| queries.reserve(count); |
| for (GLsizei i = 0; i < count; ++i) { |
| gl::GLImage* image = nullptr; |
| GLuint texture_id = textures[i]; |
| if (texture_id) { |
| TextureRef* ref = texture_manager()->GetTexture(texture_id); |
| if (!ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, |
| "glScheduleCALayerInUseQueryCHROMIUM", |
| "unknown texture"); |
| return; |
| } |
| Texture::ImageState image_state; |
| image = ref->texture()->GetLevelImage(ref->texture()->target(), 0, |
| &image_state); |
| } |
| gl::GLSurface::CALayerInUseQuery query; |
| query.image = image; |
| query.texture = texture_id; |
| queries.push_back(query); |
| } |
| |
| surface_->ScheduleCALayerInUseQuery(std::move(queries)); |
| } |
| |
| error::Error GLES2DecoderImpl::GetAttribLocationHelper( |
| GLuint client_id, |
| uint32_t location_shm_id, |
| uint32_t location_shm_offset, |
| const std::string& name_str) { |
| if (!StringIsValidForGLES(name_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<GLint*>( |
| location_shm_id, location_shm_offset, sizeof(GLint)); |
| if (!location) { |
| return error::kOutOfBounds; |
| } |
| // Check that the client initialized the result. |
| if (*location != -1) { |
| return error::kInvalidArguments; |
| } |
| *location = program->GetAttribLocation(name_str); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetAttribLocation( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetAttribLocation& c = |
| *static_cast<const volatile gles2::cmds::GetAttribLocation*>(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_t location_shm_id, |
| uint32_t location_shm_offset, |
| const std::string& name_str) { |
| if (!StringIsValidForGLES(name_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<GLint*>( |
| location_shm_id, location_shm_offset, sizeof(GLint)); |
| if (!location) { |
| return error::kOutOfBounds; |
| } |
| // Check that the client initialized the result. |
| if (*location != -1) { |
| return error::kInvalidArguments; |
| } |
| *location = program->GetUniformFakeLocation(name_str); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetUniformLocation( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetUniformLocation& c = |
| *static_cast<const volatile gles2::cmds::GetUniformLocation*>(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetUniformIndices& c = |
| *static_cast<const volatile gles2::cmds::GetUniformIndices*>(cmd_data); |
| Bucket* bucket = GetBucket(c.names_bucket_id); |
| if (!bucket) { |
| return error::kInvalidArguments; |
| } |
| GLsizei count = 0; |
| std::vector<char*> names; |
| std::vector<GLint> len; |
| if (!bucket->GetAsStrings(&count, &names, &len) || count <= 0) { |
| return error::kInvalidArguments; |
| } |
| typedef cmds::GetUniformIndices::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| c.indices_shm_id, c.indices_shm_offset, |
| Result::ComputeSize(static_cast<size_t>(count))); |
| GLuint* indices = result ? result->GetData() : nullptr; |
| if (indices == nullptr) { |
| 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; |
| api()->glGetProgramivFn(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"); |
| api()->glGetUniformIndicesFn(service_id, count, &names[0], indices); |
| GLenum error = api()->glGetErrorFn(); |
| 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_t location_shm_id, |
| uint32_t location_shm_offset, |
| const std::string& name_str) { |
| const char kFunctionName[] = "glGetFragDataLocation"; |
| GLint* location = GetSharedMemoryAs<GLint*>( |
| location_shm_id, location_shm_offset, sizeof(GLint)); |
| if (!location) { |
| return error::kOutOfBounds; |
| } |
| // Check that the client initialized the result. |
| if (*location != -1) { |
| return error::kInvalidArguments; |
| } |
| Program* program = GetProgramInfoNotShader(client_id, kFunctionName); |
| if (!program) { |
| return error::kNoError; |
| } |
| if (!program->IsValid()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "program not linked"); |
| return error::kNoError; |
| } |
| |
| *location = program->GetFragDataLocation(name_str); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetFragDataLocation( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetFragDataLocation& c = |
| *static_cast<const volatile gles2::cmds::GetFragDataLocation*>(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::GetFragDataIndexHelper( |
| GLuint program_id, |
| uint32_t index_shm_id, |
| uint32_t index_shm_offset, |
| const std::string& name_str) { |
| const char kFunctionName[] = "glGetFragDataIndexEXT"; |
| GLint* index = |
| GetSharedMemoryAs<GLint*>(index_shm_id, index_shm_offset, sizeof(GLint)); |
| if (!index) { |
| return error::kOutOfBounds; |
| } |
| // Check that the client initialized the result. |
| if (*index != -1) { |
| return error::kInvalidArguments; |
| } |
| Program* program = GetProgramInfoNotShader(program_id, kFunctionName); |
| if (!program) { |
| return error::kNoError; |
| } |
| if (!program->IsValid()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "program not linked"); |
| return error::kNoError; |
| } |
| |
| *index = program->GetFragDataIndex(name_str); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetFragDataIndexEXT( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!features().ext_blend_func_extended) { |
| return error::kUnknownCommand; |
| } |
| const volatile gles2::cmds::GetFragDataIndexEXT& c = |
| *static_cast<const volatile gles2::cmds::GetFragDataIndexEXT*>(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 GetFragDataIndexHelper(c.program, c.index_shm_id, c.index_shm_offset, |
| name_str); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetUniformBlockIndex( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetUniformBlockIndex& c = |
| *static_cast<const volatile gles2::cmds::GetUniformBlockIndex*>(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<GLuint*>( |
| c.index_shm_id, c.index_shm_offset, sizeof(GLuint)); |
| if (!index) { |
| return error::kOutOfBounds; |
| } |
| // Check that the client initialized the result. |
| if (*index != GL_INVALID_INDEX) { |
| return error::kInvalidArguments; |
| } |
| Program* program = GetProgramInfoNotShader( |
| c.program, "glGetUniformBlockIndex"); |
| if (!program) { |
| return error::kNoError; |
| } |
| *index = |
| api()->glGetUniformBlockIndexFn(program->service_id(), name_str.c_str()); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetString(uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetString& c = |
| *static_cast<const volatile gles2::cmds::GetString*>(cmd_data); |
| GLenum name = static_cast<GLenum>(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 = GetServiceVersionString(feature_info_.get()); |
| break; |
| case GL_SHADING_LANGUAGE_VERSION: |
| str = GetServiceShadingLanguageVersionString(feature_info_.get()); |
| break; |
| case GL_EXTENSIONS: { |
| gfx::ExtensionSet extension_set = feature_info_->extensions(); |
| // For WebGL contexts, strip out shader extensions if they have not |
| // been enabled on WebGL1 or no longer exist (become core) in WebGL2. |
| if (feature_info_->IsWebGLContext()) { |
| if (!derivatives_explicitly_enabled_) |
| extension_set.erase(kOESDerivativeExtension); |
| if (!frag_depth_explicitly_enabled_) |
| extension_set.erase(kEXTFragDepthExtension); |
| if (!draw_buffers_explicitly_enabled_) |
| extension_set.erase(kEXTDrawBuffersExtension); |
| if (!shader_texture_lod_explicitly_enabled_) |
| extension_set.erase(kEXTShaderTextureLodExtension); |
| } |
| if (supports_post_sub_buffer_) |
| extension_set.insert("GL_CHROMIUM_post_sub_buffer"); |
| extensions = gfx::MakeExtensionString(extension_set); |
| str = extensions.c_str(); |
| break; |
| } |
| default: |
| str = reinterpret_cast<const char*>(api()->glGetStringFn(name)); |
| break; |
| } |
| Bucket* bucket = CreateBucket(c.bucket_id); |
| bucket->SetFromString(str); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleBufferData(uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::BufferData& c = |
| *static_cast<const volatile gles2::cmds::BufferData*>(cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLsizeiptr size = static_cast<GLsizeiptr>(c.size); |
| uint32_t data_shm_id = static_cast<uint32_t>(c.data_shm_id); |
| uint32_t data_shm_offset = static_cast<uint32_t>(c.data_shm_offset); |
| GLenum usage = static_cast<GLenum>(c.usage); |
| const void* data = nullptr; |
| if (data_shm_id != 0 || data_shm_offset != 0) { |
| data = GetSharedMemoryAs<const void*>(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 format, |
| unsigned type, |
| int xoffset, |
| int yoffset, |
| int width, |
| int height) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::ClearLevel"); |
| DCHECK(target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY && |
| target != GL_TEXTURE_EXTERNAL_OES); |
| uint32_t channels = GLES2Util::GetChannelsForFormat(format); |
| |
| bool must_use_gl_clear = false; |
| if ((channels & GLES2Util::kDepth) != 0 && |
| feature_info_->feature_flags().angle_depth_texture && |
| feature_info_->gl_version_info().is_es2) { |
| // It's a depth format and ANGLE doesn't allow texImage2D or texSubImage2D |
| // on depth formats in ES2. |
| must_use_gl_clear = true; |
| } |
| |
| uint32_t size; |
| uint32_t padded_row_size; |
| if (!GLES2Util::ComputeImageDataSizes(width, height, 1, format, type, |
| state_.unpack_alignment, &size, nullptr, |
| &padded_row_size)) { |
| return false; |
| } |
| |
| // Prefer to do the clear using a glClear call unless the clear is very small |
| // (less than 64x64, or one page). Calls to TexSubImage2D on large textures |
| // can take hundreds of milliseconds because of slow uploads on macOS. Do this |
| // only on macOS because clears are buggy on other drivers. |
| // https://crbug.com/848952 (slow uploads on macOS) |
| // https://crbug.com/883276 (buggy clears on Android) |
| bool prefer_use_gl_clear = false; |
| #if defined(OS_MACOSX) |
| const uint32_t kMinSizeForGLClear = 4 * 1024; |
| prefer_use_gl_clear = size > kMinSizeForGLClear; |
| #endif |
| if (must_use_gl_clear || prefer_use_gl_clear) { |
| if (ClearLevelUsingGL(texture, channels, target, level, xoffset, yoffset, |
| width, height)) { |
| return true; |
| } |
| } |
| if (must_use_gl_clear) |
| return false; |
| |
| TRACE_EVENT1("gpu", "Clear using TexSubImage2D", "size", size); |
| |
| int tile_height; |
| |
| const uint32_t kMaxZeroSize = 1024 * 1024 * 4; |
| 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, |
| nullptr, nullptr)) { |
| return false; |
| } |
| } else { |
| tile_height = height; |
| } |
| |
| api()->glBindTextureFn(texture->target(), texture->service_id()); |
| { |
| // Add extra scope to destroy zero and the object it owns right |
| // after its usage. |
| // Assumes the size has already been checked. |
| std::unique_ptr<char[]> zero(new char[size]); |
| memset(zero.get(), 0, size); |
| |
| ScopedPixelUnpackState reset_restore(&state_); |
| GLint y = 0; |
| while (y < height) { |
| GLint h = y + tile_height > height ? height - y : tile_height; |
| api()->glTexSubImage2DFn( |
| target, level, xoffset, yoffset + y, width, h, |
| TextureManager::AdjustTexFormat(feature_info_.get(), format), type, |
| zero.get()); |
| y += tile_height; |
| } |
| } |
| |
| TextureRef* bound_texture = |
| texture_manager()->GetTextureInfoForTarget(&state_, texture->target()); |
| api()->glBindTextureFn(texture->target(), |
| bound_texture ? bound_texture->service_id() : 0); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::ClearLevelUsingGL(Texture* texture, |
| uint32_t channels, |
| unsigned target, |
| int level, |
| int xoffset, |
| int yoffset, |
| int width, |
| int height) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::ClearLevelUsingGL"); |
| GLenum fb_target = GetDrawFramebufferTarget(); |
| GLuint fb = 0; |
| api()->glGenFramebuffersEXTFn(1, &fb); |
| api()->glBindFramebufferEXTFn(fb_target, fb); |
| |
| bool have_color = (channels & GLES2Util::kRGBA) != 0; |
| if (have_color) { |
| api()->glFramebufferTexture2DEXTFn(fb_target, GL_COLOR_ATTACHMENT0, target, |
| texture->service_id(), level); |
| } |
| bool have_depth = (channels & GLES2Util::kDepth) != 0; |
| if (have_depth) { |
| api()->glFramebufferTexture2DEXTFn(fb_target, GL_DEPTH_ATTACHMENT, target, |
| texture->service_id(), level); |
| } |
| bool have_stencil = (channels & GLES2Util::kStencil) != 0; |
| if (have_stencil) { |
| api()->glFramebufferTexture2DEXTFn(fb_target, GL_STENCIL_ATTACHMENT, target, |
| texture->service_id(), level); |
| } |
| // Attempt to do the clear only if the framebuffer is complete. ANGLE |
| // promises a depth only attachment ok. |
| bool result = false; |
| if (api()->glCheckFramebufferStatusEXTFn(fb_target) == |
| GL_FRAMEBUFFER_COMPLETE) { |
| api()->glColorMaskFn(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| api()->glClearColorFn(0.0, 0.0, 0.0, 0.0); |
| api()->glClearStencilFn(0); |
| state_.SetDeviceStencilMaskSeparate(GL_FRONT, kDefaultStencilMask); |
| state_.SetDeviceStencilMaskSeparate(GL_BACK, kDefaultStencilMask); |
| api()->glClearDepthFn(1.0f); |
| state_.SetDeviceDepthMask(GL_TRUE); |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, true); |
| gfx::Vector2d scissor_offset = GetBoundFramebufferDrawOffset(); |
| api()->glScissorFn(xoffset + scissor_offset.x(), |
| yoffset + scissor_offset.y(), width, height); |
| ClearDeviceWindowRectangles(); |
| |
| api()->glClearFn((have_color ? GL_COLOR_BUFFER_BIT : 0) | |
| (have_depth ? GL_DEPTH_BUFFER_BIT : 0) | |
| (have_stencil ? GL_STENCIL_BUFFER_BIT : 0)); |
| result = true; |
| } |
| RestoreClearState(); |
| api()->glDeleteFramebuffersEXTFn(1, &fb); |
| Framebuffer* framebuffer = GetFramebufferInfoForTarget(fb_target); |
| GLuint fb_service_id = |
| framebuffer ? framebuffer->service_id() : GetBackbufferServiceId(); |
| api()->glBindFramebufferEXTFn(fb_target, fb_service_id); |
| return result; |
| } |
| |
| bool GLES2DecoderImpl::ClearCompressedTextureLevel(Texture* texture, |
| unsigned target, |
| int level, |
| unsigned format, |
| int width, |
| int height) { |
| DCHECK(target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY); |
| // This code path can only be called if the texture was originally |
| // allocated via TexStorage2D. Note that TexStorage2D is exposed |
| // internally for ES 2.0 contexts, but compressed texture support is |
| // not part of that exposure. |
| DCHECK(feature_info_->IsWebGL2OrES3Context()); |
| |
| GLsizei bytes_required = 0; |
| if (!GetCompressedTexSizeInBytes("ClearCompressedTextureLevel", width, height, |
| 1, format, &bytes_required, |
| state_.GetErrorState())) { |
| return false; |
| } |
| |
| TRACE_EVENT1("gpu", "GLES2DecoderImpl::ClearCompressedTextureLevel", |
| "bytes_required", bytes_required); |
| |
| api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, 0); |
| { |
| // Add extra scope to destroy zero and the object it owns right |
| // after its usage. |
| std::unique_ptr<char[]> zero(new char[bytes_required]); |
| memset(zero.get(), 0, bytes_required); |
| api()->glBindTextureFn(texture->target(), texture->service_id()); |
| api()->glCompressedTexSubImage2DFn(target, level, 0, 0, width, height, |
| format, bytes_required, zero.get()); |
| } |
| TextureRef* bound_texture = |
| texture_manager()->GetTextureInfoForTarget(&state_, texture->target()); |
| api()->glBindTextureFn(texture->target(), |
| bound_texture ? bound_texture->service_id() : 0); |
| Buffer* bound_buffer = buffer_manager()->GetBufferInfoForTarget( |
| &state_, GL_PIXEL_UNPACK_BUFFER); |
| if (bound_buffer) { |
| api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, bound_buffer->service_id()); |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::IsCompressedTextureFormat(unsigned format) { |
| return feature_info_->validators()->compressed_texture_format.IsValid( |
| format); |
| } |
| |
| bool GLES2DecoderImpl::ClearLevel3D(Texture* texture, |
| unsigned target, |
| int level, |
| unsigned format, |
| unsigned type, |
| int width, |
| int height, |
| int depth) { |
| DCHECK(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY); |
| DCHECK(feature_info_->IsWebGL2OrES3Context()); |
| if (width == 0 || height == 0 || depth == 0) |
| return true; |
| |
| uint32_t size; |
| uint32_t padded_row_size; |
| uint32_t padding; |
| // Here we use unpack buffer to upload zeros into the texture, one layer |
| // at a time. |
| // We only take into consideration UNPACK_ALIGNMENT, and clear other unpack |
| // parameters if necessary before TexSubImage3D calls. |
| PixelStoreParams params; |
| params.alignment = state_.unpack_alignment; |
| if (!GLES2Util::ComputeImageDataSizesES3(width, height, depth, |
| format, type, |
| params, |
| &size, |
| nullptr, |
| &padded_row_size, |
| nullptr, |
| &padding)) { |
| return false; |
| } |
| const uint32_t kMaxZeroSize = 1024 * 1024 * 2; |
| uint32_t buffer_size; |
| std::vector<TexSubCoord3D> subs; |
| if (size < kMaxZeroSize) { |
| // Case 1: one TexSubImage3D call clears the entire 3D texture. |
| buffer_size = size; |
| subs.push_back(TexSubCoord3D(0, 0, 0, width, height, depth)); |
| } else { |
| uint32_t size_per_layer; |
| if (!SafeMultiplyUint32(padded_row_size, height, &size_per_layer)) { |
| return false; |
| } |
| if (size_per_layer < kMaxZeroSize) { |
| // Case 2: Each TexSubImage3D call clears 1 or more layers. |
| uint32_t depth_step = kMaxZeroSize / size_per_layer; |
| uint32_t num_of_slices = depth / depth_step; |
| if (num_of_slices * depth_step < static_cast<uint32_t>(depth)) |
| num_of_slices++; |
| DCHECK_LT(0u, num_of_slices); |
| buffer_size = size_per_layer * depth_step; |
| int depth_of_last_slice = depth - (num_of_slices - 1) * depth_step; |
| DCHECK_LT(0, depth_of_last_slice); |
| for (uint32_t ii = 0; ii < num_of_slices; ++ii) { |
| int depth_ii = |
| (ii + 1 == num_of_slices ? depth_of_last_slice : depth_step); |
| subs.push_back( |
| TexSubCoord3D(0, 0, depth_step * ii, width, height, depth_ii)); |
| } |
| } else { |
| // Case 3: Multiple TexSubImage3D calls clear 1 layer. |
| if (kMaxZeroSize < padded_row_size) { |
| // That'd be an awfully large texture. |
| return false; |
| } |
| uint32_t height_step = kMaxZeroSize / padded_row_size; |
| uint32_t num_of_slices = height / height_step; |
| if (num_of_slices * height_step < static_cast<uint32_t>(height)) |
| num_of_slices++; |
| DCHECK_LT(0u, num_of_slices); |
| buffer_size = padded_row_size * height_step; |
| int height_of_last_slice = height - (num_of_slices - 1) * height_step; |
| DCHECK_LT(0, height_of_last_slice); |
| for (int zz = 0; zz < depth; ++zz) { |
| for (uint32_t ii = 0; ii < num_of_slices; ++ii) { |
| int height_ii = |
| (ii + 1 == num_of_slices ? height_of_last_slice : height_step); |
| subs.push_back( |
| TexSubCoord3D(0, height_step * ii, zz, width, height_ii, 1)); |
| } |
| } |
| } |
| } |
| |
| TRACE_EVENT1("gpu", "GLES2DecoderImpl::ClearLevel3D", "size", size); |
| |
| { |
| ScopedPixelUnpackState reset_restore(&state_); |
| GLuint buffer_id = 0; |
| api()->glGenBuffersARBFn(1, &buffer_id); |
| api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, buffer_id); |
| { |
| // Include padding as some drivers incorrectly requires padding for the |
| // last row. |
| buffer_size += padding; |
| std::unique_ptr<char[]> zero(new char[buffer_size]); |
| memset(zero.get(), 0, buffer_size); |
| // TODO(zmo): Consider glMapBufferRange instead. |
| api()->glBufferDataFn(GL_PIXEL_UNPACK_BUFFER, buffer_size, zero.get(), |
| GL_STATIC_DRAW); |
| } |
| |
| api()->glBindTextureFn(texture->target(), texture->service_id()); |
| for (size_t ii = 0; ii < subs.size(); ++ii) { |
| api()->glTexSubImage3DFn(target, level, subs[ii].xoffset, |
| subs[ii].yoffset, subs[ii].zoffset, |
| subs[ii].width, subs[ii].height, subs[ii].depth, |
| format, type, nullptr); |
| } |
| api()->glDeleteBuffersARBFn(1, &buffer_id); |
| } |
| |
| TextureRef* bound_texture = |
| texture_manager()->GetTextureInfoForTarget(&state_, texture->target()); |
| api()->glBindTextureFn(texture->target(), |
| bound_texture ? bound_texture->service_id() : 0); |
| return true; |
| } |
| |
| namespace { |
| |
| bool CheckETCFormatSupport(const FeatureInfo& feature_info) { |
| const gl::GLVersionInfo& version_info = feature_info.gl_version_info(); |
| return version_info.IsAtLeastGL(4, 3) || version_info.IsAtLeastGLES(3, 0) || |
| feature_info.feature_flags().arb_es3_compatibility; |
| } |
| |
| using CompressedFormatSupportCheck = bool (*)(const FeatureInfo&); |
| using CompressedFormatDecompressionFunction = void (*)(size_t width, |
| size_t height, |
| size_t depth, |
| const uint8_t* input, |
| size_t inputRowPitch, |
| size_t inputDepthPitch, |
| uint8_t* output, |
| size_t outputRowPitch, |
| size_t outputDepthPitch); |
| |
| struct CompressedFormatInfo { |
| GLenum format; |
| uint32_t block_size; |
| uint32_t bytes_per_block; |
| CompressedFormatSupportCheck support_check; |
| CompressedFormatDecompressionFunction decompression_function; |
| GLenum decompressed_internal_format; |
| GLenum decompressed_format; |
| GLenum decompressed_type; |
| }; |
| |
| const CompressedFormatInfo kCompressedFormatInfoArray[] = { |
| { |
| GL_COMPRESSED_R11_EAC, 4, 8, CheckETCFormatSupport, |
| angle::LoadEACR11ToR8, GL_R8, GL_RED, GL_UNSIGNED_BYTE, |
| }, |
| { |
| GL_COMPRESSED_SIGNED_R11_EAC, 4, 8, CheckETCFormatSupport, |
| angle::LoadEACR11SToR8, GL_R8_SNORM, GL_RED, GL_BYTE, |
| }, |
| { |
| GL_COMPRESSED_RG11_EAC, 4, 16, CheckETCFormatSupport, |
| angle::LoadEACRG11ToRG8, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, |
| }, |
| { |
| GL_COMPRESSED_SIGNED_RG11_EAC, 4, 16, CheckETCFormatSupport, |
| angle::LoadEACRG11SToRG8, GL_RG8_SNORM, GL_RG, GL_BYTE, |
| }, |
| { |
| GL_COMPRESSED_RGB8_ETC2, 4, 8, CheckETCFormatSupport, |
| angle::LoadETC2RGB8ToRGBA8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, |
| }, |
| { |
| GL_COMPRESSED_SRGB8_ETC2, 4, 8, CheckETCFormatSupport, |
| angle::LoadETC2SRGB8ToRGBA8, GL_SRGB8_ALPHA8, GL_SRGB_ALPHA, |
| GL_UNSIGNED_BYTE, |
| }, |
| { |
| GL_COMPRESSED_RGBA8_ETC2_EAC, 4, 16, CheckETCFormatSupport, |
| angle::LoadETC2RGBA8ToRGBA8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, |
| }, |
| { |
| GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, 4, 8, |
| CheckETCFormatSupport, angle::LoadETC2RGB8A1ToRGBA8, GL_RGBA8, GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| }, |
| { |
| GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, 4, 16, CheckETCFormatSupport, |
| angle::LoadETC2SRGBA8ToSRGBA8, GL_SRGB8_ALPHA8, GL_SRGB_ALPHA, |
| GL_UNSIGNED_BYTE, |
| }, |
| { |
| GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 4, 8, |
| CheckETCFormatSupport, angle::LoadETC2SRGB8A1ToRGBA8, GL_SRGB8_ALPHA8, |
| GL_SRGB_ALPHA, GL_UNSIGNED_BYTE, |
| }, |
| }; |
| |
| const CompressedFormatInfo* GetCompressedFormatInfo(GLenum format) { |
| for (size_t i = 0; i < arraysize(kCompressedFormatInfoArray); i++) { |
| if (kCompressedFormatInfoArray[i].format == format) { |
| return &kCompressedFormatInfoArray[i]; |
| } |
| } |
| return nullptr; |
| } |
| |
| uint32_t GetCompressedFormatRowPitch(const CompressedFormatInfo& info, |
| uint32_t width) { |
| uint32_t num_blocks_wide = (width + info.block_size - 1) / info.block_size; |
| return num_blocks_wide * info.bytes_per_block; |
| } |
| |
| uint32_t GetCompressedFormatDepthPitch(const CompressedFormatInfo& info, |
| uint32_t width, |
| uint32_t height) { |
| uint32_t num_blocks_high = (height + info.block_size - 1) / info.block_size; |
| return num_blocks_high * GetCompressedFormatRowPitch(info, width); |
| } |
| |
| std::unique_ptr<uint8_t[]> DecompressTextureData( |
| const ContextState& state, |
| const CompressedFormatInfo& info, |
| uint32_t width, |
| uint32_t height, |
| uint32_t depth, |
| GLsizei image_size, |
| const void* data) { |
| auto* api = state.api(); |
| uint32_t output_pixel_size = GLES2Util::ComputeImageGroupSize( |
| info.decompressed_format, info.decompressed_type); |
| std::unique_ptr<uint8_t[]> decompressed_data( |
| new uint8_t[output_pixel_size * width * height]); |
| |
| // If a PBO is bound, map it to decompress the data. |
| const void* input_data = data; |
| if (state.bound_pixel_unpack_buffer) { |
| input_data = api->glMapBufferRangeFn(GL_PIXEL_UNPACK_BUFFER, |
| reinterpret_cast<GLintptr>(data), |
| image_size, GL_MAP_READ_BIT); |
| if (input_data == nullptr) { |
| LOG(ERROR) << "Failed to map pixel unpack buffer."; |
| return nullptr; |
| } |
| } |
| |
| DCHECK_NE(input_data, nullptr); |
| info.decompression_function( |
| width, height, depth, static_cast<const uint8_t*>(input_data), |
| GetCompressedFormatRowPitch(info, width), |
| GetCompressedFormatDepthPitch(info, width, height), |
| decompressed_data.get(), output_pixel_size * width, |
| output_pixel_size * width * height); |
| |
| if (state.bound_pixel_unpack_buffer) { |
| if (api->glUnmapBufferFn(GL_PIXEL_UNPACK_BUFFER) != GL_TRUE) { |
| LOG(ERROR) << "glUnmapBuffer unexpectedly returned GL_FALSE"; |
| return nullptr; |
| } |
| } |
| |
| return decompressed_data; |
| } |
| |
| } // anonymous namespace. |
| |
| bool GLES2DecoderImpl::ValidateCompressedTexFuncData(const char* function_name, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLenum format, |
| GLsizei size, |
| const GLvoid* data) { |
| GLsizei bytes_required = 0; |
| if (!GetCompressedTexSizeInBytes(function_name, width, height, depth, format, |
| &bytes_required, state_.GetErrorState())) { |
| return false; |
| } |
| |
| if (size != bytes_required) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, function_name, "size is not correct for dimensions"); |
| return false; |
| } |
| |
| Buffer* buffer = state_.bound_pixel_unpack_buffer.get(); |
| if (buffer && |
| !buffer_manager()->RequestBufferAccess( |
| state_.GetErrorState(), buffer, reinterpret_cast<GLintptr>(data), |
| static_cast<GLsizeiptr>(bytes_required), function_name, |
| "pixel unpack buffer")) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::ValidateCompressedTexDimensions( |
| const char* function_name, GLenum target, GLint level, |
| GLsizei width, GLsizei height, GLsizei depth, GLenum format) { |
| const char* error_message = ""; |
| if (!::gpu::gles2::ValidateCompressedTexDimensions( |
| target, level, width, height, depth, format, |
| feature_info_->IsWebGLContext(), &error_message)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, error_message); |
| return false; |
| } |
| return true; |
| } |
| |
| 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) { |
| const char* error_message = ""; |
| if (!::gpu::gles2::ValidateCompressedTexSubDimensions( |
| target, level, xoffset, yoffset, zoffset, width, height, depth, |
| format, texture, feature_info_->IsWebGLContext(), &error_message)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, error_message); |
| return false; |
| } |
| return true; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCompressedTexImage2DBucket( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| const volatile gles2::cmds::CompressedTexImage2DBucket& c = |
| *static_cast<const volatile gles2::cmds::CompressedTexImage2DBucket*>( |
| cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLenum internal_format = static_cast<GLenum>(c.internalformat); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLuint bucket_id = static_cast<GLuint>(c.bucket_id); |
| GLint border = static_cast<GLint>(c.border); |
| |
| if (state_.bound_pixel_unpack_buffer.get()) { |
| return error::kInvalidArguments; |
| } |
| Bucket* bucket = GetBucket(bucket_id); |
| if (!bucket) |
| return error::kInvalidArguments; |
| uint32_t image_size = bucket->size(); |
| const void* data = bucket->GetData(0, image_size); |
| DCHECK(data || !image_size); |
| return DoCompressedTexImage(target, level, internal_format, width, height, 1, |
| border, image_size, data, ContextState::k2D); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCompressedTexImage2D( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| const volatile gles2::cmds::CompressedTexImage2D& c = |
| *static_cast<const volatile gles2::cmds::CompressedTexImage2D*>(cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLenum internal_format = static_cast<GLenum>(c.internalformat); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLint border = static_cast<GLint>(c.border); |
| GLsizei image_size = static_cast<GLsizei>(c.imageSize); |
| uint32_t data_shm_id = c.data_shm_id; |
| uint32_t data_shm_offset = c.data_shm_offset; |
| |
| const void* data; |
| if (state_.bound_pixel_unpack_buffer.get()) { |
| if (data_shm_id) { |
| return error::kInvalidArguments; |
| } |
| data = reinterpret_cast<const void*>(data_shm_offset); |
| } else { |
| if (!data_shm_id && data_shm_offset) { |
| return error::kInvalidArguments; |
| } |
| data = GetSharedMemoryAs<const void*>( |
| data_shm_id, data_shm_offset, image_size); |
| } |
| return DoCompressedTexImage(target, level, internal_format, width, height, 1, |
| border, image_size, data, ContextState::k2D); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCompressedTexImage3DBucket( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::CompressedTexImage3DBucket& c = |
| *static_cast<const volatile gles2::cmds::CompressedTexImage3DBucket*>( |
| cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLenum internal_format = static_cast<GLenum>(c.internalformat); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLsizei depth = static_cast<GLsizei>(c.depth); |
| GLuint bucket_id = static_cast<GLuint>(c.bucket_id); |
| GLint border = static_cast<GLint>(c.border); |
| |
| if (state_.bound_pixel_unpack_buffer.get()) { |
| return error::kInvalidArguments; |
| } |
| Bucket* bucket = GetBucket(bucket_id); |
| if (!bucket) |
| return error::kInvalidArguments; |
| uint32_t image_size = bucket->size(); |
| const void* data = bucket->GetData(0, image_size); |
| DCHECK(data || !image_size); |
| return DoCompressedTexImage(target, level, internal_format, width, height, |
| depth, border, image_size, data, |
| ContextState::k3D); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCompressedTexImage3D( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::CompressedTexImage3D& c = |
| *static_cast<const volatile gles2::cmds::CompressedTexImage3D*>(cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLenum internal_format = static_cast<GLenum>(c.internalformat); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLsizei depth = static_cast<GLsizei>(c.depth); |
| GLint border = static_cast<GLint>(c.border); |
| GLsizei image_size = static_cast<GLsizei>(c.imageSize); |
| uint32_t data_shm_id = c.data_shm_id; |
| uint32_t data_shm_offset = c.data_shm_offset; |
| |
| const void* data; |
| if (state_.bound_pixel_unpack_buffer.get()) { |
| if (data_shm_id) { |
| return error::kInvalidArguments; |
| } |
| data = reinterpret_cast<const void*>(data_shm_offset); |
| } else { |
| if (!data_shm_id && data_shm_offset) { |
| return error::kInvalidArguments; |
| } |
| data = GetSharedMemoryAs<const void*>( |
| data_shm_id, data_shm_offset, image_size); |
| } |
| return DoCompressedTexImage(target, level, internal_format, width, height, |
| depth, border, image_size, data, |
| ContextState::k3D); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCompressedTexSubImage3DBucket( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::CompressedTexSubImage3DBucket& c = |
| *static_cast<const volatile gles2::cmds::CompressedTexSubImage3DBucket*>( |
| cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLint xoffset = static_cast<GLint>(c.xoffset); |
| GLint yoffset = static_cast<GLint>(c.yoffset); |
| GLint zoffset = static_cast<GLint>(c.zoffset); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLsizei depth = static_cast<GLsizei>(c.depth); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLuint bucket_id = static_cast<GLuint>(c.bucket_id); |
| |
| if (state_.bound_pixel_unpack_buffer.get()) { |
| return error::kInvalidArguments; |
| } |
| Bucket* bucket = GetBucket(bucket_id); |
| if (!bucket) |
| return error::kInvalidArguments; |
| uint32_t image_size = bucket->size(); |
| const void* data = bucket->GetData(0, image_size); |
| DCHECK(data || !image_size); |
| return DoCompressedTexSubImage(target, level, xoffset, yoffset, zoffset, |
| width, height, depth, format, image_size, |
| data, ContextState::k3D); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCompressedTexSubImage3D( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::CompressedTexSubImage3D& c = |
| *static_cast<const volatile gles2::cmds::CompressedTexSubImage3D*>( |
| cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLint xoffset = static_cast<GLint>(c.xoffset); |
| GLint yoffset = static_cast<GLint>(c.yoffset); |
| GLint zoffset = static_cast<GLint>(c.zoffset); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLsizei depth = static_cast<GLsizei>(c.depth); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLsizei image_size = static_cast<GLsizei>(c.imageSize); |
| uint32_t data_shm_id = c.data_shm_id; |
| uint32_t data_shm_offset = c.data_shm_offset; |
| |
| const void* data; |
| if (state_.bound_pixel_unpack_buffer.get()) { |
| if (data_shm_id) { |
| return error::kInvalidArguments; |
| } |
| data = reinterpret_cast<const void*>(data_shm_offset); |
| } else { |
| if (!data_shm_id && data_shm_offset) { |
| return error::kInvalidArguments; |
| } |
| data = GetSharedMemoryAs<const void*>( |
| data_shm_id, data_shm_offset, image_size); |
| } |
| return DoCompressedTexSubImage(target, level, xoffset, yoffset, zoffset, |
| width, height, depth, format, image_size, |
| data, ContextState::k3D); |
| } |
| |
| error::Error GLES2DecoderImpl::DoCompressedTexImage( |
| GLenum target, GLint level, GLenum internal_format, GLsizei width, |
| GLsizei height, GLsizei depth, GLint border, GLsizei image_size, |
| const void* data, ContextState::Dimension dimension) { |
| const char* func_name; |
| if (dimension == ContextState::k2D) { |
| func_name = "glCompressedTexImage2D"; |
| if (!validators_->texture_target.IsValid(target)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, target, "target"); |
| return error::kNoError; |
| } |
| // TODO(ccameron): Add a separate texture from |texture_target| for |
| // [Compressed]Tex[Sub]Image2D and related functions. |
| // http://crbug.com/536854 |
| if (target == GL_TEXTURE_RECTANGLE_ARB) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, target, "target"); |
| return error::kNoError; |
| } |
| } else { |
| DCHECK_EQ(ContextState::k3D, dimension); |
| func_name = "glCompressedTexImage3D"; |
| if (!validators_->texture_3_d_target.IsValid(target)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, target, "target"); |
| return error::kNoError; |
| } |
| } |
| if (!validators_->compressed_texture_format.IsValid(internal_format)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM( |
| func_name, internal_format, "internalformat"); |
| return error::kNoError; |
| } |
| if (image_size < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "imageSize < 0"); |
| return error::kNoError; |
| } |
| if (!texture_manager()->ValidForTarget(target, level, width, height, depth) || |
| border != 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "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, func_name, "no texture bound at target"); |
| return error::kNoError; |
| } |
| Texture* texture = texture_ref->texture(); |
| if (texture->IsImmutable()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "texture is immutable"); |
| return error::kNoError; |
| } |
| |
| if (!ValidateCompressedTexDimensions(func_name, target, level, |
| width, height, depth, internal_format) || |
| !ValidateCompressedTexFuncData(func_name, width, height, depth, |
| internal_format, image_size, data)) { |
| return error::kNoError; |
| } |
| |
| if (texture->IsAttachedToFramebuffer()) { |
| framebuffer_state_.clear_state_dirty = true; |
| } |
| |
| std::unique_ptr<int8_t[]> zero; |
| if (!state_.bound_pixel_unpack_buffer && !data) { |
| zero.reset(new int8_t[image_size]); |
| memset(zero.get(), 0, image_size); |
| data = zero.get(); |
| } |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(func_name); |
| const CompressedFormatInfo* format_info = |
| GetCompressedFormatInfo(internal_format); |
| if (format_info != nullptr && !format_info->support_check(*feature_info_)) { |
| std::unique_ptr<uint8_t[]> decompressed_data = DecompressTextureData( |
| state_, *format_info, width, height, depth, image_size, data); |
| if (!decompressed_data) { |
| MarkContextLost(error::kGuilty); |
| group_->LoseContexts(error::kInnocent); |
| return error::kLostContext; |
| } |
| ScopedPixelUnpackState reset_restore(&state_); |
| if (dimension == ContextState::k2D) { |
| api()->glTexImage2DFn( |
| target, level, format_info->decompressed_internal_format, width, |
| height, border, format_info->decompressed_format, |
| format_info->decompressed_type, decompressed_data.get()); |
| } else { |
| api()->glTexImage3DFn( |
| target, level, format_info->decompressed_internal_format, width, |
| height, depth, border, format_info->decompressed_format, |
| format_info->decompressed_type, decompressed_data.get()); |
| } |
| } else { |
| if (dimension == ContextState::k2D) { |
| api()->glCompressedTexImage2DFn(target, level, internal_format, width, |
| height, border, image_size, data); |
| } else { |
| api()->glCompressedTexImage3DFn(target, level, internal_format, width, |
| height, depth, border, image_size, data); |
| } |
| } |
| GLenum error = LOCAL_PEEK_GL_ERROR(func_name); |
| if (error == GL_NO_ERROR) { |
| texture_manager()->SetLevelInfo(texture_ref, target, level, internal_format, |
| width, height, depth, border, 0, 0, |
| gfx::Rect(width, height)); |
| } |
| |
| // 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::HandleTexImage2D(uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const char* func_name = "glTexImage2D"; |
| const volatile gles2::cmds::TexImage2D& c = |
| *static_cast<const volatile gles2::cmds::TexImage2D*>(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<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLint internal_format = static_cast<GLint>(c.internalformat); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLint border = static_cast<GLint>(c.border); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLenum type = static_cast<GLenum>(c.type); |
| uint32_t pixels_shm_id = static_cast<uint32_t>(c.pixels_shm_id); |
| uint32_t pixels_shm_offset = static_cast<uint32_t>(c.pixels_shm_offset); |
| |
| if (width < 0 || height < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "dimensions < 0"); |
| return error::kNoError; |
| } |
| |
| PixelStoreParams params; |
| Buffer* buffer = state_.bound_pixel_unpack_buffer.get(); |
| if (buffer) { |
| if (pixels_shm_id) |
| return error::kInvalidArguments; |
| if (buffer->GetMappedRange()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "pixel unpack buffer should not be mapped to client memory"); |
| return error::kNoError; |
| } |
| params = state_.GetUnpackParams(ContextState::k2D); |
| } else { |
| if (!pixels_shm_id && pixels_shm_offset) |
| return error::kInvalidArguments; |
| // When reading from client buffer, the command buffer client side took |
| // the responsibility to take the pixels from the client buffer and |
| // unpack them according to the full ES3 pack parameters as source, all |
| // parameters for 0 (except for alignment) as destination mem for the |
| // service side. |
| params.alignment = state_.unpack_alignment; |
| } |
| uint32_t pixels_size; |
| uint32_t skip_size; |
| uint32_t padding; |
| if (!GLES2Util::ComputeImageDataSizesES3(width, height, 1, |
| format, type, |
| params, |
| &pixels_size, |
| nullptr, |
| nullptr, |
| &skip_size, |
| &padding)) { |
| return error::kOutOfBounds; |
| } |
| DCHECK_EQ(0u, skip_size); |
| |
| const void* pixels; |
| if (pixels_shm_id) { |
| pixels = GetSharedMemoryAs<const void*>( |
| pixels_shm_id, pixels_shm_offset, pixels_size); |
| if (!pixels) |
| return error::kOutOfBounds; |
| } else { |
| pixels = reinterpret_cast<const void*>(pixels_shm_offset); |
| } |
| |
| // For testing only. Allows us to stress the ability to respond to OOM errors. |
| uint32_t num_pixels; |
| if (workarounds().simulate_out_of_memory_on_large_textures && |
| (!SafeMultiplyUint32(width, height, &num_pixels) || |
| (num_pixels >= 4096 * 4096))) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, func_name, "synthetic out of memory"); |
| return error::kNoError; |
| } |
| |
| TextureManager::DoTexImageArguments args = { |
| target, level, internal_format, width, height, 1, border, format, type, |
| pixels, pixels_size, padding, |
| TextureManager::DoTexImageArguments::kTexImage2D }; |
| texture_manager()->ValidateAndDoTexImage( |
| &texture_state_, &state_, &framebuffer_state_, func_name, 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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| |
| const char* func_name = "glTexImage3D"; |
| const volatile gles2::cmds::TexImage3D& c = |
| *static_cast<const volatile gles2::cmds::TexImage3D*>(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<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLint internal_format = static_cast<GLint>(c.internalformat); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLsizei depth = static_cast<GLsizei>(c.depth); |
| GLint border = static_cast<GLint>(c.border); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLenum type = static_cast<GLenum>(c.type); |
| uint32_t pixels_shm_id = static_cast<uint32_t>(c.pixels_shm_id); |
| uint32_t pixels_shm_offset = static_cast<uint32_t>(c.pixels_shm_offset); |
| |
| if (width < 0 || height < 0 || depth < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "dimensions < 0"); |
| return error::kNoError; |
| } |
| |
| PixelStoreParams params; |
| Buffer* buffer = state_.bound_pixel_unpack_buffer.get(); |
| if (buffer) { |
| if (pixels_shm_id) |
| return error::kInvalidArguments; |
| if (buffer->GetMappedRange()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "pixel unpack buffer should not be mapped to client memory"); |
| return error::kNoError; |
| } |
| params = state_.GetUnpackParams(ContextState::k3D); |
| } else { |
| if (!pixels_shm_id && pixels_shm_offset) |
| return error::kInvalidArguments; |
| // When reading from client buffer, the command buffer client side took |
| // the responsibility to take the pixels from the client buffer and |
| // unpack them according to the full ES3 pack parameters as source, all |
| // parameters for 0 (except for alignment) as destination mem for the |
| // service side. |
| params.alignment = state_.unpack_alignment; |
| } |
| uint32_t pixels_size; |
| uint32_t skip_size; |
| uint32_t padding; |
| if (!GLES2Util::ComputeImageDataSizesES3(width, height, depth, |
| format, type, |
| params, |
| &pixels_size, |
| nullptr, |
| nullptr, |
| &skip_size, |
| &padding)) { |
| return error::kOutOfBounds; |
| } |
| DCHECK_EQ(0u, skip_size); |
| |
| const void* pixels; |
| if (pixels_shm_id) { |
| pixels = GetSharedMemoryAs<const void*>( |
| pixels_shm_id, pixels_shm_offset, pixels_size); |
| if (!pixels) |
| return error::kOutOfBounds; |
| } else { |
| pixels = reinterpret_cast<const void*>(pixels_shm_offset); |
| } |
| |
| // For testing only. Allows us to stress the ability to respond to OOM errors. |
| uint32_t num_pixels; |
| if (workarounds().simulate_out_of_memory_on_large_textures && |
| (!SafeMultiplyUint32(width, height, &num_pixels) || |
| (num_pixels >= 4096 * 4096))) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, func_name, "synthetic out of memory"); |
| return error::kNoError; |
| } |
| |
| TextureManager::DoTexImageArguments args = { |
| target, level, internal_format, width, height, depth, border, format, type, |
| pixels, pixels_size, padding, |
| TextureManager::DoTexImageArguments::kTexImage3D }; |
| texture_manager()->ValidateAndDoTexImage( |
| &texture_state_, &state_, &framebuffer_state_, func_name, 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::HandleCompressedTexSubImage2DBucket( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| const volatile gles2::cmds::CompressedTexSubImage2DBucket& c = |
| *static_cast<const volatile gles2::cmds::CompressedTexSubImage2DBucket*>( |
| cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLint xoffset = static_cast<GLint>(c.xoffset); |
| GLint yoffset = static_cast<GLint>(c.yoffset); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLuint bucket_id = static_cast<GLuint>(c.bucket_id); |
| |
| if (state_.bound_pixel_unpack_buffer.get()) { |
| return error::kInvalidArguments; |
| } |
| Bucket* bucket = GetBucket(bucket_id); |
| if (!bucket) |
| return error::kInvalidArguments; |
| uint32_t image_size = bucket->size(); |
| const void* data = bucket->GetData(0, image_size); |
| DCHECK(data || !image_size); |
| return DoCompressedTexSubImage(target, level, xoffset, yoffset, 0, |
| width, height, 1, format, image_size, data, |
| ContextState::k2D); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCompressedTexSubImage2D( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::CompressedTexSubImage2D& c = |
| *static_cast<const volatile gles2::cmds::CompressedTexSubImage2D*>( |
| cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLint xoffset = static_cast<GLint>(c.xoffset); |
| GLint yoffset = static_cast<GLint>(c.yoffset); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLsizei image_size = static_cast<GLsizei>(c.imageSize); |
| uint32_t data_shm_id = c.data_shm_id; |
| uint32_t data_shm_offset = c.data_shm_offset; |
| |
| const void* data; |
| if (state_.bound_pixel_unpack_buffer.get()) { |
| if (data_shm_id) { |
| return error::kInvalidArguments; |
| } |
| data = reinterpret_cast<const void*>(data_shm_offset); |
| } else { |
| if (!data_shm_id) { |
| return error::kInvalidArguments; |
| } |
| data = GetSharedMemoryAs<const void*>( |
| data_shm_id, data_shm_offset, image_size); |
| } |
| return DoCompressedTexSubImage(target, level, xoffset, yoffset, 0, |
| width, height, 1, format, image_size, data, |
| ContextState::k2D); |
| } |
| |
| error::Error GLES2DecoderImpl::DoCompressedTexSubImage( |
| GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, |
| GLsizei width, GLsizei height, GLsizei depth, GLenum format, |
| GLsizei image_size, const void * data, ContextState::Dimension dimension) { |
| const char* func_name; |
| if (dimension == ContextState::k2D) { |
| func_name = "glCompressedTexSubImage2D"; |
| if (!validators_->texture_target.IsValid(target)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, target, "target"); |
| return error::kNoError; |
| } |
| } else { |
| DCHECK_EQ(ContextState::k3D, dimension); |
| func_name = "glCompressedTexSubImage3D"; |
| if (!validators_->texture_3_d_target.IsValid(target)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, target, "target"); |
| return error::kNoError; |
| } |
| } |
| if (!validators_->compressed_texture_format.IsValid(format)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, format, "format"); |
| return error::kNoError; |
| } |
| if (image_size < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "imageSize < 0"); |
| return error::kNoError; |
| } |
| if (!texture_manager()->ValidForTarget(target, level, width, height, depth)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "dimensions out of range"); |
| return error::kNoError; |
| } |
| TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( |
| &state_, target); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, func_name, "no texture bound at target"); |
| return error::kNoError; |
| } |
| Texture* texture = texture_ref->texture(); |
| GLenum type = 0; |
| GLenum internal_format = 0; |
| if (!texture->GetLevelType(target, level, &type, &internal_format)) { |
| std::string msg = base::StringPrintf("level %d does not exist", level); |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, msg.c_str()); |
| return error::kNoError; |
| } |
| if (internal_format != format) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "format does not match internalformat."); |
| return error::kNoError; |
| } |
| if (!texture->ValidForTexture(target, level, xoffset, yoffset, zoffset, |
| width, height, depth)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "bad dimensions."); |
| return error::kNoError; |
| } |
| |
| if (!ValidateCompressedTexFuncData( |
| func_name, width, height, depth, format, image_size, data) || |
| !ValidateCompressedTexSubDimensions( |
| func_name, target, level, xoffset, yoffset, zoffset, |
| width, height, depth, format, texture)) { |
| return error::kNoError; |
| } |
| if (image_size > 0 && !state_.bound_pixel_unpack_buffer && !data) |
| return error::kInvalidArguments; |
| |
| if (!texture->IsLevelCleared(target, level)) { |
| // This can only happen if the compressed texture was allocated |
| // using TexStorage{2|3}D. |
| DCHECK(texture->IsImmutable()); |
| GLsizei level_width = 0, level_height = 0, level_depth = 0; |
| bool success = texture->GetLevelSize( |
| target, level, &level_width, &level_height, &level_depth); |
| DCHECK(success); |
| if (xoffset == 0 && width == level_width && |
| yoffset == 0 && height == level_height && |
| zoffset == 0 && depth == level_depth) { |
| // We can skip the clear if we're uploading the entire level. |
| texture_manager()->SetLevelCleared(texture_ref, target, level, true); |
| } else { |
| texture_manager()->ClearTextureLevel(this, texture_ref, target, level); |
| } |
| DCHECK(texture->IsLevelCleared(target, level)); |
| } |
| |
| const CompressedFormatInfo* format_info = |
| GetCompressedFormatInfo(internal_format); |
| if (format_info != nullptr && !format_info->support_check(*feature_info_)) { |
| std::unique_ptr<uint8_t[]> decompressed_data = DecompressTextureData( |
| state_, *format_info, width, height, depth, image_size, data); |
| if (!decompressed_data) { |
| MarkContextLost(error::kGuilty); |
| group_->LoseContexts(error::kInnocent); |
| return error::kLostContext; |
| } |
| ScopedPixelUnpackState reset_restore(&state_); |
| if (dimension == ContextState::k2D) { |
| api()->glTexSubImage2DFn(target, level, xoffset, yoffset, width, height, |
| format_info->decompressed_format, |
| format_info->decompressed_type, |
| decompressed_data.get()); |
| } else { |
| api()->glTexSubImage3DFn(target, level, xoffset, yoffset, zoffset, width, |
| height, depth, format_info->decompressed_format, |
| format_info->decompressed_type, |
| decompressed_data.get()); |
| } |
| } else { |
| if (dimension == ContextState::k2D) { |
| api()->glCompressedTexSubImage2DFn(target, level, xoffset, yoffset, width, |
| height, format, image_size, data); |
| } else { |
| api()->glCompressedTexSubImage3DFn(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(); |
| return error::kNoError; |
| } |
| |
| bool GLES2DecoderImpl::ValidateCopyTexFormat(const char* func_name, |
| GLenum internal_format, |
| GLenum read_format, |
| GLenum read_type) { |
| std::string output_error_msg; |
| if (!ValidateCopyTexFormatHelper(GetFeatureInfo(), internal_format, |
| read_format, read_type, &output_error_msg)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| output_error_msg.c_str()); |
| return false; |
| } |
| return true; |
| } |
| |
| void GLES2DecoderImpl::DoCopyTexImage2D( |
| GLenum target, |
| GLint level, |
| GLenum internal_format, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| GLint border) { |
| const char* func_name = "glCopyTexImage2D"; |
| DCHECK(!ShouldDeferReads()); |
| TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( |
| &state_, target); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, func_name, "unknown texture for target"); |
| return; |
| } |
| Texture* texture = texture_ref->texture(); |
| if (texture->IsImmutable()) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, func_name, "texture is immutable"); |
| return; |
| } |
| if (!texture_manager()->ValidForTarget(target, level, width, height, 1) || |
| border != 0) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, func_name, "dimensions out of range"); |
| return; |
| } |
| |
| if (!CheckBoundReadFramebufferValid(func_name, |
| GL_INVALID_FRAMEBUFFER_OPERATION)) { |
| return; |
| } |
| |
| GLenum read_format = GetBoundReadFramebufferInternalFormat(); |
| GLenum read_type = GetBoundReadFramebufferTextureType(); |
| if (!ValidateCopyTexFormat(func_name, internal_format, |
| read_format, read_type)) { |
| return; |
| } |
| |
| uint32_t pixels_size = 0; |
| // TODO(piman): OpenGL ES 3.0.4 Section 3.8.5 specifies how to pick an |
| // effective internal format if internal_format is unsized, which is a fairly |
| // involved logic. For now, just make sure we pick something valid. |
| GLenum format = |
| TextureManager::ExtractFormatFromStorageFormat(internal_format); |
| GLenum type = TextureManager::ExtractTypeFromStorageFormat(internal_format); |
| bool internal_format_unsized = internal_format == format; |
| // The picks made by the temporary logic above may not be valid on ES3. |
| // This if-block checks the temporary logic and should be removed with it. |
| if (internal_format_unsized && feature_info_->IsWebGL2OrES3Context()) { |
| // While there are other possible types for unsized formats (cf. OpenGL ES |
| // 3.0.5, section 3.7, table 3.3, page 113), they won't appear here. |
| // ExtractTypeFromStorageFormat will always return UNSIGNED_BYTE for |
| // unsized formats. |
| DCHECK(type == GL_UNSIGNED_BYTE); |
| switch (internal_format) { |
| case GL_RGB: |
| case GL_RGBA: |
| case GL_LUMINANCE_ALPHA: |
| case GL_LUMINANCE: |
| case GL_ALPHA: |
| case GL_BGRA_EXT: |
| break; |
| default: |
| // Other unsized internal_formats are invalid in ES3. |
| format = GL_NONE; |
| break; |
| } |
| } |
| if (!format || !type) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| func_name, "Invalid unsized internal format."); |
| return; |
| } |
| |
| DCHECK(texture_manager()->ValidateTextureParameters( |
| GetErrorState(), func_name, true, format, type, internal_format, level)); |
| |
| // Only target image size is validated here. |
| if (!GLES2Util::ComputeImageDataSizes(width, height, 1, format, type, |
| state_.unpack_alignment, &pixels_size, |
| nullptr, nullptr)) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, func_name, "dimensions too large"); |
| return; |
| } |
| |
| if (FormsTextureCopyingFeedbackLoop(texture_ref, level, 0)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| func_name, "source and destination textures are the same"); |
| return; |
| } |
| |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(func_name); |
| ScopedResolvedFramebufferBinder binder(this, false, true); |
| gfx::Size size = GetBoundReadFramebufferSize(); |
| |
| if (texture->IsAttachedToFramebuffer()) { |
| framebuffer_state_.clear_state_dirty = true; |
| } |
| |
| bool requires_luma_blit = |
| CopyTexImageResourceManager::CopyTexImageRequiresBlit(feature_info_.get(), |
| format); |
| if (requires_luma_blit && |
| !InitializeCopyTexImageBlitter(func_name)) { |
| return; |
| } |
| |
| // If fbo's read buffer and the target texture are the same texture, but |
| // different levels, and if the read buffer is non-base texture level, |
| // then following internal glTexImage2D() calls may change the target texture |
| // and make the originally mipmap complete texture mipmap incomplete, which |
| // in turn make the fbo incomplete. |
| // In order to avoid that, we clamp the BASE_LEVEL and MAX_LEVEL to the same |
| // texture level that's attached to the fbo's read buffer. |
| bool reset_source_texture_base_level_max_level = false; |
| GLint attached_texture_level = -1; |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| if (framebuffer) { |
| const Framebuffer::Attachment* attachment = |
| framebuffer->GetReadBufferAttachment(); |
| if (attachment->IsTexture(texture_ref)) { |
| DCHECK(attachment->IsTextureAttachment()); |
| attached_texture_level = attachment->level(); |
| DCHECK_GE(attached_texture_level, 0); |
| if (attached_texture_level != texture->base_level()) |
| reset_source_texture_base_level_max_level = true; |
| } |
| } |
| if (reset_source_texture_base_level_max_level) { |
| api()->glTexParameteriFn(target, GL_TEXTURE_BASE_LEVEL, |
| attached_texture_level); |
| api()->glTexParameteriFn(target, GL_TEXTURE_MAX_LEVEL, |
| attached_texture_level); |
| } |
| |
| // Clip to size to source dimensions |
| gfx::Rect src(x, y, width, height); |
| const gfx::Rect dst(0, 0, size.width(), size.height()); |
| src.Intersect(dst); |
| |
| GLenum final_internal_format = TextureManager::AdjustTexInternalFormat( |
| feature_info_.get(), internal_format); |
| if (workarounds().force_int_or_srgb_cube_texture_complete && |
| texture->target() == GL_TEXTURE_CUBE_MAP && |
| (GLES2Util::IsIntegerFormat(final_internal_format) || |
| GLES2Util::GetColorEncodingFromInternalFormat(final_internal_format) == |
| GL_SRGB)) { |
| TextureManager::DoTexImageArguments args = { |
| target, |
| level, |
| final_internal_format, |
| width, |
| height, |
| 1, |
| border, |
| format, |
| type, |
| nullptr, |
| pixels_size, |
| 0, |
| TextureManager::DoTexImageArguments::kTexImage2D}; |
| texture_manager()->WorkaroundCopyTexImageCubeMap( |
| &texture_state_, &state_, &framebuffer_state_, texture_ref, func_name, |
| args); |
| } |
| |
| if (src.x() != x || src.y() != y || |
| src.width() != width || src.height() != height || |
| final_internal_format == GL_BGRA_EXT) { |
| // GL_BGRA_EXT is not allowed as internalformat for glCopyTexImage2D, |
| // which is a bit of a quirk in the spec, but this path works. |
| { |
| // Add extra scope to destroy zero and the object it owns right |
| // after its usage. |
| // some part was clipped so clear the rect. |
| |
| std::unique_ptr<char[]> zero(new char[pixels_size]); |
| memset(zero.get(), 0, pixels_size); |
| ScopedPixelUnpackState reset_restore(&state_); |
| api()->glTexImage2DFn(target, level, final_internal_format, width, height, |
| border, format, type, zero.get()); |
| } |
| |
| if (!src.IsEmpty()) { |
| GLint destX = src.x() - x; |
| GLint destY = src.y() - y; |
| if (requires_luma_blit) { |
| copy_tex_image_blit_->DoCopyTexSubImageToLUMACompatibilityTexture( |
| this, texture->service_id(), texture->target(), target, format, |
| type, level, destX, destY, 0, |
| src.x(), src.y(), src.width(), src.height(), |
| GetBoundReadFramebufferServiceId(), |
| GetBoundReadFramebufferInternalFormat()); |
| } else { |
| api()->glCopyTexSubImage2DFn(target, level, destX, destY, src.x(), |
| src.y(), src.width(), src.height()); |
| } |
| } |
| } else { |
| if (workarounds().init_two_cube_map_levels_before_copyteximage && |
| texture->target() == GL_TEXTURE_CUBE_MAP && |
| target != GL_TEXTURE_CUBE_MAP_POSITIVE_X) { |
| for (int i = 0; i < 2; ++i) { |
| TextureManager::DoTexImageArguments args = { |
| target, i, final_internal_format, width, height, 1, border, |
| format, type, nullptr, pixels_size, 0, |
| TextureManager::DoTexImageArguments::kTexImage2D }; |
| texture_manager()->WorkaroundCopyTexImageCubeMap(&texture_state_, |
| &state_, &framebuffer_state_, texture_ref, func_name, args); |
| } |
| } |
| |
| // The service id and target of the texture attached to READ_FRAMEBUFFER. |
| GLuint source_texture_service_id = 0; |
| GLenum source_texture_target = 0; |
| uint32_t channels_exist = GLES2Util::GetChannelsForFormat(read_format); |
| bool use_workaround = NeedsCopyTextureImageWorkaround( |
| final_internal_format, channels_exist, &source_texture_service_id, |
| &source_texture_target); |
| if (requires_luma_blit) { |
| copy_tex_image_blit_->DoCopyTexImage2DToLUMACompatibilityTexture( |
| this, texture->service_id(), texture->target(), target, format, |
| type, level, internal_format, x, y, width, height, |
| GetBoundReadFramebufferServiceId(), |
| GetBoundReadFramebufferInternalFormat()); |
| } else if (use_workaround) { |
| GLenum dest_texture_target = target; |
| GLenum framebuffer_target = GetReadFramebufferTarget(); |
| |
| GLenum temp_internal_format = 0; |
| if (channels_exist == GLES2Util::kRGBA) { |
| temp_internal_format = GL_RGBA; |
| } else if (channels_exist == GLES2Util::kRGB) { |
| temp_internal_format = GL_RGB; |
| } else { |
| NOTREACHED(); |
| } |
| |
| if (workarounds().clear_pixel_unpack_buffer_before_copyteximage) |
| state_.PushTextureUnpackState(); |
| GLuint temp_texture; |
| { |
| // Copy from the read framebuffer into |temp_texture|. |
| api()->glGenTexturesFn(1, &temp_texture); |
| ScopedTextureBinder binder(&state_, temp_texture, |
| source_texture_target); |
| api()->glCopyTexImage2DFn(source_texture_target, 0, |
| temp_internal_format, x, y, width, height, |
| border); |
| |
| // Attach the temp texture to the read framebuffer. |
| api()->glFramebufferTexture2DEXTFn( |
| framebuffer_target, GL_COLOR_ATTACHMENT0, source_texture_target, |
| temp_texture, 0); |
| } |
| |
| // Copy to the final texture. |
| DCHECK_EQ(static_cast<GLuint>(GL_TEXTURE_2D), dest_texture_target); |
| api()->glCopyTexImage2DFn(dest_texture_target, level, |
| final_internal_format, 0, 0, width, height, 0); |
| |
| // Rebind source texture. |
| api()->glFramebufferTexture2DEXTFn( |
| framebuffer_target, GL_COLOR_ATTACHMENT0, source_texture_target, |
| source_texture_service_id, 0); |
| if (workarounds().clear_pixel_unpack_buffer_before_copyteximage) |
| state_.RestoreUnpackState(); |
| |
| api()->glDeleteTexturesFn(1, &temp_texture); |
| } else { |
| if (workarounds().init_one_cube_map_level_before_copyteximage && |
| texture->target() == GL_TEXTURE_CUBE_MAP && |
| target != GL_TEXTURE_CUBE_MAP_POSITIVE_X) { |
| TextureManager::DoTexImageArguments args = { |
| target, level, final_internal_format, width, height, 1, border, |
| format, type, nullptr, pixels_size, 0, |
| TextureManager::DoTexImageArguments::kTexImage2D }; |
| texture_manager()->WorkaroundCopyTexImageCubeMap(&texture_state_, |
| &state_, &framebuffer_state_, texture_ref, func_name, args); |
| } |
| if (workarounds().clear_pixel_unpack_buffer_before_copyteximage) |
| state_.PushTextureUnpackState(); |
| api()->glCopyTexImage2DFn(target, level, final_internal_format, x, y, |
| width, height, border); |
| if (workarounds().clear_pixel_unpack_buffer_before_copyteximage) |
| state_.RestoreUnpackState(); |
| } |
| } |
| if (reset_source_texture_base_level_max_level) { |
| api()->glTexParameteriFn(target, GL_TEXTURE_BASE_LEVEL, |
| texture->base_level()); |
| api()->glTexParameteriFn(target, GL_TEXTURE_MAX_LEVEL, |
| texture->max_level()); |
| } |
| GLenum error = LOCAL_PEEK_GL_ERROR(func_name); |
| if (error == GL_NO_ERROR) { |
| texture_manager()->SetLevelInfo(texture_ref, target, level, internal_format, |
| width, height, 1, border, format, |
| type, gfx::Rect(width, height)); |
| texture->ApplyFormatWorkarounds(feature_info_.get()); |
| } |
| |
| // 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) { |
| const char* func_name = "glCopyTexSubImage2D"; |
| DCHECK(!ShouldDeferReads()); |
| TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( |
| &state_, target); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, func_name, "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) || |
| !texture->ValidForTexture( |
| target, level, xoffset, yoffset, 0, width, height, 1)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "bad dimensions."); |
| return; |
| } |
| |
| if (!CheckBoundReadFramebufferValid(func_name, |
| GL_INVALID_FRAMEBUFFER_OPERATION)) { |
| return; |
| } |
| |
| GLenum read_format = GetBoundReadFramebufferInternalFormat(); |
| GLenum read_type = GetBoundReadFramebufferTextureType(); |
| if (!ValidateCopyTexFormat(func_name, internal_format, |
| read_format, read_type)) { |
| return; |
| } |
| |
| if (FormsTextureCopyingFeedbackLoop(texture_ref, level, 0)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| func_name, "source and destination textures are the same"); |
| return; |
| } |
| |
| ScopedResolvedFramebufferBinder binder(this, false, true); |
| gfx::Size size = GetBoundReadFramebufferSize(); |
| |
| gfx::Rect src(x, y, width, height); |
| const gfx::Rect dst(0, 0, size.width(), size.height()); |
| src.Intersect(dst); |
| |
| if (src.IsEmpty()) |
| return; |
| |
| GLint dx = src.x() - x; |
| GLint dy = src.y() - y; |
| GLint destX = xoffset + dx; |
| GLint destY = yoffset + dy; |
| // It's only legal to skip clearing the level of the target texture |
| // if the entire level is being redefined. |
| GLsizei level_width = 0; |
| GLsizei level_height = 0; |
| GLsizei level_depth = 0; |
| bool have_level = texture->GetLevelSize( |
| target, level, &level_width, &level_height, &level_depth); |
| // Validated above. |
| DCHECK(have_level); |
| if (destX == 0 && destY == 0 && |
| src.width() == level_width && src.height() == level_height) { |
| // Write all pixels in below. |
| texture_manager()->SetLevelCleared(texture_ref, target, level, true); |
| } else { |
| gfx::Rect cleared_rect; |
| if (TextureManager::CombineAdjacentRects( |
| texture->GetLevelClearedRect(target, level), |
| gfx::Rect(destX, destY, src.width(), src.height()), |
| &cleared_rect)) { |
| DCHECK_GE(cleared_rect.size().GetArea(), |
| texture->GetLevelClearedRect(target, level).size().GetArea()); |
| texture_manager()->SetLevelClearedRect(texture_ref, target, level, |
| cleared_rect); |
| } else { |
| // Otherwise clear part of texture level that is not already cleared. |
| if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, |
| level)) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, func_name, "dimensions too big"); |
| return; |
| } |
| } |
| } |
| |
| if (CopyTexImageResourceManager::CopyTexImageRequiresBlit( |
| feature_info_.get(), internal_format)) { |
| if (!InitializeCopyTexImageBlitter("glCopyTexSubImage2D")) { |
| return; |
| } |
| copy_tex_image_blit_->DoCopyTexSubImageToLUMACompatibilityTexture( |
| this, texture->service_id(), texture->target(), target, |
| internal_format, type, level, destX, destY, 0, |
| src.x(), src.y(), src.width(), src.height(), |
| GetBoundReadFramebufferServiceId(), |
| GetBoundReadFramebufferInternalFormat()); |
| } else { |
| api()->glCopyTexSubImage2DFn(target, level, destX, destY, src.x(), src.y(), |
| src.width(), src.height()); |
| } |
| |
| // This may be a slow command. Exit command processing to allow for |
| // context preemption and GPU watchdog checks. |
| ExitCommandProcessingEarly(); |
| } |
| |
| void GLES2DecoderImpl::DoCopyTexSubImage3D( |
| GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint zoffset, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height) { |
| const char* func_name = "glCopyTexSubImage3D"; |
| DCHECK(!ShouldDeferReads()); |
| TextureRef* texture_ref = texture_manager()->GetTextureInfoForTarget( |
| &state_, target); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, func_name, "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) || |
| !texture->ValidForTexture( |
| target, level, xoffset, yoffset, zoffset, width, height, 1)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "bad dimensions."); |
| return; |
| } |
| |
| if (!CheckBoundReadFramebufferValid(func_name, |
| GL_INVALID_FRAMEBUFFER_OPERATION)) { |
| return; |
| } |
| |
| GLenum read_format = GetBoundReadFramebufferInternalFormat(); |
| GLenum read_type = GetBoundReadFramebufferTextureType(); |
| if (!ValidateCopyTexFormat(func_name, internal_format, |
| read_format, read_type)) { |
| return; |
| } |
| |
| if (FormsTextureCopyingFeedbackLoop(texture_ref, level, zoffset)) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| func_name, "source and destination textures are the same"); |
| return; |
| } |
| |
| ScopedResolvedFramebufferBinder binder(this, false, true); |
| gfx::Size size = GetBoundReadFramebufferSize(); |
| |
| gfx::Rect src(x, y, width, height); |
| const gfx::Rect dst(0, 0, size.width(), size.height()); |
| src.Intersect(dst); |
| if (src.IsEmpty()) |
| return; |
| |
| GLint dx = src.x() - x; |
| GLint dy = src.y() - y; |
| GLint destX = xoffset + dx; |
| GLint destY = yoffset + dy; |
| // For 3D textures, we always clear the entire texture to 0 if it is not |
| // cleared. See the code in TextureManager::ValidateAndDoTexSubImage |
| // for TexSubImage3D. |
| if (!texture->IsLevelCleared(target, level)) { |
| if (!texture_manager()->ClearTextureLevel(this, texture_ref, target, |
| level)) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, func_name, "dimensions too big"); |
| return; |
| } |
| DCHECK(texture->IsLevelCleared(target, level)); |
| } |
| |
| if (CopyTexImageResourceManager::CopyTexImageRequiresBlit( |
| feature_info_.get(), internal_format)) { |
| if (!InitializeCopyTexImageBlitter(func_name)) { |
| return; |
| } |
| copy_tex_image_blit_->DoCopyTexSubImageToLUMACompatibilityTexture( |
| this, texture->service_id(), texture->target(), target, |
| internal_format, type, level, destX, destY, zoffset, |
| src.x(), src.y(), src.width(), src.height(), |
| GetBoundReadFramebufferServiceId(), |
| GetBoundReadFramebufferInternalFormat()); |
| } else { |
| api()->glCopyTexSubImage3DFn(target, level, destX, destY, zoffset, src.x(), |
| src.y(), src.width(), src.height()); |
| } |
| |
| // This may be a slow command. Exit command processing to allow for |
| // context preemption and GPU watchdog checks. |
| ExitCommandProcessingEarly(); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleTexSubImage2D( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| const char* func_name = "glTexSubImage2D"; |
| const volatile gles2::cmds::TexSubImage2D& c = |
| *static_cast<const volatile gles2::cmds::TexSubImage2D*>(cmd_data); |
| TRACE_EVENT2("gpu", "GLES2DecoderImpl::HandleTexSubImage2D", |
| "width", c.width, "height", c.height); |
| GLboolean internal = static_cast<GLboolean>(c.internal); |
| if (internal == GL_TRUE && texture_state_.tex_image_failed) |
| return error::kNoError; |
| |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLint xoffset = static_cast<GLint>(c.xoffset); |
| GLint yoffset = static_cast<GLint>(c.yoffset); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLenum type = static_cast<GLenum>(c.type); |
| uint32_t pixels_shm_id = static_cast<uint32_t>(c.pixels_shm_id); |
| uint32_t pixels_shm_offset = static_cast<uint32_t>(c.pixels_shm_offset); |
| |
| if (width < 0 || height < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "dimensions < 0"); |
| return error::kNoError; |
| } |
| |
| PixelStoreParams params; |
| Buffer* buffer = state_.bound_pixel_unpack_buffer.get(); |
| if (buffer) { |
| if (pixels_shm_id) |
| return error::kInvalidArguments; |
| if (buffer->GetMappedRange()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "pixel unpack buffer should not be mapped to client memory"); |
| return error::kNoError; |
| } |
| params = state_.GetUnpackParams(ContextState::k2D); |
| } else { |
| if (!pixels_shm_id && pixels_shm_offset) |
| return error::kInvalidArguments; |
| // When reading from client buffer, the command buffer client side took |
| // the responsibility to take the pixels from the client buffer and |
| // unpack them according to the full ES3 pack parameters as source, all |
| // parameters for 0 (except for alignment) as destination mem for the |
| // service side. |
| params.alignment = state_.unpack_alignment; |
| } |
| uint32_t pixels_size; |
| uint32_t skip_size; |
| uint32_t padding; |
| if (!GLES2Util::ComputeImageDataSizesES3(width, height, 1, |
| format, type, |
| params, |
| &pixels_size, |
| nullptr, |
| nullptr, |
| &skip_size, |
| &padding)) { |
| return error::kOutOfBounds; |
| } |
| DCHECK_EQ(0u, skip_size); |
| |
| const void* pixels; |
| if (pixels_shm_id) { |
| pixels = GetSharedMemoryAs<const void*>( |
| pixels_shm_id, pixels_shm_offset, pixels_size); |
| if (!pixels) |
| return error::kOutOfBounds; |
| } else { |
| DCHECK(buffer || !pixels_shm_offset); |
| pixels = reinterpret_cast<const void*>(pixels_shm_offset); |
| } |
| |
| TextureManager::DoTexSubImageArguments args = { |
| target, level, xoffset, yoffset, 0, width, height, 1, |
| format, type, pixels, pixels_size, padding, |
| TextureManager::DoTexSubImageArguments::kTexSubImage2D}; |
| texture_manager()->ValidateAndDoTexSubImage(this, &texture_state_, &state_, |
| &framebuffer_state_, |
| func_name, 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::HandleTexSubImage3D( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| |
| const char* func_name = "glTexSubImage3D"; |
| const volatile gles2::cmds::TexSubImage3D& c = |
| *static_cast<const volatile gles2::cmds::TexSubImage3D*>(cmd_data); |
| TRACE_EVENT2("gpu", "GLES2DecoderImpl::HandleTexSubImage3D", |
| "widthXheight", c.width * c.height, "depth", c.depth); |
| GLboolean internal = static_cast<GLboolean>(c.internal); |
| if (internal == GL_TRUE && texture_state_.tex_image_failed) |
| return error::kNoError; |
| |
| GLenum target = static_cast<GLenum>(c.target); |
| GLint level = static_cast<GLint>(c.level); |
| GLint xoffset = static_cast<GLint>(c.xoffset); |
| GLint yoffset = static_cast<GLint>(c.yoffset); |
| GLint zoffset = static_cast<GLint>(c.zoffset); |
| GLsizei width = static_cast<GLsizei>(c.width); |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLsizei depth = static_cast<GLsizei>(c.depth); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLenum type = static_cast<GLenum>(c.type); |
| uint32_t pixels_shm_id = static_cast<uint32_t>(c.pixels_shm_id); |
| uint32_t pixels_shm_offset = static_cast<uint32_t>(c.pixels_shm_offset); |
| |
| if (width < 0 || height < 0 || depth < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "dimensions < 0"); |
| return error::kNoError; |
| } |
| |
| PixelStoreParams params; |
| Buffer* buffer = state_.bound_pixel_unpack_buffer.get(); |
| if (buffer) { |
| if (pixels_shm_id) |
| return error::kInvalidArguments; |
| if (buffer->GetMappedRange()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "pixel unpack buffer should not be mapped to client memory"); |
| return error::kNoError; |
| } |
| params = state_.GetUnpackParams(ContextState::k3D); |
| } else { |
| if (!pixels_shm_id && pixels_shm_offset) |
| return error::kInvalidArguments; |
| // When reading from client buffer, the command buffer client side took |
| // the responsibility to take the pixels from the client buffer and |
| // unpack them according to the full ES3 pack parameters as source, all |
| // parameters for 0 (except for alignment) as destination mem for the |
| // service side. |
| params.alignment = state_.unpack_alignment; |
| } |
| uint32_t pixels_size; |
| uint32_t skip_size; |
| uint32_t padding; |
| if (!GLES2Util::ComputeImageDataSizesES3(width, height, depth, |
| format, type, |
| params, |
| &pixels_size, |
| nullptr, |
| nullptr, |
| &skip_size, |
| &padding)) { |
| return error::kOutOfBounds; |
| } |
| DCHECK_EQ(0u, skip_size); |
| |
| const void* pixels; |
| if (pixels_shm_id) { |
| pixels = GetSharedMemoryAs<const void*>( |
| pixels_shm_id, pixels_shm_offset, pixels_size); |
| if (!pixels) |
| return error::kOutOfBounds; |
| } else { |
| DCHECK(buffer || !pixels_shm_offset); |
| pixels = reinterpret_cast<const void*>(pixels_shm_offset); |
| } |
| |
| TextureManager::DoTexSubImageArguments args = { |
| target, level, xoffset, yoffset, zoffset, width, height, depth, |
| format, type, pixels, pixels_size, padding, |
| TextureManager::DoTexSubImageArguments::kTexSubImage3D}; |
| texture_manager()->ValidateAndDoTexSubImage(this, &texture_state_, &state_, |
| &framebuffer_state_, |
| func_name, 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::HandleGetVertexAttribPointerv( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetVertexAttribPointerv& c = |
| *static_cast<const volatile gles2::cmds::GetVertexAttribPointerv*>( |
| cmd_data); |
| GLuint index = static_cast<GLuint>(c.index); |
| GLenum pname = static_cast<GLenum>(c.pname); |
| typedef cmds::GetVertexAttribPointerv::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| 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 <class T> |
| bool GLES2DecoderImpl::GetUniformSetup(GLuint program_id, |
| GLint fake_location, |
| uint32_t shm_id, |
| uint32_t shm_offset, |
| error::Error* error, |
| GLint* real_location, |
| GLuint* service_id, |
| SizedResult<T>** 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<T>* result; |
| result = GetSharedMemoryAs<SizedResult<T>*>( |
| shm_id, shm_offset, SizedResult<T>::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_t num_elements = GLES2Util::GetElementCountForUniformType(type); |
| if (num_elements == 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glGetUniform", "unknown type"); |
| return false; |
| } |
| result = GetSharedMemoryAs<SizedResult<T>*>( |
| shm_id, shm_offset, SizedResult<T>::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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetUniformiv& c = |
| *static_cast<const volatile gles2::cmds::GetUniformiv*>(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<GLint>(program, fake_location, c.params_shm_id, |
| c.params_shm_offset, &error, &real_location, |
| &service_id, &result, &result_type, |
| &result_size)) { |
| api()->glGetUniformivFn(service_id, real_location, result->GetData()); |
| } |
| return error; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetUniformuiv( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| |
| const volatile gles2::cmds::GetUniformuiv& c = |
| *static_cast<const volatile gles2::cmds::GetUniformuiv*>(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<GLuint>(program, fake_location, c.params_shm_id, |
| c.params_shm_offset, &error, &real_location, |
| &service_id, &result, &result_type, |
| &result_size)) { |
| api()->glGetUniformuivFn(service_id, real_location, result->GetData()); |
| } |
| return error; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetUniformfv( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetUniformfv& c = |
| *static_cast<const volatile gles2::cmds::GetUniformfv*>(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<GLfloat>(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); |
| std::unique_ptr<GLint[]> temp(new GLint[num_values]); |
| api()->glGetUniformivFn(service_id, real_location, temp.get()); |
| GLfloat* dst = result->GetData(); |
| for (GLsizei ii = 0; ii < num_values; ++ii) { |
| dst[ii] = (temp[ii] != 0); |
| } |
| } else { |
| api()->glGetUniformfvFn(service_id, real_location, result->GetData()); |
| } |
| } |
| return error; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetShaderPrecisionFormat( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetShaderPrecisionFormat& c = |
| *static_cast<const volatile gles2::cmds::GetShaderPrecisionFormat*>( |
| cmd_data); |
| GLenum shader_type = static_cast<GLenum>(c.shadertype); |
| GLenum precision_type = static_cast<GLenum>(c.precisiontype); |
| typedef cmds::GetShaderPrecisionFormat::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| 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; |
| QueryShaderPrecisionFormat(gl_version_info(), 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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetAttachedShaders& c = |
| *static_cast<const volatile gles2::cmds::GetAttachedShaders*>(cmd_data); |
| uint32_t result_size = c.result_size; |
| GLuint program_id = static_cast<GLuint>(c.program); |
| Program* program = GetProgramInfoNotShader( |
| program_id, "glGetAttachedShaders"); |
| if (!program) { |
| return error::kNoError; |
| } |
| typedef cmds::GetAttachedShaders::Result Result; |
| uint32_t max_count = Result::ComputeMaxResults(result_size); |
| Result* result = GetSharedMemoryAs<Result*>( |
| 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; |
| api()->glGetAttachedShadersFn(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetActiveUniform& c = |
| *static_cast<const volatile gles2::cmds::GetActiveUniform*>(cmd_data); |
| GLuint program_id = c.program; |
| GLuint index = c.index; |
| uint32_t name_bucket_id = c.name_bucket_id; |
| typedef cmds::GetActiveUniform::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| 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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetActiveUniformBlockiv& c = |
| *static_cast<const volatile gles2::cmds::GetActiveUniformBlockiv*>( |
| cmd_data); |
| GLuint program_id = c.program; |
| GLuint index = static_cast<GLuint>(c.index); |
| GLenum pname = static_cast<GLenum>(c.pname); |
| Program* program = GetProgramInfoNotShader( |
| program_id, "glGetActiveUniformBlockiv"); |
| if (!program) { |
| return error::kNoError; |
| } |
| GLuint service_id = program->service_id(); |
| GLint link_status = GL_FALSE; |
| api()->glGetProgramivFn(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; |
| } |
| if (index >= program->uniform_block_size_info().size()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glGetActiveUniformBlockiv", |
| "uniformBlockIndex >= active uniform blocks"); |
| return error::kNoError; |
| } |
| GLsizei num_values = 1; |
| if (pname == GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES) { |
| GLint num = 0; |
| api()->glGetActiveUniformBlockivFn(service_id, index, |
| GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &num); |
| GLenum error = api()->glGetErrorFn(); |
| 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<GLsizei>(num); |
| } |
| typedef cmds::GetActiveUniformBlockiv::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| c.params_shm_id, c.params_shm_offset, Result::ComputeSize(num_values)); |
| GLint* params = result ? result->GetData() : nullptr; |
| if (params == nullptr) { |
| return error::kOutOfBounds; |
| } |
| // Check that the client initialized the result. |
| if (result->size != 0) { |
| return error::kInvalidArguments; |
| } |
| api()->glGetActiveUniformBlockivFn(service_id, index, pname, params); |
| result->SetNumResults(num_values); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetActiveUniformBlockName( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetActiveUniformBlockName& c = |
| *static_cast<const volatile gles2::cmds::GetActiveUniformBlockName*>( |
| cmd_data); |
| GLuint program_id = c.program; |
| GLuint index = c.index; |
| uint32_t name_bucket_id = c.name_bucket_id; |
| typedef cmds::GetActiveUniformBlockName::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| 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; |
| api()->glGetProgramivFn(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; |
| } |
| if (index >= program->uniform_block_size_info().size()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glGetActiveUniformBlockName", |
| "uniformBlockIndex >= active uniform blocks"); |
| return error::kNoError; |
| } |
| GLint max_length = 0; |
| api()->glGetProgramivFn(service_id, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, |
| &max_length); |
| // Increase one so &buffer[0] is always valid. |
| GLsizei buf_size = static_cast<GLsizei>(max_length) + 1; |
| std::vector<char> buffer(buf_size); |
| GLsizei length = 0; |
| api()->glGetActiveUniformBlockNameFn(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetActiveUniformsiv& c = |
| *static_cast<const volatile gles2::cmds::GetActiveUniformsiv*>(cmd_data); |
| GLuint program_id = c.program; |
| GLenum pname = static_cast<GLenum>(c.pname); |
| Bucket* bucket = GetBucket(c.indices_bucket_id); |
| if (!bucket) { |
| return error::kInvalidArguments; |
| } |
| if (!validators_->uniform_parameter.IsValid(pname)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM("glGetActiveUniformsiv", pname, "pname"); |
| return error::kNoError; |
| } |
| GLsizei count = static_cast<GLsizei>(bucket->size() / sizeof(GLuint)); |
| const GLuint* indices = bucket->GetDataAs<const GLuint*>(0, bucket->size()); |
| typedef cmds::GetActiveUniformsiv::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| c.params_shm_id, c.params_shm_offset, Result::ComputeSize(count)); |
| GLint* params = result ? result->GetData() : nullptr; |
| if (params == nullptr) { |
| 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; |
| } |
| GLint activeUniforms = 0; |
| program->GetProgramiv(GL_ACTIVE_UNIFORMS, &activeUniforms); |
| for (int i = 0; i < count; i++) { |
| if (indices[i] >= static_cast<GLuint>(activeUniforms)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, |
| "glGetActiveUniformsiv", "index >= active uniforms"); |
| return error::kNoError; |
| } |
| } |
| GLuint service_id = program->service_id(); |
| GLint link_status = GL_FALSE; |
| api()->glGetProgramivFn(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; |
| } |
| api()->glGetActiveUniformsivFn(service_id, count, indices, pname, params); |
| result->SetNumResults(count); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetActiveAttrib( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetActiveAttrib& c = |
| *static_cast<const volatile gles2::cmds::GetActiveAttrib*>(cmd_data); |
| GLuint program_id = c.program; |
| GLuint index = c.index; |
| uint32_t name_bucket_id = c.name_bucket_id; |
| typedef cmds::GetActiveAttrib::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| 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_t immediate_data_size, |
| const volatile 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<GLsizei>(c.n); |
| if (n < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glShaderBinary", "n < 0"); |
| return error::kNoError; |
| } |
| GLsizei length = static_cast<GLsizei>(c.length); |
| if (length < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glShaderBinary", "length < 0"); |
| return error::kNoError; |
| } |
| uint32_t data_size; |
| if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) { |
| return error::kOutOfBounds; |
| } |
| const GLuint* shaders = GetSharedMemoryAs<const GLuint*>( |
| c.shaders_shm_id, c.shaders_shm_offset, data_size); |
| GLenum binaryformat = static_cast<GLenum>(c.binaryformat); |
| const void* binary = GetSharedMemoryAs<const void*>( |
| c.binary_shm_id, c.binary_shm_offset, length); |
| if (shaders == nullptr || binary == nullptr) { |
| return error::kOutOfBounds; |
| } |
| std::unique_ptr<GLuint[]> 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(uint64_t swap_id, GLbitfield flags) { |
| 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<int>(gl::GetGLImplementation()), "width", |
| (is_offscreen ? offscreen_size_.width() : surface_->GetSize().width())); |
| TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoSwapBuffers", |
| "offscreen", is_offscreen, |
| "frame", this_frame_number); |
| |
| ScopedGPUTrace scoped_gpu_trace(gpu_tracer_.get(), kTraceDecoder, |
| "GLES2Decoder", "SwapBuffer"); |
| |
| bool is_tracing; |
| TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("gpu.debug"), |
| &is_tracing); |
| if (is_tracing) { |
| ScopedFramebufferBinder binder(this, GetBoundDrawFramebufferServiceId()); |
| gpu_state_tracer_->TakeSnapshotWithCurrentFramebuffer( |
| is_offscreen ? offscreen_size_ : surface_->GetSize()); |
| } |
| |
| ClearScheduleCALayerState(); |
| ClearScheduleDCLayerState(); |
| |
| // 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_single_buffer_) |
| return; |
| |
| 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(); |
| api()->glFinishFn(); |
| } |
| |
| // The size has changed, so none of the cached BackTextures are useful |
| // anymore. |
| ReleaseNotInUseBackTextures(); |
| |
| // 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()); |
| api()->glClearColorFn(0, 0, 0, BackBufferAlphaClearColor()); |
| state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); |
| ClearDeviceWindowRectangles(); |
| api()->glClearFn(GL_COLOR_BUFFER_BIT); |
| RestoreClearState(); |
| } |
| } |
| } |
| |
| 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(); |
| } else { |
| offscreen_saved_color_texture_.swap(offscreen_target_color_texture_); |
| offscreen_target_frame_buffer_->AttachRenderTexture( |
| offscreen_target_color_texture_.get()); |
| offscreen_saved_frame_buffer_->AttachRenderTexture( |
| offscreen_saved_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 underlying device/context for all contexts. |
| if (!gl_version_info().is_angle) |
| api()->glFlushFn(); |
| } |
| } else if (supports_async_swap_) { |
| TRACE_EVENT_ASYNC_BEGIN0("gpu", "AsyncSwapBuffers", swap_id); |
| |
| client_->OnSwapBuffers(swap_id, flags); |
| surface_->SwapBuffersAsync( |
| base::Bind(&GLES2DecoderImpl::FinishAsyncSwapBuffers, |
| weak_ptr_factory_.GetWeakPtr(), swap_id), |
| base::DoNothing()); |
| } else { |
| client_->OnSwapBuffers(swap_id, flags); |
| FinishSwapBuffers(surface_->SwapBuffers(base::DoNothing())); |
| } |
| |
| // This may be a slow command. Exit command processing to allow for |
| // context preemption and GPU watchdog checks. |
| ExitCommandProcessingEarly(); |
| } |
| |
| void GLES2DecoderImpl::FinishAsyncSwapBuffers( |
| uint64_t swap_id, |
| gfx::SwapResult result, |
| std::unique_ptr<gfx::GpuFence> gpu_fence) { |
| TRACE_EVENT_ASYNC_END0("gpu", "AsyncSwapBuffers", swap_id); |
| // Handling of the out-fence should have already happened before reaching |
| // this function, so we don't expect to get a valid fence here. |
| DCHECK(!gpu_fence); |
| |
| FinishSwapBuffers(result); |
| } |
| |
| void GLES2DecoderImpl::FinishSwapBuffers(gfx::SwapResult result) { |
| if (result == gfx::SwapResult::SWAP_FAILED) { |
| LOG(ERROR) << "Context lost because SwapBuffers failed."; |
| if (!CheckResetStatus()) { |
| MarkContextLost(error::kUnknown); |
| group_->LoseContexts(error::kUnknown); |
| } |
| } |
| ++swaps_since_resize_; |
| if (swaps_since_resize_ == 1 && surface_->BuffersFlipped()) { |
| // The second buffer after a resize is new and needs to be cleared to |
| // known values. |
| backbuffer_needs_clear_bits_ |= GL_COLOR_BUFFER_BIT; |
| } |
| } |
| |
| void GLES2DecoderImpl::DoCommitOverlayPlanes(uint64_t swap_id, |
| GLbitfield flags) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoCommitOverlayPlanes"); |
| if (!supports_commit_overlay_planes_) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCommitOverlayPlanes", |
| "command not supported by surface"); |
| return; |
| } |
| ClearScheduleCALayerState(); |
| ClearScheduleDCLayerState(); |
| if (supports_async_swap_) { |
| client_->OnSwapBuffers(swap_id, flags); |
| surface_->CommitOverlayPlanesAsync( |
| base::Bind(&GLES2DecoderImpl::FinishAsyncSwapBuffers, |
| weak_ptr_factory_.GetWeakPtr(), swap_id), |
| base::DoNothing()); |
| } else { |
| client_->OnSwapBuffers(swap_id, flags); |
| FinishSwapBuffers(surface_->CommitOverlayPlanes(base::DoNothing())); |
| } |
| } |
| |
| error::Error GLES2DecoderImpl::HandleEnableFeatureCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::EnableFeatureCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::EnableFeatureCHROMIUM*>( |
| cmd_data); |
| Bucket* bucket = GetBucket(c.bucket_id); |
| if (!bucket || bucket->size() == 0) { |
| return error::kInvalidArguments; |
| } |
| typedef cmds::EnableFeatureCHROMIUM::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| 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*>(validators_)->vertex_attrib_type.AddValue(GL_FIXED); |
| } else { |
| return error::kNoError; |
| } |
| |
| *result = 1; // true. |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetRequestableExtensionsCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetRequestableExtensionsCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::GetRequestableExtensionsCHROMIUM*>( |
| cmd_data); |
| Bucket* bucket = CreateBucket(c.bucket_id); |
| scoped_refptr<FeatureInfo> info( |
| new FeatureInfo(workarounds(), group_->gpu_feature_info())); |
| DisallowedFeatures disallowed_features = feature_info_->disallowed_features(); |
| disallowed_features.AllowExtensions(); |
| info->Initialize(feature_info_->context_type(), |
| false /* is_passthrough_cmd_decoder */, disallowed_features); |
| bucket->SetFromString(gfx::MakeExtensionString(info->extensions()).c_str()); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleRequestExtensionCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::RequestExtensionCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::RequestExtensionCHROMIUM*>( |
| 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; |
| } |
| feature_str = feature_str + " "; |
| |
| bool desire_standard_derivatives = false; |
| bool desire_frag_depth = false; |
| bool desire_draw_buffers = false; |
| bool desire_shader_texture_lod = false; |
| if (feature_info_->context_type() == CONTEXT_TYPE_WEBGL1) { |
| 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_standard_derivatives != derivatives_explicitly_enabled_ || |
| desire_frag_depth != frag_depth_explicitly_enabled_ || |
| desire_draw_buffers != draw_buffers_explicitly_enabled_ || |
| desire_shader_texture_lod != shader_texture_lod_explicitly_enabled_) { |
| 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; |
| DestroyShaderTranslator(); |
| } |
| |
| if (feature_str.find("GL_CHROMIUM_color_buffer_float_rgba ") != |
| std::string::npos) { |
| feature_info_->EnableCHROMIUMColorBufferFloatRGBA(); |
| } |
| if (feature_str.find("GL_CHROMIUM_color_buffer_float_rgb ") != |
| std::string::npos) { |
| feature_info_->EnableCHROMIUMColorBufferFloatRGB(); |
| } |
| if (feature_str.find("GL_EXT_color_buffer_float ") != std::string::npos) { |
| feature_info_->EnableEXTColorBufferFloat(); |
| } |
| if (feature_str.find("GL_EXT_color_buffer_half_float ") != |
| std::string::npos) { |
| feature_info_->EnableEXTColorBufferHalfFloat(); |
| } |
| if (feature_str.find("GL_OES_texture_float_linear ") != std::string::npos) { |
| feature_info_->EnableOESTextureFloatLinear(); |
| } |
| if (feature_str.find("GL_OES_texture_half_float_linear ") != |
| std::string::npos) { |
| feature_info_->EnableOESTextureHalfFloatLinear(); |
| } |
| |
| UpdateCapabilities(); |
| |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetProgramInfoCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GetProgramInfoCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::GetProgramInfoCHROMIUM*>( |
| cmd_data); |
| GLuint program_id = static_cast<GLuint>(c.program); |
| uint32_t bucket_id = c.bucket_id; |
| Bucket* bucket = CreateBucket(bucket_id); |
| bucket->SetSize(sizeof(ProgramInfoHeader)); // in case we fail. |
| Program* program = nullptr; |
| program = GetProgram(program_id); |
| if (!program || !program->IsValid()) { |
| return error::kNoError; |
| } |
| program->GetProgramInfo(program_manager(), bucket); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetUniformBlocksCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetUniformBlocksCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::GetUniformBlocksCHROMIUM*>( |
| cmd_data); |
| GLuint program_id = static_cast<GLuint>(c.program); |
| uint32_t bucket_id = c.bucket_id; |
| Bucket* bucket = CreateBucket(bucket_id); |
| bucket->SetSize(sizeof(UniformBlocksHeader)); // in case we fail. |
| Program* program = nullptr; |
| program = GetProgram(program_id); |
| if (!program || !program->IsValid()) { |
| return error::kNoError; |
| } |
| program->GetUniformBlocks(bucket); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetUniformsES3CHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetUniformsES3CHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::GetUniformsES3CHROMIUM*>( |
| cmd_data); |
| GLuint program_id = static_cast<GLuint>(c.program); |
| uint32_t bucket_id = c.bucket_id; |
| Bucket* bucket = CreateBucket(bucket_id); |
| bucket->SetSize(sizeof(UniformsES3Header)); // in case we fail. |
| Program* program = nullptr; |
| program = GetProgram(program_id); |
| if (!program || !program->IsValid()) { |
| return error::kNoError; |
| } |
| program->GetUniformsES3(bucket); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetTransformFeedbackVarying( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetTransformFeedbackVarying& c = |
| *static_cast<const volatile gles2::cmds::GetTransformFeedbackVarying*>( |
| cmd_data); |
| GLuint program_id = c.program; |
| GLuint index = c.index; |
| uint32_t name_bucket_id = c.name_bucket_id; |
| typedef cmds::GetTransformFeedbackVarying::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| 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) { |
| // An error is already set. |
| return error::kNoError; |
| } |
| GLuint service_id = program->service_id(); |
| GLint link_status = GL_FALSE; |
| api()->glGetProgramivFn(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 num_varyings = 0; |
| api()->glGetProgramivFn(service_id, GL_TRANSFORM_FEEDBACK_VARYINGS, |
| &num_varyings); |
| if (index >= static_cast<GLuint>(num_varyings)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, |
| "glGetTransformFeedbackVarying", "index out of bounds"); |
| return error::kNoError; |
| } |
| GLint max_length = 0; |
| api()->glGetProgramivFn(service_id, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, |
| &max_length); |
| max_length = std::max(1, max_length); |
| std::vector<char> buffer(max_length); |
| GLsizei length = 0; |
| GLsizei size = 0; |
| GLenum type = 0; |
| api()->glGetTransformFeedbackVaryingFn(service_id, index, max_length, &length, |
| &size, &type, &buffer[0]); |
| result->success = 1; // true. |
| result->size = static_cast<int32_t>(size); |
| result->type = static_cast<uint32_t>(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_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetTransformFeedbackVaryingsCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::GetTransformFeedbackVaryingsCHROMIUM*>( |
| cmd_data); |
| GLuint program_id = static_cast<GLuint>(c.program); |
| uint32_t bucket_id = c.bucket_id; |
| Bucket* bucket = CreateBucket(bucket_id); |
| bucket->SetSize(sizeof(TransformFeedbackVaryingsHeader)); // in case we fail. |
| Program* program = nullptr; |
| program = GetProgram(program_id); |
| if (!program || !program->IsValid()) { |
| return error::kNoError; |
| } |
| program->GetTransformFeedbackVaryings(bucket); |
| return error::kNoError; |
| } |
| |
| 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. |
| command_buffer_service()->SetContextLostReason(reason); |
| current_decoder_error_ = error::kLostContext; |
| context_was_lost_ = true; |
| |
| if (transform_feedback_manager_.get()) { |
| transform_feedback_manager_->MarkContextLost(); |
| } |
| if (vertex_array_manager_.get()) { |
| vertex_array_manager_->MarkContextLost(); |
| } |
| state_.MarkContextLost(); |
| } |
| |
| bool GLES2DecoderImpl::CheckResetStatus() { |
| DCHECK(!WasContextLost()); |
| DCHECK(context_->IsCurrent(nullptr)); |
| |
| 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 = api()->glGetGraphicsResetStatusARBFn(); |
| 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::HandleDescheduleUntilFinishedCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!gl::GLFence::IsSupported()) |
| return error::kNoError; |
| std::unique_ptr<gl::GLFence> fence = gl::GLFence::Create(); |
| deschedule_until_finished_fences_.push_back(std::move(fence)); |
| |
| if (deschedule_until_finished_fences_.size() == 1) |
| return error::kNoError; |
| |
| DCHECK_EQ(2u, deschedule_until_finished_fences_.size()); |
| if (deschedule_until_finished_fences_[0]->HasCompleted()) { |
| deschedule_until_finished_fences_.erase( |
| deschedule_until_finished_fences_.begin()); |
| return error::kNoError; |
| } |
| |
| TRACE_EVENT_ASYNC_BEGIN0("cc", "GLES2DecoderImpl::DescheduleUntilFinished", |
| this); |
| client_->OnDescheduleUntilFinished(); |
| return error::kDeferLaterCommands; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleInsertFenceSyncCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::InsertFenceSyncCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::InsertFenceSyncCHROMIUM*>( |
| cmd_data); |
| |
| const uint64_t release_count = c.release_count(); |
| client_->OnFenceSyncRelease(release_count); |
| // Exit inner command processing loop so that we check the scheduling state |
| // and yield if necessary as we may have unblocked a higher priority context. |
| ExitCommandProcessingEarly(); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleWaitSyncTokenCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::WaitSyncTokenCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::WaitSyncTokenCHROMIUM*>( |
| cmd_data); |
| |
| const gpu::CommandBufferNamespace kMinNamespaceId = |
| gpu::CommandBufferNamespace::INVALID; |
| const gpu::CommandBufferNamespace kMaxNamespaceId = |
| gpu::CommandBufferNamespace::NUM_COMMAND_BUFFER_NAMESPACES; |
| |
| gpu::CommandBufferNamespace namespace_id = |
| static_cast<gpu::CommandBufferNamespace>(c.namespace_id); |
| if ((namespace_id < static_cast<int32_t>(kMinNamespaceId)) || |
| (namespace_id >= static_cast<int32_t>(kMaxNamespaceId))) { |
| namespace_id = gpu::CommandBufferNamespace::INVALID; |
| } |
| const CommandBufferId command_buffer_id = |
| CommandBufferId::FromUnsafeValue(c.command_buffer_id()); |
| const uint64_t release = c.release_count(); |
| |
| gpu::SyncToken sync_token; |
| sync_token.Set(namespace_id, command_buffer_id, release); |
| return client_->OnWaitSyncToken(sync_token) ? error::kDeferCommandUntilLater |
| : error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleDiscardBackbufferCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile 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_->IsValidQuery(client_ids[ii])) { |
| return false; |
| } |
| } |
| query_manager_->GenQueries(n, client_ids); |
| return true; |
| } |
| |
| void GLES2DecoderImpl::DeleteQueriesEXTHelper( |
| GLsizei n, |
| const volatile GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| GLuint client_id = client_ids[ii]; |
| query_manager_->RemoveQuery(client_id); |
| } |
| } |
| |
| void GLES2DecoderImpl::SetQueryCallback(unsigned int query_client_id, |
| base::OnceClosure callback) { |
| QueryManager::Query* query = query_manager_->GetQuery(query_client_id); |
| if (query) { |
| query->AddCallback(std::move(callback)); |
| } else { |
| VLOG(1) << "GLES2DecoderImpl::SetQueryCallback: No query with ID " |
| << query_client_id << ". Running the callback immediately."; |
| std::move(callback).Run(); |
| } |
| } |
| |
| bool GLES2DecoderImpl::HasPendingQueries() const { |
| return query_manager_.get() && query_manager_->HavePendingQueries(); |
| } |
| |
| void GLES2DecoderImpl::ProcessPendingQueries(bool did_finish) { |
| if (!query_manager_.get()) |
| return; |
| query_manager_->ProcessPendingQueries(did_finish); |
| } |
| |
| // Note that if there are no pending readpixels right now, |
| // this function will call the callback immediately. |
| void GLES2DecoderImpl::WaitForReadPixels(base::OnceClosure callback) { |
| if (features().use_async_readpixels && !pending_readpixel_fences_.empty()) { |
| pending_readpixel_fences_.back().callbacks.push_back(std::move(callback)); |
| } else { |
| std::move(callback).Run(); |
| } |
| } |
| |
| void GLES2DecoderImpl::ProcessPendingReadPixels(bool did_finish) { |
| // Note: |did_finish| guarantees that the GPU has passed the fence but |
| // we cannot assume that GLFence::HasCompleted() will return true yet as |
| // that's not guaranteed by all GLFence implementations. |
| while (!pending_readpixel_fences_.empty() && |
| (did_finish || |
| pending_readpixel_fences_.front().fence->HasCompleted())) { |
| std::vector<base::OnceClosure> callbacks = |
| std::move(pending_readpixel_fences_.front().callbacks); |
| pending_readpixel_fences_.pop(); |
| for (size_t i = 0; i < callbacks.size(); i++) { |
| std::move(callbacks[i]).Run(); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::ProcessDescheduleUntilFinished() { |
| if (deschedule_until_finished_fences_.size() < 2) |
| return; |
| DCHECK_EQ(2u, deschedule_until_finished_fences_.size()); |
| |
| if (!deschedule_until_finished_fences_[0]->HasCompleted()) |
| return; |
| |
| TRACE_EVENT_ASYNC_END0("cc", "GLES2DecoderImpl::DescheduleUntilFinished", |
| this); |
| deschedule_until_finished_fences_.erase( |
| deschedule_until_finished_fences_.begin()); |
| client_->OnRescheduleAfterFinished(); |
| } |
| |
| bool GLES2DecoderImpl::HasMoreIdleWork() const { |
| return !pending_readpixel_fences_.empty() || |
| gpu_tracer_->HasTracesToProcess() || |
| !texture_refs_pending_destruction_.empty(); |
| } |
| |
| void GLES2DecoderImpl::PerformIdleWork() { |
| gpu_tracer_->ProcessTraces(); |
| ProcessPendingReadPixels(false); |
| } |
| |
| bool GLES2DecoderImpl::HasPollingWork() const { |
| return deschedule_until_finished_fences_.size() >= 2; |
| } |
| |
| void GLES2DecoderImpl::PerformPollingWork() { |
| ProcessDescheduleUntilFinished(); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleBeginQueryEXT( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::BeginQueryEXT& c = |
| *static_cast<const volatile gles2::cmds::BeginQueryEXT*>(cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLuint client_id = static_cast<GLuint>(c.id); |
| int32_t sync_shm_id = static_cast<int32_t>(c.sync_data_shm_id); |
| uint32_t sync_shm_offset = static_cast<uint32_t>(c.sync_data_shm_offset); |
| |
| switch (target) { |
| case GL_COMMANDS_ISSUED_CHROMIUM: |
| case GL_LATENCY_QUERY_CHROMIUM: |
| case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: |
| case GL_GET_ERROR_QUERY_CHROMIUM: |
| break; |
| case GL_READBACK_SHADOW_COPIES_UPDATED_CHROMIUM: |
| 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; |
| case GL_SAMPLES_PASSED_ARB: |
| if (!features().occlusion_query) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glBeginQueryEXT", |
| "not enabled for occlusion queries"); |
| return error::kNoError; |
| } |
| break; |
| case GL_ANY_SAMPLES_PASSED: |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
| if (!features().occlusion_query_boolean) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glBeginQueryEXT", |
| "not enabled for boolean occlusion queries"); |
| return error::kNoError; |
| } |
| break; |
| case GL_TIME_ELAPSED: |
| if (!query_manager_->GPUTimingAvailable()) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glBeginQueryEXT", |
| "not enabled for timing queries"); |
| return error::kNoError; |
| } |
| break; |
| case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
| if (feature_info_->IsWebGL2OrES3Context()) { |
| break; |
| } |
| FALLTHROUGH; |
| default: |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_ENUM, "glBeginQueryEXT", |
| "unknown query target"); |
| return error::kNoError; |
| } |
| |
| if (query_manager_->GetActiveQuery(target)) { |
| 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; |
| } |
| |
| scoped_refptr<gpu::Buffer> buffer = GetSharedMemoryBuffer(sync_shm_id); |
| if (!buffer) |
| return error::kInvalidArguments; |
| QuerySync* sync = static_cast<QuerySync*>( |
| buffer->GetDataAddress(sync_shm_offset, sizeof(QuerySync))); |
| if (!sync) |
| return error::kOutOfBounds; |
| |
| 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, std::move(buffer), sync); |
| } else { |
| if (query->target() != target) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBeginQueryEXT", |
| "target does not match"); |
| return error::kNoError; |
| } else if (query->sync() != sync) { |
| DLOG(ERROR) << "Shared memory used by query not the same as before"; |
| return error::kInvalidArguments; |
| } |
| } |
| |
| query_manager_->BeginQuery(query); |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderImpl::ReadBackBuffersIntoShadowCopies( |
| base::flat_set<scoped_refptr<Buffer>> buffers_to_shadow_copy) { |
| GLuint old_binding = |
| state_.bound_array_buffer ? state_.bound_array_buffer->service_id() : 0; |
| |
| for (scoped_refptr<Buffer>& buffer : buffers_to_shadow_copy) { |
| if (buffer->IsDeleted()) { |
| continue; |
| } |
| void* shadow = nullptr; |
| scoped_refptr<gpu::Buffer> gpu_buffer = |
| buffer->TakeReadbackShadowAllocation(&shadow); |
| if (!shadow) { |
| continue; |
| } |
| |
| if (buffer->GetMappedRange()) { |
| // The buffer is already mapped by the client. It's okay that the shadow |
| // copy will be out-of-date, because the client will never read it: |
| // * Client issues READBACK_SHADOW_COPIES_UPDATED_CHROMIUM query |
| // * Client maps buffer |
| // * Client receives signal that the query completed |
| // * Client unmaps buffer - invalidating the shadow copy |
| // * Client maps buffer to read back - hits the round-trip path |
| continue; |
| } |
| |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, buffer->service_id()); |
| void* mapped = api()->glMapBufferRangeFn(GL_ARRAY_BUFFER, 0, buffer->size(), |
| GL_MAP_READ_BIT); |
| if (!mapped) { |
| DLOG(ERROR) << "glMapBufferRange unexpectedly returned nullptr"; |
| MarkContextLost(error::kOutOfMemory); |
| group_->LoseContexts(error::kUnknown); |
| return; |
| } |
| memcpy(shadow, mapped, buffer->size()); |
| bool unmap_ok = api()->glUnmapBufferFn(GL_ARRAY_BUFFER); |
| if (unmap_ok == GL_FALSE) { |
| DLOG(ERROR) << "glUnmapBuffer unexpectedly returned GL_FALSE"; |
| MarkContextLost(error::kUnknown); |
| group_->LoseContexts(error::kUnknown); |
| return; |
| } |
| } |
| |
| // Restore original GL_ARRAY_BUFFER binding |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, old_binding); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleEndQueryEXT( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::EndQueryEXT& c = |
| *static_cast<const volatile gles2::cmds::EndQueryEXT*>(cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| uint32_t submit_count = static_cast<GLuint>(c.submit_count); |
| |
| QueryManager::Query* query = query_manager_->GetActiveQuery(target); |
| if (!query) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glEndQueryEXT", "No active query"); |
| return error::kNoError; |
| } |
| |
| if (target == GL_READBACK_SHADOW_COPIES_UPDATED_CHROMIUM && |
| !writes_submitted_but_not_completed_.empty()) { |
| // - This callback will be called immediately by MarkAsCompleted, so it's |
| // okay that it's called after UnmarkAsPending. (It's guaranteed to not be |
| // called immediately here, because the query has not even ended yet.) |
| // |
| // - It's okay to capture Unretained(this) because the callback is called by |
| // the query, which is owned by query_manager_, which is owned by `this`. |
| query->AddCallback( |
| base::BindOnce(&GLES2DecoderImpl::ReadBackBuffersIntoShadowCopies, |
| base::Unretained(this), |
| std::move(writes_submitted_but_not_completed_))); |
| writes_submitted_but_not_completed_.clear(); |
| } |
| |
| query_manager_->EndQuery(query, submit_count); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleQueryCounterEXT( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::QueryCounterEXT& c = |
| *static_cast<const volatile gles2::cmds::QueryCounterEXT*>(cmd_data); |
| GLuint client_id = static_cast<GLuint>(c.id); |
| GLenum target = static_cast<GLenum>(c.target); |
| int32_t sync_shm_id = static_cast<int32_t>(c.sync_data_shm_id); |
| uint32_t sync_shm_offset = static_cast<uint32_t>(c.sync_data_shm_offset); |
| uint32_t submit_count = static_cast<GLuint>(c.submit_count); |
| |
| switch (target) { |
| case GL_TIMESTAMP: |
| if (!query_manager_->GPUTimingAvailable()) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, "glQueryCounterEXT", |
| "not enabled for timing queries"); |
| return error::kNoError; |
| } |
| break; |
| default: |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_ENUM, "glQueryCounterEXT", |
| "unknown query target"); |
| return error::kNoError; |
| } |
| |
| scoped_refptr<gpu::Buffer> buffer = GetSharedMemoryBuffer(sync_shm_id); |
| if (!buffer) |
| return error::kInvalidArguments; |
| QuerySync* sync = static_cast<QuerySync*>( |
| buffer->GetDataAddress(sync_shm_offset, sizeof(QuerySync))); |
| if (!sync) |
| return error::kOutOfBounds; |
| |
| QueryManager::Query* query = query_manager_->GetQuery(client_id); |
| if (!query) { |
| if (!query_manager_->IsValidQuery(client_id)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "glQueryCounterEXT", |
| "id not made by glGenQueriesEXT"); |
| return error::kNoError; |
| } |
| query = |
| query_manager_->CreateQuery(target, client_id, std::move(buffer), sync); |
| } else { |
| if (query->target() != target) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glQueryCounterEXT", |
| "target does not match"); |
| return error::kNoError; |
| } else if (query->sync() != sync) { |
| DLOG(ERROR) << "Shared memory used by query not the same as before"; |
| return error::kInvalidArguments; |
| } |
| } |
| query_manager_->QueryCounter(query, submit_count); |
| |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleSetDisjointValueSyncCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::SetDisjointValueSyncCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::SetDisjointValueSyncCHROMIUM*>( |
| cmd_data); |
| int32_t sync_shm_id = static_cast<int32_t>(c.sync_data_shm_id); |
| uint32_t sync_shm_offset = static_cast<uint32_t>(c.sync_data_shm_offset); |
| |
| return query_manager_->SetDisjointSync(sync_shm_id, sync_shm_offset); |
| } |
| |
| 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 { |
| std::unique_ptr<GLuint[]> service_ids(new GLuint[n]); |
| |
| api()->glGenVertexArraysOESFn(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 volatile GLuint* client_ids) { |
| for (GLsizei ii = 0; ii < n; ++ii) { |
| GLuint client_id = client_ids[ii]; |
| VertexAttribManager* vao = GetVertexAttribManager(client_id); |
| if (vao && !vao->IsDeleted()) { |
| if (state_.vertex_attrib_manager.get() == vao) { |
| DoBindVertexArrayOES(0); |
| } |
| RemoveVertexAttribManager(client_id); |
| } |
| } |
| } |
| |
| void GLES2DecoderImpl::DoBindVertexArrayOES(GLuint client_id) { |
| VertexAttribManager* vao = nullptr; |
| 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) { |
| if (state_.vertex_attrib_manager) |
| state_.vertex_attrib_manager->SetIsBound(false); |
| state_.vertex_attrib_manager = vao; |
| if (vao) |
| vao->SetIsBound(true); |
| if (!features().native_vertex_array_object) { |
| EmulateVertexArrayState(); |
| } else { |
| GLuint service_id = vao->service_id(); |
| api()->glBindVertexArrayOESFn(service_id); |
| } |
| } |
| } |
| |
| // Used when OES_vertex_array_object isn't natively supported |
| void GLES2DecoderImpl::EmulateVertexArrayState() { |
| // Setup the Vertex attribute state |
| for (uint32_t 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(); |
| api()->glBindBufferFn( |
| 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(); |
| } |
| |
| bool GLES2DecoderImpl::DoIsPathCHROMIUM(GLuint client_id) { |
| GLuint service_id = 0; |
| return path_manager()->GetPath(client_id, &service_id) && |
| api()->glIsPathNVFn(service_id) == GL_TRUE; |
| } |
| |
| bool GLES2DecoderImpl::DoIsSync(GLuint client_id) { |
| GLsync service_sync = 0; |
| return group_->GetSyncServiceId(client_id, &service_sync); |
| } |
| |
| bool GLES2DecoderImpl::ValidateCopyTextureCHROMIUMTextures( |
| const char* function_name, |
| GLenum dest_target, |
| TextureRef* source_texture_ref, |
| TextureRef* dest_texture_ref) { |
| if (!source_texture_ref || !dest_texture_ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "unknown texture id"); |
| 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() != |
| GLES2Util::GLFaceTargetToTextureTarget(dest_target)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "target should be aligned with dest target"); |
| return false; |
| } |
| switch (dest_texture->target()) { |
| case GL_TEXTURE_2D: |
| case GL_TEXTURE_CUBE_MAP: |
| case GL_TEXTURE_RECTANGLE_ARB: |
| break; |
| default: |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "invalid dest texture target binding"); |
| return false; |
| } |
| |
| switch (source_texture->target()) { |
| case GL_TEXTURE_2D: |
| case GL_TEXTURE_RECTANGLE_ARB: |
| case GL_TEXTURE_EXTERNAL_OES: |
| break; |
| default: |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "invalid source texture target binding"); |
| return false; |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::CanUseCopyTextureCHROMIUMInternalFormat( |
| GLenum dest_internal_format) { |
| switch (dest_internal_format) { |
| case GL_RGB: |
| case GL_RGBA: |
| case GL_RGB8: |
| case GL_RGBA8: |
| case GL_BGRA_EXT: |
| case GL_BGRA8_EXT: |
| case GL_SRGB_EXT: |
| case GL_SRGB_ALPHA_EXT: |
| case GL_R8: |
| case GL_R8UI: |
| case GL_RG8: |
| case GL_RG8UI: |
| case GL_SRGB8: |
| case GL_RGB565: |
| case GL_RGB8UI: |
| case GL_SRGB8_ALPHA8: |
| case GL_RGB5_A1: |
| case GL_RGBA4: |
| case GL_RGBA8UI: |
| case GL_RGB9_E5: |
| case GL_R16F: |
| case GL_R32F: |
| case GL_RG16F: |
| case GL_RG32F: |
| case GL_RGB16F: |
| case GL_RGB32F: |
| case GL_RGBA16F: |
| case GL_RGBA32F: |
| case GL_R11F_G11F_B10F: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void GLES2DecoderImpl::DoCopyTextureCHROMIUM( |
| GLuint source_id, |
| GLint source_level, |
| GLenum dest_target, |
| GLuint dest_id, |
| GLint dest_level, |
| GLenum internal_format, |
| GLenum dest_type, |
| GLboolean unpack_flip_y, |
| GLboolean unpack_premultiply_alpha, |
| GLboolean unpack_unmultiply_alpha) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoCopyTextureCHROMIUM"); |
| static const char kFunctionName[] = "glCopyTextureCHROMIUM"; |
| |
| TextureRef* source_texture_ref = GetTexture(source_id); |
| TextureRef* dest_texture_ref = GetTexture(dest_id); |
| |
| if (!ValidateCopyTextureCHROMIUMTextures( |
| kFunctionName, dest_target, source_texture_ref, dest_texture_ref)) { |
| return; |
| } |
| |
| if (source_level < 0 || dest_level < 0 || |
| (feature_info_->IsWebGL1OrES2Context() && source_level > 0)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, |
| "source_level or dest_level out of range"); |
| return; |
| } |
| |
| Texture* source_texture = source_texture_ref->texture(); |
| Texture* dest_texture = dest_texture_ref->texture(); |
| GLenum source_target = source_texture->target(); |
| GLenum dest_binding_target = dest_texture->target(); |
| |
| GLenum source_type = 0; |
| GLenum source_internal_format = 0; |
| source_texture->GetLevelType(source_target, source_level, &source_type, |
| &source_internal_format); |
| GLenum format = |
| TextureManager::ExtractFormatFromStorageFormat(internal_format); |
| if (!texture_manager()->ValidateTextureParameters( |
| GetErrorState(), kFunctionName, true, format, dest_type, |
| internal_format, dest_level)) { |
| return; |
| } |
| |
| std::string output_error_msg; |
| if (!ValidateCopyTextureCHROMIUMInternalFormats( |
| GetFeatureInfo(), source_internal_format, internal_format, |
| &output_error_msg)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| output_error_msg.c_str()); |
| return; |
| } |
| |
| if (feature_info_->feature_flags().desktop_srgb_support) { |
| bool enable_framebuffer_srgb = |
| GLES2Util::GetColorEncodingFromInternalFormat(source_internal_format) == |
| GL_SRGB || |
| GLES2Util::GetColorEncodingFromInternalFormat(internal_format) == |
| GL_SRGB; |
| state_.EnableDisableFramebufferSRGB(enable_framebuffer_srgb); |
| } |
| |
| int source_width = 0; |
| int source_height = 0; |
| gl::GLImage* image = |
| source_texture->GetLevelImage(source_target, source_level); |
| 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, kFunctionName, "invalid image size"); |
| return; |
| } |
| } else { |
| if (!source_texture->GetLevelSize(source_target, source_level, |
| &source_width, &source_height, nullptr)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, |
| "source texture has no data for level"); |
| return; |
| } |
| |
| // Check that this type of texture is allowed. |
| if (!texture_manager()->ValidForTarget(source_target, source_level, |
| source_width, source_height, 1)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "Bad dimensions"); |
| return; |
| } |
| } |
| |
| if (dest_texture->IsImmutable()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "texture is immutable"); |
| return; |
| } |
| |
| // Clear the source texture if necessary. |
| if (!texture_manager()->ClearTextureLevel(this, source_texture_ref, |
| source_target, source_level)) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, kFunctionName, "dimensions too big"); |
| return; |
| } |
| |
| if (!InitializeCopyTextureCHROMIUM(kFunctionName)) |
| 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( |
| dest_target, dest_level, &dest_width, &dest_height, nullptr); |
| |
| if (dest_level_defined) { |
| dest_texture->GetLevelType(dest_target, dest_level, &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(kFunctionName); |
| api()->glBindTextureFn(dest_binding_target, dest_texture->service_id()); |
| ScopedPixelUnpackState reset_restore(&state_); |
| api()->glTexImage2DFn( |
| dest_target, dest_level, |
| TextureManager::AdjustTexInternalFormat(feature_info_.get(), |
| internal_format), |
| source_width, source_height, 0, |
| TextureManager::AdjustTexFormat(feature_info_.get(), format), dest_type, |
| nullptr); |
| GLenum error = LOCAL_PEEK_GL_ERROR(kFunctionName); |
| if (error != GL_NO_ERROR) { |
| RestoreCurrentTextureBindings(&state_, dest_binding_target, |
| state_.active_texture_unit); |
| return; |
| } |
| |
| texture_manager()->SetLevelInfo(dest_texture_ref, dest_target, dest_level, |
| internal_format, source_width, |
| source_height, 1, 0, format, dest_type, |
| gfx::Rect(source_width, source_height)); |
| dest_texture->ApplyFormatWorkarounds(feature_info_.get()); |
| } else { |
| texture_manager()->SetLevelCleared(dest_texture_ref, dest_target, |
| dest_level, true); |
| } |
| |
| // Try using GLImage::CopyTexImage when possible. |
| bool unpack_premultiply_alpha_change = |
| (unpack_premultiply_alpha ^ unpack_unmultiply_alpha) != 0; |
| // TODO(qiankun.miao@intel.com): Support level > 0 for CopyTexImage. |
| if (image && internal_format == source_internal_format && dest_level == 0 && |
| !unpack_flip_y && !unpack_premultiply_alpha_change) { |
| api()->glBindTextureFn(dest_binding_target, dest_texture->service_id()); |
| if (image->CopyTexImage(dest_target)) |
| return; |
| } |
| |
| DoBindOrCopyTexImageIfNeeded(source_texture, source_target, 0); |
| |
| CopyTextureMethod method = GetCopyTextureCHROMIUMMethod( |
| GetFeatureInfo(), source_target, source_level, source_internal_format, |
| source_type, dest_binding_target, dest_level, internal_format, |
| unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE, |
| unpack_unmultiply_alpha == GL_TRUE, false /* dither */); |
| |
| // GL_TEXTURE_EXTERNAL_OES texture requires that we apply a transform matrix |
| // before presenting. |
| if (source_target == GL_TEXTURE_EXTERNAL_OES) { |
| if (GLStreamTextureImage* image = |
| source_texture->GetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, |
| source_level)) { |
| GLfloat transform_matrix[16]; |
| image->GetTextureMatrix(transform_matrix); |
| copy_texture_chromium_->DoCopyTextureWithTransform( |
| this, source_target, source_texture->service_id(), source_level, |
| source_internal_format, dest_target, dest_texture->service_id(), |
| dest_level, internal_format, source_width, source_height, |
| unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE, |
| unpack_unmultiply_alpha == GL_TRUE, false /* dither */, |
| transform_matrix, method, copy_tex_image_blit_.get()); |
| return; |
| } |
| } |
| copy_texture_chromium_->DoCopyTexture( |
| this, source_target, source_texture->service_id(), source_level, |
| source_internal_format, dest_target, dest_texture->service_id(), |
| dest_level, internal_format, source_width, source_height, |
| unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE, |
| unpack_unmultiply_alpha == GL_TRUE, false /* dither */, method, |
| copy_tex_image_blit_.get()); |
| } |
| |
| void GLES2DecoderImpl::CopySubTextureHelper(const char* function_name, |
| GLuint source_id, |
| GLint source_level, |
| GLenum dest_target, |
| GLuint dest_id, |
| GLint dest_level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| GLboolean unpack_flip_y, |
| GLboolean unpack_premultiply_alpha, |
| GLboolean unpack_unmultiply_alpha, |
| GLboolean dither) { |
| TextureRef* source_texture_ref = GetTexture(source_id); |
| TextureRef* dest_texture_ref = GetTexture(dest_id); |
| |
| if (!ValidateCopyTextureCHROMIUMTextures( |
| function_name, dest_target, source_texture_ref, dest_texture_ref)) { |
| return; |
| } |
| |
| if (source_level < 0 || dest_level < 0 || |
| (feature_info_->IsWebGL1OrES2Context() && source_level > 0)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "source_level or dest_level out of range"); |
| return; |
| } |
| |
| Texture* source_texture = source_texture_ref->texture(); |
| Texture* dest_texture = dest_texture_ref->texture(); |
| GLenum source_target = source_texture->target(); |
| GLenum dest_binding_target = dest_texture->target(); |
| int source_width = 0; |
| int source_height = 0; |
| gl::GLImage* image = |
| source_texture->GetLevelImage(source_target, source_level); |
| 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, function_name, "invalid image size"); |
| return; |
| } |
| |
| // Ideally we should not need to check that the sub-texture copy rectangle |
| // is valid in two different ways, here and below. However currently there |
| // is no guarantee that a texture backed by a GLImage will have sensible |
| // level info. If this synchronization were to be enforced then this and |
| // other functions in this file could be cleaned up. |
| // See: https://crbug.com/586476 |
| int32_t max_x; |
| int32_t max_y; |
| if (!SafeAddInt32(x, width, &max_x) || !SafeAddInt32(y, height, &max_y) || |
| x < 0 || y < 0 || max_x > source_width || max_y > source_height) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "source texture bad dimensions"); |
| return; |
| } |
| } else { |
| if (!source_texture->GetLevelSize(source_target, source_level, |
| &source_width, &source_height, nullptr)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "source texture has no data for level"); |
| return; |
| } |
| |
| // Check that this type of texture is allowed. |
| if (!texture_manager()->ValidForTarget(source_target, source_level, |
| source_width, source_height, 1)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "source texture bad dimensions"); |
| return; |
| } |
| |
| if (!source_texture->ValidForTexture(source_target, source_level, x, y, 0, |
| width, height, 1)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "source texture bad dimensions."); |
| return; |
| } |
| } |
| |
| GLenum source_type = 0; |
| GLenum source_internal_format = 0; |
| source_texture->GetLevelType(source_target, source_level, &source_type, |
| &source_internal_format); |
| |
| GLenum dest_type = 0; |
| GLenum dest_internal_format = 0; |
| bool dest_level_defined = dest_texture->GetLevelType( |
| dest_target, dest_level, &dest_type, &dest_internal_format); |
| if (!dest_level_defined) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "destination texture is not defined"); |
| return; |
| } |
| if (!dest_texture->ValidForTexture(dest_target, dest_level, xoffset, yoffset, |
| 0, width, height, 1)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, |
| "destination texture bad dimensions."); |
| return; |
| } |
| |
| std::string output_error_msg; |
| if (!ValidateCopyTextureCHROMIUMInternalFormats( |
| GetFeatureInfo(), source_internal_format, dest_internal_format, |
| &output_error_msg)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| output_error_msg.c_str()); |
| return; |
| } |
| |
| if (feature_info_->feature_flags().desktop_srgb_support) { |
| bool enable_framebuffer_srgb = |
| GLES2Util::GetColorEncodingFromInternalFormat(source_internal_format) == |
| GL_SRGB || |
| GLES2Util::GetColorEncodingFromInternalFormat(dest_internal_format) == |
| GL_SRGB; |
| state_.EnableDisableFramebufferSRGB(enable_framebuffer_srgb); |
| } |
| |
| // Clear the source texture if necessary. |
| if (!texture_manager()->ClearTextureLevel(this, source_texture_ref, |
| source_target, source_level)) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, function_name, |
| "source texture dimensions too big"); |
| return; |
| } |
| |
| if (!InitializeCopyTextureCHROMIUM(function_name)) |
| return; |
| |
| int dest_width = 0; |
| int dest_height = 0; |
| bool ok = dest_texture->GetLevelSize(dest_target, dest_level, &dest_width, |
| &dest_height, nullptr); |
| DCHECK(ok); |
| if (xoffset != 0 || yoffset != 0 || width != dest_width || |
| height != dest_height) { |
| gfx::Rect cleared_rect; |
| if (TextureManager::CombineAdjacentRects( |
| dest_texture->GetLevelClearedRect(dest_target, dest_level), |
| gfx::Rect(xoffset, yoffset, width, height), &cleared_rect)) { |
| DCHECK_GE(cleared_rect.size().GetArea(), |
| dest_texture->GetLevelClearedRect(dest_target, dest_level) |
| .size() |
| .GetArea()); |
| texture_manager()->SetLevelClearedRect(dest_texture_ref, dest_target, |
| dest_level, cleared_rect); |
| } else { |
| // Otherwise clear part of texture level that is not already cleared. |
| if (!texture_manager()->ClearTextureLevel(this, dest_texture_ref, |
| dest_target, dest_level)) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, function_name, |
| "destination texture dimensions too big"); |
| return; |
| } |
| } |
| } else { |
| texture_manager()->SetLevelCleared(dest_texture_ref, dest_target, |
| dest_level, true); |
| } |
| |
| // Try using GLImage::CopyTexSubImage when possible. |
| bool unpack_premultiply_alpha_change = |
| (unpack_premultiply_alpha ^ unpack_unmultiply_alpha) != 0; |
| // TODO(qiankun.miao@intel.com): Support level > 0 for CopyTexSubImage. |
| if (image && dest_internal_format == source_internal_format && |
| dest_level == 0 && !unpack_flip_y && !unpack_premultiply_alpha_change && |
| !dither) { |
| ScopedTextureBinder binder(&state_, dest_texture->service_id(), |
| dest_binding_target); |
| if (image->CopyTexSubImage(dest_target, gfx::Point(xoffset, yoffset), |
| gfx::Rect(x, y, width, height))) { |
| return; |
| } |
| } |
| |
| DoBindOrCopyTexImageIfNeeded(source_texture, source_target, 0); |
| |
| // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix |
| // before presenting. |
| if (source_target == GL_TEXTURE_EXTERNAL_OES) { |
| if (GLStreamTextureImage* image = |
| source_texture->GetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, |
| source_level)) { |
| GLfloat transform_matrix[16]; |
| image->GetTextureMatrix(transform_matrix); |
| copy_texture_chromium_->DoCopySubTextureWithTransform( |
| this, source_target, source_texture->service_id(), source_level, |
| source_internal_format, dest_target, dest_texture->service_id(), |
| dest_level, dest_internal_format, xoffset, yoffset, x, y, width, |
| height, dest_width, dest_height, source_width, source_height, |
| unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE, |
| unpack_unmultiply_alpha == GL_TRUE, dither == GL_TRUE, |
| transform_matrix, copy_tex_image_blit_.get()); |
| return; |
| } |
| } |
| |
| CopyTextureMethod method = GetCopyTextureCHROMIUMMethod( |
| GetFeatureInfo(), source_target, source_level, source_internal_format, |
| source_type, dest_binding_target, dest_level, dest_internal_format, |
| unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE, |
| unpack_unmultiply_alpha == GL_TRUE, dither == GL_TRUE); |
| #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) |
| // glDrawArrays is faster than glCopyTexSubImage2D on IA Mesa driver, |
| // although opposite in Android. |
| // TODO(dshwang): After Mesa fixes this issue, remove this hack. |
| // https://bugs.freedesktop.org/show_bug.cgi?id=98478, |
| // https://crbug.com/535198. |
| if (Texture::ColorRenderable(GetFeatureInfo(), dest_internal_format, |
| dest_texture->IsImmutable()) && |
| method == CopyTextureMethod::DIRECT_COPY) { |
| method = CopyTextureMethod::DIRECT_DRAW; |
| } |
| #endif |
| |
| copy_texture_chromium_->DoCopySubTexture( |
| this, source_target, source_texture->service_id(), source_level, |
| source_internal_format, dest_target, dest_texture->service_id(), |
| dest_level, dest_internal_format, xoffset, yoffset, x, y, width, height, |
| dest_width, dest_height, source_width, source_height, |
| unpack_flip_y == GL_TRUE, unpack_premultiply_alpha == GL_TRUE, |
| unpack_unmultiply_alpha == GL_TRUE, dither == GL_TRUE, method, |
| copy_tex_image_blit_.get()); |
| } |
| |
| void GLES2DecoderImpl::DoCopySubTextureCHROMIUM( |
| GLuint source_id, |
| GLint source_level, |
| GLenum dest_target, |
| GLuint dest_id, |
| GLint dest_level, |
| GLint xoffset, |
| GLint yoffset, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height, |
| GLboolean unpack_flip_y, |
| GLboolean unpack_premultiply_alpha, |
| GLboolean unpack_unmultiply_alpha) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoCopySubTextureCHROMIUM"); |
| static const char kFunctionName[] = "glCopySubTextureCHROMIUM"; |
| CopySubTextureHelper(kFunctionName, source_id, source_level, dest_target, |
| dest_id, dest_level, xoffset, yoffset, x, y, width, |
| height, unpack_flip_y, unpack_premultiply_alpha, |
| unpack_unmultiply_alpha, GL_FALSE /* dither */); |
| } |
| |
| bool GLES2DecoderImpl::InitializeCopyTexImageBlitter( |
| const char* function_name) { |
| if (!copy_tex_image_blit_.get()) { |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(function_name); |
| copy_tex_image_blit_.reset( |
| new CopyTexImageResourceManager(feature_info_.get())); |
| copy_tex_image_blit_->Initialize(this); |
| if (LOCAL_PEEK_GL_ERROR(function_name) != GL_NO_ERROR) |
| return false; |
| } |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::InitializeCopyTextureCHROMIUM( |
| const char* function_name) { |
| // 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(function_name); |
| copy_texture_chromium_.reset(CopyTextureCHROMIUMResourceManager::Create()); |
| copy_texture_chromium_->Initialize(this, features()); |
| if (LOCAL_PEEK_GL_ERROR(function_name) != GL_NO_ERROR) |
| return false; |
| |
| // On the desktop core profile this also needs emulation of |
| // CopyTex{Sub}Image2D for luminance, alpha, and luminance_alpha |
| // textures. |
| if (CopyTexImageResourceManager::CopyTexImageRequiresBlit( |
| feature_info_.get(), GL_LUMINANCE)) { |
| if (!InitializeCopyTexImageBlitter(function_name)) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void GLES2DecoderImpl::TexStorageImpl(GLenum target, |
| GLsizei levels, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| ContextState::Dimension dimension, |
| const char* function_name) { |
| if (levels == 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "levels == 0"); |
| return; |
| } |
| bool is_compressed_format = IsCompressedTextureFormat(internal_format); |
| if (is_compressed_format && target == GL_TEXTURE_3D) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, function_name, "target invalid for format"); |
| return; |
| } |
| // The glTexStorage entry points require width, height, and depth to be |
| // at least 1, but the other texture entry points (those which use |
| // ValidForTarget) do not. So we have to add an extra check here. |
| bool is_invalid_texstorage_size = width < 1 || height < 1 || depth < 1; |
| if (!texture_manager()->ValidForTarget(target, 0, width, height, depth) || |
| is_invalid_texstorage_size || |
| TextureManager::ComputeMipMapCount(target, width, height, depth) < |
| levels) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_VALUE, function_name, "dimensions out of range"); |
| return; |
| } |
| 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; |
| } |
| Texture* texture = texture_ref->texture(); |
| if (texture->IsAttachedToFramebuffer()) { |
| framebuffer_state_.clear_state_dirty = true; |
| } |
| if (texture->IsImmutable()) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, function_name, "texture is immutable"); |
| return; |
| } |
| |
| GLenum format = TextureManager::ExtractFormatFromStorageFormat( |
| internal_format); |
| GLenum type = TextureManager::ExtractTypeFromStorageFormat(internal_format); |
| |
| { |
| GLsizei level_width = width; |
| GLsizei level_height = height; |
| GLsizei level_depth = depth; |
| base::CheckedNumeric<uint32_t> estimated_size(0); |
| PixelStoreParams params; |
| params.alignment = 1; |
| for (int ii = 0; ii < levels; ++ii) { |
| uint32_t size; |
| if (is_compressed_format) { |
| GLsizei level_size; |
| if (!GetCompressedTexSizeInBytes( |
| function_name, level_width, level_height, level_depth, |
| internal_format, &level_size, state_.GetErrorState())) { |
| // GetCompressedTexSizeInBytes() already generates a GL error. |
| return; |
| } |
| size = static_cast<uint32_t>(level_size); |
| } else { |
| if (!GLES2Util::ComputeImageDataSizesES3(level_width, |
| level_height, |
| level_depth, |
| format, type, |
| params, |
| &size, |
| nullptr, nullptr, |
| nullptr, nullptr)) { |
| LOCAL_SET_GL_ERROR( |
| GL_OUT_OF_MEMORY, function_name, "dimensions too large"); |
| return; |
| } |
| } |
| estimated_size += size; |
| level_width = std::max(1, level_width >> 1); |
| level_height = std::max(1, level_height >> 1); |
| if (target == GL_TEXTURE_3D) |
| level_depth = std::max(1, level_depth >> 1); |
| } |
| if (!estimated_size.IsValid()) { |
| LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, function_name, "out of memory"); |
| return; |
| } |
| } |
| |
| // First lookup compatibility format via texture manager for swizzling legacy |
| // LUMINANCE/ALPHA formats. |
| GLenum compatibility_internal_format = |
| texture_manager()->AdjustTexStorageFormat(feature_info_.get(), |
| internal_format); |
| |
| // Then lookup compatibility format for compressed formats. |
| const CompressedFormatInfo* format_info = |
| GetCompressedFormatInfo(internal_format); |
| if (format_info != nullptr && !format_info->support_check(*feature_info_)) { |
| compatibility_internal_format = format_info->decompressed_internal_format; |
| } |
| |
| { |
| GLsizei level_width = width; |
| GLsizei level_height = height; |
| GLsizei level_depth = depth; |
| |
| GLenum adjusted_internal_format = |
| feature_info_->IsWebGL1OrES2Context() ? format : internal_format; |
| for (int ii = 0; ii < levels; ++ii) { |
| if (target == GL_TEXTURE_CUBE_MAP) { |
| for (int jj = 0; jj < 6; ++jj) { |
| GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + jj; |
| texture_manager()->SetLevelInfo( |
| texture_ref, face, ii, adjusted_internal_format, level_width, |
| level_height, 1, 0, format, type, gfx::Rect()); |
| } |
| } else { |
| texture_manager()->SetLevelInfo( |
| texture_ref, target, ii, adjusted_internal_format, level_width, |
| level_height, level_depth, 0, format, type, gfx::Rect()); |
| } |
| level_width = std::max(1, level_width >> 1); |
| level_height = std::max(1, level_height >> 1); |
| if (target == GL_TEXTURE_3D) |
| level_depth = std::max(1, level_depth >> 1); |
| } |
| texture->ApplyFormatWorkarounds(feature_info_.get()); |
| texture->SetImmutable(true); |
| } |
| |
| if (workarounds().reset_base_mipmap_level_before_texstorage && |
| texture->base_level() > 0) |
| api()->glTexParameteriFn(target, GL_TEXTURE_BASE_LEVEL, 0); |
| |
| // TODO(zmo): We might need to emulate TexStorage using TexImage or |
| // CompressedTexImage on Mac OSX where we expose ES3 APIs when the underlying |
| // driver is lower than 4.2 and ARB_texture_storage extension doesn't exist. |
| if (dimension == ContextState::k2D) { |
| api()->glTexStorage2DEXTFn(target, levels, compatibility_internal_format, |
| width, height); |
| } else { |
| api()->glTexStorage3DFn(target, levels, compatibility_internal_format, |
| width, height, depth); |
| } |
| if (workarounds().reset_base_mipmap_level_before_texstorage && |
| texture->base_level() > 0) { |
| // Note base_level is already clamped due to texture->SetImmutable(true). |
| // This is necessary for certain NVidia Linux drivers; otherwise they |
| // may trigger segmentation fault. See https://crbug.com/877874. |
| api()->glTexParameteriFn(target, GL_TEXTURE_BASE_LEVEL, |
| texture->base_level()); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoTexStorage2DEXT(GLenum target, |
| GLsizei levels, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height) { |
| TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage2D", |
| "width", width, "height", height); |
| TexStorageImpl(target, levels, internal_format, width, height, 1, |
| ContextState::k2D, "glTexStorage2D"); |
| } |
| |
| void GLES2DecoderImpl::DoTexStorage3D(GLenum target, |
| GLsizei levels, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth) { |
| TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage3D", |
| "widthXheight", width * height, "depth", depth); |
| TexStorageImpl(target, levels, internal_format, width, height, depth, |
| ContextState::k3D, "glTexStorage3D"); |
| } |
| |
| void GLES2DecoderImpl::DoTexStorage2DImageCHROMIUM(GLenum target, |
| GLenum internal_format, |
| GLenum buffer_usage, |
| GLsizei width, |
| GLsizei height) { |
| TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoTexStorage2DImageCHROMIUM", "width", |
| width, "height", height); |
| |
| ScopedGLErrorSuppressor suppressor( |
| "GLES2CmdDecoder::DoTexStorage2DImageCHROMIUM", state_.GetErrorState()); |
| |
| if (!texture_manager()->ValidForTarget(target, 0, width, height, 1)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DImageCHROMIUM", |
| "dimensions out of range"); |
| return; |
| } |
| |
| TextureRef* texture_ref = |
| texture_manager()->GetTextureInfoForTarget(&state_, target); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexStorage2DImageCHROMIUM", |
| "unknown texture for target"); |
| return; |
| } |
| |
| Texture* texture = texture_ref->texture(); |
| if (texture->IsImmutable()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexStorage2DImageCHROMIUM", |
| "texture is immutable"); |
| return; |
| } |
| |
| gfx::BufferFormat buffer_format; |
| switch (internal_format) { |
| case GL_RGBA8_OES: |
| buffer_format = gfx::BufferFormat::RGBA_8888; |
| break; |
| case GL_BGRA8_EXT: |
| buffer_format = gfx::BufferFormat::BGRA_8888; |
| break; |
| case GL_RGBA16F_EXT: |
| buffer_format = gfx::BufferFormat::RGBA_F16; |
| break; |
| case GL_R8_EXT: |
| buffer_format = gfx::BufferFormat::R_8; |
| break; |
| default: |
| LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, "glTexStorage2DImageCHROMIUM", |
| "Invalid buffer format"); |
| return; |
| } |
| |
| DCHECK_EQ(buffer_usage, static_cast<GLenum>(GL_SCANOUT_CHROMIUM)); |
| |
| if (!GetContextGroup()->image_factory()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexStorage2DImageCHROMIUM", |
| "Cannot create GL image"); |
| return; |
| } |
| |
| bool is_cleared = false; |
| scoped_refptr<gl::GLImage> image = |
| GetContextGroup()->image_factory()->CreateAnonymousImage( |
| gfx::Size(width, height), buffer_format, gfx::BufferUsage::SCANOUT, |
| &is_cleared); |
| if (!image || !image->BindTexImage(target)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glTexStorage2DImageCHROMIUM", |
| "Failed to create or bind GL Image"); |
| return; |
| } |
| |
| gfx::Rect cleared_rect; |
| if (is_cleared) |
| cleared_rect = gfx::Rect(width, height); |
| |
| texture_manager()->SetLevelInfo( |
| texture_ref, target, 0, image->GetInternalFormat(), width, height, 1, 0, |
| image->GetInternalFormat(), GL_UNSIGNED_BYTE, cleared_rect); |
| texture_manager()->SetLevelImage(texture_ref, target, 0, image.get(), |
| Texture::BOUND); |
| |
| if (texture->IsAttachedToFramebuffer()) |
| framebuffer_state_.clear_state_dirty = true; |
| |
| texture->SetImmutable(true); |
| } |
| |
| void GLES2DecoderImpl::DoProduceTextureDirectCHROMIUM( |
| GLuint client_id, |
| const volatile GLbyte* data) { |
| TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoProduceTextureDirectCHROMIUM", |
| "context", logger_.GetLogPrefix(), |
| "mailbox[0]", static_cast<unsigned char>(data[0])); |
| Mailbox mailbox = |
| Mailbox::FromVolatile(*reinterpret_cast<const volatile Mailbox*>(data)); |
| DLOG_IF(ERROR, !mailbox.Verify()) |
| << "ProduceTextureDirectCHROMIUM was not passed a crypto-random mailbox."; |
| |
| TextureRef* texture_ref = GetTexture(client_id); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glProduceTextureDirectCHROMIUM", |
| "unknown texture"); |
| return; |
| } |
| |
| group_->mailbox_manager()->ProduceTexture(mailbox, texture_ref->texture()); |
| } |
| |
| void GLES2DecoderImpl::DoCreateAndConsumeTextureINTERNAL( |
| GLuint client_id, |
| const volatile GLbyte* data) { |
| TRACE_EVENT2("gpu", "GLES2DecoderImpl::DoCreateAndConsumeTextureINTERNAL", |
| "context", logger_.GetLogPrefix(), |
| "mailbox[0]", static_cast<unsigned char>(data[0])); |
| Mailbox mailbox = |
| Mailbox::FromVolatile(*reinterpret_cast<const volatile Mailbox*>(data)); |
| DLOG_IF(ERROR, !mailbox.Verify()) << "CreateAndConsumeTextureCHROMIUM was " |
| "passed a mailbox that was not " |
| "generated by GenMailboxCHROMIUM."; |
| if (!client_id) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "glCreateAndConsumeTextureCHROMIUM", |
| "invalid client id"); |
| return; |
| } |
| |
| TextureRef* texture_ref = GetTexture(client_id); |
| if (texture_ref) { |
| // No need to create texture 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 = |
| Texture::CheckedCast(group_->mailbox_manager()->ConsumeTexture(mailbox)); |
| if (!texture) { |
| // Create texture to handle invalid mailbox (see http://crbug.com/472465). |
| bool result = GenTexturesHelper(1, &client_id); |
| DCHECK(result); |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glCreateAndConsumeTextureCHROMIUM", "invalid mailbox name"); |
| return; |
| } |
| |
| texture_ref = texture_manager()->Consume(client_id, texture); |
| } |
| |
| void GLES2DecoderImpl::DoCreateAndTexStorage2DSharedImageINTERNAL( |
| GLuint client_id, |
| GLenum internal_format, |
| const volatile GLbyte* data) { |
| TRACE_EVENT2("gpu", |
| "GLES2DecoderImpl::DoCreateAndTexStorage2DSharedImageCHROMIUM", |
| "context", logger_.GetLogPrefix(), "mailbox[0]", |
| static_cast<unsigned char>(data[0])); |
| Mailbox mailbox = |
| Mailbox::FromVolatile(*reinterpret_cast<const volatile Mailbox*>(data)); |
| DLOG_IF(ERROR, !mailbox.Verify()) |
| << "DoCreateAndTexStorage2DSharedImageCHROMIUM was passed an invalid " |
| "mailbox."; |
| if (!client_id) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "DoCreateAndTexStorage2DSharedImageINTERNAL", |
| "invalid client id"); |
| return; |
| } |
| |
| TextureRef* texture_ref = GetTexture(client_id); |
| if (texture_ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "DoCreateAndTexStorage2DSharedImageINTERNAL", |
| "client id already in use"); |
| return; |
| } |
| |
| std::unique_ptr<SharedImageRepresentationGLTexture> shared_image = |
| group_->shared_image_representation_factory()->ProduceGLTexture(mailbox); |
| if (!shared_image) { |
| // Mailbox missing, generate a texture. |
| bool result = GenTexturesHelper(1, &client_id); |
| DCHECK(result); |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "DoCreateAndTexStorage2DSharedImageINTERNAL", |
| "invalid mailbox name"); |
| return; |
| } |
| |
| texture_ref = |
| texture_manager()->ConsumeSharedImage(client_id, std::move(shared_image)); |
| } |
| |
| void GLES2DecoderImpl::DoBeginSharedImageAccessDirectCHROMIUM(GLuint client_id, |
| GLenum mode) { |
| TextureRef* texture_ref = GetTexture(client_id); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "DoBeginSharedImageAccessCHROMIUM", |
| "invalid texture id"); |
| return; |
| } |
| |
| SharedImageRepresentationGLTexture* shared_image = |
| texture_ref->shared_image(); |
| if (!shared_image) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "DoBeginSharedImageAccessCHROMIUM", |
| "bound texture is not a shared image"); |
| return; |
| } |
| |
| if (!shared_image->BeginAccess(mode)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "DoBeginSharedImageAccessCHROMIUM", |
| "Unable to begin access"); |
| return; |
| } |
| } |
| |
| void GLES2DecoderImpl::DoEndSharedImageAccessDirectCHROMIUM(GLuint client_id) { |
| TextureRef* texture_ref = GetTexture(client_id); |
| if (!texture_ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "DoBeginSharedImageAccessCHROMIUM", |
| "invalid texture id"); |
| return; |
| } |
| |
| SharedImageRepresentationGLTexture* shared_image = |
| texture_ref->shared_image(); |
| if (!shared_image) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "DoEndSharedImageAccessCHROMIUM", |
| "bound texture is not a shared image"); |
| return; |
| } |
| |
| shared_image->EndAccess(); |
| } |
| |
| void GLES2DecoderImpl::DoApplyScreenSpaceAntialiasingCHROMIUM() { |
| Framebuffer* bound_framebuffer = |
| GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER); |
| // TODO(dshwang): support it even after glBindFrameBuffer(GL_FRAMEBUFFER, 0). |
| // skia will need to render to the window. crbug.com/656618 |
| if (!bound_framebuffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "glApplyScreenSpaceAntialiasingCHROMIUM", |
| "no bound framebuffer object"); |
| return; |
| } |
| |
| // Apply CMAA(Conservative Morphological Anti-Aliasing) algorithm to the |
| // color attachments of currently bound draw framebuffer. |
| // Reference GL_INTEL_framebuffer_CMAA for details. |
| // Use platform version if available. |
| if (!feature_info_->feature_flags() |
| .use_chromium_screen_space_antialiasing_via_shaders) { |
| api()->glApplyFramebufferAttachmentCMAAINTELFn(); |
| } else { |
| // Defer initializing the CopyTextureCHROMIUMResourceManager until it is |
| // needed because it takes ??s of milliseconds to initialize. |
| if (!apply_framebuffer_attachment_cmaa_intel_.get()) { |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER( |
| "glApplyFramebufferAttachmentCMAAINTEL"); |
| apply_framebuffer_attachment_cmaa_intel_.reset( |
| new ApplyFramebufferAttachmentCMAAINTELResourceManager()); |
| apply_framebuffer_attachment_cmaa_intel_->Initialize(this); |
| if (LOCAL_PEEK_GL_ERROR("glApplyFramebufferAttachmentCMAAINTEL") != |
| GL_NO_ERROR) |
| return; |
| } |
| static const char kFunctionName[] = |
| "glApplyScreenSpaceAntialiasingCHROMIUM"; |
| if (!InitializeCopyTextureCHROMIUM(kFunctionName)) |
| return; |
| for (uint32_t i = 0; i < group_->max_draw_buffers(); ++i) { |
| const Framebuffer::Attachment* attachment = |
| bound_framebuffer->GetAttachment(GL_COLOR_ATTACHMENT0 + i); |
| if (attachment && attachment->IsTextureAttachment()) { |
| GLenum internal_format = attachment->internal_format(); |
| if (!CanUseCopyTextureCHROMIUMInternalFormat(internal_format)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "Apply CMAA on framebuffer with attachment in " |
| "invalid internalformat."); |
| return; |
| } |
| } |
| } |
| |
| apply_framebuffer_attachment_cmaa_intel_ |
| ->ApplyFramebufferAttachmentCMAAINTEL(this, bound_framebuffer, |
| copy_texture_chromium_.get(), |
| texture_manager()); |
| } |
| } |
| |
| 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*/) { |
| } |
| |
| void GLES2DecoderImpl::DoPopGroupMarkerEXT(void) { |
| } |
| |
| void GLES2DecoderImpl::BindImage(uint32_t client_texture_id, |
| uint32_t texture_target, |
| gl::GLImage* image, |
| bool can_bind_to_sampler) { |
| TextureRef* ref = texture_manager()->GetTexture(client_texture_id); |
| if (!ref) { |
| return; |
| } |
| |
| GLenum bind_target = GLES2Util::GLFaceTargetToTextureTarget(texture_target); |
| if (ref->texture()->target() != bind_target) { |
| return; |
| } |
| |
| texture_manager()->SetLevelImage(ref, texture_target, 0, image, |
| can_bind_to_sampler |
| ? gpu::gles2::Texture::BOUND |
| : gpu::gles2::Texture::UNBOUND); |
| } |
| |
| void GLES2DecoderImpl::DoBindTexImage2DCHROMIUM( |
| GLenum target, GLint image_id) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoBindTexImage2DCHROMIUM"); |
| |
| BindTexImage2DCHROMIUMImpl("glBindTexImage2DCHROMIUM", target, 0, image_id); |
| } |
| |
| void GLES2DecoderImpl::DoBindTexImage2DWithInternalformatCHROMIUM( |
| GLenum target, |
| GLenum internalformat, |
| GLint image_id) { |
| TRACE_EVENT0("gpu", |
| "GLES2DecoderImpl::DoBindTexImage2DWithInternalformatCHROMIUM"); |
| |
| BindTexImage2DCHROMIUMImpl("glBindTexImage2DWithInternalformatCHROMIUM", |
| target, internalformat, image_id); |
| } |
| |
| void GLES2DecoderImpl::BindTexImage2DCHROMIUMImpl(const char* function_name, |
| GLenum target, |
| GLenum internalformat, |
| GLint image_id) { |
| if (target == GL_TEXTURE_CUBE_MAP) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, function_name, "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, function_name, "no texture bound"); |
| return; |
| } |
| |
| gl::GLImage* image = image_manager()->LookupImage(image_id); |
| if (!image) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
| "no image found with the given ID"); |
| return; |
| } |
| |
| Texture::ImageState image_state = Texture::UNBOUND; |
| |
| { |
| ScopedGLErrorSuppressor suppressor( |
| "GLES2DecoderImpl::DoBindTexImage2DCHROMIUM", GetErrorState()); |
| |
| // Note: We fallback to using CopyTexImage() before the texture is used |
| // when BindTexImage() fails. |
| if (internalformat) { |
| if (image->BindTexImageWithInternalformat(target, internalformat)) |
| image_state = Texture::BOUND; |
| } else { |
| if (image->BindTexImage(target)) |
| image_state = Texture::BOUND; |
| } |
| } |
| |
| gfx::Size size = image->GetSize(); |
| GLenum texture_internalformat = |
| internalformat ? internalformat : image->GetInternalFormat(); |
| texture_manager()->SetLevelInfo(texture_ref, target, 0, |
| texture_internalformat, size.width(), |
| size.height(), 1, 0, texture_internalformat, |
| GL_UNSIGNED_BYTE, gfx::Rect(size)); |
| texture_manager()->SetLevelImage(texture_ref, target, 0, image, image_state); |
| } |
| |
| 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; |
| } |
| |
| gl::GLImage* image = image_manager()->LookupImage(image_id); |
| if (!image) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glReleaseTexImage2DCHROMIUM", "no image found with the given ID"); |
| return; |
| } |
| |
| Texture::ImageState image_state; |
| |
| // Do nothing when image is not currently bound. |
| if (texture_ref->texture()->GetLevelImage(target, 0, &image_state) != image) |
| return; |
| |
| if (image_state == Texture::BOUND) { |
| ScopedGLErrorSuppressor suppressor( |
| "GLES2DecoderImpl::DoReleaseTexImage2DCHROMIUM", GetErrorState()); |
| |
| image->ReleaseTexImage(target); |
| texture_manager()->SetLevelInfo(texture_ref, target, 0, GL_RGBA, 0, 0, 1, 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect()); |
| } |
| |
| texture_manager()->SetLevelImage(texture_ref, target, 0, nullptr, |
| Texture::UNBOUND); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleTraceBeginCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::TraceBeginCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::TraceBeginCHROMIUM*>(cmd_data); |
| Bucket* category_bucket = GetBucket(c.category_bucket_id); |
| Bucket* name_bucket = GetBucket(c.name_bucket_id); |
| static constexpr size_t kMaxStrLen = 256; |
| if (!category_bucket || category_bucket->size() == 0 || |
| category_bucket->size() > kMaxStrLen || !name_bucket || |
| name_bucket->size() == 0 || name_bucket->size() > kMaxStrLen) { |
| 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; |
| } |
| |
| debug_marker_manager_.PushGroup(trace_name); |
| 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() { |
| debug_marker_manager_.PopGroup(); |
| if (!gpu_tracer_->End(kTraceCHROMIUM)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, |
| "glTraceEndCHROMIUM", "no trace begin found"); |
| return; |
| } |
| } |
| |
| void GLES2DecoderImpl::DoDrawBuffersEXT(GLsizei count, |
| const volatile GLenum* bufs) { |
| DCHECK_LE(group_->max_draw_buffers(), 16u); |
| if (count > static_cast<GLsizei>(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) { |
| GLenum safe_bufs[16]; |
| for (GLsizei i = 0; i < count; ++i) { |
| GLenum buf = bufs[i]; |
| if (buf != static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i) && |
| buf != GL_NONE) { |
| LOCAL_SET_GL_ERROR( |
| GL_INVALID_OPERATION, |
| "glDrawBuffersEXT", |
| "bufs[i] not GL_NONE or GL_COLOR_ATTACHMENTi_EXT"); |
| return; |
| } |
| safe_bufs[i] = buf; |
| } |
| api()->glDrawBuffersARBFn(count, safe_bufs); |
| framebuffer->SetDrawBuffers(count, safe_bufs); |
| } else { // backbuffer |
| if (count != 1) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glDrawBuffersEXT", |
| "invalid number of buffers"); |
| return; |
| } |
| GLenum buf = bufs[0]; |
| if (buf != GL_BACK && buf != GL_NONE) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glDrawBuffersEXT", |
| "buffer is not GL_NONE or GL_BACK"); |
| return; |
| } |
| back_buffer_draw_buffer_ = buf; |
| if (buf == GL_BACK && GetBackbufferServiceId() != 0) // emulated backbuffer |
| buf = GL_COLOR_ATTACHMENT0; |
| api()->glDrawBuffersARBFn(count, &buf); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoLoseContextCHROMIUM(GLenum current, GLenum other) { |
| MarkContextLost(GetContextLostReasonFromResetStatus(current)); |
| group_->LoseContexts(GetContextLostReasonFromResetStatus(other)); |
| reset_by_robustness_extension_ = true; |
| } |
| |
| void GLES2DecoderImpl::DoFlushDriverCachesCHROMIUM(void) { |
| // On Adreno Android devices we need to use a workaround to force caches to |
| // clear. |
| if (workarounds().unbind_egl_context_to_flush_driver_caches) { |
| context_->ReleaseCurrent(nullptr); |
| context_->MakeCurrent(surface_.get()); |
| } |
| } |
| |
| void GLES2DecoderImpl::DoMatrixLoadfCHROMIUM(GLenum matrix_mode, |
| const volatile GLfloat* matrix) { |
| DCHECK(matrix_mode == GL_PATH_PROJECTION_CHROMIUM || |
| matrix_mode == GL_PATH_MODELVIEW_CHROMIUM); |
| |
| GLfloat* target_matrix = matrix_mode == GL_PATH_PROJECTION_CHROMIUM |
| ? state_.projection_matrix |
| : state_.modelview_matrix; |
| memcpy(target_matrix, const_cast<const GLfloat*>(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. |
| api()->glMatrixLoadfEXTFn(matrix_mode, target_matrix); |
| } |
| |
| void GLES2DecoderImpl::DoMatrixLoadIdentityCHROMIUM(GLenum matrix_mode) { |
| DCHECK(matrix_mode == GL_PATH_PROJECTION_CHROMIUM || |
| matrix_mode == GL_PATH_MODELVIEW_CHROMIUM); |
| |
| 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. |
| api()->glMatrixLoadIdentityEXTFn(matrix_mode); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleUniformBlockBinding( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const char* func_name = "glUniformBlockBinding"; |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::UniformBlockBinding& c = |
| *static_cast<const volatile gles2::cmds::UniformBlockBinding*>(cmd_data); |
| GLuint client_id = c.program; |
| GLuint index = static_cast<GLuint>(c.index); |
| GLuint binding = static_cast<GLuint>(c.binding); |
| Program* program = GetProgramInfoNotShader(client_id, func_name); |
| if (!program) { |
| return error::kNoError; |
| } |
| if (index >= program->uniform_block_size_info().size()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, |
| "uniformBlockIndex is not an active uniform block index"); |
| return error::kNoError; |
| } |
| if (binding >= group_->max_uniform_buffer_bindings()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, |
| "uniformBlockBinding >= MAX_UNIFORM_BUFFER_BINDINGS"); |
| return error::kNoError; |
| } |
| GLuint service_id = program->service_id(); |
| api()->glUniformBlockBindingFn(service_id, index, binding); |
| program->SetUniformBlockBinding(index, binding); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleClientWaitSync( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const char* function_name = "glClientWaitSync"; |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::ClientWaitSync& c = |
| *static_cast<const volatile gles2::cmds::ClientWaitSync*>(cmd_data); |
| const GLuint sync = static_cast<GLuint>(c.sync); |
| GLbitfield flags = static_cast<GLbitfield>(c.flags); |
| const GLuint64 timeout = c.timeout(); |
| typedef cmds::ClientWaitSync::Result Result; |
| Result* result_dst = GetSharedMemoryAs<Result*>( |
| 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, function_name, "invalid sync"); |
| return error::kNoError; |
| } |
| if ((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) != 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid flags"); |
| return error::kNoError; |
| } |
| // Force GL_SYNC_FLUSH_COMMANDS_BIT to avoid infinite wait. |
| flags |= GL_SYNC_FLUSH_COMMANDS_BIT; |
| |
| GLenum status = api()->glClientWaitSyncFn(service_sync, flags, timeout); |
| switch (status) { |
| case GL_ALREADY_SIGNALED: |
| case GL_TIMEOUT_EXPIRED: |
| case GL_CONDITION_SATISFIED: |
| break; |
| case GL_WAIT_FAILED: |
| // Avoid leaking GL errors when using virtual contexts. |
| LOCAL_PEEK_GL_ERROR(function_name); |
| *result_dst = status; |
| // If validation is complete, this only happens if the context is lost. |
| return error::kLostContext; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| *result_dst = status; |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleWaitSync(uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const char* function_name = "glWaitSync"; |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::WaitSync& c = |
| *static_cast<const volatile gles2::cmds::WaitSync*>(cmd_data); |
| const GLuint sync = static_cast<GLuint>(c.sync); |
| const GLbitfield flags = static_cast<GLbitfield>(c.flags); |
| const GLuint64 timeout = c.timeout(); |
| GLsync service_sync = 0; |
| if (!group_->GetSyncServiceId(sync, &service_sync)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid sync"); |
| return error::kNoError; |
| } |
| if (flags != 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid flags"); |
| return error::kNoError; |
| } |
| if (timeout != GL_TIMEOUT_IGNORED) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid timeout"); |
| return error::kNoError; |
| } |
| api()->glWaitSyncFn(service_sync, flags, timeout); |
| return error::kNoError; |
| } |
| |
| GLsync GLES2DecoderImpl::DoFenceSync(GLenum condition, GLbitfield flags) { |
| const char* function_name = "glFenceSync"; |
| if (condition != GL_SYNC_GPU_COMMANDS_COMPLETE) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, function_name, "invalid condition"); |
| return 0; |
| } |
| if (flags != 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, function_name, "invalid flags"); |
| return 0; |
| } |
| return api()->glFenceSyncFn(condition, flags); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleGetInternalformativ( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) |
| return error::kUnknownCommand; |
| const volatile gles2::cmds::GetInternalformativ& c = |
| *static_cast<const volatile gles2::cmds::GetInternalformativ*>(cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLenum pname = static_cast<GLenum>(c.pname); |
| if (!validators_->render_buffer_target.IsValid(target)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM("glGetInternalformativ", target, "target"); |
| return error::kNoError; |
| } |
| if (!validators_->render_buffer_format.IsValid(format)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM("glGetInternalformativ", format, |
| "internalformat"); |
| return error::kNoError; |
| } |
| if (!validators_->internal_format_parameter.IsValid(pname)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM("glGetInternalformativ", pname, "pname"); |
| return error::kNoError; |
| } |
| |
| typedef cmds::GetInternalformativ::Result Result; |
| GLsizei num_values = 0; |
| std::vector<GLint> samples; |
| if (gl_version_info().IsLowerThanGL(4, 2)) { |
| if (!GLES2Util::IsIntegerFormat(format)) { |
| // No multisampling for integer formats. |
| GLint max_samples = renderbuffer_manager()->max_samples(); |
| while (max_samples > 0) { |
| samples.push_back(max_samples); |
| --max_samples; |
| } |
| } |
| switch (pname) { |
| case GL_NUM_SAMPLE_COUNTS: |
| num_values = 1; |
| break; |
| case GL_SAMPLES: |
| num_values = static_cast<GLsizei>(samples.size()); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } else { |
| switch (pname) { |
| case GL_NUM_SAMPLE_COUNTS: |
| num_values = 1; |
| break; |
| case GL_SAMPLES: |
| { |
| GLint value = 0; |
| api()->glGetInternalformativFn(target, format, GL_NUM_SAMPLE_COUNTS, |
| 1, &value); |
| num_values = static_cast<GLsizei>(value); |
| } |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| Result* result = GetSharedMemoryAs<Result*>( |
| c.params_shm_id, c.params_shm_offset, Result::ComputeSize(num_values)); |
| GLint* params = result ? result->GetData() : nullptr; |
| if (params == nullptr) { |
| return error::kOutOfBounds; |
| } |
| // Check that the client initialized the result. |
| if (result->size != 0) { |
| return error::kInvalidArguments; |
| } |
| if (gl_version_info().IsLowerThanGL(4, 2)) { |
| switch (pname) { |
| case GL_NUM_SAMPLE_COUNTS: |
| params[0] = static_cast<GLint>(samples.size()); |
| break; |
| case GL_SAMPLES: |
| for (size_t ii = 0; ii < samples.size(); ++ii) { |
| params[ii] = samples[ii]; |
| } |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } else { |
| api()->glGetInternalformativFn(target, format, pname, num_values, params); |
| } |
| result->SetNumResults(num_values); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleMapBufferRange( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) { |
| return error::kUnknownCommand; |
| } |
| |
| const char* func_name = "glMapBufferRange"; |
| const volatile gles2::cmds::MapBufferRange& c = |
| *static_cast<const volatile gles2::cmds::MapBufferRange*>(cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| GLbitfield access = static_cast<GLbitfield>(c.access); |
| GLintptr offset = static_cast<GLintptr>(c.offset); |
| GLsizeiptr size = static_cast<GLsizeiptr>(c.size); |
| uint32_t data_shm_id = static_cast<uint32_t>(c.data_shm_id); |
| uint32_t data_shm_offset = static_cast<uint32_t>(c.data_shm_offset); |
| |
| typedef cmds::MapBufferRange::Result Result; |
| Result* result = GetSharedMemoryAs<Result*>( |
| c.result_shm_id, c.result_shm_offset, sizeof(*result)); |
| if (!result) { |
| return error::kOutOfBounds; |
| } |
| if (*result != 0) { |
| *result = 0; |
| return error::kInvalidArguments; |
| } |
| if (!validators_->buffer_target.IsValid(target)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, target, "target"); |
| return error::kNoError; |
| } |
| if (size == 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "length is zero"); |
| return error::kNoError; |
| } |
| Buffer* buffer = buffer_manager()->RequestBufferAccess( |
| &state_, target, offset, size, func_name); |
| if (!buffer) { |
| // An error is already set. |
| return error::kNoError; |
| } |
| if (state_.bound_transform_feedback->active() && |
| !state_.bound_transform_feedback->paused()) { |
| size_t used_binding_count = |
| state_.current_program->effective_transform_feedback_varyings().size(); |
| if (state_.bound_transform_feedback->UsesBuffer( |
| used_binding_count, buffer)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "active transform feedback is using this buffer"); |
| return error::kNoError; |
| } |
| } |
| |
| int8_t* mem = |
| GetSharedMemoryAs<int8_t*>(data_shm_id, data_shm_offset, size); |
| if (!mem) { |
| return error::kOutOfBounds; |
| } |
| if (AnyOtherBitsSet(access, (GL_MAP_READ_BIT | |
| GL_MAP_WRITE_BIT | |
| GL_MAP_INVALIDATE_RANGE_BIT | |
| GL_MAP_INVALIDATE_BUFFER_BIT | |
| GL_MAP_FLUSH_EXPLICIT_BIT | |
| GL_MAP_UNSYNCHRONIZED_BIT))) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "invalid access bits"); |
| return error::kNoError; |
| } |
| if (!AnyBitsSet(access, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "neither MAP_READ_BIT nor MAP_WRITE_BIT is set"); |
| return error::kNoError; |
| } |
| if (AllBitsSet(access, GL_MAP_READ_BIT) && |
| AnyBitsSet(access, (GL_MAP_INVALIDATE_RANGE_BIT | |
| GL_MAP_INVALIDATE_BUFFER_BIT | |
| GL_MAP_UNSYNCHRONIZED_BIT))) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "incompatible access bits with MAP_READ_BIT"); |
| return error::kNoError; |
| } |
| if (AllBitsSet(access, GL_MAP_FLUSH_EXPLICIT_BIT) && |
| !AllBitsSet(access, GL_MAP_WRITE_BIT)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "MAP_FLUSH_EXPLICIT_BIT set without MAP_WRITE_BIT"); |
| return error::kNoError; |
| } |
| GLbitfield filtered_access = access; |
| if (AllBitsSet(filtered_access, GL_MAP_INVALIDATE_BUFFER_BIT)) { |
| // To be on the safe side, always map GL_MAP_INVALIDATE_BUFFER_BIT to |
| // GL_MAP_INVALIDATE_RANGE_BIT. |
| filtered_access = (filtered_access & ~GL_MAP_INVALIDATE_BUFFER_BIT); |
| filtered_access = (filtered_access | GL_MAP_INVALIDATE_RANGE_BIT); |
| } |
| // Always filter out GL_MAP_UNSYNCHRONIZED_BIT to get rid of undefined |
| // behaviors. |
| filtered_access = (filtered_access & ~GL_MAP_UNSYNCHRONIZED_BIT); |
| if (AllBitsSet(filtered_access, GL_MAP_WRITE_BIT) && |
| !AllBitsSet(filtered_access, GL_MAP_INVALIDATE_RANGE_BIT)) { |
| filtered_access = (filtered_access | GL_MAP_READ_BIT); |
| } |
| void* ptr = api()->glMapBufferRangeFn(target, offset, size, filtered_access); |
| if (ptr == nullptr) { |
| // This should mean GL_OUT_OF_MEMORY (or context loss). |
| LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(func_name); |
| return error::kNoError; |
| } |
| // The un-filtered bits in |access| are deliberately used here. |
| buffer->SetMappedRange(offset, size, access, ptr, |
| GetSharedMemoryBuffer(data_shm_id), |
| static_cast<unsigned int>(data_shm_offset)); |
| if ((filtered_access & GL_MAP_INVALIDATE_RANGE_BIT) == 0) { |
| memcpy(mem, ptr, size); |
| } |
| *result = 1; |
| return error::kNoError; |
| } |
| |
| bool GLES2DecoderImpl::UnmapBufferHelper(Buffer* buffer, GLenum target) { |
| DCHECK(buffer); |
| const Buffer::MappedRange* mapped_range = buffer->GetMappedRange(); |
| if (!mapped_range) |
| return true; |
| if (!AllBitsSet(mapped_range->access, GL_MAP_WRITE_BIT) || |
| AllBitsSet(mapped_range->access, GL_MAP_FLUSH_EXPLICIT_BIT)) { |
| // If we don't need to write back, or explict flush is required, no copying |
| // back is needed. |
| } else if (!WasContextLost()) { |
| void* mem = mapped_range->GetShmPointer(); |
| DCHECK(mem); |
| DCHECK(mapped_range->pointer); |
| memcpy(mapped_range->pointer, mem, mapped_range->size); |
| if (buffer->shadowed()) { |
| buffer->SetRange(mapped_range->offset, mapped_range->size, mem); |
| } |
| } |
| buffer->RemoveMappedRange(); |
| if (WasContextLost()) |
| return true; |
| GLboolean rt = api()->glUnmapBufferFn(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 false; |
| } |
| return true; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleUnmapBuffer( |
| uint32_t immediate_data_size, const volatile void* cmd_data) { |
| if (!feature_info_->IsWebGL2OrES3Context()) { |
| return error::kUnknownCommand; |
| } |
| const char* func_name = "glUnmapBuffer"; |
| |
| const volatile gles2::cmds::UnmapBuffer& c = |
| *static_cast<const volatile gles2::cmds::UnmapBuffer*>(cmd_data); |
| GLenum target = static_cast<GLenum>(c.target); |
| |
| if (!validators_->buffer_target.IsValid(target)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(func_name, target, "target"); |
| return error::kNoError; |
| } |
| |
| Buffer* buffer = buffer_manager()->GetBufferInfoForTarget(&state_, target); |
| if (!buffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "no buffer bound"); |
| return error::kNoError; |
| } |
| const Buffer::MappedRange* mapped_range = buffer->GetMappedRange(); |
| if (!mapped_range) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "buffer is unmapped"); |
| return error::kNoError; |
| } |
| if (!UnmapBufferHelper(buffer, target)) |
| return error::kLostContext; |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderImpl::DoFlushMappedBufferRange( |
| GLenum target, GLintptr offset, GLsizeiptr size) { |
| const char* func_name = "glFlushMappedBufferRange"; |
| // |size| is validated in HandleFlushMappedBufferRange(). |
| if (offset < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, "offset < 0"); |
| return; |
| } |
| Buffer* buffer = buffer_manager()->GetBufferInfoForTarget(&state_, target); |
| if (!buffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "no buffer bound"); |
| return; |
| } |
| const Buffer::MappedRange* mapped_range = buffer->GetMappedRange(); |
| if (!mapped_range) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, "buffer is unmapped"); |
| return; |
| } |
| if (!AllBitsSet(mapped_range->access, GL_MAP_FLUSH_EXPLICIT_BIT)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name, |
| "buffer is mapped without MAP_FLUSH_EXPLICIT_BIT flag"); |
| return; |
| } |
| base::CheckedNumeric<int32_t> range_size = size; |
| range_size += offset; |
| if (!range_size.IsValid() || |
| range_size.ValueOrDefault(0) > mapped_range->size) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name, |
| "offset + size out of bounds"); |
| return; |
| } |
| char* client_data = reinterpret_cast<char*>(mapped_range->GetShmPointer()); |
| DCHECK(client_data); |
| char* gpu_data = reinterpret_cast<char*>(mapped_range->pointer); |
| DCHECK(gpu_data); |
| memcpy(gpu_data + offset, client_data + offset, size); |
| if (buffer->shadowed()) { |
| buffer->SetRange(mapped_range->offset + offset, size, client_data + offset); |
| } |
| api()->glFlushMappedBufferRangeFn(target, offset, size); |
| } |
| |
| // 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); |
| } |
| } |
| |
| // Class to validate path rendering command parameters. Contains validation |
| // for the common parameters that are used in multiple different commands. |
| // The individual functions are needed in order to control the order of the |
| // validation. |
| // The Get* functions will return false if the function call should be stopped. |
| // In this case, PathCommandValidatorContext::error() will return the command |
| // buffer error that should be returned. The decoder error state will be set to |
| // appropriate GL error if needed. |
| // The Get* functions will return true if the function call should |
| // continue as-is. |
| class PathCommandValidatorContext { |
| public: |
| PathCommandValidatorContext(GLES2DecoderImpl* decoder, |
| const char* function_name) |
| : decoder_(decoder), |
| error_state_(decoder->GetErrorState()), |
| validators_(decoder->GetContextGroup()->feature_info()->validators()), |
| function_name_(function_name), |
| error_(error::kNoError) {} |
| |
| error::Error error() const { return error_; } |
| |
| template <typename Cmd> |
| bool GetPathRange(const Cmd& cmd, GLsizei* out_range) { |
| GLsizei range = static_cast<GLsizei>(cmd.range); |
| if (range < 0) { |
| ERRORSTATE_SET_GL_ERROR(error_state_, GL_INVALID_VALUE, function_name_, |
| "range < 0"); |
| return false; |
| } |
| *out_range = range; |
| return true; |
| } |
| template <typename Cmd> |
| bool GetPathCountAndType(const Cmd& cmd, |
| GLuint* out_num_paths, |
| GLenum* out_path_name_type) { |
| int32_t numPaths = cmd.numPaths; |
| if (numPaths < 0) { |
| ERRORSTATE_SET_GL_ERROR(error_state_, GL_INVALID_VALUE, function_name_, |
| "numPaths < 0"); |
| return false; |
| } |
| GLenum path_name_type = static_cast<GLenum>(cmd.pathNameType); |
| if (!validators_->path_name_type.IsValid(path_name_type)) { |
| ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(error_state_, function_name_, |
| path_name_type, "pathNameType"); |
| return false; |
| } |
| *out_num_paths = static_cast<GLsizei>(numPaths); |
| *out_path_name_type = path_name_type; |
| return true; |
| } |
| template <typename Cmd> |
| bool GetFillMode(const Cmd& cmd, GLenum* out_fill_mode) { |
| GLenum fill_mode = static_cast<GLenum>(cmd.fillMode); |
| if (!validators_->path_fill_mode.IsValid(fill_mode)) { |
| ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(error_state_, function_name_, |
| fill_mode, "fillMode"); |
| return false; |
| } |
| *out_fill_mode = fill_mode; |
| return true; |
| } |
| template <typename Cmd> |
| bool GetFillModeAndMask(const Cmd& cmd, |
| GLenum* out_fill_mode, |
| GLuint* out_mask) { |
| GLenum fill_mode; |
| if (!GetFillMode(cmd, &fill_mode)) |
| return false; |
| GLuint mask = static_cast<GLuint>(cmd.mask); |
| /* The error INVALID_VALUE is generated if /fillMode/ is COUNT_UP_CHROMIUM |
| or COUNT_DOWN_CHROMIUM and the effective /mask/+1 is not an integer |
| power of two */ |
| if ((fill_mode == GL_COUNT_UP_CHROMIUM || |
| fill_mode == GL_COUNT_DOWN_CHROMIUM) && |
| GLES2Util::IsNPOT(mask + 1)) { |
| ERRORSTATE_SET_GL_ERROR(error_state_, GL_INVALID_VALUE, function_name_, |
| "mask+1 is not power of two"); |
| return false; |
| } |
| *out_fill_mode = fill_mode; |
| *out_mask = mask; |
| return true; |
| } |
| template <typename Cmd> |
| bool GetTransformType(const Cmd& cmd, GLenum* out_transform_type) { |
| GLenum transform_type = static_cast<GLenum>(cmd.transformType); |
| if (!validators_->path_transform_type.IsValid(transform_type)) { |
| ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(error_state_, function_name_, |
| transform_type, "transformType"); |
| return false; |
| } |
| *out_transform_type = transform_type; |
| return true; |
| } |
| template <typename Cmd> |
| bool GetPathNameData(const Cmd& cmd, |
| GLuint num_paths, |
| GLenum path_name_type, |
| std::unique_ptr<GLuint[]>* out_buffer) { |
| DCHECK(validators_->path_name_type.IsValid(path_name_type)); |
| GLuint path_base = static_cast<GLuint>(cmd.pathBase); |
| uint32_t shm_id = static_cast<uint32_t>(cmd.paths_shm_id); |
| uint32_t shm_offset = static_cast<uint32_t>(cmd.paths_shm_offset); |
| if (shm_id == 0 && shm_offset == 0) { |
| error_ = error::kOutOfBounds; |
| return false; |
| } |
| switch (path_name_type) { |
| case GL_BYTE: |
| return GetPathNameDataImpl<GLbyte>(num_paths, path_base, shm_id, |
| shm_offset, out_buffer); |
| case GL_UNSIGNED_BYTE: |
| return GetPathNameDataImpl<GLubyte>(num_paths, path_base, shm_id, |
| shm_offset, out_buffer); |
| case GL_SHORT: |
| return GetPathNameDataImpl<GLshort>(num_paths, path_base, shm_id, |
| shm_offset, out_buffer); |
| case GL_UNSIGNED_SHORT: |
| return GetPathNameDataImpl<GLushort>(num_paths, path_base, shm_id, |
| shm_offset, out_buffer); |
| case GL_INT: |
| return GetPathNameDataImpl<GLint>(num_paths, path_base, shm_id, |
| shm_offset, out_buffer); |
| case GL_UNSIGNED_INT: |
| return GetPathNameDataImpl<GLuint>(num_paths, path_base, shm_id, |
| shm_offset, out_buffer); |
| default: |
| break; |
| } |
| NOTREACHED(); |
| error_ = error::kOutOfBounds; |
| return false; |
| } |
| template <typename Cmd> |
| bool GetTransforms(const Cmd& cmd, |
| GLuint num_paths, |
| GLenum transform_type, |
| const GLfloat** out_transforms) { |
| if (transform_type == GL_NONE) { |
| *out_transforms = nullptr; |
| return true; |
| } |
| uint32_t transforms_shm_id = |
| static_cast<uint32_t>(cmd.transformValues_shm_id); |
| uint32_t transforms_shm_offset = |
| static_cast<uint32_t>(cmd.transformValues_shm_offset); |
| uint32_t transforms_component_count = |
| GLES2Util::GetComponentCountForGLTransformType(transform_type); |
| // Below multiplication will not overflow. |
| DCHECK_LE(transforms_component_count, 12U); |
| uint32_t one_transform_size = sizeof(GLfloat) * transforms_component_count; |
| uint32_t transforms_size = 0; |
| if (!SafeMultiplyUint32(one_transform_size, num_paths, &transforms_size)) { |
| error_ = error::kOutOfBounds; |
| return false; |
| } |
| const GLfloat* transforms = nullptr; |
| if (transforms_shm_id != 0 || transforms_shm_offset != 0) |
| transforms = decoder_->GetSharedMemoryAs<const GLfloat*>( |
| transforms_shm_id, transforms_shm_offset, transforms_size); |
| if (!transforms) { |
| error_ = error::kOutOfBounds; |
| return false; |
| } |
| *out_transforms = transforms; |
| return true; |
| } |
| template <typename Cmd> |
| bool GetCoverMode(const Cmd& cmd, GLenum* out_cover_mode) { |
| GLenum cover_mode = static_cast<GLuint>(cmd.coverMode); |
| if (!validators_->path_instanced_cover_mode.IsValid(cover_mode)) { |
| ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(error_state_, function_name_, |
| cover_mode, "coverMode"); |
| return false; |
| } |
| *out_cover_mode = cover_mode; |
| return true; |
| } |
| |
| private: |
| template <typename T> |
| bool GetPathNameDataImpl(GLuint num_paths, |
| GLuint path_base, |
| uint32_t shm_id, |
| uint32_t shm_offset, |
| std::unique_ptr<GLuint[]>* out_buffer) { |
| uint32_t paths_size = 0; |
| if (!SafeMultiplyUint32(num_paths, sizeof(T), &paths_size)) { |
| error_ = error::kOutOfBounds; |
| return false; |
| } |
| T* paths = decoder_->GetSharedMemoryAs<T*>(shm_id, shm_offset, paths_size); |
| if (!paths) { |
| error_ = error::kOutOfBounds; |
| return false; |
| } |
| std::unique_ptr<GLuint[]> result_paths(new GLuint[num_paths]); |
| bool has_paths = false; |
| for (GLuint i = 0; i < num_paths; ++i) { |
| GLuint service_id = 0; |
| // The below addition is ok even with over- and underflows. |
| // There is no difference if client passes: |
| // * base==4, T=GLbyte, paths[0]==0xfa (-6) |
| // * base==0xffffffff, T=GLuint, paths[0]==0xffffffff |
| // * base==0, T=GLuint, paths[0]==0xfffffffe |
| // For the all the cases, the interpretation is that |
| // client intends to use the path 0xfffffffe. |
| // The client_id verification is only done after the addition. |
| uint32_t client_id = path_base + paths[i]; |
| if (decoder_->path_manager()->GetPath(client_id, &service_id)) |
| has_paths = true; |
| // Will use path 0 if the path is not found. This is in line |
| // of the spec: missing paths will produce nothing, let |
| // the instanced draw continue. |
| result_paths[i] = service_id; |
| } |
| out_buffer->reset(result_paths.release()); |
| |
| return has_paths; |
| } |
| GLES2DecoderImpl* decoder_; |
| ErrorState* error_state_; |
| const Validators* validators_; |
| const char* function_name_; |
| error::Error error_; |
| }; |
| |
| error::Error GLES2DecoderImpl::HandleGenPathsCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::GenPathsCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::GenPathsCHROMIUM*>(cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, "glGenPathsCHROMIUM"); |
| GLsizei range = 0; |
| if (!v.GetPathRange(c, &range)) |
| return v.error(); |
| |
| GLuint first_client_id = static_cast<GLuint>(c.first_client_id); |
| if (first_client_id == 0) |
| return error::kInvalidArguments; |
| |
| if (range == 0) |
| return error::kNoError; |
| |
| if (!GenPathsCHROMIUMHelper(first_client_id, range)) |
| return error::kInvalidArguments; |
| |
| return error::kNoError; |
| } |
| error::Error GLES2DecoderImpl::HandleDeletePathsCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::DeletePathsCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::DeletePathsCHROMIUM*>(cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, "glDeletePathsCHROMIUM"); |
| GLsizei range = 0; |
| if (!v.GetPathRange(c, &range)) |
| return v.error(); |
| |
| if (range == 0) |
| return error::kNoError; |
| |
| GLuint first_client_id = c.first_client_id; |
| // first_client_id can be 0, because non-existing path ids are skipped. |
| |
| if (!DeletePathsCHROMIUMHelper(first_client_id, range)) |
| return error::kInvalidArguments; |
| |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandlePathCommandsCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glPathCommandsCHROMIUM"; |
| const volatile gles2::cmds::PathCommandsCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::PathCommandsCHROMIUM*>(cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "invalid path name"); |
| return error::kNoError; |
| } |
| |
| GLsizei num_commands = static_cast<GLsizei>(c.numCommands); |
| if (num_commands < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "numCommands < 0"); |
| return error::kNoError; |
| } |
| |
| GLsizei num_coords = static_cast<uint32_t>(c.numCoords); |
| if (num_coords < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "numCoords < 0"); |
| return error::kNoError; |
| } |
| |
| GLenum coord_type = static_cast<uint32_t>(c.coordType); |
| if (!validators_->path_coord_type.IsValid(static_cast<GLint>(coord_type))) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, kFunctionName, "invalid coordType"); |
| return error::kNoError; |
| } |
| |
| std::unique_ptr<GLubyte[]> commands; |
| base::CheckedNumeric<GLsizei> num_coords_expected = 0; |
| |
| if (num_commands > 0) { |
| uint32_t commands_shm_id = static_cast<uint32_t>(c.commands_shm_id); |
| uint32_t commands_shm_offset = static_cast<uint32_t>(c.commands_shm_offset); |
| if (commands_shm_id != 0 || commands_shm_offset != 0) { |
| const GLubyte* shared_commands = GetSharedMemoryAs<const GLubyte*>( |
| commands_shm_id, commands_shm_offset, num_commands); |
| if (shared_commands) { |
| commands.reset(new GLubyte[num_commands]); |
| memcpy(commands.get(), shared_commands, num_commands); |
| } |
| } |
| if (!commands) |
| return error::kOutOfBounds; |
| |
| for (GLsizei i = 0; i < num_commands; ++i) { |
| switch (commands[i]) { |
| case GL_CLOSE_PATH_CHROMIUM: |
| // Close has no coords. |
| break; |
| case GL_MOVE_TO_CHROMIUM: |
| // Fallthrough. |
| case GL_LINE_TO_CHROMIUM: |
| num_coords_expected += 2; |
| break; |
| case GL_QUADRATIC_CURVE_TO_CHROMIUM: |
| num_coords_expected += 4; |
| break; |
| case GL_CUBIC_CURVE_TO_CHROMIUM: |
| num_coords_expected += 6; |
| break; |
| case GL_CONIC_CURVE_TO_CHROMIUM: |
| num_coords_expected += 5; |
| break; |
| default: |
| LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, kFunctionName, "invalid command"); |
| return error::kNoError; |
| } |
| } |
| } |
| |
| if (!num_coords_expected.IsValid() || |
| num_coords != num_coords_expected.ValueOrDefault(0)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "numCoords does not match commands"); |
| return error::kNoError; |
| } |
| |
| const void* coords = nullptr; |
| |
| if (num_coords > 0) { |
| uint32_t coords_size = 0; |
| uint32_t coord_type_size = |
| GLES2Util::GetGLTypeSizeForPathCoordType(coord_type); |
| if (!SafeMultiplyUint32(num_coords, coord_type_size, &coords_size)) |
| return error::kOutOfBounds; |
| |
| uint32_t coords_shm_id = static_cast<uint32_t>(c.coords_shm_id); |
| uint32_t coords_shm_offset = static_cast<uint32_t>(c.coords_shm_offset); |
| if (coords_shm_id != 0 || coords_shm_offset != 0) |
| coords = GetSharedMemoryAs<const void*>(coords_shm_id, coords_shm_offset, |
| coords_size); |
| |
| if (!coords) |
| return error::kOutOfBounds; |
| } |
| |
| api()->glPathCommandsNVFn(service_id, num_commands, commands.get(), |
| num_coords, coord_type, coords); |
| |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandlePathParameterfCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glPathParameterfCHROMIUM"; |
| const volatile gles2::cmds::PathParameterfCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::PathParameterfCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "invalid path name"); |
| return error::kNoError; |
| } |
| |
| GLenum pname = static_cast<GLenum>(c.pname); |
| GLfloat value = static_cast<GLfloat>(c.value); |
| bool hasValueError = false; |
| |
| switch (pname) { |
| case GL_PATH_STROKE_WIDTH_CHROMIUM: |
| case GL_PATH_MITER_LIMIT_CHROMIUM: |
| hasValueError = std::isnan(value) || !std::isfinite(value) || value < 0; |
| break; |
| case GL_PATH_STROKE_BOUND_CHROMIUM: |
| value = std::max(std::min(1.0f, value), 0.0f); |
| break; |
| case GL_PATH_END_CAPS_CHROMIUM: |
| hasValueError = !validators_->path_parameter_cap_values.IsValid( |
| static_cast<GLint>(value)); |
| break; |
| case GL_PATH_JOIN_STYLE_CHROMIUM: |
| hasValueError = !validators_->path_parameter_join_values.IsValid( |
| static_cast<GLint>(value)); |
| break; |
| default: |
| DCHECK(!validators_->path_parameter.IsValid(pname)); |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, pname, "pname"); |
| return error::kNoError; |
| } |
| DCHECK(validators_->path_parameter.IsValid(pname)); |
| |
| if (hasValueError) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "value not correct"); |
| return error::kNoError; |
| } |
| |
| api()->glPathParameterfNVFn(service_id, pname, value); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandlePathParameteriCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glPathParameteriCHROMIUM"; |
| const volatile gles2::cmds::PathParameteriCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::PathParameteriCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "invalid path name"); |
| return error::kNoError; |
| } |
| |
| GLenum pname = static_cast<GLenum>(c.pname); |
| GLint value = static_cast<GLint>(c.value); |
| bool hasValueError = false; |
| |
| switch (pname) { |
| case GL_PATH_STROKE_WIDTH_CHROMIUM: |
| case GL_PATH_MITER_LIMIT_CHROMIUM: |
| hasValueError = value < 0; |
| break; |
| case GL_PATH_STROKE_BOUND_CHROMIUM: |
| value = std::max(std::min(1, value), 0); |
| break; |
| case GL_PATH_END_CAPS_CHROMIUM: |
| hasValueError = !validators_->path_parameter_cap_values.IsValid(value); |
| break; |
| case GL_PATH_JOIN_STYLE_CHROMIUM: |
| hasValueError = !validators_->path_parameter_join_values.IsValid(value); |
| break; |
| default: |
| DCHECK(!validators_->path_parameter.IsValid(pname)); |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, pname, "pname"); |
| return error::kNoError; |
| } |
| DCHECK(validators_->path_parameter.IsValid(pname)); |
| |
| if (hasValueError) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "value not correct"); |
| return error::kNoError; |
| } |
| |
| api()->glPathParameteriNVFn(service_id, pname, value); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleStencilFillPathCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glStencilFillPathCHROMIUM"; |
| const volatile gles2::cmds::StencilFillPathCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::StencilFillPathCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLenum fill_mode = GL_COUNT_UP_CHROMIUM; |
| GLuint mask = 0; |
| if (!v.GetFillModeAndMask(c, &fill_mode, &mask)) |
| return v.error(); |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) { |
| // "If /path/ does not name an existing path object, the command does |
| // nothing (and no error is generated)." |
| // This holds for other rendering functions, too. |
| return error::kNoError; |
| } |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glStencilFillPathNVFn(service_id, fill_mode, mask); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleStencilStrokePathCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glStencilStrokePathCHROMIUM"; |
| const volatile gles2::cmds::StencilStrokePathCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::StencilStrokePathCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) { |
| return error::kNoError; |
| } |
| GLint reference = static_cast<GLint>(c.reference); |
| GLuint mask = static_cast<GLuint>(c.mask); |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glStencilStrokePathNVFn(service_id, reference, mask); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCoverFillPathCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glCoverFillPathCHROMIUM"; |
| const volatile gles2::cmds::CoverFillPathCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::CoverFillPathCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLenum cover_mode = GL_BOUNDING_BOX_CHROMIUM; |
| if (!v.GetCoverMode(c, &cover_mode)) |
| return v.error(); |
| |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) |
| return error::kNoError; |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glCoverFillPathNVFn(service_id, cover_mode); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCoverStrokePathCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glCoverStrokePathCHROMIUM"; |
| const volatile gles2::cmds::CoverStrokePathCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::CoverStrokePathCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLenum cover_mode = GL_BOUNDING_BOX_CHROMIUM; |
| if (!v.GetCoverMode(c, &cover_mode)) |
| return v.error(); |
| |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) |
| return error::kNoError; |
| |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glCoverStrokePathNVFn(service_id, cover_mode); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleStencilThenCoverFillPathCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glStencilThenCoverFillPathCHROMIUM"; |
| const volatile gles2::cmds::StencilThenCoverFillPathCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::StencilThenCoverFillPathCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLenum fill_mode = GL_COUNT_UP_CHROMIUM; |
| GLuint mask = 0; |
| GLenum cover_mode = GL_BOUNDING_BOX_CHROMIUM; |
| if (!v.GetFillModeAndMask(c, &fill_mode, &mask) || |
| !v.GetCoverMode(c, &cover_mode)) |
| return v.error(); |
| |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) |
| return error::kNoError; |
| |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glStencilThenCoverFillPathNVFn(service_id, fill_mode, mask, |
| cover_mode); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleStencilThenCoverStrokePathCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glStencilThenCoverStrokePathCHROMIUM"; |
| const volatile gles2::cmds::StencilThenCoverStrokePathCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::StencilThenCoverStrokePathCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLenum cover_mode = GL_BOUNDING_BOX_CHROMIUM; |
| if (!v.GetCoverMode(c, &cover_mode)) |
| return v.error(); |
| |
| GLuint service_id = 0; |
| if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) |
| return error::kNoError; |
| |
| GLint reference = static_cast<GLint>(c.reference); |
| GLuint mask = static_cast<GLuint>(c.mask); |
| |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glStencilThenCoverStrokePathNVFn(service_id, reference, mask, |
| cover_mode); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleStencilFillPathInstancedCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glStencilFillPathInstancedCHROMIUM"; |
| const volatile gles2::cmds::StencilFillPathInstancedCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::StencilFillPathInstancedCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLuint num_paths = 0; |
| GLenum path_name_type = GL_NONE; |
| GLenum fill_mode = GL_COUNT_UP_CHROMIUM; |
| GLuint mask = 0; |
| GLenum transform_type = GL_NONE; |
| if (!v.GetPathCountAndType(c, &num_paths, &path_name_type) || |
| !v.GetFillModeAndMask(c, &fill_mode, &mask) || |
| !v.GetTransformType(c, &transform_type)) |
| return v.error(); |
| |
| if (num_paths == 0) |
| return error::kNoError; |
| |
| std::unique_ptr<GLuint[]> paths; |
| if (!v.GetPathNameData(c, num_paths, path_name_type, &paths)) |
| return v.error(); |
| |
| const GLfloat* transforms = nullptr; |
| if (!v.GetTransforms(c, num_paths, transform_type, &transforms)) |
| return v.error(); |
| |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glStencilFillPathInstancedNVFn(num_paths, GL_UNSIGNED_INT, paths.get(), |
| 0, fill_mode, mask, transform_type, |
| transforms); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleStencilStrokePathInstancedCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glStencilStrokePathInstancedCHROMIUM"; |
| const volatile gles2::cmds::StencilStrokePathInstancedCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::StencilStrokePathInstancedCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLuint num_paths = 0; |
| GLenum path_name_type = GL_NONE; |
| GLenum transform_type = GL_NONE; |
| if (!v.GetPathCountAndType(c, &num_paths, &path_name_type) || |
| !v.GetTransformType(c, &transform_type)) |
| return v.error(); |
| |
| if (num_paths == 0) |
| return error::kNoError; |
| |
| std::unique_ptr<GLuint[]> paths; |
| if (!v.GetPathNameData(c, num_paths, path_name_type, &paths)) |
| return v.error(); |
| |
| const GLfloat* transforms = nullptr; |
| if (!v.GetTransforms(c, num_paths, transform_type, &transforms)) |
| return v.error(); |
| |
| GLint reference = static_cast<GLint>(c.reference); |
| GLuint mask = static_cast<GLuint>(c.mask); |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glStencilStrokePathInstancedNVFn(num_paths, GL_UNSIGNED_INT, |
| paths.get(), 0, reference, mask, |
| transform_type, transforms); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCoverFillPathInstancedCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glCoverFillPathInstancedCHROMIUM"; |
| const volatile gles2::cmds::CoverFillPathInstancedCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::CoverFillPathInstancedCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLuint num_paths = 0; |
| GLenum path_name_type = GL_NONE; |
| GLenum cover_mode = GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM; |
| GLenum transform_type = GL_NONE; |
| if (!v.GetPathCountAndType(c, &num_paths, &path_name_type) || |
| !v.GetCoverMode(c, &cover_mode) || |
| !v.GetTransformType(c, &transform_type)) |
| return v.error(); |
| |
| if (num_paths == 0) |
| return error::kNoError; |
| |
| std::unique_ptr<GLuint[]> paths; |
| if (!v.GetPathNameData(c, num_paths, path_name_type, &paths)) |
| return v.error(); |
| |
| const GLfloat* transforms = nullptr; |
| if (!v.GetTransforms(c, num_paths, transform_type, &transforms)) |
| return v.error(); |
| |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glCoverFillPathInstancedNVFn(num_paths, GL_UNSIGNED_INT, paths.get(), |
| 0, cover_mode, transform_type, |
| transforms); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleCoverStrokePathInstancedCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glCoverStrokePathInstancedCHROMIUM"; |
| const volatile gles2::cmds::CoverStrokePathInstancedCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::CoverStrokePathInstancedCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLuint num_paths = 0; |
| GLenum path_name_type = GL_NONE; |
| GLenum cover_mode = GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM; |
| GLenum transform_type = GL_NONE; |
| if (!v.GetPathCountAndType(c, &num_paths, &path_name_type) || |
| !v.GetCoverMode(c, &cover_mode) || |
| !v.GetTransformType(c, &transform_type)) |
| return v.error(); |
| |
| if (num_paths == 0) |
| return error::kNoError; |
| |
| std::unique_ptr<GLuint[]> paths; |
| if (!v.GetPathNameData(c, num_paths, path_name_type, &paths)) |
| return v.error(); |
| |
| const GLfloat* transforms = nullptr; |
| if (!v.GetTransforms(c, num_paths, transform_type, &transforms)) |
| return v.error(); |
| |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glCoverStrokePathInstancedNVFn(num_paths, GL_UNSIGNED_INT, paths.get(), |
| 0, cover_mode, transform_type, |
| transforms); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleStencilThenCoverFillPathInstancedCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = |
| "glStencilThenCoverFillPathInstancedCHROMIUM"; |
| const volatile gles2::cmds::StencilThenCoverFillPathInstancedCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds:: |
| StencilThenCoverFillPathInstancedCHROMIUM*>(cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| PathCommandValidatorContext v(this, kFunctionName); |
| |
| GLuint num_paths = 0; |
| GLenum path_name_type = GL_NONE; |
| GLenum fill_mode = GL_COUNT_UP_CHROMIUM; |
| GLuint mask = 0; |
| GLenum cover_mode = GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM; |
| GLenum transform_type = GL_NONE; |
| if (!v.GetPathCountAndType(c, &num_paths, &path_name_type) || |
| !v.GetFillModeAndMask(c, &fill_mode, &mask) || |
| !v.GetCoverMode(c, &cover_mode) || |
| !v.GetTransformType(c, &transform_type)) |
| return v.error(); |
| |
| if (num_paths == 0) |
| return error::kNoError; |
| |
| std::unique_ptr<GLuint[]> paths; |
| if (!v.GetPathNameData(c, num_paths, path_name_type, &paths)) |
| return v.error(); |
| |
| const GLfloat* transforms = nullptr; |
| if (!v.GetTransforms(c, num_paths, transform_type, &transforms)) |
| return v.error(); |
| |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glStencilThenCoverFillPathInstancedNVFn( |
| num_paths, GL_UNSIGNED_INT, paths.get(), 0, fill_mode, mask, cover_mode, |
| transform_type, transforms); |
| return error::kNoError; |
| } |
| |
| error::Error |
| GLES2DecoderImpl::HandleStencilThenCoverStrokePathInstancedCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = |
| "glStencilThenCoverStrokeInstancedCHROMIUM"; |
| const volatile gles2::cmds::StencilThenCoverStrokePathInstancedCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds:: |
| StencilThenCoverStrokePathInstancedCHROMIUM*>(cmd_data); |
| if (!features().chromium_path_rendering) |
| return error::kUnknownCommand; |
| PathCommandValidatorContext v(this, kFunctionName); |
| GLuint num_paths = 0; |
| GLenum path_name_type = GL_NONE; |
| GLenum cover_mode = GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM; |
| GLenum transform_type = GL_NONE; |
| if (!v.GetPathCountAndType(c, &num_paths, &path_name_type) || |
| !v.GetCoverMode(c, &cover_mode) || |
| !v.GetTransformType(c, &transform_type)) |
| return v.error(); |
| |
| if (num_paths == 0) |
| return error::kNoError; |
| |
| std::unique_ptr<GLuint[]> paths; |
| if (!v.GetPathNameData(c, num_paths, path_name_type, &paths)) |
| return v.error(); |
| |
| const GLfloat* transforms = nullptr; |
| if (!v.GetTransforms(c, num_paths, transform_type, &transforms)) |
| return v.error(); |
| |
| GLint reference = static_cast<GLint>(c.reference); |
| GLuint mask = static_cast<GLuint>(c.mask); |
| |
| if (!CheckBoundDrawFramebufferValid(kFunctionName)) |
| return error::kNoError; |
| ApplyDirtyState(); |
| api()->glStencilThenCoverStrokePathInstancedNVFn( |
| num_paths, GL_UNSIGNED_INT, paths.get(), 0, reference, mask, cover_mode, |
| transform_type, transforms); |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderImpl::DoBindFragmentInputLocationCHROMIUM( |
| GLuint program_id, |
| GLint location, |
| const std::string& name) { |
| static const char kFunctionName[] = "glBindFragmentInputLocationCHROMIUM"; |
| if (!StringIsValidForGLES(name)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "invalid character"); |
| return; |
| } |
| if (ProgramManager::HasBuiltInPrefix(name)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "reserved prefix"); |
| return; |
| } |
| Program* program = GetProgram(program_id); |
| if (!program || program->IsDeleted()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "invalid program"); |
| return; |
| } |
| if (location < 0 || |
| static_cast<uint32_t>(location) >= group_->max_varying_vectors() * 4) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, |
| "location out of range"); |
| return; |
| } |
| |
| program->SetFragmentInputLocationBinding(name, location); |
| } |
| |
| const SamplerState& GLES2DecoderImpl::GetSamplerStateForTextureUnit( |
| GLenum target, GLuint unit) { |
| if (features().enable_samplers) { |
| Sampler* sampler = state_.sampler_units[unit].get(); |
| if (sampler) |
| return sampler->sampler_state(); |
| } |
| TextureUnit& texture_unit = state_.texture_units[unit]; |
| TextureRef* texture_ref = texture_unit.GetInfoForSamplerType(target); |
| if (texture_ref) |
| return texture_ref->texture()->sampler_state(); |
| |
| return default_sampler_state_; |
| } |
| |
| bool GLES2DecoderImpl::NeedsCopyTextureImageWorkaround( |
| GLenum internal_format, |
| int32_t channels_exist, |
| GLuint* source_texture_service_id, |
| GLenum* source_texture_target) { |
| // On some OSX devices, copyTexImage2D will fail if all of these conditions |
| // are met: |
| // 1. The internal format of the new texture is not GL_RGB or GL_RGBA. |
| // 2. The image of the read FBO is backed by an IOSurface. |
| // See https://crbug.com/581777#c4 for more details. |
| if (!workarounds().use_intermediary_for_copy_texture_image) |
| return false; |
| |
| if (internal_format == GL_RGB || internal_format == GL_RGBA) |
| return false; |
| |
| Framebuffer* framebuffer = GetBoundReadFramebuffer(); |
| if (!framebuffer) |
| return false; |
| |
| const Framebuffer::Attachment* attachment = |
| framebuffer->GetReadBufferAttachment(); |
| if (!attachment) |
| return false; |
| |
| if (!attachment->IsTextureAttachment()) |
| return false; |
| |
| TextureRef* texture = |
| texture_manager()->GetTexture(attachment->object_name()); |
| if (!texture->texture()->HasImages()) |
| return false; |
| |
| // The workaround only works if the source texture consists of the channels |
| // kRGB or kRGBA. |
| if (channels_exist != GLES2Util::kRGBA && channels_exist != GLES2Util::kRGB) |
| return false; |
| |
| *source_texture_target = texture->texture()->target(); |
| *source_texture_service_id = texture->service_id(); |
| return true; |
| } |
| |
| bool GLES2DecoderImpl::ChromiumImageNeedsRGBEmulation() { |
| gpu::ImageFactory* factory = GetContextGroup()->image_factory(); |
| return factory ? !factory->SupportsFormatRGB() : false; |
| } |
| |
| void GLES2DecoderImpl::ClearScheduleCALayerState() { |
| ca_layer_shared_state_.reset(); |
| } |
| |
| void GLES2DecoderImpl::ClearScheduleDCLayerState() { |
| dc_layer_shared_state_.reset(); |
| } |
| |
| void GLES2DecoderImpl::ClearFramebufferForWorkaround(GLbitfield mask) { |
| ScopedGLErrorSuppressor suppressor("GLES2DecoderImpl::ClearWorkaround", |
| GetErrorState()); |
| clear_framebuffer_blit_->ClearFramebuffer( |
| this, gfx::Size(viewport_max_width_, viewport_max_height_), mask, |
| state_.color_clear_red, state_.color_clear_green, state_.color_clear_blue, |
| state_.color_clear_alpha, state_.depth_clear, state_.stencil_clear); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleBindFragmentInputLocationCHROMIUMBucket( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::BindFragmentInputLocationCHROMIUMBucket& c = |
| *static_cast< |
| const volatile gles2::cmds::BindFragmentInputLocationCHROMIUMBucket*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) { |
| return error::kUnknownCommand; |
| } |
| |
| GLuint program = static_cast<GLuint>(c.program); |
| GLint location = static_cast<GLint>(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; |
| } |
| DoBindFragmentInputLocationCHROMIUM(program, location, name_str); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleProgramPathFragmentInputGenCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| static const char kFunctionName[] = "glProgramPathFragmentInputGenCHROMIUM"; |
| const volatile gles2::cmds::ProgramPathFragmentInputGenCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::ProgramPathFragmentInputGenCHROMIUM*>( |
| cmd_data); |
| if (!features().chromium_path_rendering) { |
| return error::kUnknownCommand; |
| } |
| |
| GLint program_id = static_cast<GLint>(c.program); |
| |
| Program* program = GetProgram(program_id); |
| if (!program || !program->IsValid() || program->IsDeleted()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "invalid program"); |
| return error::kNoError; |
| } |
| |
| GLenum gen_mode = static_cast<GLint>(c.genMode); |
| if (!validators_->path_fragment_input_gen_mode.IsValid(gen_mode)) { |
| LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, gen_mode, "genMode"); |
| return error::kNoError; |
| } |
| |
| GLint components = static_cast<GLint>(c.components); |
| if (components < 0 || components > 4) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, |
| "components out of range"); |
| return error::kNoError; |
| } |
| |
| if ((components != 0 && gen_mode == GL_NONE) || |
| (components == 0 && gen_mode != GL_NONE)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, |
| "components and genMode do not match"); |
| return error::kNoError; |
| } |
| |
| GLint location = static_cast<GLint>(c.location); |
| if (program->IsInactiveFragmentInputLocationByFakeLocation(location)) |
| return error::kNoError; |
| |
| const Program::FragmentInputInfo* fragment_input_info = |
| program->GetFragmentInputInfoByFakeLocation(location); |
| if (!fragment_input_info) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "unknown location"); |
| return error::kNoError; |
| } |
| GLint real_location = fragment_input_info->location; |
| |
| const GLfloat* coeffs = nullptr; |
| |
| if (components > 0) { |
| GLint components_needed = -1; |
| |
| switch (fragment_input_info->type) { |
| case GL_FLOAT: |
| components_needed = 1; |
| break; |
| case GL_FLOAT_VEC2: |
| components_needed = 2; |
| break; |
| case GL_FLOAT_VEC3: |
| components_needed = 3; |
| break; |
| case GL_FLOAT_VEC4: |
| components_needed = 4; |
| break; |
| default: |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "fragment input type is not single-precision " |
| "floating-point scalar or vector"); |
| return error::kNoError; |
| } |
| |
| if (components_needed != components) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "components does not match fragment input type"); |
| return error::kNoError; |
| } |
| uint32_t coeffs_per_component = |
| GLES2Util::GetCoefficientCountForGLPathFragmentInputGenMode(gen_mode); |
| // The multiplication below will not overflow. |
| DCHECK(coeffs_per_component > 0 && coeffs_per_component <= 4); |
| DCHECK(components > 0 && components <= 4); |
| uint32_t coeffs_size = sizeof(GLfloat) * coeffs_per_component * components; |
| |
| uint32_t coeffs_shm_id = static_cast<uint32_t>(c.coeffs_shm_id); |
| uint32_t coeffs_shm_offset = static_cast<uint32_t>(c.coeffs_shm_offset); |
| |
| if (coeffs_shm_id != 0 || coeffs_shm_offset != 0) { |
| coeffs = GetSharedMemoryAs<const GLfloat*>( |
| coeffs_shm_id, coeffs_shm_offset, coeffs_size); |
| } |
| |
| if (!coeffs) { |
| return error::kOutOfBounds; |
| } |
| } |
| api()->glProgramPathFragmentInputGenNVFn(program->service_id(), real_location, |
| gen_mode, components, coeffs); |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderImpl::RestoreAllExternalTextureBindingsIfNeeded() { |
| if (texture_manager()->GetServiceIdGeneration() == |
| texture_manager_service_id_generation_) |
| return; |
| |
| // Texture manager's version has changed, so rebind all external textures |
| // in case their service ids have changed. |
| for (unsigned texture_unit_index = 0; |
| texture_unit_index < state_.texture_units.size(); texture_unit_index++) { |
| TextureUnit& texture_unit = state_.texture_units[texture_unit_index]; |
| if (texture_unit.bind_target != GL_TEXTURE_EXTERNAL_OES) |
| continue; |
| |
| if (TextureRef* texture_ref = |
| texture_unit.bound_texture_external_oes.get()) { |
| api()->glActiveTextureFn(GL_TEXTURE0 + texture_unit_index); |
| api()->glBindTextureFn(GL_TEXTURE_EXTERNAL_OES, |
| texture_ref->service_id()); |
| } |
| } |
| |
| api()->glActiveTextureFn(GL_TEXTURE0 + state_.active_texture_unit); |
| |
| texture_manager_service_id_generation_ = |
| texture_manager()->GetServiceIdGeneration(); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleInitializeDiscardableTextureCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::InitializeDiscardableTextureCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::InitializeDiscardableTextureCHROMIUM*>( |
| cmd_data); |
| GLuint texture_id = c.texture_id; |
| uint32_t shm_id = c.shm_id; |
| uint32_t shm_offset = c.shm_offset; |
| |
| TextureRef* texture = texture_manager()->GetTexture(texture_id); |
| if (!texture) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, |
| "glInitializeDiscardableTextureCHROMIUM", |
| "Invalid texture ID"); |
| return error::kNoError; |
| } |
| scoped_refptr<gpu::Buffer> buffer = GetSharedMemoryBuffer(shm_id); |
| if (!DiscardableHandleBase::ValidateParameters(buffer.get(), shm_offset)) |
| return error::kInvalidArguments; |
| |
| size_t size = texture->texture()->estimated_size(); |
| ServiceDiscardableHandle handle(std::move(buffer), shm_offset, shm_id); |
| GetContextGroup()->discardable_manager()->InsertLockedTexture( |
| texture_id, size, group_->texture_manager(), std::move(handle)); |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleUnlockDiscardableTextureCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::UnlockDiscardableTextureCHROMIUM& c = |
| *static_cast< |
| const volatile gles2::cmds::UnlockDiscardableTextureCHROMIUM*>( |
| cmd_data); |
| GLuint texture_id = c.texture_id; |
| ServiceDiscardableManager* discardable_manager = |
| GetContextGroup()->discardable_manager(); |
| TextureRef* texture_to_unbind; |
| if (!discardable_manager->UnlockTexture(texture_id, group_->texture_manager(), |
| &texture_to_unbind)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glUnlockDiscardableTextureCHROMIUM", |
| "Texture ID not initialized"); |
| } |
| if (texture_to_unbind) |
| UnbindTexture(texture_to_unbind, SupportsSeparateFramebufferBinds()); |
| |
| return error::kNoError; |
| } |
| |
| error::Error GLES2DecoderImpl::HandleLockDiscardableTextureCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile gles2::cmds::LockDiscardableTextureCHROMIUM& c = |
| *static_cast<const volatile gles2::cmds::LockDiscardableTextureCHROMIUM*>( |
| cmd_data); |
| GLuint texture_id = c.texture_id; |
| if (!GetContextGroup()->discardable_manager()->LockTexture( |
| texture_id, group_->texture_manager())) { |
| // Temporarily log a crash dump for debugging crbug.com/870317. |
| base::debug::DumpWithoutCrashing(); |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glLockDiscardableTextureCHROMIUM", |
| "Texture ID not initialized"); |
| } |
| return error::kNoError; |
| } |
| |
| |
| scoped_refptr<gpu::Buffer> GLES2DecoderImpl::GetShmBuffer(uint32_t shm_id) { |
| return GetSharedMemoryBuffer(shm_id); |
| } |
| |
| void GLES2DecoderImpl::DoWindowRectanglesEXT(GLenum mode, |
| GLsizei n, |
| const volatile GLint* box) { |
| std::vector<GLint> box_copy(box, box + (n * 4)); |
| if (static_cast<size_t>(n) > state_.GetMaxWindowRectangles()) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glWindowRectanglesEXT", |
| "count > GL_MAX_WINDOW_RECTANGLES_EXT"); |
| return; |
| } |
| for (int i = 0; i < n; ++i) { |
| int boxindex = i * 4; |
| if (box_copy[boxindex + 2] < 0 || box_copy[boxindex + 3] < 0) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glWindowRectanglesEXT", |
| "negative box width or height"); |
| return; |
| } |
| } |
| state_.SetWindowRectangles(mode, n, box_copy.data()); |
| state_.UpdateWindowRectangles(); |
| } |
| |
| void GLES2DecoderImpl::DoUnpremultiplyAndDitherCopyCHROMIUM(GLuint source_id, |
| GLuint dest_id, |
| GLint x, |
| GLint y, |
| GLsizei width, |
| GLsizei height) { |
| TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoUnpremultiplyAndDitherCopyCHROMIUM"); |
| static const char kFunctionName[] = "glUnpremultiplyAndDitherCopyCHROMIUM"; |
| |
| // Do basic validation of our params. Because we don't rely on the caller to |
| // provide the targets / formats of src / dst, we read them here before |
| // forwarding to CopySubTextureHelper. This extension always deals with level |
| // 0. |
| const GLint kLevel = 0; |
| |
| TextureRef* source_texture_ref = GetTexture(source_id); |
| TextureRef* dest_texture_ref = GetTexture(dest_id); |
| if (!source_texture_ref || !dest_texture_ref) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "unknown texture id"); |
| return; |
| } |
| |
| Texture* source_texture = source_texture_ref->texture(); |
| GLenum source_target = source_texture->target(); |
| Texture* dest_texture = dest_texture_ref->texture(); |
| GLenum dest_target = dest_texture->target(); |
| if ((source_target != GL_TEXTURE_2D && |
| source_target != GL_TEXTURE_RECTANGLE_ARB && |
| source_target != GL_TEXTURE_EXTERNAL_OES) || |
| (dest_target != GL_TEXTURE_2D && |
| dest_target != GL_TEXTURE_RECTANGLE_ARB)) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, |
| "invalid texture target"); |
| return; |
| } |
| |
| GLenum source_type = 0; |
| GLenum source_internal_format = 0; |
| source_texture->GetLevelType(source_target, kLevel, &source_type, |
| &source_internal_format); |
| |
| GLenum dest_type = 0; |
| GLenum dest_internal_format = 0; |
| dest_texture->GetLevelType(dest_target, kLevel, &dest_type, |
| &dest_internal_format); |
| GLenum format = |
| TextureManager::ExtractFormatFromStorageFormat(dest_internal_format); |
| |
| if (format != GL_BGRA && format != GL_RGBA) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "invalid format"); |
| return; |
| } |
| |
| if (dest_type != GL_UNSIGNED_SHORT_4_4_4_4) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, |
| "invalid destination type"); |
| return; |
| } |
| |
| CopySubTextureHelper( |
| kFunctionName, source_id, kLevel, dest_target, dest_id, kLevel, x, y, x, |
| y, width, height, GL_FALSE /* unpack_flip_y */, |
| GL_FALSE /* unpack_premultiply_alpha */, |
| GL_TRUE /* unpack_unmultiply_alpha */, GL_TRUE /* dither */); |
| } |
| |
| std::unique_ptr<AbstractTexture> GLES2DecoderImpl::CreateAbstractTexture( |
| GLenum target, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type) { |
| GLuint service_id = 0; |
| api()->glGenTexturesFn(1, &service_id); |
| scoped_refptr<gpu::gles2::TextureRef> texture_ref = |
| TextureRef::Create(texture_manager(), 0, service_id); |
| texture_manager()->SetTarget(texture_ref.get(), target); |
| const GLint level = 0; |
| // Mark the texture as "not cleared". |
| gfx::Rect cleared_rect = gfx::Rect(); |
| texture_manager()->SetLevelInfo(texture_ref.get(), target, level, |
| internal_format, width, height, depth, border, |
| format, type, cleared_rect); |
| |
| // Unretained is safe, because of the destruction cb. |
| std::unique_ptr<ValidatingAbstractTextureImpl> abstract_texture = |
| std::make_unique<ValidatingAbstractTextureImpl>( |
| std::move(texture_ref), this, |
| base::BindOnce(&GLES2DecoderImpl::OnAbstractTextureDestroyed, |
| base::Unretained(this))); |
| abstract_textures_.insert(abstract_texture.get()); |
| |
| return abstract_texture; |
| } |
| |
| void GLES2DecoderImpl::OnAbstractTextureDestroyed( |
| ValidatingAbstractTextureImpl* abstract_texture, |
| scoped_refptr<TextureRef> texture_ref) { |
| DCHECK(texture_ref); |
| abstract_textures_.erase(abstract_texture); |
| // Keep |texture_ref| until we have a current context to destroy it, unless |
| // the context is current. In that case, clear everything that's pending |
| // destruction and let |texture_ref| go out of scope. |
| // TODO(liberato): Consider moving this to the context group, so that any |
| // context in our group can delete textures sooner. |
| if (context_->IsCurrent(nullptr)) |
| texture_refs_pending_destruction_.clear(); |
| else |
| texture_refs_pending_destruction_.insert(std::move(texture_ref)); |
| } |
| |
| void GLES2DecoderImpl::DoSetReadbackBufferShadowAllocationINTERNAL( |
| GLuint buffer_id, |
| GLuint shm_id, |
| GLuint shm_offset, |
| GLuint size) { |
| static const char kFunctionName[] = "glSetBufferShadowAllocationINTERNAL"; |
| scoped_refptr<Buffer> buffer = buffer_manager()->GetBuffer(buffer_id); |
| if (!buffer) { |
| LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName, "unknown buffer"); |
| return; |
| } |
| if (static_cast<GLsizeiptr>(size) != buffer->size()) { |
| MarkContextLost(error::kGuilty); |
| group_->LoseContexts(error::kUnknown); |
| return; |
| } |
| |
| scoped_refptr<gpu::Buffer> shm = GetSharedMemoryBuffer(shm_id); |
| buffer->SetReadbackShadowAllocation(shm, shm_offset); |
| // All buffers in writes_submitted_but_not_completed_ should have shm |
| // allocations. |
| writes_submitted_but_not_completed_.insert(buffer); |
| } |
| |
| void GLES2DecoderImpl::CompileShaderAndExitCommandProcessingEarly( |
| Shader* shader) { |
| // No need to call DoCompile or exit command processing early if the call |
| // to DoCompile will be a no-op. |
| if (!shader->CanCompile()) |
| return; |
| |
| shader->DoCompile(); |
| |
| // Shader compilation can be very slow (see https://crbug.com/844780), Exit |
| // command processing to allow for context preemption and GPU watchdog |
| // checks. |
| ExitCommandProcessingEarly(); |
| } |
| |
| void GLES2DecoderImpl::ReportProgress() { |
| if (group_) |
| group_->ReportProgress(); |
| } |
| |
| error::Error GLES2DecoderImpl::HandleSetActiveURLCHROMIUM( |
| uint32_t immediate_data_size, |
| const volatile void* cmd_data) { |
| const volatile cmds::SetActiveURLCHROMIUM& c = |
| *static_cast<const volatile cmds::SetActiveURLCHROMIUM*>(cmd_data); |
| Bucket* url_bucket = GetBucket(c.url_bucket_id); |
| static constexpr size_t kMaxStrLen = 1024; |
| if (!url_bucket || url_bucket->size() == 0 || |
| url_bucket->size() > kMaxStrLen) { |
| return error::kInvalidArguments; |
| } |
| |
| size_t size = url_bucket->size(); |
| const char* url_str = url_bucket->GetDataAs<const char*>(0, size); |
| if (!url_str) |
| return error::kInvalidArguments; |
| |
| GURL url(base::StringPiece(url_str, size)); |
| client_->SetActiveURL(std::move(url)); |
| return error::kNoError; |
| } |
| |
| // Include the auto-generated part of this file. We split this because it means |
| // we can easily edit the non-auto generated parts right here in this file |
| // instead of having to edit some template or the code generator. |
| #include "base/macros.h" |
| #include "gpu/command_buffer/service/gles2_cmd_decoder_autogen.h" |
| |
| } // namespace gles2 |
| } // namespace gpu |