| // 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 <stddef.h> |
| #include <stdint.h> |
| |
| #include "base/format_macros.h" |
| #include "base/logging.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/threading/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/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) { |
| 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, "gpu::RenderbufferManager", 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_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::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 GpuDriverBugWorkarounds& workarounds) { |
| // There are two workarounds which need this code path: |
| // depth_stencil_renderbuffer_resize_emulation |
| // multisample_renderbuffer_resize_emulation |
| bool multisample_workaround = |
| workarounds.multisample_renderbuffer_resize_emulation; |
| bool depth_stencil_workaround = |
| workarounds.depth_stencil_renderbuffer_resize_emulation; |
| if (!multisample_workaround && !depth_stencil_workaround) { |
| return false; |
| } |
| |
| if (!allocated_ || !has_been_bound_) { |
| return false; |
| } |
| |
| bool workaround_needed = (multisample_workaround && samples_ > 0) || |
| (depth_stencil_workaround && |
| TextureManager::ExtractFormatFromStorageFormat( |
| internal_format_) == GL_DEPTH_STENCIL); |
| |
| 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_) { |
| glBindFramebufferEXT(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::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> renderbuffer( |
| new 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); |
| |
| uint32_t temp = 0; |
| if (!SafeMultiplyUint32(width, height, &temp)) { |
| return false; |
| } |
| if (!SafeMultiplyUint32(temp, (samples == 0 ? 1 : 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) { |
| using base::trace_event::MemoryAllocatorDump; |
| using base::trace_event::MemoryDumpLevelOfDetail; |
| const uint64_t context_group_tracing_id = |
| memory_tracker_->ContextGroupTracingId(); |
| |
| if (args.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) { |
| 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 |