// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "gpu/command_buffer/service/renderbuffer_manager.h" #include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/thread_task_runner_handle.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/feature_info.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/memory_tracking.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) { memset(this, 0, sizeof(RenderbufferSignature)); internal_format_ = internal_format; samples_ = samples; width_ = width; height_ = height; } }; RenderbufferManager::RenderbufferManager(MemoryTracker* memory_tracker, GLint max_renderbuffer_size, GLint max_samples, FeatureInfo* feature_info) : memory_type_tracker_( new MemoryTypeTracker(memory_tracker)), memory_tracker_(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_tracker_) { base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( this, base::ThreadTaskRunnerHandle::Get()); } } 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 size = 0; manager_->ComputeEstimatedRenderbufferSize( width_, height_, samples_, internal_format_, &size); return size; } size_t Renderbuffer::GetSignatureSize() const { return sizeof(RenderbufferTag) + sizeof(RenderbufferSignature); } 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(&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), has_been_bound_(false), samples_(0), internal_format_(GL_RGBA4), width_(0), height_(0) { manager_->StartTracking(this); } Renderbuffer::~Renderbuffer() { if (manager_) { if (manager_->have_context_) { GLuint id = service_id(); glDeleteRenderbuffersEXT(1, &id); } manager_->StopTracking(this); manager_ = NULL; } } 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::SetInfo( 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->SetInfo(samples, internalformat, width, height); 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) { scoped_refptr renderbuffer( new Renderbuffer(this, client_id, service_id)); std::pair 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() : NULL; } 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* size) const { DCHECK(size); uint32 temp = 0; if (!SafeMultiplyUint32(width, height, &temp)) { return false; } if (!SafeMultiplyUint32(temp, samples, &temp)) { return false; } GLenum impl_format = InternalRenderbufferFormatToImplFormat(internal_format); if (!SafeMultiplyUint32( temp, GLES2Util::RenderbufferBytesPerPixel(impl_format), &temp)) { return false; } *size = temp; return true; } GLenum RenderbufferManager::InternalRenderbufferFormatToImplFormat( GLenum impl_format) const { if (!feature_info_->gl_version_info().BehavesLikeGLES()) { switch (impl_format) { case GL_DEPTH_COMPONENT16: return GL_DEPTH_COMPONENT; case GL_RGBA4: case GL_RGB5_A1: return GL_RGBA; case GL_RGB565: return GL_RGB; } } else { // 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) { int client_id = memory_tracker_->ClientId(); 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/client_%d/renderbuffer_%d", client_id, client_renderbuffer_id); base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, base::trace_event::MemoryAllocatorDump::kUnitsBytes, static_cast(renderbuffer->EstimatedSize())); auto guid = gfx::GetGLRenderbufferGUIDForTracing( memory_tracker_->ShareGroupTracingGUID(), client_renderbuffer_id); pmd->CreateSharedGlobalAllocatorDump(guid); pmd->AddOwnershipEdge(dump->guid(), guid); } return true; } } // namespace gles2 } // namespace gpu