diff options
Diffstat (limited to 'chromium/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc')
-rw-r--r-- | chromium/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc | 711 |
1 files changed, 698 insertions, 13 deletions
diff --git a/chromium/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/chromium/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc index f033ced36c0..4d427bec788 100644 --- a/chromium/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +++ b/chromium/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc @@ -46,6 +46,31 @@ bool GetClientID(const ClientServiceMap<ClientType, ServiceType>* map, return true; }; +void ResizeRenderbuffer(GLuint renderbuffer, + const gfx::Size& size, + GLsizei samples, + GLenum internal_format, + const FeatureInfo* feature_info) { + ScopedRenderbufferBindingReset scoped_renderbuffer_reset; + + glBindRenderbufferEXT(GL_RENDERBUFFER, renderbuffer); + if (samples > 0) { + if (feature_info->feature_flags().angle_framebuffer_multisample) { + glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, samples, + internal_format, size.width(), + size.height()); + } else { + DCHECK(feature_info->gl_version_info().is_es3); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + internal_format, size.width(), + size.height()); + } + } else { + glRenderbufferStorageEXT(GL_RENDERBUFFER, internal_format, size.width(), + size.height()); + } +} + } // anonymous namespace PassthroughResources::PassthroughResources() {} @@ -90,6 +115,34 @@ void PassthroughResources::Destroy(bool have_context) { texture_object_map.clear(); } +ScopedFramebufferBindingReset::ScopedFramebufferBindingReset() + : draw_framebuffer_(0), read_framebuffer_(0) { + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer_); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer_); +} + +ScopedFramebufferBindingReset::~ScopedFramebufferBindingReset() { + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, draw_framebuffer_); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, read_framebuffer_); +} + +ScopedRenderbufferBindingReset::ScopedRenderbufferBindingReset() + : renderbuffer_(0) { + glGetIntegerv(GL_RENDERBUFFER_BINDING, &renderbuffer_); +} + +ScopedRenderbufferBindingReset::~ScopedRenderbufferBindingReset() { + glBindRenderbufferEXT(GL_RENDERBUFFER, renderbuffer_); +} + +ScopedTexture2DBindingReset::ScopedTexture2DBindingReset() : texture_(0) { + glGetIntegerv(GL_TEXTURE_2D_BINDING_EXT, &texture_); +} + +ScopedTexture2DBindingReset::~ScopedTexture2DBindingReset() { + glBindTexture(GL_TEXTURE_2D, texture_); +} + GLES2DecoderPassthroughImpl::PendingQuery::PendingQuery() = default; GLES2DecoderPassthroughImpl::PendingQuery::~PendingQuery() = default; GLES2DecoderPassthroughImpl::PendingQuery::PendingQuery(const PendingQuery&) = @@ -125,11 +178,237 @@ GLES2DecoderPassthroughImpl::BoundTexture::operator=(const BoundTexture&) = 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::EmulatedColorBuffer::EmulatedColorBuffer( + const EmulatedDefaultFramebufferFormat& format_in) + : format(format_in) { + ScopedTexture2DBindingReset scoped_texture_reset; + + GLuint color_buffer_texture = 0; + glGenTextures(1, &color_buffer_texture); + glBindTexture(GL_TEXTURE_2D, color_buffer_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + texture = new TexturePassthrough(color_buffer_texture, GL_TEXTURE_2D); +} + +GLES2DecoderPassthroughImpl::EmulatedColorBuffer::~EmulatedColorBuffer() = + default; + +bool GLES2DecoderPassthroughImpl::EmulatedColorBuffer::Resize( + const gfx::Size& new_size) { + if (size == new_size) { + return true; + } + size = new_size; + + ScopedTexture2DBindingReset scoped_texture_reset; + + DCHECK(texture); + DCHECK(texture->target() == GL_TEXTURE_2D); + + glBindTexture(texture->target(), texture->service_id()); + glTexImage2D(texture->target(), 0, format.color_texture_internal_format, + size.width(), size.height(), 0, format.color_texture_format, + format.color_texture_type, nullptr); + + return true; +} + +void GLES2DecoderPassthroughImpl::EmulatedColorBuffer::Destroy( + bool have_context) { + if (!have_context) { + texture->MarkContextLost(); + } + texture = nullptr; +} + +GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer:: + EmulatedDefaultFramebuffer( + const EmulatedDefaultFramebufferFormat& format_in, + const FeatureInfo* feature_info) + : format(format_in) { + ScopedFramebufferBindingReset scoped_fbo_reset; + ScopedRenderbufferBindingReset scoped_renderbuffer_reset; + + glGenFramebuffersEXT(1, &framebuffer_service_id); + glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_service_id); + + if (format.samples > 0) { + glGenRenderbuffersEXT(1, &color_buffer_service_id); + glBindRenderbufferEXT(GL_RENDERBUFFER, color_buffer_service_id); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, color_buffer_service_id); + } else { + color_texture.reset(new EmulatedColorBuffer(format)); + glFramebufferTexture2DEXT(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); + glGenRenderbuffersEXT(1, &depth_stencil_buffer_service_id); + glBindRenderbufferEXT(GL_RENDERBUFFER, depth_stencil_buffer_service_id); + if (feature_info->gl_version_info().IsAtLeastGLES(3, 0) || + feature_info->feature_flags().angle_webgl_compatibility) { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, + depth_stencil_buffer_service_id); + } else { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + depth_stencil_buffer_service_id); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, + depth_stencil_buffer_service_id); + } + } else { + if (format.depth_internal_format != GL_NONE) { + glGenRenderbuffersEXT(1, &depth_buffer_service_id); + glBindRenderbufferEXT(GL_RENDERBUFFER, depth_buffer_service_id); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, depth_buffer_service_id); + } + + if (format.stencil_internal_format != GL_NONE) { + glGenRenderbuffersEXT(1, &stencil_buffer_service_id); + glBindRenderbufferEXT(GL_RENDERBUFFER, stencil_buffer_service_id); + glFramebufferRenderbufferEXT(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; + glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_service_id); + glFramebufferTexture2DEXT(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; + + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, framebuffer_service_id); + + GLuint temp_fbo; + glGenFramebuffersEXT(1, &temp_fbo); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, temp_fbo); + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, target->texture->service_id(), 0); + + glBlitFramebufferANGLE(0, 0, size.width(), size.height(), 0, 0, + target->size.width(), target->size.height(), + GL_COLOR_BUFFER_BIT, GL_NEAREST); + + glDeleteFramebuffersEXT(1, &temp_fbo); +} + +bool GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer::Resize( + const gfx::Size& new_size, + const FeatureInfo* feature_info) { + if (size == new_size) { + return true; + } + size = new_size; + + if (color_buffer_service_id != 0) { + ResizeRenderbuffer(color_buffer_service_id, size, format.samples, + format.color_renderbuffer_internal_format, feature_info); + } + if (color_texture) { + if (!color_texture->Resize(size)) { + return false; + } + } + if (depth_stencil_buffer_service_id != 0) { + ResizeRenderbuffer(depth_stencil_buffer_service_id, size, format.samples, + format.depth_stencil_internal_format, feature_info); + } + if (depth_buffer_service_id != 0) { + ResizeRenderbuffer(depth_buffer_service_id, size, format.samples, + format.depth_internal_format, feature_info); + } + if (stencil_buffer_service_id != 0) { + ResizeRenderbuffer(stencil_buffer_service_id, size, format.samples, + format.stencil_internal_format, feature_info); + } + + // Check that the framebuffer is complete + { + ScopedFramebufferBindingReset scoped_fbo_reset; + glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_service_id); + if (glCheckFramebufferStatusEXT(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) { + glDeleteFramebuffersEXT(1, &framebuffer_service_id); + framebuffer_service_id = 0; + + glDeleteRenderbuffersEXT(1, &color_buffer_service_id); + color_buffer_service_id = 0; + + glDeleteRenderbuffersEXT(1, &depth_stencil_buffer_service_id); + color_buffer_service_id = 0; + + glDeleteRenderbuffersEXT(1, &depth_buffer_service_id); + depth_buffer_service_id = 0; + + glDeleteRenderbuffersEXT(1, &stencil_buffer_service_id); + stencil_buffer_service_id = 0; + } + if (color_texture) { + color_texture->Destroy(have_context); + } +} + GLES2DecoderPassthroughImpl::GLES2DecoderPassthroughImpl( GLES2DecoderClient* client, CommandBufferServiceBase* command_buffer_service, + Outputter* outputter, ContextGroup* group) - : GLES2Decoder(command_buffer_service), + : GLES2Decoder(command_buffer_service, outputter), client_(client), commands_to_process_(0), debug_marker_manager_(), @@ -139,6 +418,13 @@ GLES2DecoderPassthroughImpl::GLES2DecoderPassthroughImpl( offscreen_(false), group_(group), feature_info_(new FeatureInfo(group->feature_info()->workarounds())), + 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), @@ -147,6 +433,7 @@ GLES2DecoderPassthroughImpl::GLES2DecoderPassthroughImpl( 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); @@ -282,6 +569,61 @@ bool GLES2DecoderPassthroughImpl::Initialize( return false; } + // 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)) { + static constexpr const char* kEnableByDefaultExtensions[] = { + "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_bind_uniform_location", + "GL_CHROMIUM_framebuffer_mixed_samples", + "GL_CHROMIUM_path_rendering", + "GL_CHROMIUM_sync_query", + "GL_EXT_blend_minmax", + "GL_EXT_debug_marker", + "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_norm16", + "GL_EXT_texture_rg", + "GL_EXT_texture_sRGB_decode", + "GL_EXT_texture_storage", + "GL_EXT_unpack_subimage", + "GL_KHR_texture_compression_astc_hdr", + "GL_KHR_texture_compression_astc_ldr", + "GL_NV_fence", + "GL_NV_pack_subimage", + "GL_OES_compressed_ETC1_RGB8_texture", + "GL_OES_depth32", + "GL_OES_fbo_render_mipmap", + "GL_OES_packed_depth_stencil", + "GL_OES_rgb8_rgba8", + "GL_OES_vertex_array_object", + }; + + // Grab the extensions that are requestable + gl::ExtensionSet requestable_extensions( + gl::GetRequestableGLExtensionsFromCurrentContext()); + for (const char* default_extension : kEnableByDefaultExtensions) { + if (gl::HasExtension(requestable_extensions, default_extension)) { + // Request the intersection of the two sets + glRequestExtensionANGLE(default_extension); + } + } + 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. @@ -292,6 +634,9 @@ bool GLES2DecoderPassthroughImpl::Initialize( } // Check for required extensions + // TODO(geofflang): verify + // feature_info_->feature_flags().angle_robust_resource_initialization and + // glIsEnabled(GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE) if (!feature_info_->feature_flags().angle_robust_client_memory || !feature_info_->feature_flags().chromium_bind_generates_resource || !feature_info_->feature_flags().chromium_copy_texture || @@ -333,6 +678,9 @@ bool GLES2DecoderPassthroughImpl::Initialize( feature_info_->feature_flags().nv_egl_stream_consumer_external) { bound_textures_[GL_TEXTURE_EXTERNAL_OES].resize(num_texture_units); } + if (feature_info_->feature_flags().arb_texture_rectangle) { + bound_textures_[GL_TEXTURE_RECTANGLE_ARB].resize(num_texture_units); + } // Initialize the tracked buffer bindings bound_buffers_[GL_ARRAY_BUFFER] = 0; @@ -367,6 +715,87 @@ bool GLES2DecoderPassthroughImpl::Initialize( 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; + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_2d_texture_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_->gl_version_info().IsAtLeastGLES(3, 0) || + feature_info_->feature_flags().angle_framebuffer_multisample; + if (attrib_helper.samples > 0 && attrib_helper.sample_buffers > 0 && + multisampled_framebuffers_supported && !offscreen_single_buffer_) { + GLint max_sample_count = 0; + glGetIntegerv(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; + } + } + + FlushErrors(); + emulated_back_buffer_.reset(new EmulatedDefaultFramebuffer( + emulated_default_framebuffer_format_, feature_info_.get())); + if (!emulated_back_buffer_->Resize(attrib_helper.offscreen_framebuffer_size, + feature_info_.get())) { + Destroy(true); + return false; + } + + if (FlushErrors()) { + LOG(ERROR) << "Creation of the offscreen framebuffer failed because " + "errors were generated."; + Destroy(true); + return false; + } + + framebuffer_id_map_.SetIDMapping( + 0, emulated_back_buffer_->framebuffer_service_id); + + // Bind the emulated default framebuffer and initialize the viewport + glBindFramebufferEXT(GL_FRAMEBUFFER, + emulated_back_buffer_->framebuffer_service_id); + glViewport(0, 0, attrib_helper.offscreen_framebuffer_size.width(), + attrib_helper.offscreen_framebuffer_size.height()); + } set_initialized(); return true; @@ -375,6 +804,12 @@ bool GLES2DecoderPassthroughImpl::Initialize( void GLES2DecoderPassthroughImpl::Destroy(bool have_context) { if (have_context) { FlushErrors(); + + // Destroy all pending read pixels operations + for (const PendingReadPixels& pending_read_pixels : pending_read_pixels_) { + glDeleteBuffersARB(1, &pending_read_pixels.buffer_service_id); + } + pending_read_pixels_.clear(); } if (!have_context) { @@ -402,6 +837,27 @@ void GLES2DecoderPassthroughImpl::Destroy(bool have_context) { glDeleteVertexArraysOES(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(); + // Destroy the GPU Tracer which may own some in process GPU Timings. if (gpu_tracer_) { gpu_tracer_->Destroy(have_context); @@ -448,13 +904,116 @@ void GLES2DecoderPassthroughImpl::ReleaseSurface() { surface_ = nullptr; } -void GLES2DecoderPassthroughImpl::TakeFrontBuffer(const Mailbox& mailbox) {} +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_.get()) { + 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_.reset( + new EmulatedColorBuffer(emulated_default_framebuffer_format_)); + if (!emulated_front_buffer_->Resize(emulated_back_buffer_->size)) { + DLOG(ERROR) << "Failed to create a new emulated front buffer texture."; + return; + } + 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) {} + bool is_lost) { + TexturePassthrough* texture = static_cast<TexturePassthrough*>( + mailbox_manager_->ConsumeTexture(mailbox)); + + 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_2d_texture_size_ || + size.height() > max_2d_texture_size_) { + LOG(ERROR) << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer " + "failed to allocate storage due to excessive dimensions."; + return false; + } + + FlushErrors(); + + if (!emulated_back_buffer_->Resize(size, feature_info_.get())) { + LOG(ERROR) << "GLES2DecoderPassthroughImpl::ResizeOffscreenFramebuffer " + "failed to resize the emulated framebuffer."; + return false; + } + + if (FlushErrors()) { + 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; } @@ -573,6 +1132,7 @@ gpu::Capabilities GLES2DecoderPassthroughImpl::GetCapabilities() { caps.multisample_compatibility = feature_info_->feature_flags().ext_multisample_compatibility; caps.dc_layers = !offscreen_ && surface_->SupportsDCLayers(); + caps.texture_npot = feature_info_->feature_flags().npot_ok; // TODO: // caps.commit_overlay_planes @@ -626,11 +1186,11 @@ void GLES2DecoderPassthroughImpl::SetForceShaderNameHashingForTest(bool force) { } size_t GLES2DecoderPassthroughImpl::GetSavedBackTextureCountForTest() { - return 0; + return in_use_color_textures_.size() + available_color_textures_.size(); } size_t GLES2DecoderPassthroughImpl::GetCreatedBackTextureCountForTest() { - return 0; + return create_color_buffer_count_for_test_; } gpu::gles2::QueryManager* GLES2DecoderPassthroughImpl::GetQueryManager() { @@ -667,11 +1227,14 @@ void GLES2DecoderPassthroughImpl::ProcessPendingQueries(bool did_finish) { } bool GLES2DecoderPassthroughImpl::HasMoreIdleWork() const { - return gpu_tracer_->HasTracesToProcess(); + return gpu_tracer_->HasTracesToProcess() || !pending_read_pixels_.empty() || + !pending_queries_.empty(); } void GLES2DecoderPassthroughImpl::PerformIdleWork() { gpu_tracer_->ProcessTraces(); + ProcessReadPixels(false); + ProcessQueries(false); } bool GLES2DecoderPassthroughImpl::HasPollingWork() const { @@ -1015,6 +1578,14 @@ GLES2DecoderPassthroughImpl::PatchGetFramebufferAttachmentParameter( } } 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; } @@ -1038,24 +1609,46 @@ GLenum GLES2DecoderPassthroughImpl::PopError() { } bool GLES2DecoderPassthroughImpl::FlushErrors() { + auto get_next_error = [this]() { + // Always read a real GL error so that it can be replaced by the injected + // error + GLenum error = glGetError(); + if (!injected_driver_errors_.empty()) { + error = injected_driver_errors_.front(); + injected_driver_errors_.pop_front(); + } + return error; + }; + bool had_error = false; - GLenum error = glGetError(); + GLenum error = get_next_error(); 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() && CheckResetStatus()) { - MarkContextLost(error::kOutOfMemory); - group_->LoseContexts(error::kUnknown); + 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(); + error = get_next_error(); } return had_error; } +void GLES2DecoderPassthroughImpl::InjectDriverError(GLenum error) { + injected_driver_errors_.push_back(error); +} + bool GLES2DecoderPassthroughImpl::CheckResetStatus() { DCHECK(!WasContextLost()); DCHECK(context_->IsCurrent(nullptr)); @@ -1125,9 +1718,20 @@ error::Error GLES2DecoderPassthroughImpl::ProcessQueries(bool did_finish) { break; case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM: - // TODO: Use a fence and do a real async readback + // 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) { + result_available = GL_FALSE; + result = GL_FALSE; + break; + } + } break; case GL_GET_ERROR_QUERY_CHROMIUM: @@ -1188,6 +1792,69 @@ void GLES2DecoderPassthroughImpl::RemovePendingQuery(GLuint service_id) { } } +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) { + glDeleteBuffersARB(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) { + glDeleteBuffersARB(1, &pending_read_pixels.buffer_service_id); + pending_read_pixels_.pop_front(); + break; + } + + glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, + pending_read_pixels.buffer_service_id); + void* data = nullptr; + if (feature_info_->feature_flags().map_buffer_range) { + data = + glMapBufferRange(GL_PIXEL_PACK_BUFFER_ARB, 0, + pending_read_pixels.pixels_size, GL_MAP_READ_BIT); + } else { + data = glMapBuffer(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); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); + glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, + resources_->buffer_id_map.GetServiceIDOrInvalid( + bound_buffers_[GL_PIXEL_PACK_BUFFER_ARB])); + glDeleteBuffersARB(1, &pending_read_pixels.buffer_service_id); + + if (result != nullptr) { + result->success = 1; + } + + pending_read_pixels_.pop_front(); + } + } + + // If glFinish() has been called, all of our fences should be completed. + DCHECK(!did_finish || pending_read_pixels_.empty()); + return error::kNoError; +} + void GLES2DecoderPassthroughImpl::UpdateTextureBinding( GLenum target, GLuint client_id, @@ -1270,6 +1937,24 @@ error::Error GLES2DecoderPassthroughImpl::HandleRasterCHROMIUM( return error::kNoError; } +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; +} + #define GLES2_CMD_OP(name) \ { \ &GLES2DecoderPassthroughImpl::Handle##name, cmds::name::kArgFlags, \ @@ -1277,7 +1962,7 @@ error::Error GLES2DecoderPassthroughImpl::HandleRasterCHROMIUM( sizeof(cmds::name) / sizeof(CommandBufferEntry) - 1, \ }, /* NOLINT */ -const GLES2DecoderPassthroughImpl::CommandInfo +constexpr GLES2DecoderPassthroughImpl::CommandInfo GLES2DecoderPassthroughImpl::command_info[] = { GLES2_COMMAND_LIST(GLES2_CMD_OP)}; |