| // Copyright (c) 2016 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_passthrough.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_split.h" |
| #include "gpu/command_buffer/service/command_buffer_service.h" |
| #include "gpu/command_buffer/service/decoder_client.h" |
| #include "gpu/command_buffer/service/feature_info.h" |
| #include "gpu/command_buffer/service/gl_utils.h" |
| #include "gpu/command_buffer/service/gpu_fence_manager.h" |
| #include "gpu/command_buffer/service/gpu_tracer.h" |
| #include "gpu/command_buffer/service/multi_draw_manager.h" |
| #include "gpu/command_buffer/service/passthrough_discardable_manager.h" |
| #include "gpu/command_buffer/service/program_cache.h" |
| #include "gpu/command_buffer/service/shared_image_representation.h" |
| #include "ui/gl/gl_version_info.h" |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| namespace { |
| template <typename ClientType, typename ServiceType, typename DeleteFunction> |
| void DeleteServiceObjects(ClientServiceMap<ClientType, ServiceType>* id_map, |
| bool have_context, |
| DeleteFunction delete_function) { |
| if (have_context) { |
| id_map->ForEach(delete_function); |
| } |
| |
| id_map->Clear(); |
| } |
| |
| template <typename ClientType, typename ServiceType, typename ResultType> |
| bool GetClientID(const ClientServiceMap<ClientType, ServiceType>* map, |
| ResultType service_id, |
| ResultType* result) { |
| ClientType client_id = 0; |
| if (!map->GetClientID(static_cast<ServiceType>(service_id), &client_id)) { |
| return false; |
| } |
| *result = static_cast<ResultType>(client_id); |
| return true; |
| } |
| |
| void ResizeRenderbuffer(gl::GLApi* api, |
| GLuint renderbuffer, |
| const gfx::Size& size, |
| GLsizei samples, |
| GLenum internal_format, |
| const FeatureInfo* feature_info) { |
| ScopedRenderbufferBindingReset scoped_renderbuffer_reset(api); |
| |
| api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, renderbuffer); |
| if (samples > 0) { |
| DCHECK(feature_info->feature_flags().chromium_framebuffer_multisample); |
| api->glRenderbufferStorageMultisampleFn( |
| GL_RENDERBUFFER, samples, internal_format, size.width(), size.height()); |
| } else { |
| api->glRenderbufferStorageEXTFn(GL_RENDERBUFFER, internal_format, |
| size.width(), size.height()); |
| } |
| } |
| |
| void RequestExtensions(gl::GLApi* api, |
| const gfx::ExtensionSet& requestable_extensions, |
| const char* const* extensions_to_request, |
| size_t count) { |
| for (size_t i = 0; i < count; i++) { |
| if (gfx::HasExtension(requestable_extensions, extensions_to_request[i])) { |
| // Request the intersection of the two sets |
| api->glRequestExtensionANGLEFn(extensions_to_request[i]); |
| } |
| } |
| } |
| |
| void APIENTRY PassthroughGLDebugMessageCallback(GLenum source, |
| GLenum type, |
| GLuint id, |
| GLenum severity, |
| GLsizei length, |
| const GLchar* message, |
| const GLvoid* user_param) { |
| DCHECK(user_param != nullptr); |
| GLES2DecoderPassthroughImpl* command_decoder = |
| static_cast<GLES2DecoderPassthroughImpl*>(const_cast<void*>(user_param)); |
| command_decoder->OnDebugMessage(source, type, id, severity, length, message); |
| LogGLDebugMessage(source, type, id, severity, length, message, |
| command_decoder->GetLogger()); |
| } |
| |
| void RunCallbacks(std::vector<base::OnceClosure> callbacks) { |
| for (base::OnceClosure& callback : callbacks) { |
| std::move(callback).Run(); |
| } |
| } |
| |
| // Converts texture targets to texture binding types. Does not validate the |
| // input. |
| GLenum TextureTargetToTextureType(GLenum texture_target) { |
| switch (texture_target) { |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_X: |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: |
| return GL_TEXTURE_CUBE_MAP; |
| |
| default: |
| return texture_target; |
| } |
| } |
| |
| void UpdateBoundTexturePassthroughSize(gl::GLApi* api, |
| TexturePassthrough* texture) { |
| GLint texture_memory_size = 0; |
| api->glGetTexParameterivFn(texture->target(), GL_MEMORY_SIZE_ANGLE, |
| &texture_memory_size); |
| |
| texture->SetEstimatedSize(texture_memory_size); |
| } |
| |
| } // anonymous namespace |
| |
| GLES2DecoderPassthroughImpl::TexturePendingBinding::TexturePendingBinding( |
| GLenum target, |
| GLuint unit, |
| base::WeakPtr<TexturePassthrough> texture) |
| : target(target), unit(unit), texture(std::move(texture)) {} |
| |
| GLES2DecoderPassthroughImpl::TexturePendingBinding::TexturePendingBinding( |
| const TexturePendingBinding& other) = default; |
| |
| GLES2DecoderPassthroughImpl::TexturePendingBinding::TexturePendingBinding( |
| TexturePendingBinding&& other) = default; |
| |
| GLES2DecoderPassthroughImpl::TexturePendingBinding::~TexturePendingBinding() = |
| default; |
| |
| GLES2DecoderPassthroughImpl::TexturePendingBinding& |
| GLES2DecoderPassthroughImpl::TexturePendingBinding::operator=( |
| const TexturePendingBinding& other) = default; |
| |
| GLES2DecoderPassthroughImpl::TexturePendingBinding& |
| GLES2DecoderPassthroughImpl::TexturePendingBinding::operator=( |
| TexturePendingBinding&& other) = default; |
| |
| PassthroughResources::PassthroughResources() : texture_object_map(nullptr) {} |
| PassthroughResources::~PassthroughResources() = default; |
| |
| void PassthroughResources::DestroyPendingTextures(bool has_context) { |
| if (!has_context) { |
| for (scoped_refptr<TexturePassthrough> iter : |
| textures_pending_destruction) { |
| iter->MarkContextLost(); |
| } |
| } |
| textures_pending_destruction.clear(); |
| } |
| |
| bool PassthroughResources::HasTexturesPendingDestruction() const { |
| return !textures_pending_destruction.empty(); |
| } |
| |
| void PassthroughResources::Destroy(gl::GLApi* api) { |
| bool have_context = !!api; |
| // Only delete textures that are not referenced by a TexturePassthrough |
| // object, they handle their own deletion once all references are lost |
| DeleteServiceObjects(&texture_id_map, have_context, |
| [this, api](GLuint client_id, GLuint texture) { |
| if (!texture_object_map.HasClientID(client_id)) { |
| api->glDeleteTexturesFn(1, &texture); |
| } |
| }); |
| DeleteServiceObjects(&buffer_id_map, have_context, |
| [api](GLuint client_id, GLuint buffer) { |
| api->glDeleteBuffersARBFn(1, &buffer); |
| }); |
| DeleteServiceObjects(&renderbuffer_id_map, have_context, |
| [api](GLuint client_id, GLuint renderbuffer) { |
| api->glDeleteRenderbuffersEXTFn(1, &renderbuffer); |
| }); |
| DeleteServiceObjects(&sampler_id_map, have_context, |
| [api](GLuint client_id, GLuint sampler) { |
| api->glDeleteSamplersFn(1, &sampler); |
| }); |
| DeleteServiceObjects(&program_id_map, have_context, |
| [api](GLuint client_id, GLuint program) { |
| api->glDeleteProgramFn(program); |
| }); |
| DeleteServiceObjects(&shader_id_map, have_context, |
| [api](GLuint client_id, GLuint shader) { |
| api->glDeleteShaderFn(shader); |
| }); |
| DeleteServiceObjects(&sync_id_map, have_context, |
| [api](GLuint client_id, uintptr_t sync) { |
| api->glDeleteSyncFn(reinterpret_cast<GLsync>(sync)); |
| }); |
| |
| if (!have_context) { |
| texture_object_map.ForEach( |
| [](GLuint client_id, scoped_refptr<TexturePassthrough> texture) { |
| texture->MarkContextLost(); |
| }); |
| for (const auto& pair : texture_shared_image_map) { |
| pair.second->OnContextLost(); |
| } |
| } |
| texture_object_map.Clear(); |
| texture_shared_image_map.clear(); |
| DestroyPendingTextures(have_context); |
| } |
| |
| ScopedFramebufferBindingReset::ScopedFramebufferBindingReset( |
| gl::GLApi* api, |
| bool supports_separate_fbo_bindings) |
| : api_(api), |
| supports_separate_fbo_bindings_(supports_separate_fbo_bindings), |
| draw_framebuffer_(0), |
| read_framebuffer_(0) { |
| if (supports_separate_fbo_bindings_) { |
| api_->glGetIntegervFn(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer_); |
| api_->glGetIntegervFn(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer_); |
| } else { |
| api_->glGetIntegervFn(GL_FRAMEBUFFER_BINDING, &draw_framebuffer_); |
| } |
| } |
| |
| ScopedFramebufferBindingReset::~ScopedFramebufferBindingReset() { |
| if (supports_separate_fbo_bindings_) { |
| api_->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, draw_framebuffer_); |
| api_->glBindFramebufferEXTFn(GL_READ_FRAMEBUFFER, read_framebuffer_); |
| } else { |
| api_->glBindFramebufferEXTFn(GL_FRAMEBUFFER, draw_framebuffer_); |
| } |
| } |
| |
| ScopedRenderbufferBindingReset::ScopedRenderbufferBindingReset(gl::GLApi* api) |
| : api_(api), renderbuffer_(0) { |
| api_->glGetIntegervFn(GL_RENDERBUFFER_BINDING, &renderbuffer_); |
| } |
| |
| ScopedRenderbufferBindingReset::~ScopedRenderbufferBindingReset() { |
| api_->glBindRenderbufferEXTFn(GL_RENDERBUFFER, renderbuffer_); |
| } |
| |
| ScopedTexture2DBindingReset::ScopedTexture2DBindingReset(gl::GLApi* api) |
| : api_(api), texture_(0) { |
| api_->glGetIntegervFn(GL_TEXTURE_2D_BINDING_EXT, &texture_); |
| } |
| |
| ScopedTexture2DBindingReset::~ScopedTexture2DBindingReset() { |
| api_->glBindTextureFn(GL_TEXTURE_2D, texture_); |
| } |
| |
| GLES2DecoderPassthroughImpl::PendingQuery::PendingQuery() = default; |
| GLES2DecoderPassthroughImpl::PendingQuery::~PendingQuery() { |
| // Run all callbacks when a query is destroyed even if it did not complete. |
| // This avoids leaks due to outstandsing callbacks. |
| RunCallbacks(std::move(callbacks)); |
| } |
| |
| GLES2DecoderPassthroughImpl::PendingQuery::PendingQuery(PendingQuery&&) = |
| default; |
| GLES2DecoderPassthroughImpl::PendingQuery& |
| GLES2DecoderPassthroughImpl::PendingQuery::operator=(PendingQuery&&) = default; |
| |
| GLES2DecoderPassthroughImpl::ActiveQuery::ActiveQuery() = default; |
| GLES2DecoderPassthroughImpl::ActiveQuery::~ActiveQuery() = default; |
| GLES2DecoderPassthroughImpl::ActiveQuery::ActiveQuery(ActiveQuery&&) = default; |
| GLES2DecoderPassthroughImpl::ActiveQuery& |
| GLES2DecoderPassthroughImpl::ActiveQuery::operator=(ActiveQuery&&) = default; |
| |
| GLES2DecoderPassthroughImpl::BoundTexture::BoundTexture() = default; |
| GLES2DecoderPassthroughImpl::BoundTexture::~BoundTexture() = default; |
| GLES2DecoderPassthroughImpl::BoundTexture::BoundTexture(const BoundTexture&) = |
| default; |
| GLES2DecoderPassthroughImpl::BoundTexture::BoundTexture(BoundTexture&&) = |
| default; |
| GLES2DecoderPassthroughImpl::BoundTexture& |
| GLES2DecoderPassthroughImpl::BoundTexture::operator=(const BoundTexture&) = |
| default; |
| GLES2DecoderPassthroughImpl::BoundTexture& |
| GLES2DecoderPassthroughImpl::BoundTexture::operator=(BoundTexture&&) = default; |
| |
| GLES2DecoderPassthroughImpl::PendingReadPixels::PendingReadPixels() = default; |
| GLES2DecoderPassthroughImpl::PendingReadPixels::~PendingReadPixels() = default; |
| GLES2DecoderPassthroughImpl::PendingReadPixels::PendingReadPixels( |
| PendingReadPixels&&) = default; |
| GLES2DecoderPassthroughImpl::PendingReadPixels& |
| GLES2DecoderPassthroughImpl::PendingReadPixels::operator=(PendingReadPixels&&) = |
| default; |
| |
| GLES2DecoderPassthroughImpl::BufferShadowUpdate::BufferShadowUpdate() = default; |
| GLES2DecoderPassthroughImpl::BufferShadowUpdate::~BufferShadowUpdate() = |
| default; |
| GLES2DecoderPassthroughImpl::BufferShadowUpdate::BufferShadowUpdate( |
| BufferShadowUpdate&&) = default; |
| GLES2DecoderPassthroughImpl::BufferShadowUpdate& |
| GLES2DecoderPassthroughImpl::BufferShadowUpdate::operator=( |
| BufferShadowUpdate&&) = default; |
| |
| GLES2DecoderPassthroughImpl::EmulatedColorBuffer::EmulatedColorBuffer( |
| gl::GLApi* api, |
| const EmulatedDefaultFramebufferFormat& format_in) |
| : api(api), format(format_in) { |
| ScopedTexture2DBindingReset scoped_texture_reset(api); |
| |
| GLuint color_buffer_texture = 0; |
| api->glGenTexturesFn(1, &color_buffer_texture); |
| api->glBindTextureFn(GL_TEXTURE_2D, color_buffer_texture); |
| api->glTexParameteriFn(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| api->glTexParameteriFn(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| api->glTexParameteriFn(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| api->glTexParameteriFn(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| texture = new TexturePassthrough(color_buffer_texture, GL_TEXTURE_2D); |
| } |
| |
| GLES2DecoderPassthroughImpl::EmulatedColorBuffer::~EmulatedColorBuffer() = |
| default; |
| |
| void GLES2DecoderPassthroughImpl::EmulatedColorBuffer::Resize( |
| const gfx::Size& new_size) { |
| if (size == new_size) |
| return; |
| size = new_size; |
| |
| ScopedTexture2DBindingReset scoped_texture_reset(api); |
| |
| DCHECK(texture); |
| DCHECK(texture->target() == GL_TEXTURE_2D); |
| |
| api->glBindTextureFn(texture->target(), texture->service_id()); |
| api->glTexImage2DFn(texture->target(), 0, |
| format.color_texture_internal_format, size.width(), |
| size.height(), 0, format.color_texture_format, |
| format.color_texture_type, nullptr); |
| UpdateBoundTexturePassthroughSize(api, texture.get()); |
| } |
| |
| void GLES2DecoderPassthroughImpl::EmulatedColorBuffer::Destroy( |
| bool have_context) { |
| if (!have_context) { |
| texture->MarkContextLost(); |
| } |
| texture = nullptr; |
| } |
| |
| GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer:: |
| EmulatedDefaultFramebuffer( |
| gl::GLApi* api, |
| const EmulatedDefaultFramebufferFormat& format_in, |
| const FeatureInfo* feature_info, |
| bool supports_separate_fbo_bindings_in) |
| : api(api), |
| supports_separate_fbo_bindings(supports_separate_fbo_bindings_in), |
| format(format_in) { |
| ScopedFramebufferBindingReset scoped_fbo_reset( |
| api, supports_separate_fbo_bindings); |
| ScopedRenderbufferBindingReset scoped_renderbuffer_reset(api); |
| |
| api->glGenFramebuffersEXTFn(1, &framebuffer_service_id); |
| api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, framebuffer_service_id); |
| |
| if (format.samples > 0) { |
| api->glGenRenderbuffersEXTFn(1, &color_buffer_service_id); |
| api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, color_buffer_service_id); |
| api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_RENDERBUFFER, |
| color_buffer_service_id); |
| } else { |
| color_texture.reset(new EmulatedColorBuffer(api, format)); |
| api->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_TEXTURE_2D, |
| color_texture->texture->service_id(), 0); |
| } |
| |
| if (format.depth_stencil_internal_format != GL_NONE) { |
| DCHECK(format.depth_internal_format == GL_NONE && |
| format.stencil_internal_format == GL_NONE); |
| api->glGenRenderbuffersEXTFn(1, &depth_stencil_buffer_service_id); |
| api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, |
| depth_stencil_buffer_service_id); |
| if (feature_info->gl_version_info().IsAtLeastGLES(3, 0) || |
| feature_info->feature_flags().angle_webgl_compatibility) { |
| api->glFramebufferRenderbufferEXTFn( |
| GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, |
| depth_stencil_buffer_service_id); |
| } else { |
| api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
| GL_RENDERBUFFER, |
| depth_stencil_buffer_service_id); |
| api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
| GL_RENDERBUFFER, |
| depth_stencil_buffer_service_id); |
| } |
| } else { |
| if (format.depth_internal_format != GL_NONE) { |
| api->glGenRenderbuffersEXTFn(1, &depth_buffer_service_id); |
| api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, depth_buffer_service_id); |
| api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
| GL_RENDERBUFFER, |
| depth_buffer_service_id); |
| } |
| |
| if (format.stencil_internal_format != GL_NONE) { |
| api->glGenRenderbuffersEXTFn(1, &stencil_buffer_service_id); |
| api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, stencil_buffer_service_id); |
| api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
| GL_RENDERBUFFER, |
| stencil_buffer_service_id); |
| } |
| } |
| } |
| |
| GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer:: |
| ~EmulatedDefaultFramebuffer() = default; |
| |
| std::unique_ptr<GLES2DecoderPassthroughImpl::EmulatedColorBuffer> |
| GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer::SetColorBuffer( |
| std::unique_ptr<EmulatedColorBuffer> new_color_buffer) { |
| DCHECK(color_texture != nullptr && new_color_buffer != nullptr); |
| DCHECK(color_texture->size == new_color_buffer->size); |
| std::unique_ptr<EmulatedColorBuffer> old_buffer(std::move(color_texture)); |
| color_texture = std::move(new_color_buffer); |
| |
| // Bind the new texture to this FBO |
| ScopedFramebufferBindingReset scoped_fbo_reset( |
| api, supports_separate_fbo_bindings); |
| api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, framebuffer_service_id); |
| api->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_TEXTURE_2D, |
| color_texture->texture->service_id(), 0); |
| |
| return old_buffer; |
| } |
| |
| void GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer::Blit( |
| EmulatedColorBuffer* target) { |
| DCHECK(target != nullptr); |
| DCHECK(target->size == size); |
| |
| ScopedFramebufferBindingReset scoped_fbo_reset( |
| api, supports_separate_fbo_bindings); |
| |
| api->glBindFramebufferEXTFn(GL_READ_FRAMEBUFFER, framebuffer_service_id); |
| |
| GLuint temp_fbo; |
| api->glGenFramebuffersEXTFn(1, &temp_fbo); |
| api->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, temp_fbo); |
| api->glFramebufferTexture2DEXTFn(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_TEXTURE_2D, target->texture->service_id(), |
| 0); |
| |
| api->glBlitFramebufferFn(0, 0, size.width(), size.height(), 0, 0, |
| target->size.width(), target->size.height(), |
| GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| |
| api->glDeleteFramebuffersEXTFn(1, &temp_fbo); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer::Resize( |
| const gfx::Size& new_size, |
| const FeatureInfo* feature_info) { |
| DCHECK(!new_size.IsEmpty()); |
| if (size == new_size) { |
| return true; |
| } |
| size = new_size; |
| |
| if (color_buffer_service_id != 0) { |
| ResizeRenderbuffer(api, color_buffer_service_id, size, format.samples, |
| format.color_renderbuffer_internal_format, feature_info); |
| } |
| if (color_texture) |
| color_texture->Resize(size); |
| if (depth_stencil_buffer_service_id != 0) { |
| ResizeRenderbuffer(api, depth_stencil_buffer_service_id, size, |
| format.samples, format.depth_stencil_internal_format, |
| feature_info); |
| } |
| if (depth_buffer_service_id != 0) { |
| ResizeRenderbuffer(api, depth_buffer_service_id, size, format.samples, |
| format.depth_internal_format, feature_info); |
| } |
| if (stencil_buffer_service_id != 0) { |
| ResizeRenderbuffer(api, stencil_buffer_service_id, size, format.samples, |
| format.stencil_internal_format, feature_info); |
| } |
| |
| // Check that the framebuffer is complete |
| { |
| ScopedFramebufferBindingReset scoped_fbo_reset( |
| api, supports_separate_fbo_bindings); |
| api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, framebuffer_service_id); |
| if (api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER) != |
| GL_FRAMEBUFFER_COMPLETE) { |
| LOG(ERROR) |
| << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer failed " |
| << "because the resulting framebuffer was not complete."; |
| return false; |
| } |
| } |
| |
| DCHECK(color_texture == nullptr || color_texture->size == size); |
| |
| return true; |
| } |
| |
| void GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer::Destroy( |
| bool have_context) { |
| if (have_context) { |
| api->glDeleteFramebuffersEXTFn(1, &framebuffer_service_id); |
| framebuffer_service_id = 0; |
| |
| api->glDeleteRenderbuffersEXTFn(1, &color_buffer_service_id); |
| color_buffer_service_id = 0; |
| |
| api->glDeleteRenderbuffersEXTFn(1, &depth_stencil_buffer_service_id); |
| color_buffer_service_id = 0; |
| |
| api->glDeleteRenderbuffersEXTFn(1, &depth_buffer_service_id); |
| depth_buffer_service_id = 0; |
| |
| api->glDeleteRenderbuffersEXTFn(1, &stencil_buffer_service_id); |
| stencil_buffer_service_id = 0; |
| } |
| if (color_texture) { |
| color_texture->Destroy(have_context); |
| } |
| } |
| |
| GLES2DecoderPassthroughImpl::GLES2DecoderPassthroughImpl( |
| DecoderClient* client, |
| CommandBufferServiceBase* command_buffer_service, |
| Outputter* outputter, |
| ContextGroup* group) |
| : GLES2Decoder(command_buffer_service, outputter), |
| client_(client), |
| commands_to_process_(0), |
| debug_marker_manager_(), |
| logger_(&debug_marker_manager_, |
| base::BindRepeating(&DecoderClient::OnConsoleMessage, |
| base::Unretained(client_), |
| 0), |
| group->gpu_preferences().disable_gl_error_limit), |
| surface_(), |
| context_(), |
| offscreen_(false), |
| group_(group), |
| feature_info_(new FeatureInfo(group->feature_info()->workarounds(), |
| group->gpu_feature_info())), |
| emulated_back_buffer_(nullptr), |
| offscreen_single_buffer_(false), |
| offscreen_target_buffer_preserved_(false), |
| create_color_buffer_count_for_test_(0), |
| max_2d_texture_size_(0), |
| bound_draw_framebuffer_(0), |
| bound_read_framebuffer_(0), |
| 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), |
| has_robustness_extension_(false), |
| context_lost_(false), |
| reset_by_robustness_extension_(false), |
| lose_context_when_out_of_memory_(false), |
| weak_ptr_factory_(this) { |
| DCHECK(client); |
| DCHECK(group); |
| } |
| |
| GLES2DecoderPassthroughImpl::~GLES2DecoderPassthroughImpl() = default; |
| |
| GLES2Decoder::Error GLES2DecoderPassthroughImpl::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); |
| } |
| } |
| |
| template <bool DebugImpl> |
| GLES2Decoder::Error GLES2DecoderPassthroughImpl::DoCommandsImpl( |
| unsigned int num_commands, |
| const volatile void* buffer, |
| int num_entries, |
| int* 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; |
| } |
| |
| // size can't overflow because it is 21 bits. |
| 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 < base::size(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); |
| } |
| } |
| |
| if (DebugImpl) { |
| VerifyServiceTextureObjectsExist(); |
| } |
| |
| uint32_t immediate_data_size = (arg_count - info_arg_count) * |
| sizeof(CommandBufferEntry); // NOLINT |
| if (info.cmd_handler) { |
| result = (this->*info.cmd_handler)(immediate_data_size, cmd_data); |
| } else { |
| result = error::kUnknownCommand; |
| } |
| |
| if (DebugImpl && doing_gpu_trace) { |
| gpu_tracer_->End(kTraceDecoder); |
| } |
| } else { |
| result = error::kInvalidArguments; |
| } |
| } else { |
| result = DoCommonCommand(command, arg_count, cmd_data); |
| } |
| |
| if (result == error::kNoError && context_lost_) { |
| result = error::kLostContext; |
| } |
| |
| if (result != error::kDeferCommandUntilLater) { |
| process_pos += size; |
| cmd_data += size; |
| } |
| } |
| |
| if (entries_processed) |
| *entries_processed = process_pos; |
| |
| return result; |
| } |
| |
| base::WeakPtr<DecoderContext> GLES2DecoderPassthroughImpl::AsWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| gpu::ContextResult GLES2DecoderPassthroughImpl::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", "GLES2DecoderPassthroughImpl::Initialize"); |
| DCHECK(context->IsCurrent(surface.get())); |
| api_ = gl::g_current_gl_context; |
| // Take ownership of the context and surface. The surface can be replaced |
| // with SetSurface. |
| context_ = context; |
| surface_ = surface; |
| offscreen_ = offscreen; |
| |
| // For WebGL contexts, log GL errors so they appear in devtools. Otherwise |
| // only enable debug logging if requested. |
| bool log_non_errors = |
| group_->gpu_preferences().enable_gpu_driver_debug_logging; |
| InitializeGLDebugLogging(log_non_errors, PassthroughGLDebugMessageCallback, |
| this); |
| |
| // Create GPU Tracer for timing values. |
| gpu_tracer_.reset(new GPUTracer(this)); |
| |
| gpu_fence_manager_.reset(new GpuFenceManager()); |
| |
| multi_draw_manager_.reset( |
| new MultiDrawManager(MultiDrawManager::IndexStorageType::Pointer)); |
| |
| 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; |
| } |
| |
| // Extensions that are enabled via emulation on the client side or needed for |
| // basic command buffer functionality. Make sure they are always enabled. |
| if (IsWebGLContextType(attrib_helper.context_type)) { |
| // Grab the extensions that are requestable |
| gfx::ExtensionSet requestable_extensions( |
| gl::GetRequestableGLExtensionsFromCurrentContext()); |
| |
| static constexpr const char* kRequiredFunctionalityExtensions[] = { |
| "GL_ANGLE_memory_size", "GL_CHROMIUM_bind_uniform_location", |
| "GL_CHROMIUM_sync_query", "GL_EXT_debug_marker", |
| "GL_KHR_debug", "GL_NV_fence", |
| }; |
| RequestExtensions(api(), requestable_extensions, |
| kRequiredFunctionalityExtensions, |
| base::size(kRequiredFunctionalityExtensions)); |
| |
| if (request_optional_extensions_) { |
| static constexpr const char* kOptionalFunctionalityExtensions[] = { |
| "GL_ANGLE_depth_texture", |
| "GL_ANGLE_framebuffer_blit", |
| "GL_ANGLE_framebuffer_multisample", |
| "GL_ANGLE_instanced_arrays", |
| "GL_ANGLE_pack_reverse_row_order", |
| "GL_ANGLE_texture_compression_dxt3", |
| "GL_ANGLE_texture_compression_dxt5", |
| "GL_ANGLE_texture_usage", |
| "GL_ANGLE_translated_shader_source", |
| "GL_CHROMIUM_framebuffer_mixed_samples", |
| "GL_CHROMIUM_path_rendering", |
| "GL_EXT_blend_minmax", |
| "GL_EXT_discard_framebuffer", |
| "GL_EXT_disjoint_timer_query", |
| "GL_EXT_occlusion_query_boolean", |
| "GL_EXT_sRGB", |
| "GL_EXT_sRGB_write_control", |
| "GL_EXT_texture_compression_dxt1", |
| "GL_EXT_texture_compression_s3tc_srgb", |
| "GL_EXT_texture_format_BGRA8888", |
| "GL_EXT_texture_norm16", |
| "GL_EXT_texture_rg", |
| "GL_EXT_texture_sRGB_decode", |
| "GL_EXT_texture_storage", |
| "GL_EXT_unpack_subimage", |
| "GL_KHR_parallel_shader_compile", |
| "GL_KHR_robust_buffer_access_behavior", |
| "GL_KHR_texture_compression_astc_hdr", |
| "GL_KHR_texture_compression_astc_ldr", |
| "GL_NV_pack_subimage", |
| "GL_OES_compressed_ETC1_RGB8_texture", |
| "GL_OES_depth32", |
| "GL_OES_EGL_image", |
| "GL_OES_EGL_image_external", |
| "GL_OES_EGL_image_external_essl3", |
| "GL_OES_fbo_render_mipmap", |
| "GL_OES_packed_depth_stencil", |
| "GL_OES_rgb8_rgba8", |
| "GL_OES_vertex_array_object", |
| "NV_EGL_stream_consumer_external", |
| }; |
| RequestExtensions(api(), requestable_extensions, |
| kOptionalFunctionalityExtensions, |
| base::size(kOptionalFunctionalityExtensions)); |
| } |
| |
| context->ReinitializeDynamicBindings(); |
| } |
| |
| // Each context initializes its own feature info because some extensions may |
| // be enabled dynamically. Don't disallow any features, leave it up to ANGLE |
| // to dynamically enable extensions. |
| feature_info_->Initialize(attrib_helper.context_type, |
| true /* is_passthrough_cmd_decoder */, |
| DisallowedFeatures()); |
| |
| // Check for required extensions |
| // TODO(geofflang): verify |
| // feature_info_->feature_flags().angle_robust_resource_initialization and |
| // api()->glIsEnabledFn(GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE) |
| |
| #define FAIL_INIT_IF_NOT(feature, message) \ |
| if (!(feature)) { \ |
| Destroy(true); \ |
| LOG(ERROR) << "ContextResult::kFatalFailure: " << (message); \ |
| return gpu::ContextResult::kFatalFailure; \ |
| } |
| |
| FAIL_INIT_IF_NOT(feature_info_->feature_flags().angle_robust_client_memory, |
| "missing GL_ANGLE_robust_client_memory"); |
| FAIL_INIT_IF_NOT( |
| feature_info_->feature_flags().chromium_bind_generates_resource, |
| "missing GL_CHROMIUM_bind_generates_resource"); |
| FAIL_INIT_IF_NOT(feature_info_->feature_flags().chromium_copy_texture, |
| "missing GL_CHROMIUM_copy_texture"); |
| FAIL_INIT_IF_NOT(feature_info_->feature_flags().angle_client_arrays, |
| "missing GL_ANGLE_client_arrays"); |
| FAIL_INIT_IF_NOT(api()->glIsEnabledFn(GL_CLIENT_ARRAYS_ANGLE) == GL_FALSE, |
| "GL_ANGLE_client_arrays shouldn't be enabled"); |
| FAIL_INIT_IF_NOT(feature_info_->feature_flags().angle_webgl_compatibility == |
| IsWebGLContextType(attrib_helper.context_type), |
| "missing GL_ANGLE_webgl_compatibility"); |
| FAIL_INIT_IF_NOT(feature_info_->feature_flags().angle_request_extension, |
| "missing GL_ANGLE_request_extension"); |
| FAIL_INIT_IF_NOT(feature_info_->feature_flags().khr_debug, |
| "missing GL_KHR_debug"); |
| FAIL_INIT_IF_NOT( |
| !IsWebGL2ComputeContextType(attrib_helper.context_type) || |
| feature_info_->feature_flags().khr_robust_buffer_access_behavior, |
| "missing GL_KHR_robust_buffer_access_behavior"); |
| FAIL_INIT_IF_NOT(!attrib_helper.enable_oop_rasterization, |
| "oop rasterization not supported"); |
| |
| #undef FAIL_INIT_IF_NOT |
| |
| bind_generates_resource_ = group_->bind_generates_resource(); |
| |
| resources_ = group_->passthrough_resources(); |
| |
| mailbox_manager_ = group_->mailbox_manager(); |
| |
| // Query information about the texture units |
| GLint num_texture_units = 0; |
| api()->glGetIntegervFn(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, |
| &num_texture_units); |
| if (num_texture_units > static_cast<GLint>(kMaxTextureUnits)) { |
| Destroy(true); |
| LOG(ERROR) << "kMaxTextureUnits (" << kMaxTextureUnits |
| << ") must be at least GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (" |
| << num_texture_units << ")."; |
| return gpu::ContextResult::kFatalFailure; |
| } |
| |
| active_texture_unit_ = 0; |
| |
| // Initialize the tracked buffer bindings |
| bound_buffers_[GL_ARRAY_BUFFER] = 0; |
| bound_buffers_[GL_ELEMENT_ARRAY_BUFFER] = 0; |
| if (feature_info_->gl_version_info().IsAtLeastGLES(3, 0) || |
| feature_info_->feature_flags().ext_pixel_buffer_object) { |
| bound_buffers_[GL_PIXEL_PACK_BUFFER] = 0; |
| bound_buffers_[GL_PIXEL_UNPACK_BUFFER] = 0; |
| } |
| if (feature_info_->gl_version_info().IsAtLeastGLES(3, 0)) { |
| bound_buffers_[GL_COPY_READ_BUFFER] = 0; |
| bound_buffers_[GL_COPY_WRITE_BUFFER] = 0; |
| bound_buffers_[GL_TRANSFORM_FEEDBACK_BUFFER] = 0; |
| bound_buffers_[GL_UNIFORM_BUFFER] = 0; |
| } |
| if (feature_info_->gl_version_info().IsAtLeastGLES(3, 1)) { |
| bound_buffers_[GL_ATOMIC_COUNTER_BUFFER] = 0; |
| bound_buffers_[GL_SHADER_STORAGE_BUFFER] = 0; |
| bound_buffers_[GL_DRAW_INDIRECT_BUFFER] = 0; |
| bound_buffers_[GL_DISPATCH_INDIRECT_BUFFER] = 0; |
| } |
| |
| if (feature_info_->feature_flags().chromium_texture_filtering_hint && |
| feature_info_->feature_flags().is_swiftshader) { |
| api()->glHintFn(GL_TEXTURE_FILTERING_HINT_CHROMIUM, GL_NICEST); |
| } |
| |
| has_robustness_extension_ = feature_info_->feature_flags().khr_robustness || |
| feature_info_->feature_flags().ext_robustness; |
| lose_context_when_out_of_memory_ = |
| attrib_helper.lose_context_when_out_of_memory; |
| |
| api()->glGetIntegervFn(GL_MAX_TEXTURE_SIZE, &max_2d_texture_size_); |
| api()->glGetIntegervFn(GL_MAX_RENDERBUFFER_SIZE, &max_renderbuffer_size_); |
| max_offscreen_framebuffer_size_ = |
| std::min(max_2d_texture_size_, max_renderbuffer_size_); |
| |
| if (offscreen_) { |
| offscreen_single_buffer_ = attrib_helper.single_buffer; |
| offscreen_target_buffer_preserved_ = attrib_helper.buffer_preserved; |
| const bool multisampled_framebuffers_supported = |
| feature_info_->feature_flags().chromium_framebuffer_multisample; |
| if (attrib_helper.samples > 0 && attrib_helper.sample_buffers > 0 && |
| multisampled_framebuffers_supported && !offscreen_single_buffer_) { |
| GLint max_sample_count = 0; |
| api()->glGetIntegervFn(GL_MAX_SAMPLES_EXT, &max_sample_count); |
| emulated_default_framebuffer_format_.samples = |
| std::min(attrib_helper.samples, max_sample_count); |
| } |
| |
| const bool rgb8_supported = feature_info_->feature_flags().oes_rgb8_rgba8; |
| const bool alpha_channel_requested = attrib_helper.alpha_size > 0; |
| // 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 && emulated_default_framebuffer_format_.samples > 0) { |
| emulated_default_framebuffer_format_.color_renderbuffer_internal_format = |
| alpha_channel_requested ? GL_RGBA8 : GL_RGB8; |
| } else { |
| emulated_default_framebuffer_format_.samples = 0; |
| } |
| |
| emulated_default_framebuffer_format_.color_texture_internal_format = |
| alpha_channel_requested ? GL_RGBA : GL_RGB; |
| emulated_default_framebuffer_format_.color_texture_format = |
| emulated_default_framebuffer_format_.color_texture_internal_format; |
| emulated_default_framebuffer_format_.color_texture_type = GL_UNSIGNED_BYTE; |
| |
| const bool depth24_stencil8_supported = |
| feature_info_->feature_flags().packed_depth24_stencil8; |
| if ((attrib_helper.depth_size > 0 || attrib_helper.stencil_size > 0) && |
| depth24_stencil8_supported) { |
| emulated_default_framebuffer_format_.depth_stencil_internal_format = |
| GL_DEPTH24_STENCIL8; |
| } else { |
| // It may be the case that this depth/stencil combination is not |
| // supported, but this will be checked later by CheckFramebufferStatus. |
| if (attrib_helper.depth_size > 0) { |
| emulated_default_framebuffer_format_.depth_internal_format = |
| GL_DEPTH_COMPONENT16; |
| } |
| if (attrib_helper.stencil_size > 0) { |
| emulated_default_framebuffer_format_.stencil_internal_format = |
| GL_STENCIL_INDEX8; |
| } |
| } |
| |
| CheckErrorCallbackState(); |
| emulated_back_buffer_ = std::make_unique<EmulatedDefaultFramebuffer>( |
| api(), emulated_default_framebuffer_format_, feature_info_.get(), |
| supports_separate_fbo_bindings_); |
| // Make sure to use a non-empty offscreen surface so that the framebuffer is |
| // complete. |
| gfx::Size initial_size( |
| std::max(1, attrib_helper.offscreen_framebuffer_size.width()), |
| std::max(1, attrib_helper.offscreen_framebuffer_size.height())); |
| if (!emulated_back_buffer_->Resize(initial_size, feature_info_.get())) { |
| bool was_lost = CheckResetStatus(); |
| Destroy(true); |
| LOG(ERROR) << (was_lost ? "ContextResult::kTransientFailure: " |
| : "ContextResult::kFatalFailure: ") |
| << "Resize of emulated back buffer failed"; |
| return was_lost ? gpu::ContextResult::kTransientFailure |
| : gpu::ContextResult::kFatalFailure; |
| } |
| |
| if (CheckErrorCallbackState()) { |
| Destroy(true); |
| // Errors are considered fatal, including OOM. |
| LOG(ERROR) |
| << "ContextResult::kFatalFailure: " |
| "Creation of the offscreen framebuffer failed because errors were " |
| "generated."; |
| return gpu::ContextResult::kFatalFailure; |
| } |
| |
| framebuffer_id_map_.SetIDMapping( |
| 0, emulated_back_buffer_->framebuffer_service_id); |
| |
| // Bind the emulated default framebuffer and initialize the viewport |
| api()->glBindFramebufferEXTFn( |
| GL_FRAMEBUFFER, emulated_back_buffer_->framebuffer_service_id); |
| api()->glViewportFn(0, 0, attrib_helper.offscreen_framebuffer_size.width(), |
| attrib_helper.offscreen_framebuffer_size.height()); |
| } |
| |
| // Initialize the tracked scissor and viewport state and then apply the |
| // surface offsets if needed. |
| api()->glGetIntegervFn(GL_VIEWPORT, viewport_); |
| api()->glGetIntegervFn(GL_SCISSOR_BOX, scissor_); |
| ApplySurfaceDrawOffset(); |
| |
| set_initialized(); |
| return gpu::ContextResult::kSuccess; |
| } |
| |
| void GLES2DecoderPassthroughImpl::Destroy(bool have_context) { |
| if (have_context) { |
| FlushErrors(); |
| } |
| |
| // Destroy all pending read pixels operations |
| for (PendingReadPixels& pending_read_pixels : pending_read_pixels_) { |
| if (have_context) { |
| api()->glDeleteBuffersARBFn(1, &pending_read_pixels.buffer_service_id); |
| } else { |
| pending_read_pixels.fence->Invalidate(); |
| } |
| } |
| pending_read_pixels_.clear(); |
| |
| for (auto& bound_texture_type : bound_textures_) { |
| for (auto& bound_texture : bound_texture_type) { |
| if (!have_context && bound_texture.texture) { |
| bound_texture.texture->MarkContextLost(); |
| } |
| bound_texture.texture = nullptr; |
| } |
| } |
| |
| if (resources_) { // Initialize may not have been called yet. |
| for (PassthroughAbstractTextureImpl* iter : abstract_textures_) { |
| resources_->textures_pending_destruction.insert( |
| iter->OnDecoderWillDestroy()); |
| } |
| abstract_textures_.clear(); |
| if (have_context) { |
| resources_->DestroyPendingTextures(/*has_context=*/true); |
| } |
| } |
| |
| DeleteServiceObjects(&framebuffer_id_map_, have_context, |
| [this](GLuint client_id, GLuint framebuffer) { |
| api()->glDeleteFramebuffersEXTFn(1, &framebuffer); |
| }); |
| DeleteServiceObjects(&transform_feedback_id_map_, have_context, |
| [this](GLuint client_id, GLuint transform_feedback) { |
| api()->glDeleteTransformFeedbacksFn( |
| 1, &transform_feedback); |
| }); |
| DeleteServiceObjects(&query_id_map_, have_context, |
| [this](GLuint client_id, GLuint query) { |
| api()->glDeleteQueriesFn(1, &query); |
| }); |
| DeleteServiceObjects(&vertex_array_id_map_, have_context, |
| [this](GLuint client_id, GLuint vertex_array) { |
| api()->glDeleteVertexArraysOESFn(1, &vertex_array); |
| }); |
| |
| // Destroy the emulated backbuffer |
| if (emulated_back_buffer_) { |
| emulated_back_buffer_->Destroy(have_context); |
| emulated_back_buffer_.reset(); |
| } |
| |
| if (emulated_front_buffer_) { |
| emulated_front_buffer_->Destroy(have_context); |
| emulated_front_buffer_.reset(); |
| } |
| |
| for (auto& in_use_color_texture : in_use_color_textures_) { |
| in_use_color_texture->Destroy(have_context); |
| } |
| in_use_color_textures_.clear(); |
| |
| for (auto& available_color_texture : available_color_textures_) { |
| available_color_texture->Destroy(have_context); |
| } |
| available_color_textures_.clear(); |
| |
| if (gpu_fence_manager_.get()) { |
| gpu_fence_manager_->Destroy(have_context); |
| gpu_fence_manager_.reset(); |
| } |
| |
| // Destroy the GPU Tracer which may own some in process GPU Timings. |
| if (gpu_tracer_) { |
| gpu_tracer_->Destroy(have_context); |
| gpu_tracer_.reset(); |
| } |
| |
| if (multi_draw_manager_.get()) { |
| multi_draw_manager_.reset(); |
| } |
| |
| if (!have_context) { |
| for (auto& fence : deschedule_until_finished_fences_) { |
| fence->Invalidate(); |
| } |
| } |
| deschedule_until_finished_fences_.clear(); |
| |
| // Destroy the surface before the context, some surface destructors make GL |
| // calls. |
| surface_ = nullptr; |
| |
| if (group_) { |
| group_->Destroy(this, have_context); |
| group_ = nullptr; |
| } |
| |
| if (context_.get()) { |
| context_->ReleaseCurrent(nullptr); |
| context_ = nullptr; |
| } |
| } |
| |
| void GLES2DecoderPassthroughImpl::SetSurface( |
| const scoped_refptr<gl::GLSurface>& surface) { |
| DCHECK(context_->IsCurrent(nullptr)); |
| DCHECK(surface_.get()); |
| surface_ = surface; |
| } |
| |
| void GLES2DecoderPassthroughImpl::ReleaseSurface() { |
| if (!context_.get()) |
| return; |
| if (WasContextLost()) { |
| DLOG(ERROR) << " GLES2DecoderImpl: Trying to release lost context."; |
| return; |
| } |
| context_->ReleaseCurrent(surface_.get()); |
| surface_ = nullptr; |
| } |
| |
| void GLES2DecoderPassthroughImpl::TakeFrontBuffer(const Mailbox& mailbox) { |
| if (offscreen_single_buffer_) { |
| DCHECK(emulated_back_buffer_->color_texture != nullptr); |
| mailbox_manager_->ProduceTexture( |
| mailbox, emulated_back_buffer_->color_texture->texture.get()); |
| return; |
| } |
| |
| if (!emulated_front_buffer_) { |
| DLOG(ERROR) << "Called TakeFrontBuffer on a non-offscreen context"; |
| return; |
| } |
| |
| mailbox_manager_->ProduceTexture(mailbox, |
| emulated_front_buffer_->texture.get()); |
| in_use_color_textures_.push_back(std::move(emulated_front_buffer_)); |
| emulated_front_buffer_ = nullptr; |
| |
| if (available_color_textures_.empty()) { |
| // Create a new color texture to use as the front buffer |
| emulated_front_buffer_ = std::make_unique<EmulatedColorBuffer>( |
| api(), emulated_default_framebuffer_format_); |
| emulated_front_buffer_->Resize(emulated_back_buffer_->size); |
| create_color_buffer_count_for_test_++; |
| } else { |
| emulated_front_buffer_ = std::move(available_color_textures_.back()); |
| available_color_textures_.pop_back(); |
| } |
| } |
| |
| void GLES2DecoderPassthroughImpl::ReturnFrontBuffer(const Mailbox& mailbox, |
| bool is_lost) { |
| TextureBase* texture = mailbox_manager_->ConsumeTexture(mailbox); |
| mailbox_manager_->TextureDeleted(texture); |
| |
| if (offscreen_single_buffer_) { |
| return; |
| } |
| |
| auto it = in_use_color_textures_.begin(); |
| while (it != in_use_color_textures_.end()) { |
| if ((*it)->texture == texture) { |
| break; |
| } |
| it++; |
| } |
| if (it == in_use_color_textures_.end()) { |
| DLOG(ERROR) << "Attempting to return a frontbuffer that was not saved."; |
| return; |
| } |
| |
| if (is_lost) { |
| (*it)->texture->MarkContextLost(); |
| (*it)->Destroy(false); |
| } else if ((*it)->size != emulated_back_buffer_->size) { |
| (*it)->Destroy(true); |
| } else { |
| available_color_textures_.push_back(std::move(*it)); |
| } |
| in_use_color_textures_.erase(it); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer( |
| const gfx::Size& size) { |
| DCHECK(offscreen_); |
| if (!emulated_back_buffer_) { |
| LOG(ERROR) |
| << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer called " |
| << " with an onscreen framebuffer."; |
| return false; |
| } |
| |
| if (emulated_back_buffer_->size == size) { |
| return true; |
| } |
| |
| if (size.width() < 0 || size.height() < 0 || |
| size.width() > max_offscreen_framebuffer_size_ || |
| size.height() > max_offscreen_framebuffer_size_) { |
| LOG(ERROR) << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer " |
| "failed to allocate storage due to excessive dimensions."; |
| return false; |
| } |
| |
| CheckErrorCallbackState(); |
| |
| if (!emulated_back_buffer_->Resize(size, feature_info_.get())) { |
| LOG(ERROR) << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer " |
| "failed to resize the emulated framebuffer."; |
| return false; |
| } |
| |
| if (CheckErrorCallbackState()) { |
| LOG(ERROR) << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer " |
| "failed to resize the emulated framebuffer because errors " |
| "were generated."; |
| return false; |
| } |
| |
| // Destroy all the available color textures, they should not be the same size |
| // as the back buffer |
| for (auto& available_color_texture : available_color_textures_) { |
| DCHECK(available_color_texture->size != size); |
| available_color_texture->Destroy(true); |
| } |
| available_color_textures_.clear(); |
| |
| return true; |
| } |
| |
| bool GLES2DecoderPassthroughImpl::MakeCurrent() { |
| if (!context_.get()) |
| return false; |
| |
| if (WasContextLost()) { |
| LOG(ERROR) << " GLES2DecoderPassthroughImpl: Trying to make lost context " |
| "current."; |
| return false; |
| } |
| |
| if (!context_->MakeCurrent(surface_.get())) { |
| LOG(ERROR) |
| << " GLES2DecoderPassthroughImpl: 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) << " GLES2DecoderPassthroughImpl: Context reset detected after " |
| "MakeCurrent."; |
| group_->LoseContexts(error::kUnknown); |
| return false; |
| } |
| |
| ProcessReadPixels(false); |
| ProcessQueries(false); |
| |
| resources_->DestroyPendingTextures(/*has_context=*/true); |
| |
| return true; |
| } |
| |
| gpu::gles2::GLES2Util* GLES2DecoderPassthroughImpl::GetGLES2Util() { |
| return nullptr; |
| } |
| |
| gl::GLContext* GLES2DecoderPassthroughImpl::GetGLContext() { |
| return context_.get(); |
| } |
| |
| gl::GLSurface* GLES2DecoderPassthroughImpl::GetGLSurface() { |
| return surface_.get(); |
| } |
| |
| gpu::gles2::ContextGroup* GLES2DecoderPassthroughImpl::GetContextGroup() { |
| return group_.get(); |
| } |
| |
| const FeatureInfo* GLES2DecoderPassthroughImpl::GetFeatureInfo() const { |
| return group_->feature_info(); |
| } |
| |
| gpu::Capabilities GLES2DecoderPassthroughImpl::GetCapabilities() { |
| DCHECK(initialized()); |
| Capabilities caps; |
| |
| PopulateNumericCapabilities(&caps, feature_info_.get()); |
| |
| api()->glGetIntegervFn(GL_BIND_GENERATES_RESOURCE_CHROMIUM, |
| &caps.bind_generates_resource_chromium); |
| DCHECK_EQ(caps.bind_generates_resource_chromium != GL_FALSE, |
| group_->bind_generates_resource()); |
| |
| 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; |
| caps.texture_rectangle = feature_info_->feature_flags().arb_texture_rectangle; |
| caps.texture_usage = feature_info_->feature_flags().angle_texture_usage; |
| caps.texture_storage = feature_info_->feature_flags().ext_texture_storage; |
| caps.discard_framebuffer = |
| feature_info_->feature_flags().ext_discard_framebuffer; |
| caps.sync_query = feature_info_->feature_flags().chromium_sync_query; |
| #if defined(OS_MACOSX) |
| // This is unconditionally true on mac, no need to test for it at runtime. |
| caps.iosurface = true; |
| #endif |
| caps.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_->feature_flags().enable_texture_half_float_linear; |
| 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 = |
| feature_info_->workarounds().max_copy_texture_chromium_size; |
| caps.render_buffer_format_bgra8888 = |
| feature_info_->feature_flags().ext_render_buffer_format_bgra8888; |
| caps.occlusion_query_boolean = |
| feature_info_->feature_flags().occlusion_query_boolean; |
| caps.timer_queries = feature_info_->feature_flags().ext_disjoint_timer_query; |
| caps.gpu_rasterization = |
| group_->gpu_feature_info() |
| .status_values[GPU_FEATURE_TYPE_GPU_RASTERIZATION] == |
| kGpuFeatureStatusEnabled; |
| caps.post_sub_buffer = surface_->SupportsPostSubBuffer(); |
| caps.surfaceless = !offscreen_ && surface_->IsSurfaceless(); |
| caps.flips_vertically = !offscreen_ && surface_->FlipsVertically(); |
| caps.msaa_is_slow = feature_info_->workarounds().msaa_is_slow; |
| caps.avoid_stencil_buffers = |
| feature_info_->workarounds().avoid_stencil_buffers; |
| caps.multisample_compatibility = |
| feature_info_->feature_flags().ext_multisample_compatibility; |
| caps.dc_layers = !offscreen_ && surface_->SupportsDCLayers(); |
| caps.commit_overlay_planes = surface_->SupportsCommitOverlayPlanes(); |
| caps.use_dc_overlays_for_video = surface_->UseOverlaysForVideo(); |
| caps.protected_video_swap_chain = surface_->SupportsProtectedVideo(); |
| caps.texture_npot = feature_info_->feature_flags().npot_ok; |
| caps.chromium_gpu_fence = feature_info_->feature_flags().chromium_gpu_fence; |
| caps.chromium_nonblocking_readback = true; |
| caps.num_surface_buffers = surface_->GetBufferCount(); |
| 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 GLES2DecoderPassthroughImpl::RestoreState(const ContextState* prev_state) { |
| } |
| |
| void GLES2DecoderPassthroughImpl::RestoreActiveTexture() const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreAllTextureUnitAndSamplerBindings( |
| const ContextState* prev_state) const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreActiveTextureUnitBinding( |
| unsigned int target) const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreBufferBinding(unsigned int target) {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreBufferBindings() const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreFramebufferBindings() const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreRenderbufferBindings() {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreGlobalState() const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreProgramBindings() const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreTextureState(unsigned service_id) {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreTextureUnitBindings( |
| unsigned unit) const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreVertexAttribArray(unsigned index) {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreAllExternalTextureBindingsIfNeeded() {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreDeviceWindowRectangles() const {} |
| |
| void GLES2DecoderPassthroughImpl::ClearAllAttributes() const {} |
| |
| void GLES2DecoderPassthroughImpl::RestoreAllAttributes() const {} |
| |
| void GLES2DecoderPassthroughImpl::SetIgnoreCachedStateForTest(bool ignore) {} |
| |
| void GLES2DecoderPassthroughImpl::SetForceShaderNameHashingForTest(bool force) { |
| } |
| |
| size_t GLES2DecoderPassthroughImpl::GetSavedBackTextureCountForTest() { |
| return in_use_color_textures_.size() + available_color_textures_.size(); |
| } |
| |
| size_t GLES2DecoderPassthroughImpl::GetCreatedBackTextureCountForTest() { |
| return create_color_buffer_count_for_test_; |
| } |
| |
| gpu::QueryManager* GLES2DecoderPassthroughImpl::GetQueryManager() { |
| return nullptr; |
| } |
| |
| void GLES2DecoderPassthroughImpl::SetQueryCallback(unsigned int query_client_id, |
| base::OnceClosure callback) { |
| GLuint service_id = query_id_map_.GetServiceIDOrInvalid(query_client_id); |
| for (auto& pending_query : pending_queries_) { |
| if (pending_query.service_id == service_id) { |
| pending_query.callbacks.push_back(std::move(callback)); |
| return; |
| } |
| } |
| |
| VLOG(1) << "GLES2DecoderPassthroughImpl::SetQueryCallback: No pending query " |
| "with ID " |
| << query_client_id << ". Running the callback immediately."; |
| std::move(callback).Run(); |
| } |
| |
| gpu::gles2::GpuFenceManager* GLES2DecoderPassthroughImpl::GetGpuFenceManager() { |
| return gpu_fence_manager_.get(); |
| } |
| |
| gpu::gles2::FramebufferManager* |
| GLES2DecoderPassthroughImpl::GetFramebufferManager() { |
| return nullptr; |
| } |
| |
| gpu::gles2::TransformFeedbackManager* |
| GLES2DecoderPassthroughImpl::GetTransformFeedbackManager() { |
| return nullptr; |
| } |
| |
| gpu::gles2::VertexArrayManager* |
| GLES2DecoderPassthroughImpl::GetVertexArrayManager() { |
| return nullptr; |
| } |
| |
| gpu::gles2::ImageManager* |
| GLES2DecoderPassthroughImpl::GetImageManagerForTest() { |
| return group_->image_manager(); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::HasPendingQueries() const { |
| return !pending_queries_.empty(); |
| } |
| |
| void GLES2DecoderPassthroughImpl::ProcessPendingQueries(bool did_finish) { |
| // TODO(geofflang): If this returned an error, store it somewhere. |
| ProcessQueries(did_finish); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::HasMoreIdleWork() const { |
| return gpu_tracer_->HasTracesToProcess() || !pending_read_pixels_.empty() || |
| resources_->HasTexturesPendingDestruction(); |
| } |
| |
| void GLES2DecoderPassthroughImpl::PerformIdleWork() { |
| gpu_tracer_->ProcessTraces(); |
| ProcessReadPixels(false); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::HasPollingWork() const { |
| return deschedule_until_finished_fences_.size() >= 2; |
| } |
| |
| void GLES2DecoderPassthroughImpl::PerformPollingWork() { |
| ProcessDescheduleUntilFinished(); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::GetServiceTextureId( |
| uint32_t client_texture_id, |
| uint32_t* service_texture_id) { |
| return resources_->texture_id_map.GetServiceID(client_texture_id, |
| service_texture_id); |
| } |
| |
| TextureBase* GLES2DecoderPassthroughImpl::GetTextureBase(uint32_t client_id) { |
| scoped_refptr<TexturePassthrough> texture = nullptr; |
| if (resources_->texture_object_map.GetServiceID(client_id, &texture)) { |
| return texture.get(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| bool GLES2DecoderPassthroughImpl::ClearLevel(Texture* texture, |
| unsigned target, |
| int level, |
| unsigned format, |
| unsigned type, |
| int xoffset, |
| int yoffset, |
| int width, |
| int height) { |
| return true; |
| } |
| |
| bool GLES2DecoderPassthroughImpl::ClearCompressedTextureLevel(Texture* texture, |
| unsigned target, |
| int level, |
| unsigned format, |
| int width, |
| int height) { |
| return true; |
| } |
| |
| bool GLES2DecoderPassthroughImpl::IsCompressedTextureFormat(unsigned format) { |
| return false; |
| } |
| |
| bool GLES2DecoderPassthroughImpl::ClearLevel3D(Texture* texture, |
| unsigned target, |
| int level, |
| unsigned format, |
| unsigned type, |
| int width, |
| int height, |
| int depth) { |
| return true; |
| } |
| |
| gpu::gles2::ErrorState* GLES2DecoderPassthroughImpl::GetErrorState() { |
| return nullptr; |
| } |
| |
| void GLES2DecoderPassthroughImpl::WaitForReadPixels( |
| base::OnceClosure callback) {} |
| |
| std::unique_ptr<AbstractTexture> |
| GLES2DecoderPassthroughImpl::CreateAbstractTexture(GLenum target, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type) { |
| // We can't support cube maps because the abstract texture does not allow it. |
| DCHECK(target != GL_TEXTURE_CUBE_MAP); |
| GLuint service_id = 0; |
| api()->glGenTexturesFn(1, &service_id); |
| scoped_refptr<TexturePassthrough> texture( |
| new TexturePassthrough(service_id, target)); |
| |
| // Unretained is safe, because of the destruction cb. |
| std::unique_ptr<PassthroughAbstractTextureImpl> abstract_texture = |
| std::make_unique<PassthroughAbstractTextureImpl>(texture, this); |
| |
| abstract_textures_.insert(abstract_texture.get()); |
| return abstract_texture; |
| } |
| |
| void GLES2DecoderPassthroughImpl::OnAbstractTextureDestroyed( |
| PassthroughAbstractTextureImpl* abstract_texture, |
| scoped_refptr<TexturePassthrough> texture) { |
| DCHECK(texture); |
| abstract_textures_.erase(abstract_texture); |
| if (context_->IsCurrent(nullptr)) { |
| resources_->DestroyPendingTextures(true); |
| } else { |
| resources_->textures_pending_destruction.insert(std::move(texture)); |
| } |
| } |
| |
| bool GLES2DecoderPassthroughImpl::WasContextLost() const { |
| return context_lost_; |
| } |
| |
| bool GLES2DecoderPassthroughImpl::WasContextLostByRobustnessExtension() const { |
| return WasContextLost() && reset_by_robustness_extension_; |
| } |
| |
| void GLES2DecoderPassthroughImpl::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); |
| context_lost_ = true; |
| } |
| |
| gpu::gles2::Logger* GLES2DecoderPassthroughImpl::GetLogger() { |
| return &logger_; |
| } |
| |
| void GLES2DecoderPassthroughImpl::BeginDecoding() { |
| gpu_tracer_->BeginDecoding(); |
| gpu_trace_commands_ = gpu_tracer_->IsTracing() && *gpu_decoder_category_; |
| gpu_debug_commands_ = log_commands() || debug() || gpu_trace_commands_; |
| } |
| |
| void GLES2DecoderPassthroughImpl::EndDecoding() { |
| gpu_tracer_->EndDecoding(); |
| } |
| |
| const gpu::gles2::ContextState* GLES2DecoderPassthroughImpl::GetContextState() { |
| return nullptr; |
| } |
| |
| scoped_refptr<ShaderTranslatorInterface> |
| GLES2DecoderPassthroughImpl::GetTranslator(GLenum type) { |
| return nullptr; |
| } |
| |
| void GLES2DecoderPassthroughImpl::BindImage(uint32_t client_texture_id, |
| uint32_t texture_target, |
| gl::GLImage* image, |
| bool can_bind_to_sampler) { |
| scoped_refptr<TexturePassthrough> passthrough_texture = nullptr; |
| if (!resources_->texture_object_map.GetServiceID(client_texture_id, |
| &passthrough_texture)) { |
| return; |
| } |
| |
| DCHECK(passthrough_texture != nullptr); |
| |
| // |can_bind_to_sampler| indicates that we don't need to take any action. |
| // Otherwise, we do it when the texture is first used for drawing. |
| passthrough_texture->set_is_bind_pending(!can_bind_to_sampler); |
| |
| GLenum bind_target = GLES2Util::GLFaceTargetToTextureTarget(texture_target); |
| if (passthrough_texture->target() != bind_target) { |
| return; |
| } |
| |
| // Reference the image even if it is not bound as a sampler. |
| passthrough_texture->SetLevelImage(texture_target, 0, image); |
| } |
| |
| void GLES2DecoderPassthroughImpl::BindOnePendingImage( |
| GLenum target, |
| TexturePassthrough* texture) { |
| // It's possible that this texture was processed by some other decoder |
| // while it was also bound here, or that it has been destroyed. In |
| // either case, do nothing. |
| if (!texture || !texture->is_bind_pending()) |
| return; |
| |
| // TODO(liberato): make this work for non-0 levels. |
| gl::GLImage* image = texture->GetLevelImage(target, 0); |
| |
| // Note that we might not have an image anymore, if it was unbound from |
| // the texture by some other decoder while the texture was still bound |
| // here. In that case, just ignore it. |
| // |
| // Similarly, we might not even get here if an image was bound to a |
| // texture that requries bind/copy, but that texture was already bound |
| // to a sampler in this decoder. |
| if (!image) |
| return; |
| |
| // TODO: internalformat? |
| if (image->ShouldBindOrCopy() == gl::GLImage::BIND) |
| image->BindTexImage(target); |
| else |
| image->CopyTexImage(target); |
| |
| // If copy / bind fail, then we could keep the bind state the same. |
| // However, for now, we only try once. |
| texture->set_is_bind_pending(false); |
| |
| // No client ID available here, can this texture already be discardable? |
| UpdateTextureSizeFromTexturePassthrough(texture, 0); |
| } |
| |
| void GLES2DecoderPassthroughImpl::BindPendingImagesForSamplers() { |
| for (TexturePendingBinding& pending : textures_pending_binding_) |
| BindOnePendingImage(pending.target, pending.texture.get()); |
| |
| // Note that we clear the texures even if they fail. We could keep |
| // them around. |
| textures_pending_binding_.clear(); |
| } |
| |
| void GLES2DecoderPassthroughImpl::OnDebugMessage(GLenum source, |
| GLenum type, |
| GLuint id, |
| GLenum severity, |
| GLsizei length, |
| const GLchar* message) { |
| if (type == GL_DEBUG_TYPE_ERROR && source == GL_DEBUG_SOURCE_API) { |
| had_error_callback_ = true; |
| } |
| } |
| |
| void GLES2DecoderPassthroughImpl::SetCopyTextureResourceManagerForTest( |
| CopyTextureCHROMIUMResourceManager* copy_texture_resource_manager) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void GLES2DecoderPassthroughImpl::SetCopyTexImageBlitterForTest( |
| CopyTexImageResourceManager* copy_tex_image_blit) { |
| NOTIMPLEMENTED(); |
| } |
| |
| const char* GLES2DecoderPassthroughImpl::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)); |
| } |
| |
| void GLES2DecoderPassthroughImpl::SetOptionalExtensionsRequestedForTesting( |
| bool request_extensions) { |
| request_optional_extensions_ = request_extensions; |
| } |
| |
| void* GLES2DecoderPassthroughImpl::GetScratchMemory(size_t size) { |
| if (scratch_memory_.size() < size) { |
| scratch_memory_.resize(size, 0); |
| } |
| return scratch_memory_.data(); |
| } |
| |
| template <typename T> |
| error::Error GLES2DecoderPassthroughImpl::PatchGetNumericResults(GLenum pname, |
| GLsizei length, |
| T* params) { |
| // Likely a gl error if no parameters were returned |
| if (length < 1) { |
| return error::kNoError; |
| } |
| |
| switch (pname) { |
| case GL_NUM_EXTENSIONS: |
| // Currently handled on the client side. |
| params[0] = 0; |
| break; |
| |
| case GL_TEXTURE_BINDING_2D: |
| case GL_TEXTURE_BINDING_CUBE_MAP: |
| case GL_TEXTURE_BINDING_2D_ARRAY: |
| case GL_TEXTURE_BINDING_3D: |
| if (*params != 0 && |
| !GetClientID(&resources_->texture_id_map, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_ARRAY_BUFFER_BINDING: |
| case GL_ELEMENT_ARRAY_BUFFER_BINDING: |
| case GL_PIXEL_PACK_BUFFER_BINDING: |
| case GL_PIXEL_UNPACK_BUFFER_BINDING: |
| case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: |
| case GL_COPY_READ_BUFFER_BINDING: |
| case GL_COPY_WRITE_BUFFER_BINDING: |
| case GL_UNIFORM_BUFFER_BINDING: |
| if (*params != 0 && |
| !GetClientID(&resources_->buffer_id_map, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_RENDERBUFFER_BINDING: |
| if (*params != 0 && |
| !GetClientID(&resources_->renderbuffer_id_map, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_SAMPLER_BINDING: |
| if (*params != 0 && |
| !GetClientID(&resources_->sampler_id_map, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_ACTIVE_PROGRAM: |
| if (*params != 0 && |
| !GetClientID(&resources_->program_id_map, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_FRAMEBUFFER_BINDING: |
| case GL_READ_FRAMEBUFFER_BINDING: |
| if (*params != 0 && !GetClientID(&framebuffer_id_map_, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_TRANSFORM_FEEDBACK_BINDING: |
| if (*params != 0 && |
| !GetClientID(&transform_feedback_id_map_, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_VERTEX_ARRAY_BINDING: |
| if (*params != 0 && |
| !GetClientID(&vertex_array_id_map_, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_VIEWPORT: |
| // The applied viewport and scissor could be offset by the current |
| // surface, return the tracked values instead |
| if (length < 4) { |
| return error::kInvalidArguments; |
| } |
| std::copy(std::begin(viewport_), std::end(viewport_), params); |
| break; |
| |
| case GL_SCISSOR_BOX: |
| // The applied viewport and scissor could be offset by the current |
| // surface, return the tracked values instead |
| if (length < 4) { |
| return error::kInvalidArguments; |
| } |
| std::copy(std::begin(scissor_), std::end(scissor_), params); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return error::kNoError; |
| } |
| |
| // Instantiate templated functions |
| #define INSTANTIATE_PATCH_NUMERIC_RESULTS(type) \ |
| template error::Error GLES2DecoderPassthroughImpl::PatchGetNumericResults( \ |
| GLenum, GLsizei, type*) |
| INSTANTIATE_PATCH_NUMERIC_RESULTS(GLint); |
| INSTANTIATE_PATCH_NUMERIC_RESULTS(GLint64); |
| INSTANTIATE_PATCH_NUMERIC_RESULTS(GLfloat); |
| INSTANTIATE_PATCH_NUMERIC_RESULTS(GLboolean); |
| #undef INSTANTIATE_PATCH_NUMERIC_RESULTS |
| |
| template <typename T> |
| error::Error GLES2DecoderPassthroughImpl::PatchGetBufferResults(GLenum target, |
| GLenum pname, |
| GLsizei bufsize, |
| GLsizei* length, |
| T* params) { |
| if (pname != GL_BUFFER_ACCESS_FLAGS) { |
| return error::kNoError; |
| } |
| |
| // If there was no error, the buffer target should exist |
| DCHECK(bound_buffers_.find(target) != bound_buffers_.end()); |
| GLuint current_client_buffer = bound_buffers_[target]; |
| |
| auto mapped_buffer_info_iter = |
| resources_->mapped_buffer_map.find(current_client_buffer); |
| if (mapped_buffer_info_iter == resources_->mapped_buffer_map.end()) { |
| // Buffer is not mapped, nothing to do |
| return error::kNoError; |
| } |
| |
| // Buffer is mapped, patch the result with the original access flags |
| DCHECK_GE(bufsize, 1); |
| DCHECK_EQ(*length, 1); |
| params[0] = mapped_buffer_info_iter->second.original_access; |
| return error::kNoError; |
| } |
| |
| template error::Error GLES2DecoderPassthroughImpl::PatchGetBufferResults( |
| GLenum target, |
| GLenum pname, |
| GLsizei bufsize, |
| GLsizei* length, |
| GLint64* params); |
| template error::Error GLES2DecoderPassthroughImpl::PatchGetBufferResults( |
| GLenum target, |
| GLenum pname, |
| GLsizei bufsize, |
| GLsizei* length, |
| GLint* params); |
| |
| error::Error |
| GLES2DecoderPassthroughImpl::PatchGetFramebufferAttachmentParameter( |
| GLenum target, |
| GLenum attachment, |
| GLenum pname, |
| GLsizei length, |
| GLint* params) { |
| // Likely a gl error if no parameters were returned |
| if (length < 1) { |
| return error::kNoError; |
| } |
| |
| switch (pname) { |
| // If the attached object name was requested, it needs to be converted back |
| // to a client id. |
| case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: { |
| GLint object_type = GL_NONE; |
| api()->glGetFramebufferAttachmentParameterivEXTFn( |
| target, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, |
| &object_type); |
| |
| switch (object_type) { |
| case GL_TEXTURE: |
| if (!GetClientID(&resources_->texture_id_map, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_RENDERBUFFER: |
| if (!GetClientID(&resources_->renderbuffer_id_map, *params, params)) { |
| return error::kInvalidArguments; |
| } |
| break; |
| |
| case GL_NONE: |
| // Default framebuffer, don't transform the result |
| break; |
| |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } break; |
| |
| // If the framebuffer is an emulated default framebuffer, all attachment |
| // object types are GL_FRAMEBUFFER_DEFAULT |
| case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: |
| if (IsEmulatedFramebufferBound(target)) { |
| *params = GL_FRAMEBUFFER_DEFAULT; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderPassthroughImpl::InsertError(GLenum error, |
| const std::string& message) { |
| errors_.insert(error); |
| LogGLDebugMessage(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, error, |
| GL_DEBUG_SEVERITY_HIGH, message.length(), message.c_str(), |
| GetLogger()); |
| } |
| |
| GLenum GLES2DecoderPassthroughImpl::PopError() { |
| GLenum error = GL_NO_ERROR; |
| if (!errors_.empty()) { |
| error = *errors_.begin(); |
| errors_.erase(errors_.begin()); |
| } |
| return error; |
| } |
| |
| bool GLES2DecoderPassthroughImpl::FlushErrors() { |
| bool had_error = false; |
| GLenum error = glGetError(); |
| while (error != GL_NO_ERROR) { |
| errors_.insert(error); |
| had_error = true; |
| |
| // Check for context loss on out-of-memory errors |
| if (error == GL_OUT_OF_MEMORY && !WasContextLost() && |
| lose_context_when_out_of_memory_) { |
| error::ContextLostReason other = error::kOutOfMemory; |
| if (CheckResetStatus()) { |
| other = error::kUnknown; |
| } else { |
| // Need to lose current context before broadcasting! |
| MarkContextLost(error::kOutOfMemory); |
| } |
| group_->LoseContexts(other); |
| break; |
| } |
| |
| error = glGetError(); |
| } |
| return had_error; |
| } |
| |
| bool GLES2DecoderPassthroughImpl::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; |
| } |
| |
| 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; |
| } |
| |
| bool GLES2DecoderPassthroughImpl::IsRobustnessSupported() { |
| return has_robustness_extension_ && |
| context_->WasAllocatedUsingRobustnessExtension(); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::IsEmulatedQueryTarget(GLenum target) const { |
| switch (target) { |
| case GL_COMMANDS_COMPLETED_CHROMIUM: |
| case GL_READBACK_SHADOW_COPIES_UPDATED_CHROMIUM: |
| case GL_COMMANDS_ISSUED_CHROMIUM: |
| case GL_LATENCY_QUERY_CHROMIUM: |
| case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: |
| case GL_GET_ERROR_QUERY_CHROMIUM: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| error::Error GLES2DecoderPassthroughImpl::ProcessQueries(bool did_finish) { |
| while (!pending_queries_.empty()) { |
| const PendingQuery& query = pending_queries_.front(); |
| GLuint result_available = GL_FALSE; |
| GLuint64 result = 0; |
| switch (query.target) { |
| case GL_COMMANDS_COMPLETED_CHROMIUM: |
| DCHECK(query.commands_completed_fence != nullptr); |
| // 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. |
| result_available = |
| did_finish || query.commands_completed_fence->HasCompleted(); |
| result = result_available; |
| break; |
| |
| case GL_COMMANDS_ISSUED_CHROMIUM: |
| result_available = GL_TRUE; |
| result = GL_TRUE; |
| break; |
| |
| case GL_LATENCY_QUERY_CHROMIUM: |
| result_available = GL_TRUE; |
| // TODO: time from when the query is ended? |
| result = (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds(); |
| break; |
| |
| case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: |
| // Initialize the result to being available. Will be marked as |
| // unavailable if any pending read pixels operations reference this |
| // query. |
| result_available = GL_TRUE; |
| result = GL_TRUE; |
| for (const PendingReadPixels& pending_read_pixels : |
| pending_read_pixels_) { |
| if (pending_read_pixels.waiting_async_pack_queries.count( |
| query.service_id) > 0) { |
| // Async read pixel processing happens before query processing. If |
| // there was a finish then there should be no pending read pixels. |
| DCHECK(!did_finish); |
| result_available = GL_FALSE; |
| result = GL_FALSE; |
| break; |
| } |
| } |
| break; |
| |
| case GL_READBACK_SHADOW_COPIES_UPDATED_CHROMIUM: |
| DCHECK(query.buffer_shadow_update_fence); |
| if (did_finish || query.buffer_shadow_update_fence->HasCompleted()) { |
| ReadBackBuffersIntoShadowCopies(query.buffer_shadow_updates); |
| result_available = GL_TRUE; |
| result = 0; |
| } |
| break; |
| |
| case GL_GET_ERROR_QUERY_CHROMIUM: |
| result_available = GL_TRUE; |
| FlushErrors(); |
| result = PopError(); |
| break; |
| |
| default: |
| DCHECK(!IsEmulatedQueryTarget(query.target)); |
| if (did_finish) { |
| result_available = GL_TRUE; |
| } else { |
| api()->glGetQueryObjectuivFn( |
| query.service_id, GL_QUERY_RESULT_AVAILABLE, &result_available); |
| } |
| if (result_available == GL_TRUE) { |
| if (feature_info_->feature_flags().ext_disjoint_timer_query) { |
| api()->glGetQueryObjectui64vFn(query.service_id, GL_QUERY_RESULT, |
| &result); |
| } else { |
| GLuint temp_result = 0; |
| api()->glGetQueryObjectuivFn(query.service_id, GL_QUERY_RESULT, |
| &temp_result); |
| result = temp_result; |
| } |
| } |
| break; |
| } |
| |
| if (!result_available) { |
| break; |
| } |
| |
| // Mark the query as complete |
| query.sync->result = result; |
| base::subtle::Release_Store(&query.sync->process_count, query.submit_count); |
| pending_queries_.pop_front(); |
| } |
| |
| // If api()->glFinishFn() has been called, all of our queries should be |
| // completed. |
| DCHECK(!did_finish || pending_queries_.empty()); |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderPassthroughImpl::RemovePendingQuery(GLuint service_id) { |
| auto pending_iter = |
| std::find_if(pending_queries_.begin(), pending_queries_.end(), |
| [service_id](const PendingQuery& pending_query) { |
| return pending_query.service_id == service_id; |
| }); |
| if (pending_iter != pending_queries_.end()) { |
| QuerySync* sync = pending_iter->sync; |
| sync->result = 0; |
| base::subtle::Release_Store(&sync->process_count, |
| pending_iter->submit_count); |
| |
| pending_queries_.erase(pending_iter); |
| } |
| } |
| |
| void GLES2DecoderPassthroughImpl::ReadBackBuffersIntoShadowCopies( |
| const BufferShadowUpdateMap& updates) { |
| GLint old_binding = 0; |
| api()->glGetIntegervFn(GL_ARRAY_BUFFER_BINDING, &old_binding); |
| for (const auto& u : updates) { |
| GLuint service_id = u.first; |
| const auto& update = u.second; |
| |
| void* shadow = update.shm->GetDataAddress(update.shm_offset, update.size); |
| DCHECK(shadow); |
| |
| api()->glBindBufferFn(GL_ARRAY_BUFFER, service_id); |
| GLint already_mapped = GL_TRUE; |
| api()->glGetBufferParameterivFn(GL_ARRAY_BUFFER, GL_BUFFER_MAPPED, |
| &already_mapped); |
| if (already_mapped) { |
| // 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; |
| } |
| |
| void* mapped = api()->glMapBufferRangeFn(GL_ARRAY_BUFFER, 0, update.size, |
| GL_MAP_READ_BIT); |
| if (!mapped) { |
| DLOG(ERROR) << "glMapBufferRange unexpectedly returned NULL"; |
| MarkContextLost(error::kOutOfMemory); |
| group_->LoseContexts(error::kUnknown); |
| return; |
| } |
| memcpy(shadow, mapped, update.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 GLES2DecoderPassthroughImpl::ProcessReadPixels(bool did_finish) { |
| while (!pending_read_pixels_.empty()) { |
| const PendingReadPixels& pending_read_pixels = pending_read_pixels_.front(); |
| if (did_finish || pending_read_pixels.fence->HasCompleted()) { |
| using Result = cmds::ReadPixels::Result; |
| Result* result = nullptr; |
| if (pending_read_pixels.result_shm_id != 0) { |
| result = GetSharedMemoryAs<Result*>( |
| pending_read_pixels.result_shm_id, |
| pending_read_pixels.result_shm_offset, sizeof(*result)); |
| if (!result) { |
| api()->glDeleteBuffersARBFn(1, |
| &pending_read_pixels.buffer_service_id); |
| pending_read_pixels_.pop_front(); |
| break; |
| } |
| } |
| |
| void* pixels = |
| GetSharedMemoryAs<void*>(pending_read_pixels.pixels_shm_id, |
| pending_read_pixels.pixels_shm_offset, |
| pending_read_pixels.pixels_size); |
| if (!pixels) { |
| api()->glDeleteBuffersARBFn(1, &pending_read_pixels.buffer_service_id); |
| pending_read_pixels_.pop_front(); |
| break; |
| } |
| |
| api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, |
| pending_read_pixels.buffer_service_id); |
| void* data = nullptr; |
| if (feature_info_->feature_flags().map_buffer_range) { |
| data = api()->glMapBufferRangeFn(GL_PIXEL_PACK_BUFFER_ARB, 0, |
| pending_read_pixels.pixels_size, |
| GL_MAP_READ_BIT); |
| } else { |
| data = api()->glMapBufferFn(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); |
| } |
| if (!data) { |
| InsertError(GL_OUT_OF_MEMORY, "Failed to map pixel pack buffer."); |
| pending_read_pixels_.pop_front(); |
| break; |
| } |
| |
| memcpy(pixels, data, pending_read_pixels.pixels_size); |
| api()->glUnmapBufferFn(GL_PIXEL_PACK_BUFFER_ARB); |
| api()->glBindBufferFn(GL_PIXEL_PACK_BUFFER_ARB, |
| resources_->buffer_id_map.GetServiceIDOrInvalid( |
| bound_buffers_[GL_PIXEL_PACK_BUFFER_ARB])); |
| api()->glDeleteBuffersARBFn(1, &pending_read_pixels.buffer_service_id); |
| |
| if (result != nullptr) { |
| result->success = 1; |
| } |
| |
| pending_read_pixels_.pop_front(); |
| } |
| } |
| |
| // If api()->glFinishFn() has been called, all of our fences should be |
| // completed. |
| DCHECK(!did_finish || pending_read_pixels_.empty()); |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderPassthroughImpl::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", "GLES2DecoderPassthroughImpl::DescheduleUntilFinished", this); |
| deschedule_until_finished_fences_.erase( |
| deschedule_until_finished_fences_.begin()); |
| client_->OnRescheduleAfterFinished(); |
| } |
| |
| void GLES2DecoderPassthroughImpl::UpdateTextureBinding( |
| GLenum target, |
| GLuint client_id, |
| TexturePassthrough* texture) { |
| GLuint texture_service_id = texture ? texture->service_id() : 0; |
| size_t cur_texture_unit = active_texture_unit_; |
| auto& target_bound_textures = |
| bound_textures_[static_cast<size_t>(GLenumToTextureTarget(target))]; |
| for (size_t bound_texture_index = 0; |
| bound_texture_index < target_bound_textures.size(); |
| bound_texture_index++) { |
| if (target_bound_textures[bound_texture_index].client_id == client_id) { |
| // Update the active texture unit if needed |
| if (bound_texture_index != cur_texture_unit) { |
| api()->glActiveTextureFn( |
| static_cast<GLenum>(GL_TEXTURE0 + bound_texture_index)); |
| cur_texture_unit = bound_texture_index; |
| } |
| |
| // Update the texture binding |
| api()->glBindTextureFn(target, texture_service_id); |
| target_bound_textures[bound_texture_index].texture = texture; |
| } |
| } |
| |
| // Reset the active texture unit if it was changed |
| if (cur_texture_unit != active_texture_unit_) { |
| api()->glActiveTextureFn( |
| static_cast<GLenum>(GL_TEXTURE0 + active_texture_unit_)); |
| } |
| } |
| |
| void GLES2DecoderPassthroughImpl::UpdateTextureSizeFromTexturePassthrough( |
| TexturePassthrough* texture, |
| GLuint client_id) { |
| if (texture == nullptr) { |
| return; |
| } |
| |
| CheckErrorCallbackState(); |
| |
| GLenum target = texture->target(); |
| TextureTarget internal_texture_type = GLenumToTextureTarget(target); |
| BoundTexture& bound_texture = |
| bound_textures_[static_cast<size_t>(internal_texture_type)] |
| [active_texture_unit_]; |
| bool needs_rebind = bound_texture.texture == texture; |
| if (needs_rebind) { |
| glBindTexture(target, texture->service_id()); |
| } |
| |
| UpdateBoundTexturePassthroughSize(api(), texture); |
| |
| // If a client ID is available, notify the discardable manager of the size |
| // change |
| if (client_id != 0) { |
| group_->passthrough_discardable_manager()->UpdateTextureSize( |
| client_id, group_.get(), texture->estimated_size()); |
| } |
| |
| if (needs_rebind) { |
| GLuint old_texture = |
| bound_texture.texture ? bound_texture.texture->service_id() : 0; |
| glBindTexture(target, old_texture); |
| } |
| |
| DCHECK(!CheckErrorCallbackState()); |
| } |
| |
| void GLES2DecoderPassthroughImpl::UpdateTextureSizeFromTarget(GLenum target) { |
| GLenum texture_type = TextureTargetToTextureType(target); |
| TextureTarget internal_texture_type = GLenumToTextureTarget(texture_type); |
| DCHECK(internal_texture_type != TextureTarget::kUnkown); |
| BoundTexture& bound_texture = |
| bound_textures_[static_cast<size_t>(internal_texture_type)] |
| [active_texture_unit_]; |
| UpdateTextureSizeFromTexturePassthrough(bound_texture.texture.get(), |
| bound_texture.client_id); |
| } |
| |
| void GLES2DecoderPassthroughImpl::UpdateTextureSizeFromClientID( |
| GLuint client_id) { |
| scoped_refptr<TexturePassthrough> texture = nullptr; |
| if (resources_->texture_object_map.GetServiceID(client_id, &texture)) { |
| UpdateTextureSizeFromTexturePassthrough(texture.get(), client_id); |
| } |
| } |
| |
| error::Error GLES2DecoderPassthroughImpl::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 + 1) { |
| return error::kInvalidArguments; |
| } |
| |
| size_t size = url_bucket->size() - 1; |
| 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; |
| } |
| |
| error::Error GLES2DecoderPassthroughImpl::BindTexImage2DCHROMIUMImpl( |
| GLenum target, |
| GLenum internalformat, |
| GLint imageId) { |
| if (target != GL_TEXTURE_2D) { |
| InsertError(GL_INVALID_ENUM, "Invalid target"); |
| return error::kNoError; |
| } |
| |
| gl::GLImage* image = group_->image_manager()->LookupImage(imageId); |
| if (image == nullptr) { |
| InsertError(GL_INVALID_OPERATION, "No image found with the given ID"); |
| return error::kNoError; |
| } |
| |
| const BoundTexture& bound_texture = |
| bound_textures_[static_cast<size_t>(TextureTarget::k2D)] |
| [active_texture_unit_]; |
| if (bound_texture.texture == nullptr) { |
| InsertError(GL_INVALID_OPERATION, "No texture bound"); |
| return error::kNoError; |
| } |
| |
| if (image->ShouldBindOrCopy() == gl::GLImage::BIND) { |
| if (internalformat) |
| image->BindTexImageWithInternalformat(target, internalformat); |
| else |
| image->BindTexImage(target); |
| } else { |
| image->CopyTexImage(target); |
| } |
| |
| // Target is already validated |
| UpdateTextureSizeFromTarget(target); |
| |
| DCHECK(bound_texture.texture != nullptr); |
| bound_texture.texture->SetLevelImage(target, 0, image); |
| |
| // If there was any GLImage bound to |target| on this texture unit, then |
| // forget it. |
| RemovePendingBindingTexture(target, active_texture_unit_); |
| |
| return error::kNoError; |
| } |
| |
| void GLES2DecoderPassthroughImpl::VerifyServiceTextureObjectsExist() { |
| resources_->texture_object_map.ForEach( |
| [this](GLuint client_id, scoped_refptr<TexturePassthrough> texture) { |
| DCHECK_EQ(GL_TRUE, api()->glIsTextureFn(texture->service_id())); |
| }); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::IsEmulatedFramebufferBound( |
| GLenum target) const { |
| if (!emulated_back_buffer_) { |
| return false; |
| } |
| |
| if ((target == GL_FRAMEBUFFER_EXT || target == GL_DRAW_FRAMEBUFFER) && |
| bound_draw_framebuffer_ == 0) { |
| return true; |
| } |
| |
| if (target == GL_READ_FRAMEBUFFER && bound_read_framebuffer_ == 0) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| error::Error GLES2DecoderPassthroughImpl::CheckSwapBuffersResult( |
| gfx::SwapResult result, |
| const char* function_name) { |
| if (result == gfx::SwapResult::SWAP_FAILED) { |
| LOG(ERROR) << "Context lost because " << function_name << " failed."; |
| if (!CheckResetStatus()) { |
| MarkContextLost(error::kUnknown); |
| group_->LoseContexts(error::kUnknown); |
| return error::kLostContext; |
| } |
| } |
| |
| return error::kNoError; |
| } |
| |
| // static |
| GLES2DecoderPassthroughImpl::TextureTarget |
| GLES2DecoderPassthroughImpl::GLenumToTextureTarget(GLenum target) { |
| switch (target) { |
| case GL_TEXTURE_2D: |
| return TextureTarget::k2D; |
| case GL_TEXTURE_CUBE_MAP: |
| return TextureTarget::kCubeMap; |
| case GL_TEXTURE_2D_ARRAY: |
| return TextureTarget::k2DArray; |
| case GL_TEXTURE_3D: |
| return TextureTarget::k3D; |
| case GL_TEXTURE_2D_MULTISAMPLE: |
| return TextureTarget::k2DMultisample; |
| case GL_TEXTURE_EXTERNAL_OES: |
| return TextureTarget::kExternal; |
| case GL_TEXTURE_RECTANGLE_ARB: |
| return TextureTarget::kRectangle; |
| default: |
| return TextureTarget::kUnkown; |
| } |
| } |
| |
| gfx::Vector2d GLES2DecoderPassthroughImpl::GetSurfaceDrawOffset() const { |
| if (bound_draw_framebuffer_ != 0 || offscreen_) { |
| return gfx::Vector2d(); |
| } |
| return surface_->GetDrawOffset(); |
| } |
| |
| void GLES2DecoderPassthroughImpl::ApplySurfaceDrawOffset() { |
| if (offscreen_ || !surface_->SupportsDCLayers()) { |
| return; |
| } |
| |
| gfx::Vector2d framebuffer_offset = GetSurfaceDrawOffset(); |
| api()->glViewportFn(viewport_[0] + framebuffer_offset.x(), |
| viewport_[1] + framebuffer_offset.y(), viewport_[2], |
| viewport_[3]); |
| api()->glScissorFn(scissor_[0] + framebuffer_offset.x(), |
| scissor_[1] + framebuffer_offset.y(), scissor_[2], |
| scissor_[3]); |
| } |
| |
| bool GLES2DecoderPassthroughImpl::CheckErrorCallbackState() { |
| bool had_error_ = had_error_callback_; |
| had_error_callback_ = false; |
| if (had_error_) { |
| // Make sure lose-context-on-OOM logic is triggered as early as possible. |
| FlushErrors(); |
| } |
| return had_error_; |
| } |
| |
| #define GLES2_CMD_OP(name) \ |
| { \ |
| &GLES2DecoderPassthroughImpl::Handle##name, cmds::name::kArgFlags, \ |
| cmds::name::cmd_flags, \ |
| sizeof(cmds::name) / sizeof(CommandBufferEntry) - 1, \ |
| }, /* NOLINT */ |
| |
| constexpr GLES2DecoderPassthroughImpl::CommandInfo |
| GLES2DecoderPassthroughImpl::command_info[] = { |
| GLES2_COMMAND_LIST(GLES2_CMD_OP)}; |
| |
| #undef GLES2_CMD_OP |
| |
| } // namespace gles2 |
| } // namespace gpu |