// 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/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/transferable_resource.h" #include "cc/scheduler/texture_uploader.h" #include "gpu/GLES2/gl2extchromium.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" #include "ui/gfx/rect.h" #include "ui/gfx/vector2d.h" using WebKit::WebGraphicsContext3D; namespace cc { 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 LUMINANCE_8: case RGB_565: case ETC1: NOTREACHED(); break; } return storage_format; } bool IsFormatSupportedForStorage(ResourceFormat format) { switch (format) { case RGBA_8888: case BGRA_8888: return true; case RGBA_4444: case LUMINANCE_8: case RGB_565: case ETC1: return false; } return false; } class ScopedSetActiveTexture { public: ScopedSetActiveTexture(WebGraphicsContext3D* context3d, GLenum unit) : context3d_(context3d), unit_(unit) { DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(context3d_)); if (unit_ != GL_TEXTURE0) GLC(context3d_, context3d_->activeTexture(unit_)); } ~ScopedSetActiveTexture() { // Active unit being GL_TEXTURE0 is effectively the ground state. if (unit_ != GL_TEXTURE0) GLC(context3d_, context3d_->activeTexture(GL_TEXTURE0)); } private: WebGraphicsContext3D* context3d_; GLenum unit_; }; } // namespace ResourceProvider::Resource::Resource() : child_id(0), gl_id(0), gl_pixel_buffer_id(0), gl_upload_query_id(0), pixels(NULL), pixel_buffer(NULL), lock_for_read_count(0), imported_count(0), exported_count(0), locked_for_write(false), external(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), allocated(false), enable_read_lock_fences(false), read_lock_fence(NULL), size(), target(0), original_filter(0), filter(0), image_id(0), bound_image_id(0), dirty_image(false), texture_pool(0), wrap_mode(0), lost(false), hint(TextureUsageAny), type(static_cast(0)), format(RGBA_8888), shared_bitmap(NULL) {} ResourceProvider::Resource::~Resource() {} ResourceProvider::Resource::Resource(unsigned texture_id, gfx::Size size, GLenum target, GLenum filter, GLenum texture_pool, GLint wrap_mode, TextureUsageHint hint, ResourceFormat format) : child_id(0), gl_id(texture_id), gl_pixel_buffer_id(0), gl_upload_query_id(0), pixels(NULL), pixel_buffer(NULL), lock_for_read_count(0), imported_count(0), exported_count(0), locked_for_write(false), external(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), allocated(false), enable_read_lock_fences(false), read_lock_fence(NULL), size(size), target(target), original_filter(filter), filter(filter), image_id(0), bound_image_id(0), dirty_image(false), texture_pool(texture_pool), wrap_mode(wrap_mode), lost(false), hint(hint), type(GLTexture), format(format), shared_bitmap(NULL) { DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT); } ResourceProvider::Resource::Resource(uint8_t* pixels, SharedBitmap* bitmap, gfx::Size size, GLenum filter, GLint wrap_mode) : child_id(0), gl_id(0), gl_pixel_buffer_id(0), gl_upload_query_id(0), pixels(pixels), pixel_buffer(NULL), lock_for_read_count(0), imported_count(0), exported_count(0), locked_for_write(false), external(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), allocated(false), enable_read_lock_fences(false), read_lock_fence(NULL), size(size), target(0), original_filter(filter), filter(filter), image_id(0), bound_image_id(0), dirty_image(false), texture_pool(0), wrap_mode(wrap_mode), lost(false), hint(TextureUsageAny), type(Bitmap), format(RGBA_8888), shared_bitmap(bitmap) { DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT); } ResourceProvider::Child::Child() {} ResourceProvider::Child::~Child() {} scoped_ptr ResourceProvider::Create( OutputSurface* output_surface, SharedBitmapManager* shared_bitmap_manager, int highp_threshold_min, bool use_rgba_4444_texture_format, size_t texture_id_allocation_chunk_size) { scoped_ptr resource_provider( new ResourceProvider(output_surface, shared_bitmap_manager, highp_threshold_min, use_rgba_4444_texture_format, texture_id_allocation_chunk_size)); bool success = false; if (resource_provider->Context3d()) { success = resource_provider->InitializeGL(); } else { resource_provider->InitializeSoftware(); success = true; } if (!success) return scoped_ptr(); DCHECK_NE(InvalidType, resource_provider->default_resource_type()); return resource_provider.Pass(); } ResourceProvider::~ResourceProvider() { while (!children_.empty()) DestroyChild(children_.begin()->first); 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; } ResourceProvider::ResourceId ResourceProvider::CreateResource( gfx::Size size, GLint wrap_mode, TextureUsageHint hint, ResourceFormat format) { DCHECK(!size.IsEmpty()); switch (default_resource_type_) { case GLTexture: return CreateGLTexture( size, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, wrap_mode, hint, format); case Bitmap: DCHECK_EQ(RGBA_8888, format); return CreateBitmap(size); case InvalidType: break; } LOG(FATAL) << "Invalid default resource type."; return 0; } ResourceProvider::ResourceId ResourceProvider::CreateManagedResource( gfx::Size size, GLint wrap_mode, TextureUsageHint hint, ResourceFormat format) { DCHECK(!size.IsEmpty()); switch (default_resource_type_) { case GLTexture: return CreateGLTexture( size, GL_TEXTURE_POOL_MANAGED_CHROMIUM, wrap_mode, hint, format); case Bitmap: DCHECK_EQ(RGBA_8888, format); return CreateBitmap(size); case InvalidType: break; } LOG(FATAL) << "Invalid default resource type."; return 0; } ResourceProvider::ResourceId ResourceProvider::CreateGLTexture( gfx::Size size, GLenum texture_pool, GLint wrap_mode, TextureUsageHint 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, GL_TEXTURE_2D, GL_LINEAR, texture_pool, wrap_mode, hint, format); resource.allocated = false; resources_[id] = resource; return id; } ResourceProvider::ResourceId ResourceProvider::CreateBitmap(gfx::Size size) { DCHECK(thread_checker_.CalledOnValidThread()); scoped_ptr bitmap; if (shared_bitmap_manager_) bitmap = shared_bitmap_manager_->AllocateSharedBitmap(size); uint8_t* pixels; if (bitmap) pixels = bitmap->pixels(); else pixels = new uint8_t[4 * size.GetArea()]; ResourceId id = next_id_++; Resource resource( pixels, bitmap.release(), size, GL_LINEAR, GL_CLAMP_TO_EDGE); resource.allocated = true; resources_[id] = resource; return id; } ResourceProvider::ResourceId ResourceProvider::CreateResourceFromExternalTexture( unsigned texture_target, unsigned texture_id) { DCHECK(thread_checker_.CalledOnValidThread()); WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->bindTexture(texture_target, texture_id)); GLC(context3d, context3d->texParameteri( texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); GLC(context3d, context3d->texParameteri( texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GLC(context3d, context3d->texParameteri( texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GLC(context3d, context3d->texParameteri( texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); ResourceId id = next_id_++; Resource resource(texture_id, gfx::Size(), texture_target, GL_LINEAR, 0, GL_CLAMP_TO_EDGE, TextureUsageAny, RGBA_8888); resource.external = true; resource.allocated = true; resources_[id] = resource; return id; } ResourceProvider::ResourceId ResourceProvider::CreateResourceFromTextureMailbox( const TextureMailbox& mailbox, scoped_ptr release_callback) { 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(), mailbox.target(), GL_LINEAR, 0, GL_CLAMP_TO_EDGE, TextureUsageAny, RGBA_8888); } else { DCHECK(mailbox.IsSharedMemory()); base::SharedMemory* shared_memory = mailbox.shared_memory(); DCHECK(shared_memory->memory()); uint8_t* pixels = reinterpret_cast(shared_memory->memory()); scoped_ptr shared_bitmap; if (shared_bitmap_manager_) { shared_bitmap = shared_bitmap_manager_->GetBitmapForSharedMemory(shared_memory); } resource = Resource(pixels, shared_bitmap.release(), mailbox.shared_memory_size(), GL_LINEAR, GL_CLAMP_TO_EDGE); } resource.external = true; resource.allocated = true; resource.mailbox = mailbox; resource.release_callback = base::Bind(&SingleReleaseCallback::Run, base::Owned(release_callback.release())); 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->lock_for_read_count); 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->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) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->destroyImageCHROMIUM(resource->image_id)); } if (resource->gl_id && !resource->external) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->deleteTexture(resource->gl_id)); } if (resource->gl_upload_query_id) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->deleteQueryEXT(resource->gl_upload_query_id)); } if (resource->gl_pixel_buffer_id) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->deleteBuffer(resource->gl_pixel_buffer_id)); } if (resource->mailbox.IsValid() && resource->external) { unsigned sync_point = resource->mailbox.sync_point(); if (resource->mailbox.IsTexture()) { lost_resource |= lost_output_surface_; WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); if (resource->gl_id) GLC(context3d, context3d->deleteTexture(resource->gl_id)); if (!lost_resource && resource->gl_id) sync_point = context3d->insertSyncPoint(); } else { DCHECK(resource->mailbox.IsSharedMemory()); base::SharedMemory* shared_memory = resource->mailbox.shared_memory(); if (resource->pixels && shared_memory) { DCHECK(shared_memory->memory() == resource->pixels); resource->pixels = NULL; delete resource->shared_bitmap; resource->shared_bitmap = NULL; } } resource->release_callback.Run(sync_point, lost_resource); } if (resource->shared_bitmap) { delete resource->shared_bitmap; resource->pixels = NULL; } if (resource->pixels) delete[] resource->pixels; if (resource->pixel_buffer) delete[] resource->pixel_buffer; resources_.erase(it); } ResourceProvider::ResourceType ResourceProvider::GetResourceType( ResourceId id) { return GetResource(id)->type; } void ResourceProvider::SetPixels(ResourceId id, const uint8_t* image, gfx::Rect image_rect, gfx::Rect source_rect, gfx::Vector2d dest_offset) { Resource* resource = GetResource(id); DCHECK(!resource->locked_for_write); DCHECK(!resource->lock_for_read_count); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); DCHECK(ReadLockFenceHasPassed(resource)); LazyAllocate(resource); if (resource->gl_id) { DCHECK(!resource->pending_set_pixels); DCHECK_EQ(resource->target, static_cast(GL_TEXTURE_2D)); WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(texture_uploader_.get()); context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id); texture_uploader_->Upload(image, image_rect, source_rect, dest_offset, resource->format, resource->size); } if (resource->pixels) { DCHECK(resource->allocated); DCHECK_EQ(RGBA_8888, resource->format); SkBitmap src_full; src_full.setConfig( SkBitmap::kARGB_8888_Config, image_rect.width(), image_rect.height()); src_full.setPixels(const_cast(image)); SkBitmap src_subset; SkIRect sk_source_rect = SkIRect::MakeXYWH(source_rect.x(), source_rect.y(), source_rect.width(), source_rect.height()); sk_source_rect.offset(-image_rect.x(), -image_rect.y()); src_full.extractSubset(&src_subset, sk_source_rect); ScopedWriteLockSoftware lock(this, id); SkCanvas* dest = lock.sk_canvas(); dest->writePixels(src_subset, 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(); } double ResourceProvider::EstimatedUploadsPerSecond() { if (!texture_uploader_) return 0.0; return texture_uploader_->EstimatedTexturesPerSecond(); } void ResourceProvider::FlushUploads() { if (!texture_uploader_) return; texture_uploader_->Flush(); } void ResourceProvider::ReleaseCachedData() { if (!texture_uploader_) return; texture_uploader_->ReleaseCachedQueries(); } base::TimeDelta ResourceProvider::TextureUpdateTickRate() { // Software resource uploads happen on impl thread, so don't bother batching // them up and trying to wait for them to complete. double rate = texture_uploader_ ? kTextureUploadTickRate : kSoftwareUploadTickRate; return base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond * rate); } void ResourceProvider::Flush() { DCHECK(thread_checker_.CalledOnValidThread()); WebGraphicsContext3D* context3d = Context3d(); if (context3d) context3d->flush(); } void ResourceProvider::Finish() { DCHECK(thread_checker_.CalledOnValidThread()); WebGraphicsContext3D* context3d = Context3d(); if (context3d) context3d->finish(); } bool ResourceProvider::ShallowFlushIfSupported() { DCHECK(thread_checker_.CalledOnValidThread()); WebGraphicsContext3D* context3d = Context3d(); if (!context3d || !use_shallow_flush_) return false; context3d->shallowFlushCHROMIUM(); return true; } 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->external) { if (!resource->gl_id && resource->mailbox.IsTexture()) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); if (resource->mailbox.sync_point()) { GLC(context3d, context3d->waitSyncPoint(resource->mailbox.sync_point())); resource->mailbox.ResetSyncPoint(); } resource->gl_id = NextTextureId(); GLC(context3d, context3d->bindTexture(resource->target, resource->gl_id)); GLC(context3d, context3d->consumeTextureCHROMIUM(resource->target, resource->mailbox.data())); } } resource->lock_for_read_count++; if (resource->enable_read_lock_fences) resource->read_lock_fence = current_read_lock_fence_; return resource; } void ResourceProvider::UnlockForRead(ResourceId id) { Resource* resource = GetResource(id); DCHECK_GT(resource->lock_for_read_count, 0); DCHECK_EQ(resource->exported_count, 0); resource->lock_for_read_count--; } const ResourceProvider::Resource* ResourceProvider::LockForWrite( ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->locked_for_write); DCHECK(!resource->lock_for_read_count); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->external); DCHECK(!resource->lost); DCHECK(ReadLockFenceHasPassed(resource)); LazyAllocate(resource); 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->external && !resource->lost && ReadLockFenceHasPassed(resource); } void ResourceProvider::UnlockForWrite(ResourceId id) { Resource* resource = GetResource(id); DCHECK(resource->locked_for_write); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->external); 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 target, GLenum filter) : ScopedReadLockGL(resource_provider, resource_id), target_(target), unit_(GL_TEXTURE0) { resource_provider->BindForSampling(resource_id, target, unit_, filter); } ResourceProvider::ScopedSamplerGL::ScopedSamplerGL( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id, GLenum target, GLenum unit, GLenum filter) : ScopedReadLockGL(resource_provider, resource_id), target_(target), unit_(unit) { resource_provider->BindForSampling(resource_id, target, unit, filter); } ResourceProvider::ScopedSamplerGL::~ScopedSamplerGL() { } ResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_id_(resource_id), texture_id_(resource_provider->LockForWrite(resource_id)->gl_id) { DCHECK(texture_id_); } ResourceProvider::ScopedWriteLockGL::~ScopedWriteLockGL() { resource_provider_->UnlockForWrite(resource_id_); } void ResourceProvider::PopulateSkBitmapWithResource( SkBitmap* sk_bitmap, const Resource* resource) { DCHECK(resource->pixels); DCHECK_EQ(RGBA_8888, resource->format); sk_bitmap->setConfig(SkBitmap::kARGB_8888_Config, resource->size.width(), resource->size.height()); sk_bitmap->setPixels(resource->pixels); } ResourceProvider::ScopedReadLockSoftware::ScopedReadLockSoftware( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_id_(resource_id) { ResourceProvider::PopulateSkBitmapWithResource( &sk_bitmap_, resource_provider->LockForRead(resource_id)); } ResourceProvider::ScopedReadLockSoftware::~ScopedReadLockSoftware() { resource_provider_->UnlockForRead(resource_id_); } ResourceProvider::ScopedWriteLockSoftware::ScopedWriteLockSoftware( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) : resource_provider_(resource_provider), resource_id_(resource_id) { ResourceProvider::PopulateSkBitmapWithResource( &sk_bitmap_, resource_provider->LockForWrite(resource_id)); sk_canvas_.reset(new SkCanvas(sk_bitmap_)); } ResourceProvider::ScopedWriteLockSoftware::~ScopedWriteLockSoftware() { resource_provider_->UnlockForWrite(resource_id_); } ResourceProvider::ResourceProvider(OutputSurface* output_surface, SharedBitmapManager* shared_bitmap_manager, int highp_threshold_min, bool use_rgba_4444_texture_format, size_t texture_id_allocation_chunk_size) : output_surface_(output_surface), shared_bitmap_manager_(shared_bitmap_manager), 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_usage_hint_(false), use_shallow_flush_(false), max_texture_size_(0), best_texture_format_(RGBA_8888), use_rgba_4444_texture_format_(use_rgba_4444_texture_format), texture_id_allocation_chunk_size_(texture_id_allocation_chunk_size) { DCHECK(output_surface_->HasClient()); DCHECK(texture_id_allocation_chunk_size_); } void ResourceProvider::InitializeSoftware() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_NE(Bitmap, default_resource_type_); CleanUpGLIfNeeded(); default_resource_type_ = Bitmap; max_texture_size_ = INT_MAX / 2; best_texture_format_ = RGBA_8888; } bool ResourceProvider::InitializeGL() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!texture_uploader_); DCHECK_NE(GLTexture, default_resource_type_); WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); if (!context3d->makeContextCurrent()) return false; default_resource_type_ = GLTexture; const ContextProvider::Capabilities& caps = output_surface_->context_provider()->ContextCapabilities(); bool use_map_sub = caps.map_sub; bool use_bgra = caps.texture_format_bgra8888; use_texture_storage_ext_ = caps.texture_storage; use_shallow_flush_ = caps.shallow_flush; use_texture_usage_hint_ = caps.texture_usage; use_compressed_texture_etc1_ = caps.texture_format_etc1; texture_uploader_ = TextureUploader::Create(context3d, use_map_sub, use_shallow_flush_); GLC(context3d, context3d->getIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_)); best_texture_format_ = PlatformColor::BestTextureFormat(use_bgra); return true; } void ResourceProvider::CleanUpGLIfNeeded() { WebGraphicsContext3D* context3d = Context3d(); if (default_resource_type_ != GLTexture) { // We are not in GL mode, but double check before returning. DCHECK(!context3d); DCHECK(!texture_uploader_); return; } DCHECK(context3d); context3d->makeContextCurrent(); texture_uploader_.reset(); if (!unused_texture_ids_.empty()) { size_t size = unused_texture_ids_.size(); scoped_ptr ids(new WebKit::WebGLId[size]); for (size_t i = 0; i < size; ++i) ids[i] = unused_texture_ids_[i]; GLC(context3d, context3d->deleteTextures(size, ids.get())); unused_texture_ids_.clear(); } 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) { DCHECK(thread_checker_.CalledOnValidThread()); ChildMap::iterator it = children_.find(child_id); DCHECK(it != children_.end()); Child& child = it->second; 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(); DeleteAndReturnUnusedResourcesToChild( &child, ForShutdown, resources_for_child); children_.erase(it); } const ResourceProvider::ResourceIdMap& ResourceProvider::GetChildToParentMap( int child) const { DCHECK(thread_checker_.CalledOnValidThread()); ChildMap::const_iterator it = children_.find(child); DCHECK(it != children_.end()); return it->second.child_to_parent_map; } void ResourceProvider::PrepareSendToParent(const ResourceIdArray& resources, TransferableResourceArray* list) { DCHECK(thread_checker_.CalledOnValidThread()); WebGraphicsContext3D* context3d = Context3d(); if (context3d) context3d->makeContextCurrent(); bool need_sync_point = false; for (ResourceIdArray::const_iterator it = resources.begin(); it != resources.end(); ++it) { TransferableResource resource; TransferResource(context3d, *it, &resource); if (!resource.sync_point && !resource.is_software) need_sync_point = true; ++resources_.find(*it)->second.exported_count; list->push_back(resource); } if (need_sync_point) { unsigned int sync_point = context3d->insertSyncPoint(); for (TransferableResourceArray::iterator it = list->begin(); it != list->end(); ++it) { if (!it->sync_point) it->sync_point = sync_point; } } } void ResourceProvider::ReceiveFromChild( int child, const TransferableResourceArray& resources) { DCHECK(thread_checker_.CalledOnValidThread()); WebGraphicsContext3D* context3d = Context3d(); if (context3d) context3d->makeContextCurrent(); Child& child_info = children_.find(child)->second; 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()) { resources_[resource_in_map_it->second].imported_count++; continue; } scoped_ptr bitmap; uint8_t* pixels = NULL; if (it->is_software) { if (shared_bitmap_manager_) bitmap = shared_bitmap_manager_->GetSharedBitmapFromId(it->size, it->mailbox); if (bitmap) pixels = bitmap->pixels(); } if ((!it->is_software && !context3d) || (it->is_software && !pixels)) { TRACE_EVENT0("cc", "ResourceProvider::ReceiveFromChild dropping invalid"); ReturnedResourceArray to_return; to_return.push_back(it->ToReturnedResource()); child_info.return_callback.Run(to_return); continue; } ResourceId local_id = next_id_++; Resource& resource = resources_[local_id]; if (it->is_software) { resource = Resource( pixels, bitmap.release(), it->size, GL_LINEAR, GL_CLAMP_TO_EDGE); } else { unsigned texture_id; // NOTE: If the parent is a browser and the child a renderer, the parent // is not supposed to have its context wait, because that could induce // deadlocks and/or security issues. The caller is responsible for // waiting asynchronously, and resetting sync_point before calling this. // However if the parent is a renderer (e.g. browser tag), it may be ok // (and is simpler) to wait. if (it->sync_point) GLC(context3d, context3d->waitSyncPoint(it->sync_point)); texture_id = NextTextureId(); GLC(context3d, context3d->bindTexture(it->target, texture_id)); GLC(context3d, context3d->consumeTextureCHROMIUM(it->target, it->mailbox.name)); resource = Resource(texture_id, it->size, it->target, it->filter, 0, GL_CLAMP_TO_EDGE, TextureUsageAny, it->format); resource.mailbox.SetName(it->mailbox); } resource.child_id = child; // Don't allocate a texture for a child. resource.allocated = true; resource.imported_count = 1; 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()); Child& child_info = children_.find(child)->second; 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; 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_info, 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()); WebGraphicsContext3D* context3d = Context3d(); if (context3d) context3d->makeContextCurrent(); int child_id = 0; Child* child_info = NULL; 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); 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; if (resource->gl_id) { if (returned.sync_point) GLC(context3d, context3d->waitSyncPoint(returned.sync_point)); } else if (!resource->shared_bitmap) { resource->mailbox = TextureMailbox(resource->mailbox.name(), 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; } // Delete the resource and return it to the child it came from one. if (resource->child_id != child_id) { ChildMap::iterator child_it = children_.find(resource->child_id); DCHECK(child_it != children_.end()); if (child_id) { DCHECK_NE(resources_for_child.size(), 0u); DeleteAndReturnUnusedResourcesToChild( child_info, Normal, resources_for_child); resources_for_child.clear(); } child_info = &child_it->second; child_id = resource->child_id; } resources_for_child.push_back(local_id); } if (child_id) { DCHECK_NE(resources_for_child.size(), 0u); DeleteAndReturnUnusedResourcesToChild( child_info, Normal, resources_for_child); } } void ResourceProvider::TransferResource(WebGraphicsContext3D* context, ResourceId id, TransferableResource* resource) { Resource* source = GetResource(id); DCHECK(!source->locked_for_write); DCHECK(!source->lock_for_read_count); DCHECK(!source->external || (source->external && source->mailbox.IsValid())); DCHECK(source->allocated); resource->id = id; resource->format = source->format; resource->target = source->target; resource->filter = source->filter; resource->size = source->size; if (source->shared_bitmap) { resource->mailbox = source->shared_bitmap->id(); resource->is_software = true; } else if (!source->mailbox.IsValid()) { // This is a resource allocated by the compositor, we need to produce it. // Don't set a sync point, the caller will do it. DCHECK(source->gl_id); GLC(context, context->bindTexture(resource->target, source->gl_id)); GLC(context, context->genMailboxCHROMIUM(resource->mailbox.name)); GLC(context, context->produceTextureCHROMIUM(resource->target, resource->mailbox.name)); source->mailbox.SetName(resource->mailbox); } else { DCHECK(source->mailbox.IsTexture()); // 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 = source->mailbox.name(); resource->sync_point = source->mailbox.sync_point(); source->mailbox.ResetSyncPoint(); } } void ResourceProvider::DeleteAndReturnUnusedResourcesToChild( Child* child_info, DeleteStyle style, const ResourceIdArray& unused) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(child_info); if (unused.empty()) return; WebGraphicsContext3D* context3d = Context3d(); if (context3d) context3d->makeContextCurrent(); ReturnedResourceArray to_return; 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(!resource.lock_for_read_count); 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.shared_bitmap && lost_output_surface_); if (resource.exported_count > 0) { if (style != ForShutdown) { // Defer this until we receive the resource back from the parent. resource.marked_for_deletion = true; continue; } // We still have an exported_count, so we'll have to lose it. is_lost = true; } if (context3d && resource.filter != resource.original_filter) { DCHECK(resource.target); DCHECK(resource.gl_id); GLC(context3d, context3d->bindTexture(resource.target, resource.gl_id)); GLC(context3d, context3d->texParameteri(resource.target, GL_TEXTURE_MIN_FILTER, resource.original_filter)); GLC(context3d, context3d->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.shared_bitmap) 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(context3d); unsigned int sync_point = context3d->insertSyncPoint(); 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); } void ResourceProvider::AcquirePixelBuffer(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); DCHECK_NE(ETC1, resource->format); if (resource->type == GLTexture) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); if (!resource->gl_pixel_buffer_id) resource->gl_pixel_buffer_id = context3d->createBuffer(); context3d->bindBuffer( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); unsigned bytes_per_pixel = BitsPerPixel(resource->format) / 8; context3d->bufferData( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->size.height() * RoundUp(bytes_per_pixel * resource->size.width(), 4u), NULL, GL_DYNAMIC_DRAW); context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } if (resource->pixels) { if (resource->pixel_buffer) return; resource->pixel_buffer = new uint8_t[4 * resource->size.GetArea()]; } } void ResourceProvider::ReleasePixelBuffer(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); 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; UnlockForWrite(id); } if (resource->type == GLTexture) { if (!resource->gl_pixel_buffer_id) return; WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); context3d->bindBuffer( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); context3d->bufferData( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0, NULL, GL_DYNAMIC_DRAW); context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } if (resource->pixels) { if (!resource->pixel_buffer) return; delete[] resource->pixel_buffer; resource->pixel_buffer = NULL; } } uint8_t* ResourceProvider::MapPixelBuffer(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); if (resource->type == GLTexture) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(resource->gl_pixel_buffer_id); context3d->bindBuffer( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); uint8_t* image = static_cast( context3d->mapBufferCHROMIUM( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, GL_WRITE_ONLY)); context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); // Buffer is required to be 4-byte aligned. CHECK(!(reinterpret_cast(image) & 3)); return image; } if (resource->pixels) return resource->pixel_buffer; return NULL; } void ResourceProvider::UnmapPixelBuffer(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); if (resource->type == GLTexture) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(resource->gl_pixel_buffer_id); context3d->bindBuffer( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); context3d->unmapBufferCHROMIUM(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM); context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } } void ResourceProvider::BindForSampling(ResourceProvider::ResourceId resource_id, GLenum target, GLenum unit, GLenum filter) { DCHECK(thread_checker_.CalledOnValidThread()); WebGraphicsContext3D* context3d = Context3d(); 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(context3d, unit); GLC(context3d, context3d->bindTexture(target, resource->gl_id)); if (filter != resource->filter) { DCHECK_EQ(resource->target, target); GLC(context3d, context3d->texParameteri(target, GL_TEXTURE_MIN_FILTER, filter)); GLC(context3d, context3d->texParameteri(target, GL_TEXTURE_MAG_FILTER, filter)); resource->filter = filter; } if (resource->image_id && resource->dirty_image) { // Release image currently bound to texture. if (resource->bound_image_id) context3d->releaseTexImage2DCHROMIUM(target, resource->bound_image_id); context3d->bindTexImage2DCHROMIUM(target, resource->image_id); resource->bound_image_id = resource->image_id; resource->dirty_image = false; } } void ResourceProvider::BeginSetPixels(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->pending_set_pixels); LazyCreate(resource); DCHECK(resource->gl_id || resource->allocated); DCHECK(ReadLockFenceHasPassed(resource)); DCHECK(!resource->image_id); bool allocate = !resource->allocated; resource->allocated = true; LockForWrite(id); if (resource->gl_id) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(resource->gl_pixel_buffer_id); DCHECK_EQ(resource->target, static_cast(GL_TEXTURE_2D)); context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id); context3d->bindBuffer( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); if (!resource->gl_upload_query_id) resource->gl_upload_query_id = context3d->createQueryEXT(); context3d->beginQueryEXT( GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM, resource->gl_upload_query_id); if (allocate) { context3d->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 { context3d->asyncTexSubImage2DCHROMIUM( GL_TEXTURE_2D, 0, /* level */ 0, /* x */ 0, /* y */ resource->size.width(), resource->size.height(), GLDataFormat(resource->format), GLDataType(resource->format), NULL); } context3d->endQueryEXT(GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM); context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } if (resource->pixels) { DCHECK(!resource->mailbox.IsValid()); DCHECK(resource->pixel_buffer); DCHECK_EQ(RGBA_8888, resource->format); std::swap(resource->pixels, resource->pixel_buffer); delete[] resource->pixel_buffer; resource->pixel_buffer = NULL; } resource->pending_set_pixels = true; resource->set_pixels_completion_forced = false; } void ResourceProvider::ForceSetPixelsToComplete(ResourceId id) { Resource* resource = GetResource(id); DCHECK(resource->locked_for_write); DCHECK(resource->pending_set_pixels); DCHECK(!resource->set_pixels_completion_forced); if (resource->gl_id) { WebGraphicsContext3D* context3d = Context3d(); GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id)); GLC(context3d, context3d->waitAsyncTexImage2DCHROMIUM(GL_TEXTURE_2D)); GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, 0)); } resource->set_pixels_completion_forced = true; } bool ResourceProvider::DidSetPixelsComplete(ResourceId id) { Resource* resource = GetResource(id); DCHECK(resource->locked_for_write); DCHECK(resource->pending_set_pixels); if (resource->gl_id) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(resource->gl_upload_query_id); unsigned complete = 1; context3d->getQueryObjectuivEXT( resource->gl_upload_query_id, GL_QUERY_RESULT_AVAILABLE_EXT, &complete); if (!complete) return false; } resource->pending_set_pixels = false; UnlockForWrite(id); return true; } void ResourceProvider::CreateForTesting(ResourceId id) { LazyCreate(GetResource(id)); } GLint ResourceProvider::WrapModeForTesting(ResourceId id) { Resource* resource = GetResource(id); return resource->wrap_mode; } GLenum ResourceProvider::TargetForTesting(ResourceId id) { Resource* resource = GetResource(id); return resource->target; } void ResourceProvider::LazyCreate(Resource* resource) { if (resource->type != GLTexture || resource->gl_id != 0) return; // Early out for resources that don't require texture creation. if (resource->texture_pool == 0) return; resource->gl_id = NextTextureId(); WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); // Create and set texture properties. Allocation is delayed until needed. GLC(context3d, context3d->bindTexture(resource->target, resource->gl_id)); GLC(context3d, context3d->texParameteri( resource->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); GLC(context3d, context3d->texParameteri( resource->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GLC(context3d, context3d->texParameteri( resource->target, GL_TEXTURE_WRAP_S, resource->wrap_mode)); GLC(context3d, context3d->texParameteri( resource->target, GL_TEXTURE_WRAP_T, resource->wrap_mode)); GLC(context3d, context3d->texParameteri( resource->target, GL_TEXTURE_POOL_CHROMIUM, resource->texture_pool)); if (use_texture_usage_hint_ && resource->hint == TextureUsageFramebuffer) { GLC(context3d, context3d->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); LazyCreate(resource); DCHECK(resource->gl_id || resource->allocated); if (resource->allocated || !resource->gl_id) return; resource->allocated = true; WebGraphicsContext3D* context3d = Context3d(); gfx::Size& size = resource->size; DCHECK_EQ(resource->target, static_cast(GL_TEXTURE_2D)); ResourceFormat format = resource->format; GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id)); if (use_texture_storage_ext_ && IsFormatSupportedForStorage(format)) { GLenum storage_format = TextureToStorageFormat(format); GLC(context3d, context3d->texStorage2DEXT(GL_TEXTURE_2D, 1, storage_format, size.width(), size.height())); } else { // ETC1 does not support preallocation. if (format != ETC1) { GLC(context3d, context3d->texImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(format), size.width(), size.height(), 0, GLDataFormat(format), GLDataType(format), NULL)); } } } void ResourceProvider::EnableReadLockFences(ResourceProvider::ResourceId id, bool enable) { Resource* resource = GetResource(id); resource->enable_read_lock_fences = enable; } void ResourceProvider::AcquireImage(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); if (resource->type != GLTexture) return; if (resource->image_id) return; resource->allocated = true; WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK_EQ(RGBA_8888, resource->format); resource->image_id = context3d->createImageCHROMIUM( resource->size.width(), resource->size.height(), GL_RGBA8_OES); DCHECK(resource->image_id); } void ResourceProvider::ReleaseImage(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); if (!resource->image_id) return; WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); context3d->destroyImageCHROMIUM(resource->image_id); resource->image_id = 0; resource->bound_image_id = 0; resource->dirty_image = false; resource->allocated = false; } uint8_t* ResourceProvider::MapImage(ResourceId id) { Resource* resource = GetResource(id); DCHECK(ReadLockFenceHasPassed(resource)); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); if (resource->image_id) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); return static_cast( context3d->mapImageCHROMIUM(resource->image_id, GL_READ_WRITE)); } if (resource->pixels) return resource->pixels; return NULL; } void ResourceProvider::UnmapImage(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); if (resource->image_id) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); context3d->unmapImageCHROMIUM(resource->image_id); resource->dirty_image = true; } } int ResourceProvider::GetImageStride(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); int stride = 0; if (resource->image_id) { WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); context3d->getImageParameterivCHROMIUM( resource->image_id, GL_IMAGE_ROWBYTES_CHROMIUM, &stride); } return stride; } base::SharedMemory* ResourceProvider::GetSharedMemory(ResourceId id) { Resource* resource = GetResource(id); DCHECK(!resource->external); DCHECK_EQ(resource->exported_count, 0); if (!resource->shared_bitmap) return NULL; return resource->shared_bitmap->memory(); } GLint ResourceProvider::GetActiveTextureUnit(WebGraphicsContext3D* context) { GLint active_unit = 0; context->getIntegerv(GL_ACTIVE_TEXTURE, &active_unit); return active_unit; } WebKit::WebGraphicsContext3D* ResourceProvider::Context3d() const { ContextProvider* context_provider = output_surface_->context_provider(); return context_provider ? context_provider->Context3d() : NULL; } unsigned ResourceProvider::NextTextureId() { if (unused_texture_ids_.empty()) { size_t size = texture_id_allocation_chunk_size_; scoped_ptr ids(new WebKit::WebGLId[size]); WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->genTextures(size, ids.get())); unused_texture_ids_.assign(ids.get(), ids.get() + size); } unsigned gl_id = unused_texture_ids_.front(); unused_texture_ids_.pop_front(); return gl_id; } } // namespace cc