summaryrefslogtreecommitdiff
path: root/chromium/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
diff options
context:
space:
mode:
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.cc711
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)};