// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "gpu/command_buffer/service/texture_manager.h" #include "base/bits.h" #include "gpu/command_buffer/service/context_group.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" namespace gpu { namespace gles2 { static GLsizei ComputeMipMapCount( GLsizei width, GLsizei height, GLsizei depth) { return 1 + base::bits::Log2Floor(std::max(std::max(width, height), depth)); } static size_t GLTargetToFaceIndex(GLenum target) { switch (target) { case GL_TEXTURE_2D: return 0; case GL_TEXTURE_CUBE_MAP_POSITIVE_X: return 0; case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: return 1; case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: return 2; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: return 3; case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: return 4; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return 5; default: NOTREACHED(); return 0; } } static size_t FaceIndexToGLTarget(size_t index) { switch (index) { case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; default: NOTREACHED(); return 0; } } bool TextureManager::TextureInfo::CanRender() const { if (target_ == 0 || IsDeleted()) { return false; } bool needs_mips = NeedsMips(); if (npot()) { return !needs_mips && wrap_s_ == GL_CLAMP_TO_EDGE && wrap_t_ == GL_CLAMP_TO_EDGE; } if (needs_mips) { if (target_ == GL_TEXTURE_2D) { return texture_complete(); } else { return texture_complete() && cube_complete(); } } else { return true; } } bool TextureManager::TextureInfo::MarkMipmapsGenerated() { if (!CanGenerateMipmaps()) { return false; } for (size_t ii = 0; ii < level_infos_.size(); ++ii) { const TextureInfo::LevelInfo& info1 = level_infos_[ii][0]; GLsizei width = info1.width; GLsizei height = info1.height; GLsizei depth = info1.depth; int num_mips = ComputeMipMapCount(width, height, depth); for (int level = 1; level < num_mips; ++level) { width = std::max(1, width >> 1); height = std::max(1, height >> 1); depth = std::max(1, depth >> 1); SetLevelInfo(target_ == GL_TEXTURE_2D ? GL_TEXTURE_2D : FaceIndexToGLTarget(ii), level, info1.internal_format, width, height, depth, info1.border, info1.format, info1.type); } } return true; } bool TextureManager::TextureInfo::CanGenerateMipmaps() const { if (npot() || level_infos_.empty() || IsDeleted()) { return false; } const TextureInfo::LevelInfo& first = level_infos_[0][0]; // TODO(gman): Check internal_format, format and type. for (size_t ii = 0; ii < level_infos_.size(); ++ii) { const LevelInfo& info = level_infos_[ii][0]; if ((!info.valid) || (info.width != first.width) || (info.height != first.height) || (info.depth != 1) || (info.format != first.format) || (info.internal_format != first.internal_format) || (info.type != first.type)) { return false; } } return true; } void TextureManager::TextureInfo::SetLevelInfo( GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type) { DCHECK_GE(level, 0); DCHECK_LT(static_cast(GLTargetToFaceIndex(target)), level_infos_.size()); DCHECK_LT(static_cast(level), level_infos_[GLTargetToFaceIndex(target)].size()); DCHECK_GE(width, 0); DCHECK_GE(height, 0); DCHECK_GE(depth, 0); TextureInfo::LevelInfo& info = level_infos_[GLTargetToFaceIndex(target)][level]; info.valid = true; info.internal_format = internal_format; info.width = width; info.height = height; info.depth = depth; info.border = border; info.format = format; info.type = type; max_level_set_ = std::max(max_level_set_, level); Update(); } bool TextureManager::TextureInfo::GetLevelSize( GLint face, GLint level, GLsizei* width, GLsizei* height) const { size_t face_index = GLTargetToFaceIndex(face); if (!IsDeleted() && level >= 0 && face_index < level_infos_.size() && static_cast(level) < level_infos_[face_index].size()) { const LevelInfo& info = level_infos_[GLTargetToFaceIndex(face)][level]; *width = info.width; *height = info.height; return true; } return false; } void TextureManager::TextureInfo::SetParameter(GLenum pname, GLint param) { switch (pname) { case GL_TEXTURE_MIN_FILTER: min_filter_ = param; break; case GL_TEXTURE_MAG_FILTER: mag_filter_ = param; break; case GL_TEXTURE_WRAP_S: wrap_s_ = param; break; case GL_TEXTURE_WRAP_T: wrap_t_ = param; break; default: NOTREACHED(); break; } } void TextureManager::TextureInfo::Update() { // Update npot status. npot_ = false; for (size_t ii = 0; ii < level_infos_.size(); ++ii) { const TextureInfo::LevelInfo& info = level_infos_[ii][0]; if (((info.width & (info.width - 1)) != 0) || ((info.height & (info.height - 1)) != 0) || ((info.depth & (info.depth - 1)) != 0)) { npot_ = true; break; } } // Update texture_complete and cube_complete status. const TextureInfo::LevelInfo& first_face = level_infos_[0][0]; texture_complete_ = (max_level_set_ == ComputeMipMapCount(first_face.width, first_face.height, first_face.depth) - 1) && max_level_set_ >= 0; cube_complete_ = (level_infos_.size() == 6) && (first_face.width == first_face.height); for (size_t ii = 0; ii < level_infos_.size() && (cube_complete_ || texture_complete_); ++ii) { const TextureInfo::LevelInfo& level0 = level_infos_[ii][0]; if (!level0.valid || level0.width != first_face.width || level0.height != first_face.height || level0.depth != 1 || level0.internal_format != first_face.internal_format || level0.format != first_face.format || level0.type != first_face.type) { cube_complete_ = false; } // Get level0 dimensions GLsizei width = level0.width; GLsizei height = level0.height; GLsizei depth = level0.depth; for (GLint jj = 1; jj <= max_level_set_; ++jj) { // compute required size for mip. width = std::max(1, width >> 1); height = std::max(1, height >> 1); depth = std::max(1, depth >> 1); const TextureInfo::LevelInfo& info = level_infos_[ii][jj]; if (!info.valid || info.width != width || info.height != height || info.depth != depth || info.internal_format != level0.internal_format || info.format != level0.format || info.type != level0.type) { texture_complete_ = false; break; } } } } TextureManager::TextureManager( GLint max_texture_size, GLint max_cube_map_texture_size) : max_texture_size_(max_texture_size), max_cube_map_texture_size_(max_cube_map_texture_size), max_levels_(ComputeMipMapCount(max_texture_size, max_texture_size, max_texture_size)), max_cube_map_levels_(ComputeMipMapCount(max_cube_map_texture_size, max_cube_map_texture_size, max_cube_map_texture_size)) { } TextureManager::TextureInfo* TextureManager::CreateTextureInfo( GLuint texture_id) { TextureInfo::Ref info(new TextureInfo(texture_id)); std::pair result = texture_infos_.insert(std::make_pair(texture_id, info)); DCHECK(result.second); return info.get(); } TextureManager::TextureInfo* TextureManager::GetTextureInfo( GLuint texture_id) { TextureInfoMap::iterator it = texture_infos_.find(texture_id); return it != texture_infos_.end() ? it->second : NULL; } void TextureManager::RemoveTextureInfo(GLuint texture_id) { TextureInfoMap::iterator it = texture_infos_.find(texture_id); if (it != texture_infos_.end()) { it->second->MarkAsDeleted(); texture_infos_.erase(texture_id); } } } // namespace gles2 } // namespace gpu