// Copyright 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 "cc/resources/resource_provider.h" #include #include #include "base/containers/hash_tables.h" #include "base/debug/trace_event.h" #include "base/metrics/histogram.h" #include "base/stl_util.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "cc/base/util.h" #include "cc/output/gl_renderer.h" // For the GLC() macro. #include "cc/resources/platform_color.h" #include "cc/resources/returned_resource.h" #include "cc/resources/shared_bitmap_manager.h" #include "cc/resources/texture_uploader.h" #include "cc/resources/transferable_resource.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrContext.h" #include "ui/gfx/frame_time.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/gpu_memory_buffer.h" using gpu::gles2::GLES2Interface; namespace cc { class IdAllocator { public: virtual ~IdAllocator() {} virtual GLuint NextId() = 0; protected: IdAllocator(GLES2Interface* gl, size_t id_allocation_chunk_size) : gl_(gl), id_allocation_chunk_size_(id_allocation_chunk_size), ids_(new GLuint[id_allocation_chunk_size]), next_id_index_(id_allocation_chunk_size) { DCHECK(id_allocation_chunk_size_); } GLES2Interface* gl_; const size_t id_allocation_chunk_size_; scoped_ptr ids_; size_t next_id_index_; }; namespace { // Measured in seconds. const double kSoftwareUploadTickRate = 0.000250; const double kTextureUploadTickRate = 0.004; GLenum TextureToStorageFormat(ResourceFormat format) { GLenum storage_format = GL_RGBA8_OES; switch (format) { case RGBA_8888: break; case BGRA_8888: storage_format = GL_BGRA8_EXT; break; case RGBA_4444: case ALPHA_8: case LUMINANCE_8: case RGB_565: case ETC1: case RED_8: NOTREACHED(); break; } return storage_format; } bool IsFormatSupportedForStorage(ResourceFormat format, bool use_bgra) { switch (format) { case RGBA_8888: return true; case BGRA_8888: return use_bgra; case RGBA_4444: case ALPHA_8: case LUMINANCE_8: case RGB_565: case ETC1: case RED_8: return false; } return false; } GrPixelConfig ToGrPixelConfig(ResourceFormat format) { switch (format) { case RGBA_8888: return kRGBA_8888_GrPixelConfig; case BGRA_8888: return kBGRA_8888_GrPixelConfig; case RGBA_4444: return kRGBA_4444_GrPixelConfig; default: break; } DCHECK(false) << "Unsupported resource format."; return kSkia8888_GrPixelConfig; } gfx::GpuMemoryBuffer::Format ToGpuMemoryBufferFormat(ResourceFormat format) { switch (format) { case RGBA_8888: return gfx::GpuMemoryBuffer::Format::RGBA_8888; case BGRA_8888: return gfx::GpuMemoryBuffer::Format::BGRA_8888; case RGBA_4444: case ALPHA_8: case LUMINANCE_8: case RGB_565: case ETC1: case RED_8: break; } NOTREACHED(); return gfx::GpuMemoryBuffer::Format::RGBA_8888; } class ScopedSetActiveTexture { public: ScopedSetActiveTexture(GLES2Interface* gl, GLenum unit) : gl_(gl), unit_(unit) { DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(gl_)); if (unit_ != GL_TEXTURE0) GLC(gl_, gl_->ActiveTexture(unit_)); } ~ScopedSetActiveTexture() { // Active unit being GL_TEXTURE0 is effectively the ground state. if (unit_ != GL_TEXTURE0) GLC(gl_, gl_->ActiveTexture(GL_TEXTURE0)); } private: GLES2Interface* gl_; GLenum unit_; }; class TextureIdAllocator : public IdAllocator { public: TextureIdAllocator(GLES2Interface* gl, size_t texture_id_allocation_chunk_size) : IdAllocator(gl, texture_id_allocation_chunk_size) {} ~TextureIdAllocator() override { gl_->DeleteTextures(id_allocation_chunk_size_ - next_id_index_, ids_.get() + next_id_index_); } // Overridden from IdAllocator: GLuint NextId() override { if (next_id_index_ == id_allocation_chunk_size_) { gl_->GenTextures(id_allocation_chunk_size_, ids_.get()); next_id_index_ = 0; } return ids_[next_id_index_++]; } private: DISALLOW_COPY_AND_ASSIGN(TextureIdAllocator); }; class BufferIdAllocator : public IdAllocator { public: BufferIdAllocator(GLES2Interface* gl, size_t buffer_id_allocation_chunk_size) : IdAllocator(gl, buffer_id_allocation_chunk_size) {} ~BufferIdAllocator() override { gl_->DeleteBuffers(id_allocation_chunk_size_ - next_id_index_, ids_.get() + next_id_index_); } // Overridden from IdAllocator: GLuint NextId() override { if (next_id_index_ == id_allocation_chunk_size_) { gl_->GenBuffers(id_allocation_chunk_size_, ids_.get()); next_id_index_ = 0; } return ids_[next_id_index_++]; } private: DISALLOW_COPY_AND_ASSIGN(BufferIdAllocator); }; // Query object based fence implementation used to detect completion of copy // texture operations. Fence has passed when query result is available. class CopyTextureFence : public ResourceProvider::Fence { public: CopyTextureFence(gpu::gles2::GLES2Interface* gl, unsigned query_id) : gl_(gl), query_id_(query_id) {} // Overridden from ResourceProvider::Fence: void Set() override {} bool HasPassed() override { unsigned available = 1; gl_->GetQueryObjectuivEXT( query_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available); if (!available) return false; ProcessResult(); return true; } void Wait() override { // ProcessResult() will wait for result to become available. ProcessResult(); } private: ~CopyTextureFence() override {} void ProcessResult() { unsigned time_elapsed_us = 0; gl_->GetQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &time_elapsed_us); UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CopyTextureLatency", time_elapsed_us, 0, 256000, 50); } gpu::gles2::GLES2Interface* gl_; unsigned query_id_; DISALLOW_COPY_AND_ASSIGN(CopyTextureFence); }; } // namespace ResourceProvider::Resource::Resource() : child_id(0), gl_id(0), gl_pixel_buffer_id(0), gl_upload_query_id(0), gl_read_lock_query_id(0), pixels(NULL), lock_for_read_count(0), imported_count(0), exported_count(0), dirty_image(false), locked_for_write(false), lost(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), allocated(false), read_lock_fences_enabled(false), has_shared_bitmap_id(false), allow_overlay(false), read_lock_fence(NULL), size(), origin(Internal), target(0), original_filter(0), filter(0), image_id(0), bound_image_id(0), texture_pool(0), wrap_mode(0), hint(TextureHintImmutable), type(InvalidType), format(RGBA_8888), shared_bitmap(NULL), gpu_memory_buffer(NULL) { } ResourceProvider::Resource::~Resource() {} ResourceProvider::Resource::Resource(GLuint texture_id, const gfx::Size& size, Origin origin, GLenum target, GLenum filter, GLenum texture_pool, GLint wrap_mode, TextureHint hint, ResourceFormat format) : child_id(0), gl_id(texture_id), gl_pixel_buffer_id(0), gl_upload_query_id(0), gl_read_lock_query_id(0), pixels(NULL), lock_for_read_count(0), imported_count(0), exported_count(0), dirty_image(false), locked_for_write(false), lost(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), allocated(false), read_lock_fences_enabled(false), has_shared_bitmap_id(false), allow_overlay(false), read_lock_fence(NULL), size(size), origin(origin), target(target), original_filter(filter), filter(filter), image_id(0), bound_image_id(0), texture_pool(texture_pool), wrap_mode(wrap_mode), hint(hint), type(GLTexture), format(format), shared_bitmap(NULL), gpu_memory_buffer(NULL) { DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT); DCHECK_EQ(origin == Internal, !!texture_pool); } ResourceProvider::Resource::Resource(uint8_t* pixels, SharedBitmap* bitmap, const gfx::Size& size, Origin origin, GLenum filter, GLint wrap_mode) : child_id(0), gl_id(0), gl_pixel_buffer_id(0), gl_upload_query_id(0), gl_read_lock_query_id(0), pixels(pixels), lock_for_read_count(0), imported_count(0), exported_count(0), dirty_image(false), locked_for_write(false), lost(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), allocated(false), read_lock_fences_enabled(false), has_shared_bitmap_id(!!bitmap), allow_overlay(false), read_lock_fence(NULL), size(size), origin(origin), target(0), original_filter(filter), filter(filter), image_id(0), bound_image_id(0), texture_pool(0), wrap_mode(wrap_mode), hint(TextureHintImmutable), type(Bitmap), format(RGBA_8888), shared_bitmap(bitmap), gpu_memory_buffer(NULL) { DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT); DCHECK(origin == Delegated || pixels); if (bitmap) shared_bitmap_id = bitmap->id(); } ResourceProvider::Resource::Resource(const SharedBitmapId& bitmap_id, const gfx::Size& size, Origin origin, GLenum filter, GLint wrap_mode) : child_id(0), gl_id(0), gl_pixel_buffer_id(0), gl_upload_query_id(0), gl_read_lock_query_id(0), pixels(NULL), lock_for_read_count(0), imported_count(0), exported_count(0), dirty_image(false), locked_for_write(false), lost(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), allocated(false), read_lock_fences_enabled(false), has_shared_bitmap_id(true), allow_overlay(false), read_lock_fence(NULL), size(size), origin(origin), target(0), original_filter(filter), filter(filter), image_id(0), bound_image_id(0), texture_pool(0), wrap_mode(wrap_mode), hint(TextureHintImmutable), type(Bitmap), format(RGBA_8888), shared_bitmap_id(bitmap_id), shared_bitmap(NULL), gpu_memory_buffer(NULL) { DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT); } ResourceProvider::Child::Child() : marked_for_deletion(false) {} ResourceProvider::Child::~Child() {} scoped_ptr ResourceProvider::Create( OutputSurface* output_surface, SharedBitmapManager* shared_bitmap_manager, gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, BlockingTaskRunner* blocking_main_thread_task_runner, int highp_threshold_min, bool use_rgba_4444_texture_format, size_t id_allocation_chunk_size) { scoped_ptr resource_provider( new ResourceProvider(output_surface, shared_bitmap_manager, gpu_memory_buffer_manager, blocking_main_thread_task_runner, highp_threshold_min, use_rgba_4444_texture_format, id_allocation_chunk_size)); if (resource_provider->ContextGL()) resource_provider->InitializeGL(); else resource_provider->InitializeSoftware(); DCHECK_NE(InvalidType, resource_provider->default_resource_type()); return resource_provider.Pass(); } ResourceProvider::~ResourceProvider() { while (!children_.empty()) DestroyChildInternal(children_.begin(), ForShutdown); while (!resources_.empty()) DeleteResourceInternal(resources_.begin(), ForShutdown); CleanUpGLIfNeeded(); } bool ResourceProvider::InUseByConsumer(ResourceId id) { Resource* resource = GetResource(id); return resource->lock_for_read_count > 0 || resource->exported_count > 0 || resource->lost; } bool ResourceProvider::IsLost(ResourceId id) { Resource* resource = GetResource(id); return resource->lost; } bool ResourceProvider::AllowOverlay(ResourceId id) { Resource* resource = GetResource(id); return resource->allow_overlay; } ResourceProvider::ResourceId ResourceProvider::CreateResource( const gfx::Size& size, GLint wrap_mode, TextureHint hint, ResourceFormat format) { DCHECK(!size.IsEmpty()); switch (default_resource_type_) { case GLTexture: return CreateGLTexture(size, GL_TEXTURE_2D, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, wrap_mode, hint, format); case Bitmap: DCHECK_EQ(RGBA_8888, format); return CreateBitmap(size, wrap_mode); case InvalidType: break; } LOG(FATAL) << "Invalid default resource type."; return 0; } ResourceProvider::ResourceId ResourceProvider::CreateManagedResource( const gfx::Size& size, GLenum target, GLint wrap_mode, TextureHint hint, ResourceFormat format) { DCHECK(!size.IsEmpty()); switch (default_resource_type_) { case GLTexture: return CreateGLTexture(size, target, GL_TEXTURE_POOL_MANAGED_CHROMIUM, wrap_mode, hint, format); case Bitmap: DCHECK_EQ(RGBA_8888, format); return CreateBitmap(size, wrap_mode); case InvalidType: break; } LOG(FATAL) << "Invalid default resource type."; return 0; } ResourceProvider::ResourceId ResourceProvider::CreateGLTexture( const gfx::Size& size, GLenum target, GLenum texture_pool, GLint wrap_mode, TextureHint hint, ResourceFormat format) { DCHECK_LE(size.width(), max_texture_size_); DCHECK_LE(size.height(), max_texture_size_); DCHECK(thread_checker_.CalledOnValidThread()); ResourceId id = next_id_++; Resource resource(0, size, Resource::Internal, target, GL_LINEAR, texture_pool, wrap_mode, hint, format); resource.allocated = false; resources_[id] = resource; return id; } ResourceProvider::ResourceId ResourceProvider::CreateBitmap( const gfx::Size& size, GLint wrap_mode) { DCHECK(thread_checker_.CalledOnValidThread()); scoped_ptr bitmap = shared_bitmap_manager_->AllocateSharedBitmap(size); uint8_t* pixels = bitmap->pixels(); DCHECK(pixels); ResourceId id = next_id_++; Resource resource( pixels, bitmap.release(), size, Resource::Internal, GL_LINEAR, wrap_mode); resource.allocated = true; resources_[id] = resource; return id; } ResourceProvider::ResourceId ResourceProvider::CreateResourceFromIOSurface( const gfx::Size& size, unsigned io_surface_id) { DCHECK(thread_checker_.CalledOnValidThread()); ResourceId id = next_id_++; Resource resource(0, gfx::Size(), Resource::Internal, GL_TEXTURE_RECTANGLE_ARB, GL_LINEAR, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, GL_CLAMP_TO_EDGE, TextureHintImmutable, RGBA_8888); LazyCreate(&resource); GLES2Interface* gl = ContextGL(); DCHECK(gl); gl->BindTexture(GL_TEXTURE_RECTANGLE_ARB, resource.gl_id); gl->TexImageIOSurface2DCHROMIUM( GL_TEXTURE_RECTANGLE_ARB, size.width(), size.height(), io_surface_id, 0); resource.allocated = true; resources_[id] = resource; return id; } ResourceProvider::ResourceId ResourceProvider::CreateResourceFromTextureMailbox( const TextureMailbox& mailbox, scoped_ptr release_callback_impl) { DCHECK(thread_checker_.CalledOnValidThread()); // Just store the information. Mailbox will be consumed in LockForRead(). ResourceId id = next_id_++; DCHECK(mailbox.IsValid()); Resource& resource = resources_[id]; if (mailbox.IsTexture()) { resource = Resource(0, gfx::Size(), Resource::External, mailbox.target(), mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR, 0, GL_CLAMP_TO_EDGE, TextureHintImmutable, RGBA_8888); } else { DCHECK(mailbox.IsSharedMemory()); SharedBitmap* shared_bitmap = mailbox.shared_bitmap(); uint8_t* pixels = shared_bitmap->pixels(); DCHECK(pixels); resource = Resource(pixels, shared_bitmap, mailbox.shared_memory_size(), Resource::External, GL_LINEAR, GL_CLAMP_TO_EDGE); } resource.allocated = true; resource.mailbox = mailbox; resource.release_callback_impl = base::Bind(&SingleReleaseCallbackImpl::Run, base::Owned(release_callback_impl.release())); resource.allow_overlay = mailbox.allow_overlay(); return id; } void ResourceProvider::DeleteResource(ResourceId id) { DCHECK(thread_checker_.CalledOnValidThread()); ResourceMap::iterator it = resources_.find(id); CHECK(it != resources_.end()); Resource* resource = &it->second; DCHECK(!resource->marked_for_deletion); DCHECK_EQ(resource->imported_count, 0); DCHECK(resource->pending_set_pixels || !resource->locked_for_write); if (resource->exported_count > 0 || resource->lock_for_read_count > 0) { resource->marked_for_deletion = true; return; } else { DeleteResourceInternal(it, Normal); } } void ResourceProvider::DeleteResourceInternal(ResourceMap::iterator it, DeleteStyle style) { TRACE_EVENT0("cc", "ResourceProvider::DeleteResourceInternal"); Resource* resource = &it->second; bool lost_resource = resource->lost; DCHECK(resource->exported_count == 0 || style != Normal); if (style == ForShutdown && resource->exported_count > 0) lost_resource = true; if (resource->image_id) { DCHECK(resource->origin == Resource::Internal); GLES2Interface* gl = ContextGL(); DCHECK(gl); GLC(gl, gl->DestroyImageCHROMIUM(resource->image_id)); } if (resource->gl_upload_query_id) { DCHECK(resource->origin == Resource::Internal); GLES2Interface* gl = ContextGL(); DCHECK(gl); GLC(gl, gl->DeleteQueriesEXT(1, &resource->gl_upload_query_id)); } if (resource->gl_read_lock_query_id) { DCHECK(resource->origin == Resource::Internal); GLES2Interface* gl = ContextGL(); DCHECK(gl); GLC(gl, gl->DeleteQueriesEXT(1, &resource->gl_read_lock_query_id)); } if (resource->gl_pixel_buffer_id) { DCHECK(resource->origin == Resource::Internal); GLES2Interface* gl = ContextGL(); DCHECK(gl); GLC(gl, gl->DeleteBuffers(1, &resource->gl_pixel_buffer_id)); } if (resource->origin == Resource::External) { DCHECK(resource->mailbox.IsValid()); GLuint sync_point = resource->mailbox.sync_point(); if (resource->type == GLTexture) { DCHECK(resource->mailbox.IsTexture()); lost_resource |= lost_output_surface_; GLES2Interface* gl = ContextGL(); DCHECK(gl); if (resource->gl_id) { GLC(gl, gl->DeleteTextures(1, &resource->gl_id)); resource->gl_id = 0; if (!lost_resource) sync_point = gl->InsertSyncPointCHROMIUM(); } } else { DCHECK(resource->mailbox.IsSharedMemory()); resource->shared_bitmap = nullptr; resource->pixels = nullptr; } resource->release_callback_impl.Run( sync_point, lost_resource, blocking_main_thread_task_runner_); } if (resource->gl_id) { GLES2Interface* gl = ContextGL(); DCHECK(gl); GLC(gl, gl->DeleteTextures(1, &resource->gl_id)); resource->gl_id = 0; } if (resource->shared_bitmap) { DCHECK(resource->origin != Resource::External); DCHECK_EQ(Bitmap, resource->type); delete resource->shared_bitmap; resource->pixels = NULL; } if (resource->pixels) { DCHECK(resource->origin == Resource::Internal); delete[] resource->pixels; resource->pixels = NULL; } if (resource->gpu_memory_buffer) { DCHECK(resource->origin == Resource::Internal); delete resource->gpu_memory_buffer; resource->gpu_memory_buffer = NULL; } resources_.erase(it); } ResourceProvider::ResourceType ResourceProvider::GetResourceType( ResourceId id) { return GetResource(id)->type; } void ResourceProvider::SetPixels(ResourceId id, const uint8_t* image, const gfx::Rect& image_rect, const gfx::Rect& source_rect, const gfx::Vector2d& dest_offset) { Resource* resource = GetResource(id); DCHECK(!resource->locked_for_write); DCHECK(!resource->lock_for_read_count); DCHECK(resource->origin == Resource::Internal); DCHECK_EQ(resource->exported_count, 0); DCHECK(ReadLockFenceHasPassed(resource)); LazyAllocate(resource); if (resource->type == GLTexture) { DCHECK(resource->gl_id); DCHECK(!resource->pending_set_pixels); DCHECK_EQ(resource->target, static_cast(GL_TEXTURE_2D)); GLES2Interface* gl = ContextGL(); DCHECK(gl); DCHECK(texture_uploader_.get()); gl->BindTexture(GL_TEXTURE_2D, resource->gl_id); texture_uploader_->Upload(image, image_rect, source_rect, dest_offset, resource->format, resource->size); } else { DCHECK_EQ(Bitmap, resource->type); DCHECK(resource->allocated); DCHECK_EQ(RGBA_8888, resource->format); DCHECK(source_rect.x() >= image_rect.x()); DCHECK(source_rect.y() >= image_rect.y()); DCHECK(source_rect.right() <= image_rect.right()); DCHECK(source_rect.bottom() <= image_rect.bottom()); SkImageInfo source_info = SkImageInfo::MakeN32Premul(source_rect.width(), source_rect.height()); size_t image_row_bytes = image_rect.width() * 4; gfx::Vector2d source_offset = source_rect.origin() - image_rect.origin(); image += source_offset.y() * image_row_bytes + source_offset.x() * 4; ScopedWriteLockSoftware lock(this, id); SkCanvas dest(lock.sk_bitmap()); dest.writePixels(source_info, image, image_row_bytes, dest_offset.x(), dest_offset.y()); } } size_t ResourceProvider::NumBlockingUploads() { if (!texture_uploader_) return 0; return texture_uploader_->NumBlockingUploads(); } void ResourceProvider::MarkPendingUploadsAsNonBlocking() { if (!texture_uploader_) return; texture_uploader_->MarkPendingUploadsAsNonBlocking(); } size_t ResourceProvider::EstimatedUploadsPerTick() { if (!texture_uploader_) return 1u; double textures_per_second = texture_uploader_->EstimatedTexturesPerSecond(); size_t textures_per_tick = floor( kTextureUploadTickRate * textures_per_second); return textures_per_tick ? textures_per_tick : 1u; } void ResourceProvider::FlushUploads() { if (!texture_uploader_) return; texture_uploader_->Flush(); } void ResourceProvider::ReleaseCachedData() { if (!texture_uploader_) return; texture_uploader_->ReleaseCachedQueries(); } base::TimeTicks ResourceProvider::EstimatedUploadCompletionTime( size_t uploads_per_tick) { if (lost_output_surface_) return base::TimeTicks(); // Software resource uploads happen on impl thread, so don't bother batching // them up and trying to wait for them to complete. if (!texture_uploader_) { return gfx::FrameTime::Now() + base::TimeDelta::FromMicroseconds( base::Time::kMicrosecondsPerSecond * kSoftwareUploadTickRate); } base::TimeDelta upload_one_texture_time = base::TimeDelta::FromMicroseconds( base::Time::kMicrosecondsPerSecond * kTextureUploadTickRate) / uploads_per_tick; size_t total_uploads = NumBlockingUploads() + uploads_per_tick; return gfx::FrameTime::Now() + upload_one_texture_time * total_uploads; } ResourceProvider::Resource* ResourceProvider::GetResource(ResourceId id) { DCHECK(thread_checker_.CalledOnValidThread()); ResourceMap::iterator it = resources_.find(id); CHECK(it != resources_.end()); return &it->second; } const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->locked_for_write || resource->set_pixels_completion_forced) << "locked for write: " << resource->locked_for_write << " pixels completion forced: " << resource->set_pixels_completion_forced; DCHECK_EQ(resource->exported_count, 0); // Uninitialized! Call SetPixels or LockForWrite first. DCHECK(resource->allocated); LazyCreate(resource); if (resource->type == GLTexture && !resource->gl_id) { DCHECK(resource->origin != Resource::Internal); DCHECK(resource->mailbox.IsTexture()); // Mailbox sync_points must be processed by a call to // WaitSyncPointIfNeeded() prior to calling LockForRead(). DCHECK(!resource->mailbox.sync_point()); GLES2Interface* gl = ContextGL(); DCHECK(gl); resource->gl_id = texture_id_allocator_->NextId(); GLC(gl, gl->BindTexture(resource->target, resource->gl_id)); GLC(gl, gl->ConsumeTextureCHROMIUM(resource->mailbox.target(), resource->mailbox.name())); } if (!resource->pixels && resource->has_shared_bitmap_id && shared_bitmap_manager_) { scoped_ptr bitmap = shared_bitmap_manager_->GetSharedBitmapFromId( resource->size, resource->shared_bitmap_id); if (bitmap) { resource->shared_bitmap = bitmap.release(); resource->pixels = resource->shared_bitmap->pixels(); } } resource->lock_for_read_count++; if (resource->read_lock_fences_enabled) { if (current_read_lock_fence_.get()) current_read_lock_fence_->Set(); resource->read_lock_fence = current_read_lock_fence_; } return resource; } void ResourceProvider::UnlockForRead(ResourceId id) { DCHECK(thread_checker_.CalledOnValidThread()); ResourceMap::iterator it = resources_.find(id); CHECK(it != resources_.end()); Resource* resource = &it->second; DCHECK_GT(resource->lock_for_read_count, 0); DCHECK_EQ(resource->exported_count, 0); resource->lock_for_read_count--; if (resource->marked_for_deletion && !resource->lock_for_read_count) { if (!resource->child_id) { // The resource belongs to this ResourceProvider, so it can be destroyed. DeleteResourceInternal(it, Normal); } else { ChildMap::iterator child_it = children_.find(resource->child_id); ResourceIdArray unused; unused.push_back(id); DeleteAndReturnUnusedResourcesToChild(child_it, Normal, unused); } } } ResourceProvider::Resource* ResourceProvider::LockForWrite(ResourceId id) { Resource* resource = GetResource(id); DCHECK(CanLockForWrite(id)); resource->locked_for_write = true; return resource; } bool ResourceProvider::CanLockForWrite(ResourceId id) { Resource* resource = GetResource(id); return !resource->locked_for_write && !resource->lock_for_read_count && !resource->exported_count && resource->origin == Resource::Internal && !resource->lost && ReadLockFenceHasPassed(resource); } void ResourceProvider::UnlockForWrite(ResourceProvider::Resource* resource) { DCHECK(resource->locked_for_write); DCHECK_EQ(resource->exported_count, 0); DCHECK(resource->origin == Resource::Internal); resource->locked_for_write = false; } ResourceProvider::ScopedReadLockGL::ScopedReadLockGL( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_id_(resource_id), texture_id_(resource_provider->LockForRead(resource_id)->gl_id) { DCHECK(texture_id_); } ResourceProvider::ScopedReadLockGL::~ScopedReadLockGL() { resource_provider_->UnlockForRead(resource_id_); } ResourceProvider::ScopedSamplerGL::ScopedSamplerGL( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id, GLenum filter) : ScopedReadLockGL(resource_provider, resource_id), unit_(GL_TEXTURE0), target_(resource_provider->BindForSampling(resource_id, unit_, filter)) { } ResourceProvider::ScopedSamplerGL::ScopedSamplerGL( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id, GLenum unit, GLenum filter) : ScopedReadLockGL(resource_provider, resource_id), unit_(unit), target_(resource_provider->BindForSampling(resource_id, unit_, filter)) { } ResourceProvider::ScopedSamplerGL::~ScopedSamplerGL() { } ResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_(resource_provider->LockForWrite(resource_id)) { resource_provider_->LazyAllocate(resource_); texture_id_ = resource_->gl_id; DCHECK(texture_id_); } ResourceProvider::ScopedWriteLockGL::~ScopedWriteLockGL() { resource_provider_->UnlockForWrite(resource_); } void ResourceProvider::PopulateSkBitmapWithResource( SkBitmap* sk_bitmap, const Resource* resource) { DCHECK_EQ(RGBA_8888, resource->format); SkImageInfo info = SkImageInfo::MakeN32Premul(resource->size.width(), resource->size.height()); sk_bitmap->installPixels(info, resource->pixels, info.minRowBytes()); } ResourceProvider::ScopedReadLockSoftware::ScopedReadLockSoftware( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_id_(resource_id) { const Resource* resource = resource_provider->LockForRead(resource_id); wrap_mode_ = resource->wrap_mode; ResourceProvider::PopulateSkBitmapWithResource(&sk_bitmap_, resource); } ResourceProvider::ScopedReadLockSoftware::~ScopedReadLockSoftware() { resource_provider_->UnlockForRead(resource_id_); } ResourceProvider::ScopedWriteLockSoftware::ScopedWriteLockSoftware( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_(resource_provider->LockForWrite(resource_id)) { ResourceProvider::PopulateSkBitmapWithResource(&sk_bitmap_, resource_); DCHECK(valid()); } ResourceProvider::ScopedWriteLockSoftware::~ScopedWriteLockSoftware() { DCHECK(thread_checker_.CalledOnValidThread()); resource_provider_->UnlockForWrite(resource_); } ResourceProvider::ScopedWriteLockGpuMemoryBuffer:: ScopedWriteLockGpuMemoryBuffer(ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_(resource_provider->LockForWrite(resource_id)), gpu_memory_buffer_manager_(resource_provider->gpu_memory_buffer_manager_), gpu_memory_buffer_(nullptr), size_(resource_->size), format_(resource_->format) { DCHECK_EQ(GLTexture, resource_->type); std::swap(gpu_memory_buffer_, resource_->gpu_memory_buffer); } ResourceProvider::ScopedWriteLockGpuMemoryBuffer:: ~ScopedWriteLockGpuMemoryBuffer() { DCHECK(thread_checker_.CalledOnValidThread()); resource_provider_->UnlockForWrite(resource_); if (!gpu_memory_buffer_) return; if (!resource_->image_id) { GLES2Interface* gl = resource_provider_->ContextGL(); DCHECK(gl); resource_->image_id = gl->CreateImageCHROMIUM(gpu_memory_buffer_->AsClientBuffer(), size_.width(), size_.height(), GL_RGBA); } std::swap(resource_->gpu_memory_buffer, gpu_memory_buffer_); resource_->allocated = true; resource_->dirty_image = true; // GpuMemoryBuffer provides direct access to the memory used by the GPU. // Read lock fences are required to ensure that we're not trying to map a // buffer that is currently in-use by the GPU. resource_->read_lock_fences_enabled = true; } gfx::GpuMemoryBuffer* ResourceProvider::ScopedWriteLockGpuMemoryBuffer::GetGpuMemoryBuffer() { if (!gpu_memory_buffer_) { scoped_ptr gpu_memory_buffer = gpu_memory_buffer_manager_->AllocateGpuMemoryBuffer( size_, ToGpuMemoryBufferFormat(format_), gfx::GpuMemoryBuffer::MAP); gpu_memory_buffer_ = gpu_memory_buffer.release(); } return gpu_memory_buffer_; } ResourceProvider::ScopedWriteLockGr::ScopedWriteLockGr( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_(resource_provider->LockForWrite(resource_id)) { } ResourceProvider::ScopedWriteLockGr::~ScopedWriteLockGr() { DCHECK(thread_checker_.CalledOnValidThread()); resource_provider_->UnlockForWrite(resource_); } SkSurface* ResourceProvider::ScopedWriteLockGr::GetSkSurface( bool use_distance_field_text, bool can_use_lcd_text) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(resource_->locked_for_write); bool create_surface = !resource_->sk_surface.get() || !SurfaceHasMatchingProperties(use_distance_field_text, can_use_lcd_text); if (create_surface) { resource_provider_->LazyAllocate(resource_); GrBackendTextureDesc desc; desc.fFlags = kRenderTarget_GrBackendTextureFlag; desc.fWidth = resource_->size.width(); desc.fHeight = resource_->size.height(); desc.fConfig = ToGrPixelConfig(resource_->format); desc.fOrigin = kTopLeft_GrSurfaceOrigin; desc.fTextureHandle = resource_->gl_id; class GrContext* gr_context = resource_provider_->GrContext(); skia::RefPtr gr_texture = skia::AdoptRef(gr_context->wrapBackendTexture(desc)); if (!gr_texture) return nullptr; uint32_t flags = use_distance_field_text ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0; // Use unknown pixel geometry to disable LCD text. SkSurfaceProps surface_props(flags, kUnknown_SkPixelGeometry); if (can_use_lcd_text) { // LegacyFontHost will get LCD text and skia figures out what type to use. surface_props = SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType); } resource_->sk_surface = skia::AdoptRef(SkSurface::NewRenderTargetDirect( gr_texture->asRenderTarget(), &surface_props)); } return resource_->sk_surface.get(); } bool ResourceProvider::ScopedWriteLockGr::SurfaceHasMatchingProperties( bool use_distance_field_text, bool can_use_lcd_text) const { const SkSurface* surface = resource_->sk_surface.get(); bool surface_uses_distance_field_text = surface->props().isUseDistanceFieldFonts(); bool surface_can_use_lcd_text = surface->props().pixelGeometry() != kUnknown_SkPixelGeometry; return use_distance_field_text == surface_uses_distance_field_text && can_use_lcd_text == surface_can_use_lcd_text; } ResourceProvider::SynchronousFence::SynchronousFence( gpu::gles2::GLES2Interface* gl) : gl_(gl), has_synchronized_(true) { } ResourceProvider::SynchronousFence::~SynchronousFence() { } void ResourceProvider::SynchronousFence::Set() { has_synchronized_ = false; } bool ResourceProvider::SynchronousFence::HasPassed() { if (!has_synchronized_) { has_synchronized_ = true; Synchronize(); } return true; } void ResourceProvider::SynchronousFence::Wait() { HasPassed(); } void ResourceProvider::SynchronousFence::Synchronize() { TRACE_EVENT0("cc", "ResourceProvider::SynchronousFence::Synchronize"); gl_->Finish(); } ResourceProvider::ResourceProvider( OutputSurface* output_surface, SharedBitmapManager* shared_bitmap_manager, gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, BlockingTaskRunner* blocking_main_thread_task_runner, int highp_threshold_min, bool use_rgba_4444_texture_format, size_t id_allocation_chunk_size) : output_surface_(output_surface), shared_bitmap_manager_(shared_bitmap_manager), gpu_memory_buffer_manager_(gpu_memory_buffer_manager), blocking_main_thread_task_runner_(blocking_main_thread_task_runner), lost_output_surface_(false), highp_threshold_min_(highp_threshold_min), next_id_(1), next_child_(1), default_resource_type_(InvalidType), use_texture_storage_ext_(false), use_texture_format_bgra_(false), use_texture_usage_hint_(false), use_compressed_texture_etc1_(false), yuv_resource_format_(LUMINANCE_8), max_texture_size_(0), best_texture_format_(RGBA_8888), use_rgba_4444_texture_format_(use_rgba_4444_texture_format), id_allocation_chunk_size_(id_allocation_chunk_size), use_sync_query_(false) { DCHECK(output_surface_->HasClient()); DCHECK(id_allocation_chunk_size_); } void ResourceProvider::InitializeSoftware() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_NE(Bitmap, default_resource_type_); CleanUpGLIfNeeded(); default_resource_type_ = Bitmap; // Pick an arbitrary limit here similar to what hardware might. max_texture_size_ = 16 * 1024; best_texture_format_ = RGBA_8888; } void ResourceProvider::InitializeGL() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!texture_uploader_); DCHECK_NE(GLTexture, default_resource_type_); DCHECK(!texture_id_allocator_); DCHECK(!buffer_id_allocator_); default_resource_type_ = GLTexture; const ContextProvider::Capabilities& caps = output_surface_->context_provider()->ContextCapabilities(); bool use_bgra = caps.gpu.texture_format_bgra8888; use_texture_storage_ext_ = caps.gpu.texture_storage; use_texture_format_bgra_ = caps.gpu.texture_format_bgra8888; use_texture_usage_hint_ = caps.gpu.texture_usage; use_compressed_texture_etc1_ = caps.gpu.texture_format_etc1; yuv_resource_format_ = caps.gpu.texture_rg ? RED_8 : LUMINANCE_8; use_sync_query_ = caps.gpu.sync_query; GLES2Interface* gl = ContextGL(); DCHECK(gl); texture_uploader_ = TextureUploader::Create(gl); max_texture_size_ = 0; // Context expects cleared value. GLC(gl, gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_)); best_texture_format_ = PlatformColor::BestTextureFormat(use_bgra); texture_id_allocator_.reset( new TextureIdAllocator(gl, id_allocation_chunk_size_)); buffer_id_allocator_.reset( new BufferIdAllocator(gl, id_allocation_chunk_size_)); } void ResourceProvider::CleanUpGLIfNeeded() { GLES2Interface* gl = ContextGL(); if (default_resource_type_ != GLTexture) { // We are not in GL mode, but double check before returning. DCHECK(!gl); DCHECK(!texture_uploader_); return; } DCHECK(gl); #if DCHECK_IS_ON() // Check that all GL resources has been deleted. for (ResourceMap::const_iterator itr = resources_.begin(); itr != resources_.end(); ++itr) { DCHECK_NE(GLTexture, itr->second.type); } #endif // DCHECK_IS_ON() texture_uploader_ = nullptr; texture_id_allocator_ = nullptr; buffer_id_allocator_ = nullptr; gl->Finish(); } int ResourceProvider::CreateChild(const ReturnCallback& return_callback) { DCHECK(thread_checker_.CalledOnValidThread()); Child child_info; child_info.return_callback = return_callback; int child = next_child_++; children_[child] = child_info; return child; } void ResourceProvider::DestroyChild(int child_id) { ChildMap::iterator it = children_.find(child_id); DCHECK(it != children_.end()); DestroyChildInternal(it, Normal); } void ResourceProvider::DestroyChildInternal(ChildMap::iterator it, DeleteStyle style) { DCHECK(thread_checker_.CalledOnValidThread()); Child& child = it->second; DCHECK(style == ForShutdown || !child.marked_for_deletion); ResourceIdArray resources_for_child; for (ResourceIdMap::iterator child_it = child.child_to_parent_map.begin(); child_it != child.child_to_parent_map.end(); ++child_it) { ResourceId id = child_it->second; resources_for_child.push_back(id); } // If the child is going away, don't consider any resources in use. child.in_use_resources.clear(); child.marked_for_deletion = true; DeleteAndReturnUnusedResourcesToChild(it, style, resources_for_child); } const ResourceProvider::ResourceIdMap& ResourceProvider::GetChildToParentMap( int child) const { DCHECK(thread_checker_.CalledOnValidThread()); ChildMap::const_iterator it = children_.find(child); DCHECK(it != children_.end()); DCHECK(!it->second.marked_for_deletion); return it->second.child_to_parent_map; } void ResourceProvider::PrepareSendToParent(const ResourceIdArray& resources, TransferableResourceArray* list) { DCHECK(thread_checker_.CalledOnValidThread()); GLES2Interface* gl = ContextGL(); bool need_sync_point = false; for (ResourceIdArray::const_iterator it = resources.begin(); it != resources.end(); ++it) { TransferableResource resource; TransferResource(gl, *it, &resource); if (!resource.mailbox_holder.sync_point && !resource.is_software) need_sync_point = true; ++resources_.find(*it)->second.exported_count; list->push_back(resource); } if (need_sync_point) { GLuint sync_point = gl->InsertSyncPointCHROMIUM(); for (TransferableResourceArray::iterator it = list->begin(); it != list->end(); ++it) { if (!it->mailbox_holder.sync_point) it->mailbox_holder.sync_point = sync_point; } } } void ResourceProvider::ReceiveFromChild( int child, const TransferableResourceArray& resources) { DCHECK(thread_checker_.CalledOnValidThread()); GLES2Interface* gl = ContextGL(); Child& child_info = children_.find(child)->second; DCHECK(!child_info.marked_for_deletion); for (TransferableResourceArray::const_iterator it = resources.begin(); it != resources.end(); ++it) { ResourceIdMap::iterator resource_in_map_it = child_info.child_to_parent_map.find(it->id); if (resource_in_map_it != child_info.child_to_parent_map.end()) { Resource& resource = resources_[resource_in_map_it->second]; resource.marked_for_deletion = false; resource.imported_count++; continue; } if ((!it->is_software && !gl) || (it->is_software && !shared_bitmap_manager_)) { TRACE_EVENT0("cc", "ResourceProvider::ReceiveFromChild dropping invalid"); ReturnedResourceArray to_return; to_return.push_back(it->ToReturnedResource()); child_info.return_callback.Run(to_return, blocking_main_thread_task_runner_); continue; } ResourceId local_id = next_id_++; Resource& resource = resources_[local_id]; if (it->is_software) { resource = Resource(it->mailbox_holder.mailbox, it->size, Resource::Delegated, GL_LINEAR, it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE); } else { resource = Resource(0, it->size, Resource::Delegated, it->mailbox_holder.texture_target, it->filter, 0, it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE, TextureHintImmutable, it->format); resource.mailbox = TextureMailbox(it->mailbox_holder.mailbox, it->mailbox_holder.texture_target, it->mailbox_holder.sync_point); } resource.child_id = child; // Don't allocate a texture for a child. resource.allocated = true; resource.imported_count = 1; resource.allow_overlay = it->allow_overlay; child_info.parent_to_child_map[local_id] = it->id; child_info.child_to_parent_map[it->id] = local_id; } } void ResourceProvider::DeclareUsedResourcesFromChild( int child, const ResourceIdArray& resources_from_child) { DCHECK(thread_checker_.CalledOnValidThread()); ChildMap::iterator child_it = children_.find(child); DCHECK(child_it != children_.end()); Child& child_info = child_it->second; DCHECK(!child_info.marked_for_deletion); child_info.in_use_resources.clear(); for (size_t i = 0; i < resources_from_child.size(); ++i) { ResourceIdMap::iterator it = child_info.child_to_parent_map.find(resources_from_child[i]); DCHECK(it != child_info.child_to_parent_map.end()); ResourceId local_id = it->second; DCHECK(!resources_[local_id].marked_for_deletion); child_info.in_use_resources.insert(local_id); } ResourceIdArray unused; for (ResourceIdMap::iterator it = child_info.child_to_parent_map.begin(); it != child_info.child_to_parent_map.end(); ++it) { ResourceId local_id = it->second; bool resource_is_in_use = child_info.in_use_resources.count(local_id) > 0; if (!resource_is_in_use) unused.push_back(local_id); } DeleteAndReturnUnusedResourcesToChild(child_it, Normal, unused); } // static bool ResourceProvider::CompareResourceMapIteratorsByChildId( const std::pair& a, const std::pair& b) { const ResourceMap::iterator& a_it = a.second; const ResourceMap::iterator& b_it = b.second; const Resource& a_resource = a_it->second; const Resource& b_resource = b_it->second; return a_resource.child_id < b_resource.child_id; } void ResourceProvider::ReceiveReturnsFromParent( const ReturnedResourceArray& resources) { DCHECK(thread_checker_.CalledOnValidThread()); GLES2Interface* gl = ContextGL(); int child_id = 0; ResourceIdArray resources_for_child; std::vector> sorted_resources; for (ReturnedResourceArray::const_iterator it = resources.begin(); it != resources.end(); ++it) { ResourceId local_id = it->id; ResourceMap::iterator map_iterator = resources_.find(local_id); // Resource was already lost (e.g. it belonged to a child that was // destroyed). if (map_iterator == resources_.end()) continue; sorted_resources.push_back( std::pair(*it, map_iterator)); } std::sort(sorted_resources.begin(), sorted_resources.end(), CompareResourceMapIteratorsByChildId); ChildMap::iterator child_it = children_.end(); for (size_t i = 0; i < sorted_resources.size(); ++i) { ReturnedResource& returned = sorted_resources[i].first; ResourceMap::iterator& map_iterator = sorted_resources[i].second; ResourceId local_id = map_iterator->first; Resource* resource = &map_iterator->second; CHECK_GE(resource->exported_count, returned.count); resource->exported_count -= returned.count; resource->lost |= returned.lost; if (resource->exported_count) continue; // Need to wait for the current read lock fence to pass before we can // recycle this resource. if (resource->read_lock_fences_enabled) { if (current_read_lock_fence_.get()) current_read_lock_fence_->Set(); resource->read_lock_fence = current_read_lock_fence_; } if (returned.sync_point) { DCHECK(!resource->has_shared_bitmap_id); if (resource->origin == Resource::Internal) { DCHECK(resource->gl_id); GLC(gl, gl->WaitSyncPointCHROMIUM(returned.sync_point)); } else { DCHECK(!resource->gl_id); resource->mailbox.set_sync_point(returned.sync_point); } } if (!resource->marked_for_deletion) continue; if (!resource->child_id) { // The resource belongs to this ResourceProvider, so it can be destroyed. DeleteResourceInternal(map_iterator, Normal); continue; } DCHECK(resource->origin == Resource::Delegated); // Delete the resource and return it to the child it came from one. if (resource->child_id != child_id) { if (child_id) { DCHECK_NE(resources_for_child.size(), 0u); DCHECK(child_it != children_.end()); DeleteAndReturnUnusedResourcesToChild( child_it, Normal, resources_for_child); resources_for_child.clear(); } child_it = children_.find(resource->child_id); DCHECK(child_it != children_.end()); child_id = resource->child_id; } resources_for_child.push_back(local_id); } if (child_id) { DCHECK_NE(resources_for_child.size(), 0u); DCHECK(child_it != children_.end()); DeleteAndReturnUnusedResourcesToChild( child_it, Normal, resources_for_child); } } void ResourceProvider::TransferResource(GLES2Interface* gl, ResourceId id, TransferableResource* resource) { Resource* source = GetResource(id); DCHECK(!source->locked_for_write); DCHECK(!source->lock_for_read_count); DCHECK(source->origin != Resource::External || source->mailbox.IsValid()); DCHECK(source->allocated); resource->id = id; resource->format = source->format; resource->mailbox_holder.texture_target = source->target; resource->filter = source->filter; resource->size = source->size; resource->is_repeated = (source->wrap_mode == GL_REPEAT); resource->allow_overlay = source->allow_overlay; if (source->type == Bitmap) { resource->mailbox_holder.mailbox = source->shared_bitmap_id; resource->is_software = true; } else if (!source->mailbox.IsValid()) { LazyCreate(source); DCHECK(source->gl_id); DCHECK(source->origin == Resource::Internal); GLC(gl, gl->BindTexture(resource->mailbox_holder.texture_target, source->gl_id)); if (source->image_id) { DCHECK(source->dirty_image); BindImageForSampling(source); } // This is a resource allocated by the compositor, we need to produce it. // Don't set a sync point, the caller will do it. GLC(gl, gl->GenMailboxCHROMIUM(resource->mailbox_holder.mailbox.name)); GLC(gl, gl->ProduceTextureCHROMIUM(resource->mailbox_holder.texture_target, resource->mailbox_holder.mailbox.name)); source->mailbox = TextureMailbox(resource->mailbox_holder); } else { DCHECK(source->mailbox.IsTexture()); if (source->image_id && source->dirty_image) { DCHECK(source->gl_id); DCHECK(source->origin == Resource::Internal); GLC(gl, gl->BindTexture(resource->mailbox_holder.texture_target, source->gl_id)); BindImageForSampling(source); } // This is either an external resource, or a compositor resource that we // already exported. Make sure to forward the sync point that we were given. resource->mailbox_holder.mailbox = source->mailbox.mailbox(); resource->mailbox_holder.texture_target = source->mailbox.target(); resource->mailbox_holder.sync_point = source->mailbox.sync_point(); source->mailbox.set_sync_point(0); } } void ResourceProvider::DeleteAndReturnUnusedResourcesToChild( ChildMap::iterator child_it, DeleteStyle style, const ResourceIdArray& unused) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(child_it != children_.end()); Child* child_info = &child_it->second; if (unused.empty() && !child_info->marked_for_deletion) return; ReturnedResourceArray to_return; GLES2Interface* gl = ContextGL(); bool need_sync_point = false; for (size_t i = 0; i < unused.size(); ++i) { ResourceId local_id = unused[i]; ResourceMap::iterator it = resources_.find(local_id); CHECK(it != resources_.end()); Resource& resource = it->second; DCHECK(!resource.locked_for_write); DCHECK_EQ(0u, child_info->in_use_resources.count(local_id)); DCHECK(child_info->parent_to_child_map.count(local_id)); ResourceId child_id = child_info->parent_to_child_map[local_id]; DCHECK(child_info->child_to_parent_map.count(child_id)); bool is_lost = resource.lost || (resource.type == GLTexture && lost_output_surface_); if (resource.exported_count > 0 || resource.lock_for_read_count > 0) { if (style != ForShutdown) { // Defer this until we receive the resource back from the parent or // the read lock is released. resource.marked_for_deletion = true; continue; } // We still have an exported_count, so we'll have to lose it. is_lost = true; } if (gl && resource.filter != resource.original_filter) { DCHECK(resource.target); DCHECK(resource.gl_id); GLC(gl, gl->BindTexture(resource.target, resource.gl_id)); GLC(gl, gl->TexParameteri(resource.target, GL_TEXTURE_MIN_FILTER, resource.original_filter)); GLC(gl, gl->TexParameteri(resource.target, GL_TEXTURE_MAG_FILTER, resource.original_filter)); } ReturnedResource returned; returned.id = child_id; returned.sync_point = resource.mailbox.sync_point(); if (!returned.sync_point && resource.type == GLTexture) need_sync_point = true; returned.count = resource.imported_count; returned.lost = is_lost; to_return.push_back(returned); child_info->parent_to_child_map.erase(local_id); child_info->child_to_parent_map.erase(child_id); resource.imported_count = 0; DeleteResourceInternal(it, style); } if (need_sync_point) { DCHECK(gl); GLuint sync_point = gl->InsertSyncPointCHROMIUM(); for (size_t i = 0; i < to_return.size(); ++i) { if (!to_return[i].sync_point) to_return[i].sync_point = sync_point; } } if (!to_return.empty()) child_info->return_callback.Run(to_return, blocking_main_thread_task_runner_); if (child_info->marked_for_deletion && child_info->parent_to_child_map.empty()) { DCHECK(child_info->child_to_parent_map.empty()); children_.erase(child_it); } } void ResourceProvider::AcquirePixelBuffer(ResourceId id) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ResourceProvider::AcquirePixelBuffer"); Resource* resource = GetResource(id); DCHECK(resource->origin == Resource::Internal); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); DCHECK_NE(ETC1, resource->format); DCHECK_EQ(GLTexture, resource->type); GLES2Interface* gl = ContextGL(); DCHECK(gl); if (!resource->gl_pixel_buffer_id) resource->gl_pixel_buffer_id = buffer_id_allocator_->NextId(); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); unsigned bytes_per_pixel = BitsPerPixel(resource->format) / 8; gl->BufferData(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->size.height() * RoundUp(bytes_per_pixel * resource->size.width(), 4u), NULL, GL_DYNAMIC_DRAW); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } void ResourceProvider::ReleasePixelBuffer(ResourceId id) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ResourceProvider::ReleasePixelBuffer"); Resource* resource = GetResource(id); DCHECK(resource->origin == Resource::Internal); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); // The pixel buffer can be released while there is a pending "set pixels" // if completion has been forced. Any shared memory associated with this // pixel buffer will not be freed until the waitAsyncTexImage2DCHROMIUM // command has been processed on the service side. It is also safe to // reuse any query id associated with this resource before they complete // as each new query has a unique submit count. if (resource->pending_set_pixels) { DCHECK(resource->set_pixels_completion_forced); resource->pending_set_pixels = false; resource->locked_for_write = false; } DCHECK_EQ(GLTexture, resource->type); if (!resource->gl_pixel_buffer_id) return; GLES2Interface* gl = ContextGL(); DCHECK(gl); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); gl->BufferData( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0, NULL, GL_DYNAMIC_DRAW); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } uint8_t* ResourceProvider::MapPixelBuffer(ResourceId id, int* stride) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ResourceProvider::MapPixelBuffer"); Resource* resource = GetResource(id); DCHECK(resource->origin == Resource::Internal); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); *stride = 0; DCHECK_EQ(GLTexture, resource->type); GLES2Interface* gl = ContextGL(); DCHECK(gl); DCHECK(resource->gl_pixel_buffer_id); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); uint8_t* image = static_cast(gl->MapBufferCHROMIUM( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, GL_WRITE_ONLY)); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); // Buffer is required to be 4-byte aligned. CHECK(!(reinterpret_cast(image) & 3)); return image; } void ResourceProvider::UnmapPixelBuffer(ResourceId id) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ResourceProvider::UnmapPixelBuffer"); Resource* resource = GetResource(id); DCHECK(resource->origin == Resource::Internal); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); DCHECK_EQ(GLTexture, resource->type); GLES2Interface* gl = ContextGL(); DCHECK(gl); DCHECK(resource->gl_pixel_buffer_id); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); gl->UnmapBufferCHROMIUM(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } GLenum ResourceProvider::BindForSampling(ResourceId resource_id, GLenum unit, GLenum filter) { DCHECK(thread_checker_.CalledOnValidThread()); GLES2Interface* gl = ContextGL(); ResourceMap::iterator it = resources_.find(resource_id); DCHECK(it != resources_.end()); Resource* resource = &it->second; DCHECK(resource->lock_for_read_count); DCHECK(!resource->locked_for_write || resource->set_pixels_completion_forced); ScopedSetActiveTexture scoped_active_tex(gl, unit); GLenum target = resource->target; GLC(gl, gl->BindTexture(target, resource->gl_id)); if (filter != resource->filter) { GLC(gl, gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter)); GLC(gl, gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter)); resource->filter = filter; } if (resource->image_id && resource->dirty_image) BindImageForSampling(resource); return target; } void ResourceProvider::BeginSetPixels(ResourceId id) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ResourceProvider::BeginSetPixels"); Resource* resource = GetResource(id); DCHECK(!resource->pending_set_pixels); LazyCreate(resource); DCHECK(resource->origin == Resource::Internal); DCHECK(resource->gl_id || resource->allocated); DCHECK(ReadLockFenceHasPassed(resource)); DCHECK(!resource->image_id); bool allocate = !resource->allocated; resource->allocated = true; LockForWrite(id); DCHECK_EQ(GLTexture, resource->type); DCHECK(resource->gl_id); GLES2Interface* gl = ContextGL(); DCHECK(gl); DCHECK(resource->gl_pixel_buffer_id); DCHECK_EQ(resource->target, static_cast(GL_TEXTURE_2D)); gl->BindTexture(GL_TEXTURE_2D, resource->gl_id); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); if (!resource->gl_upload_query_id) gl->GenQueriesEXT(1, &resource->gl_upload_query_id); gl->BeginQueryEXT(GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM, resource->gl_upload_query_id); if (allocate) { gl->AsyncTexImage2DCHROMIUM(GL_TEXTURE_2D, 0, /* level */ GLInternalFormat(resource->format), resource->size.width(), resource->size.height(), 0, /* border */ GLDataFormat(resource->format), GLDataType(resource->format), NULL); } else { gl->AsyncTexSubImage2DCHROMIUM(GL_TEXTURE_2D, 0, /* level */ 0, /* x */ 0, /* y */ resource->size.width(), resource->size.height(), GLDataFormat(resource->format), GLDataType(resource->format), NULL); } gl->EndQueryEXT(GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); resource->pending_set_pixels = true; resource->set_pixels_completion_forced = false; } void ResourceProvider::ForceSetPixelsToComplete(ResourceId id) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ResourceProvider::ForceSetPixelsToComplete"); Resource* resource = GetResource(id); DCHECK(resource->locked_for_write); DCHECK(resource->pending_set_pixels); DCHECK(!resource->set_pixels_completion_forced); if (resource->gl_id) { GLES2Interface* gl = ContextGL(); GLC(gl, gl->BindTexture(GL_TEXTURE_2D, resource->gl_id)); GLC(gl, gl->WaitAsyncTexImage2DCHROMIUM(GL_TEXTURE_2D)); GLC(gl, gl->BindTexture(GL_TEXTURE_2D, 0)); } resource->set_pixels_completion_forced = true; } bool ResourceProvider::DidSetPixelsComplete(ResourceId id) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ResourceProvider::DidSetPixelsComplete"); Resource* resource = GetResource(id); DCHECK(resource->locked_for_write); DCHECK(resource->pending_set_pixels); if (resource->gl_id) { GLES2Interface* gl = ContextGL(); DCHECK(gl); DCHECK(resource->gl_upload_query_id); GLuint complete = 1; gl->GetQueryObjectuivEXT( resource->gl_upload_query_id, GL_QUERY_RESULT_AVAILABLE_EXT, &complete); if (!complete) return false; } resource->pending_set_pixels = false; UnlockForWrite(resource); // Async set pixels commands are not necessarily processed in-sequence with // drawing commands. Read lock fences are required to ensure that async // commands don't access the resource while used for drawing. resource->read_lock_fences_enabled = true; return true; } void ResourceProvider::CreateForTesting(ResourceId id) { LazyCreate(GetResource(id)); } GLenum ResourceProvider::TargetForTesting(ResourceId id) { Resource* resource = GetResource(id); return resource->target; } void ResourceProvider::LazyCreate(Resource* resource) { if (resource->type != GLTexture || resource->origin != Resource::Internal) return; if (resource->gl_id) return; DCHECK(resource->texture_pool); DCHECK(resource->origin == Resource::Internal); DCHECK(!resource->mailbox.IsValid()); resource->gl_id = texture_id_allocator_->NextId(); GLES2Interface* gl = ContextGL(); DCHECK(gl); // Create and set texture properties. Allocation is delayed until needed. GLC(gl, gl->BindTexture(resource->target, resource->gl_id)); GLC(gl, gl->TexParameteri( resource->target, GL_TEXTURE_MIN_FILTER, resource->original_filter)); GLC(gl, gl->TexParameteri( resource->target, GL_TEXTURE_MAG_FILTER, resource->original_filter)); GLC(gl, gl->TexParameteri( resource->target, GL_TEXTURE_WRAP_S, resource->wrap_mode)); GLC(gl, gl->TexParameteri( resource->target, GL_TEXTURE_WRAP_T, resource->wrap_mode)); GLC(gl, gl->TexParameteri( resource->target, GL_TEXTURE_POOL_CHROMIUM, resource->texture_pool)); if (use_texture_usage_hint_ && (resource->hint & TextureHintFramebuffer)) { GLC(gl, gl->TexParameteri(resource->target, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE)); } } void ResourceProvider::AllocateForTesting(ResourceId id) { LazyAllocate(GetResource(id)); } void ResourceProvider::LazyAllocate(Resource* resource) { DCHECK(resource); if (resource->allocated) return; LazyCreate(resource); if (!resource->gl_id) return; resource->allocated = true; GLES2Interface* gl = ContextGL(); gfx::Size& size = resource->size; DCHECK_EQ(resource->target, static_cast(GL_TEXTURE_2D)); ResourceFormat format = resource->format; GLC(gl, gl->BindTexture(GL_TEXTURE_2D, resource->gl_id)); if (use_texture_storage_ext_ && IsFormatSupportedForStorage(format, use_texture_format_bgra_) && (resource->hint & TextureHintImmutable)) { GLenum storage_format = TextureToStorageFormat(format); GLC(gl, gl->TexStorage2DEXT( GL_TEXTURE_2D, 1, storage_format, size.width(), size.height())); } else { // ETC1 does not support preallocation. if (format != ETC1) { GLC(gl, gl->TexImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(format), size.width(), size.height(), 0, GLDataFormat(format), GLDataType(format), NULL)); } } } void ResourceProvider::BindImageForSampling(Resource* resource) { GLES2Interface* gl = ContextGL(); DCHECK(resource->gl_id); DCHECK(resource->image_id); // Release image currently bound to texture. if (resource->bound_image_id) gl->ReleaseTexImage2DCHROMIUM(resource->target, resource->bound_image_id); gl->BindTexImage2DCHROMIUM(resource->target, resource->image_id); resource->bound_image_id = resource->image_id; resource->dirty_image = false; } void ResourceProvider::CopyResource(ResourceId source_id, ResourceId dest_id) { TRACE_EVENT0("cc", "ResourceProvider::CopyResource"); Resource* source_resource = GetResource(source_id); DCHECK(!source_resource->lock_for_read_count); DCHECK(source_resource->origin == Resource::Internal); DCHECK_EQ(source_resource->exported_count, 0); DCHECK_EQ(GLTexture, source_resource->type); DCHECK(source_resource->allocated); LazyCreate(source_resource); Resource* dest_resource = GetResource(dest_id); DCHECK(!dest_resource->locked_for_write); DCHECK(!dest_resource->lock_for_read_count); DCHECK(dest_resource->origin == Resource::Internal); DCHECK_EQ(dest_resource->exported_count, 0); DCHECK_EQ(GLTexture, dest_resource->type); LazyAllocate(dest_resource); DCHECK_EQ(source_resource->type, dest_resource->type); DCHECK_EQ(source_resource->format, dest_resource->format); DCHECK(source_resource->size == dest_resource->size); GLES2Interface* gl = ContextGL(); DCHECK(gl); if (source_resource->image_id && source_resource->dirty_image) { gl->BindTexture(source_resource->target, source_resource->gl_id); BindImageForSampling(source_resource); } if (use_sync_query_) { if (!source_resource->gl_read_lock_query_id) gl->GenQueriesEXT(1, &source_resource->gl_read_lock_query_id); gl->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, source_resource->gl_read_lock_query_id); } DCHECK(!dest_resource->image_id); dest_resource->allocated = true; gl->CopyTextureCHROMIUM(dest_resource->target, source_resource->gl_id, dest_resource->gl_id, 0, GLInternalFormat(dest_resource->format), GLDataType(dest_resource->format)); if (source_resource->gl_read_lock_query_id) { // End query and create a read lock fence that will prevent access to // source resource until CopyTextureCHROMIUM command has completed. gl->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM); source_resource->read_lock_fence = make_scoped_refptr( new CopyTextureFence(gl, source_resource->gl_read_lock_query_id)); } else { // Create a SynchronousFence when CHROMIUM_sync_query extension is missing. // Try to use one synchronous fence for as many CopyResource operations as // possible as that reduce the number of times we have to synchronize with // the GL. if (!synchronous_fence_.get() || synchronous_fence_->has_synchronized()) synchronous_fence_ = make_scoped_refptr(new SynchronousFence(gl)); source_resource->read_lock_fence = synchronous_fence_; source_resource->read_lock_fence->Set(); } } void ResourceProvider::WaitSyncPointIfNeeded(ResourceId id) { Resource* resource = GetResource(id); DCHECK_EQ(resource->exported_count, 0); DCHECK(resource->allocated); if (resource->type != GLTexture || resource->gl_id) return; if (!resource->mailbox.sync_point()) return; DCHECK(resource->mailbox.IsValid()); GLES2Interface* gl = ContextGL(); DCHECK(gl); GLC(gl, gl->WaitSyncPointCHROMIUM(resource->mailbox.sync_point())); resource->mailbox.set_sync_point(0); } void ResourceProvider::WaitReadLockIfNeeded(ResourceId id) { Resource* resource = GetResource(id); DCHECK_EQ(resource->exported_count, 0); if (!resource->read_lock_fence.get()) return; resource->read_lock_fence->Wait(); } GLint ResourceProvider::GetActiveTextureUnit(GLES2Interface* gl) { GLint active_unit = 0; gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit); return active_unit; } GLES2Interface* ResourceProvider::ContextGL() const { ContextProvider* context_provider = output_surface_->context_provider(); return context_provider ? context_provider->ContextGL() : NULL; } class GrContext* ResourceProvider::GrContext() const { ContextProvider* context_provider = output_surface_->context_provider(); return context_provider ? context_provider->GrContext() : NULL; } } // namespace cc