| // Copyright 2012 The Chromium Authors | |
| // 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/renderbuffer_manager.h" | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include "base/check_op.h" | |
| #include "base/compiler_specific.h" | |
| #include "base/format_macros.h" | |
| #include "base/strings/stringprintf.h" | |
| #include "base/task/single_thread_task_runner.h" | |
| #include "base/trace_event/memory_dump_manager.h" | |
| #include "base/trace_event/trace_event.h" | |
| #include "gpu/command_buffer/common/gles2_cmd_utils.h" | |
| #include "gpu/command_buffer/service/decoder_context.h" | |
| #include "gpu/command_buffer/service/feature_info.h" | |
| #include "gpu/command_buffer/service/framebuffer_manager.h" | |
| #include "gpu/command_buffer/service/gles2_cmd_decoder.h" | |
| #include "gpu/command_buffer/service/memory_tracking.h" | |
| #include "gpu/command_buffer/service/texture_manager.h" | |
| #include "gpu/config/gpu_driver_bug_workarounds.h" | |
| #include "ui/gl/gl_implementation.h" | |
| #include "ui/gl/gl_version_info.h" | |
| #include "ui/gl/trace_util.h" | |
| namespace gpu { | |
| namespace gles2 { | |
| // This should contain everything to uniquely identify a Renderbuffer. | |
| static const char RenderbufferTag[] = "|Renderbuffer|"; | |
| struct RenderbufferSignature { | |
| GLenum internal_format_; | |
| GLsizei samples_; | |
| GLsizei width_; | |
| GLsizei height_; | |
| // Since we will be hashing this signature structure, the padding must be | |
| // zero initialized. Although the C++11 specifications specify that this is | |
| // true, we will use a constructor with a memset to further enforce it instead | |
| // of relying on compilers adhering to this deep dark corner specification. | |
| RenderbufferSignature(GLenum internal_format, | |
| GLsizei samples, | |
| GLsizei width, | |
| GLsizei height) { | |
| UNSAFE_TODO(memset(this, 0, sizeof(RenderbufferSignature))); | |
| internal_format_ = internal_format; | |
| samples_ = samples; | |
| width_ = width; | |
| height_ = height; | |
| } | |
| }; | |
| RenderbufferManager::RenderbufferManager( | |
| scoped_refptr<MemoryTracker> memory_tracker, | |
| GLint max_renderbuffer_size, | |
| GLint max_samples, | |
| FeatureInfo* feature_info) | |
| : memory_type_tracker_(new MemoryTypeTracker(std::move(memory_tracker))), | |
| max_renderbuffer_size_(max_renderbuffer_size), | |
| max_samples_(max_samples), | |
| feature_info_(feature_info), | |
| num_uncleared_renderbuffers_(0), | |
| renderbuffer_count_(0), | |
| have_context_(true) { | |
| // When created from InProcessCommandBuffer, we won't have a |memory_tracker_| | |
| // so don't register a dump provider. | |
| if (memory_type_tracker_->memory_tracker()) { | |
| base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | |
| this, "gpu::RenderbufferManager", | |
| base::SingleThreadTaskRunner::GetCurrentDefault()); | |
| } | |
| } | |
| RenderbufferManager::~RenderbufferManager() { | |
| DCHECK(renderbuffers_.empty()); | |
| // If this triggers, that means something is keeping a reference to | |
| // a Renderbuffer belonging to this. | |
| CHECK_EQ(renderbuffer_count_, 0u); | |
| DCHECK_EQ(0, num_uncleared_renderbuffers_); | |
| base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | |
| this); | |
| } | |
| size_t Renderbuffer::EstimatedSize() { | |
| uint32_t size = 0; | |
| manager_->ComputeEstimatedRenderbufferSize( | |
| width_, height_, samples_, internal_format_, &size); | |
| return size; | |
| } | |
| size_t Renderbuffer::GetSignatureSize() const { | |
| return sizeof(RenderbufferTag) + sizeof(RenderbufferSignature); | |
| } | |
| void Renderbuffer::SetInfoAndInvalidate(GLsizei samples, | |
| GLenum internalformat, | |
| GLsizei width, | |
| GLsizei height) { | |
| samples_ = samples; | |
| internal_format_ = internalformat; | |
| width_ = width; | |
| height_ = height; | |
| cleared_ = false; | |
| allocated_ = true; | |
| for (auto& point : framebuffer_attachment_points_) { | |
| point.first->UnmarkAsComplete(); | |
| } | |
| } | |
| void Renderbuffer::SetAllocationFailed() { | |
| samples_ = 1; | |
| internal_format_ = 0x0; | |
| width_ = 0; | |
| height_ = 0; | |
| cleared_ = false; | |
| allocated_ = false; | |
| for (auto& point : framebuffer_attachment_points_) { | |
| point.first->UnmarkAsComplete(); | |
| } | |
| } | |
| void Renderbuffer::AddToSignature(std::string* signature) const { | |
| DCHECK(signature); | |
| RenderbufferSignature signature_data(internal_format_, | |
| samples_, | |
| width_, | |
| height_); | |
| signature->append(RenderbufferTag, sizeof(RenderbufferTag)); | |
| signature->append(reinterpret_cast<const char*>(&signature_data), | |
| sizeof(signature_data)); | |
| } | |
| Renderbuffer::Renderbuffer(RenderbufferManager* manager, | |
| GLuint client_id, | |
| GLuint service_id) | |
| : manager_(manager), | |
| client_id_(client_id), | |
| service_id_(service_id), | |
| cleared_(true), | |
| allocated_(false), | |
| has_been_bound_(false), | |
| samples_(0), | |
| internal_format_(GL_RGBA4), | |
| width_(0), | |
| height_(0) { | |
| manager_->StartTracking(this); | |
| } | |
| bool Renderbuffer::RegenerateAndBindBackingObjectIfNeeded( | |
| const DecoderContext* decoder, | |
| const GpuDriverBugWorkarounds& workarounds) { | |
| bool multisample_workaround = | |
| workarounds.multisample_renderbuffer_resize_emulation; | |
| if (!multisample_workaround) { | |
| return false; | |
| } | |
| if (!allocated_ || !has_been_bound_) { | |
| return false; | |
| } | |
| bool workaround_needed = (multisample_workaround && samples_ > 0); | |
| if (!workaround_needed) { | |
| return false; | |
| } | |
| GLint original_fbo = 0; | |
| glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &original_fbo); | |
| glDeleteRenderbuffersEXT(1, &service_id_); | |
| service_id_ = 0; | |
| glGenRenderbuffersEXT(1, &service_id_); | |
| glBindRenderbufferEXT(GL_RENDERBUFFER, service_id_); | |
| // Attach new renderbuffer to all framebuffers | |
| for (auto& point : framebuffer_attachment_points_) { | |
| decoder->BindFramebuffer(GL_DRAW_FRAMEBUFFER, point.first->service_id()); | |
| glFramebufferRenderbufferEXT(GL_DRAW_FRAMEBUFFER, point.second, | |
| GL_RENDERBUFFER, service_id_); | |
| } | |
| glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, original_fbo); | |
| allocated_ = false; | |
| return true; | |
| } | |
| void Renderbuffer::AddFramebufferAttachmentPoint(Framebuffer* framebuffer, | |
| GLenum attachment) { | |
| DCHECK_NE(static_cast<GLenum>(GL_DEPTH_STENCIL_ATTACHMENT), attachment); | |
| framebuffer_attachment_points_.insert( | |
| std::make_pair(framebuffer, attachment)); | |
| } | |
| void Renderbuffer::RemoveFramebufferAttachmentPoint(Framebuffer* framebuffer, | |
| GLenum attachment) { | |
| DCHECK_NE(static_cast<GLenum>(GL_DEPTH_STENCIL_ATTACHMENT), attachment); | |
| framebuffer_attachment_points_.erase(std::make_pair(framebuffer, attachment)); | |
| } | |
| Renderbuffer::~Renderbuffer() { | |
| if (manager_) { | |
| if (manager_->have_context_) { | |
| GLuint id = service_id(); | |
| glDeleteRenderbuffersEXT(1, &id); | |
| } | |
| manager_->StopTracking(this); | |
| manager_ = nullptr; | |
| } | |
| } | |
| void RenderbufferManager::Destroy(bool have_context) { | |
| have_context_ = have_context; | |
| renderbuffers_.clear(); | |
| DCHECK_EQ(0u, memory_type_tracker_->GetMemRepresented()); | |
| } | |
| void RenderbufferManager::StartTracking(Renderbuffer* /* renderbuffer */) { | |
| ++renderbuffer_count_; | |
| } | |
| void RenderbufferManager::StopTracking(Renderbuffer* renderbuffer) { | |
| --renderbuffer_count_; | |
| if (!renderbuffer->cleared()) { | |
| --num_uncleared_renderbuffers_; | |
| } | |
| memory_type_tracker_->TrackMemFree(renderbuffer->EstimatedSize()); | |
| } | |
| void RenderbufferManager::SetInfoAndInvalidate(Renderbuffer* renderbuffer, | |
| GLsizei samples, | |
| GLenum internalformat, | |
| GLsizei width, | |
| GLsizei height) { | |
| DCHECK(renderbuffer); | |
| if (!renderbuffer->cleared()) { | |
| --num_uncleared_renderbuffers_; | |
| } | |
| memory_type_tracker_->TrackMemFree(renderbuffer->EstimatedSize()); | |
| renderbuffer->SetInfoAndInvalidate(samples, internalformat, width, height); | |
| memory_type_tracker_->TrackMemAlloc(renderbuffer->EstimatedSize()); | |
| if (!renderbuffer->cleared()) { | |
| ++num_uncleared_renderbuffers_; | |
| } | |
| } | |
| void RenderbufferManager::SetAllocationFailed(Renderbuffer* renderbuffer) { | |
| DCHECK(renderbuffer); | |
| if (!renderbuffer->cleared()) { | |
| --num_uncleared_renderbuffers_; | |
| } | |
| memory_type_tracker_->TrackMemFree(renderbuffer->EstimatedSize()); | |
| renderbuffer->SetAllocationFailed(); | |
| memory_type_tracker_->TrackMemAlloc(renderbuffer->EstimatedSize()); | |
| if (!renderbuffer->cleared()) { | |
| ++num_uncleared_renderbuffers_; | |
| } | |
| } | |
| void RenderbufferManager::SetCleared(Renderbuffer* renderbuffer, | |
| bool cleared) { | |
| DCHECK(renderbuffer); | |
| if (!renderbuffer->cleared()) { | |
| --num_uncleared_renderbuffers_; | |
| } | |
| renderbuffer->set_cleared(cleared); | |
| if (!renderbuffer->cleared()) { | |
| ++num_uncleared_renderbuffers_; | |
| } | |
| } | |
| void RenderbufferManager::CreateRenderbuffer( | |
| GLuint client_id, GLuint service_id) { | |
| auto renderbuffer = | |
| base::MakeRefCounted<Renderbuffer>(this, client_id, service_id); | |
| std::pair<RenderbufferMap::iterator, bool> result = | |
| renderbuffers_.insert(std::make_pair(client_id, renderbuffer)); | |
| DCHECK(result.second); | |
| if (!renderbuffer->cleared()) { | |
| ++num_uncleared_renderbuffers_; | |
| } | |
| } | |
| Renderbuffer* RenderbufferManager::GetRenderbuffer( | |
| GLuint client_id) { | |
| RenderbufferMap::iterator it = renderbuffers_.find(client_id); | |
| return it != renderbuffers_.end() ? it->second.get() : nullptr; | |
| } | |
| void RenderbufferManager::RemoveRenderbuffer(GLuint client_id) { | |
| RenderbufferMap::iterator it = renderbuffers_.find(client_id); | |
| if (it != renderbuffers_.end()) { | |
| Renderbuffer* renderbuffer = it->second.get(); | |
| renderbuffer->MarkAsDeleted(); | |
| renderbuffers_.erase(it); | |
| } | |
| } | |
| bool RenderbufferManager::ComputeEstimatedRenderbufferSize( | |
| int width, | |
| int height, | |
| int samples, | |
| int internal_format, | |
| uint32_t* size) const { | |
| DCHECK(size); | |
| GLenum impl_format = InternalRenderbufferFormatToImplFormat(internal_format); | |
| uint32_t bytes_per_pixel = GLES2Util::RenderbufferBytesPerPixel(impl_format); | |
| base::CheckedNumeric<uint32_t> checked_size = width; | |
| checked_size *= height; | |
| checked_size *= (samples == 0 ? 1 : samples); | |
| checked_size *= bytes_per_pixel; | |
| return checked_size.AssignIfValid(size); | |
| } | |
| GLenum RenderbufferManager::InternalRenderbufferFormatToImplFormat( | |
| GLenum impl_format) const { | |
| // Upgrade 16-bit depth to 24-bit if possible. | |
| if (impl_format == GL_DEPTH_COMPONENT16 && | |
| feature_info_->feature_flags().oes_depth24) { | |
| return GL_DEPTH_COMPONENT24; | |
| } | |
| return impl_format; | |
| } | |
| bool RenderbufferManager::OnMemoryDump( | |
| const base::trace_event::MemoryDumpArgs& args, | |
| base::trace_event::ProcessMemoryDump* pmd) { | |
| using base::trace_event::MemoryAllocatorDump; | |
| using base::trace_event::MemoryDumpLevelOfDetail; | |
| const uint64_t context_group_tracing_id = | |
| memory_type_tracker_->memory_tracker() | |
| ? memory_type_tracker_->memory_tracker()->ContextGroupTracingId() | |
| : 0; | |
| if (args.level_of_detail == MemoryDumpLevelOfDetail::kBackground) { | |
| std::string dump_name = | |
| base::StringPrintf("gpu/gl/renderbuffers/context_group_0x%" PRIX64, | |
| context_group_tracing_id); | |
| MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); | |
| dump->AddScalar(MemoryAllocatorDump::kNameSize, | |
| MemoryAllocatorDump::kUnitsBytes, mem_represented()); | |
| // Early out, no need for more detail in a BACKGROUND dump. | |
| return true; | |
| } | |
| for (const auto& renderbuffer_entry : renderbuffers_) { | |
| const auto& client_renderbuffer_id = renderbuffer_entry.first; | |
| const auto& renderbuffer = renderbuffer_entry.second; | |
| std::string dump_name = | |
| base::StringPrintf("gpu/gl/renderbuffers/context_group_0x%" PRIX64 | |
| "/renderbuffer_0x%" PRIX32, | |
| context_group_tracing_id, client_renderbuffer_id); | |
| MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); | |
| dump->AddScalar(MemoryAllocatorDump::kNameSize, | |
| MemoryAllocatorDump::kUnitsBytes, | |
| static_cast<uint64_t>(renderbuffer->EstimatedSize())); | |
| auto guid = gl::GetGLRenderbufferGUIDForTracing(context_group_tracing_id, | |
| client_renderbuffer_id); | |
| pmd->CreateSharedGlobalAllocatorDump(guid); | |
| pmd->AddOwnershipEdge(dump->guid(), guid); | |
| } | |
| return true; | |
| } | |
| } // namespace gles2 | |
| } // namespace gpu |