blob: aa786680bd78e103315c01df0b1ef9751d96427c [file]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/service/texture_manager.h"
#include "base/bits.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/texture_definition.h"
namespace gpu {
namespace gles2 {
static size_t GLTargetToFaceIndex(GLenum target) {
switch (target) {
case GL_TEXTURE_2D:
case GL_TEXTURE_EXTERNAL_OES:
case GL_TEXTURE_RECTANGLE_ARB:
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;
}
}
TextureManager::~TextureManager() {
DCHECK(texture_infos_.empty());
// If this triggers, that means something is keeping a reference to
// a TextureInfo belonging to this.
CHECK_EQ(texture_info_count_, 0u);
DCHECK_EQ(0, num_unrenderable_textures_);
DCHECK_EQ(0, num_unsafe_textures_);
DCHECK_EQ(0, num_uncleared_mips_);
}
void TextureManager::Destroy(bool have_context) {
have_context_ = have_context;
texture_infos_.clear();
for (int ii = 0; ii < kNumDefaultTextures; ++ii) {
default_textures_[ii] = NULL;
}
if (have_context) {
glDeleteTextures(arraysize(black_texture_ids_), black_texture_ids_);
}
DCHECK_EQ(0u, mem_represented_);
UpdateMemRepresented();
}
TextureManager::TextureInfo::TextureInfo(TextureManager* manager,
GLuint service_id)
: manager_(manager),
service_id_(service_id),
deleted_(false),
cleared_(true),
num_uncleared_mips_(0),
target_(0),
min_filter_(GL_NEAREST_MIPMAP_LINEAR),
mag_filter_(GL_LINEAR),
wrap_s_(GL_REPEAT),
wrap_t_(GL_REPEAT),
usage_(GL_NONE),
max_level_set_(-1),
texture_complete_(false),
cube_complete_(false),
npot_(false),
has_been_bound_(false),
framebuffer_attachment_count_(0),
owned_(true),
stream_texture_(false),
immutable_(false),
estimated_size_(0) {
if (manager_) {
manager_->StartTracking(this);
}
}
TextureManager::TextureInfo::~TextureInfo() {
if (manager_) {
if (owned_ && manager_->have_context_) {
GLuint id = service_id();
glDeleteTextures(1, &id);
}
MarkAsDeleted();
manager_->StopTracking(this);
manager_ = NULL;
}
}
TextureManager::TextureInfo::LevelInfo::LevelInfo()
: cleared(true),
target(0),
level(-1),
internal_format(0),
width(0),
height(0),
depth(0),
border(0),
format(0),
type(0),
estimated_size(0) {
}
TextureManager::TextureInfo::LevelInfo::LevelInfo(const LevelInfo& rhs)
: cleared(rhs.cleared),
target(rhs.target),
level(rhs.level),
internal_format(rhs.internal_format),
width(rhs.width),
height(rhs.height),
depth(rhs.depth),
border(rhs.border),
format(rhs.format),
type(rhs.type),
estimated_size(rhs.estimated_size) {
}
bool TextureManager::TextureInfo::CanRender(
const FeatureInfo* feature_info) const {
if (target_ == 0) {
return false;
}
bool needs_mips = NeedsMips();
if ((npot() && !feature_info->feature_flags().npot_ok) ||
(target_ == GL_TEXTURE_RECTANGLE_ARB)) {
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(
const FeatureInfo* feature_info) {
if (!CanGenerateMipmaps(feature_info)) {
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;
GLenum target = target_ == GL_TEXTURE_2D ? GL_TEXTURE_2D :
FaceIndexToGLTarget(ii);
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(feature_info,
target,
level,
info1.internal_format,
width,
height,
depth,
info1.border,
info1.format,
info1.type,
true);
}
}
return true;
}
void TextureManager::TextureInfo::SetTarget(GLenum target, GLint max_levels) {
DCHECK_EQ(0u, target_); // you can only set this once.
target_ = target;
size_t num_faces = (target == GL_TEXTURE_CUBE_MAP) ? 6 : 1;
level_infos_.resize(num_faces);
for (size_t ii = 0; ii < num_faces; ++ii) {
level_infos_[ii].resize(max_levels);
}
if (target == GL_TEXTURE_EXTERNAL_OES || target == GL_TEXTURE_RECTANGLE_ARB) {
min_filter_ = GL_LINEAR;
wrap_s_ = wrap_t_ = GL_CLAMP_TO_EDGE;
}
}
bool TextureManager::TextureInfo::CanGenerateMipmaps(
const FeatureInfo* feature_info) const {
if ((npot() && !feature_info->feature_flags().npot_ok) ||
level_infos_.empty() ||
target_ == GL_TEXTURE_EXTERNAL_OES ||
target_ == GL_TEXTURE_RECTANGLE_ARB) {
return false;
}
// Can't generate mips for depth or stencil textures.
const TextureInfo::LevelInfo& first = level_infos_[0][0];
uint32 channels = GLES2Util::GetChannelsForFormat(first.format);
if (channels & (GLES2Util::kDepth | GLES2Util::kStencil)) {
return false;
}
// 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.target == 0) ||
(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) ||
feature_info->validators()->compressed_texture_format.IsValid(
info.internal_format)) {
return false;
}
}
return true;
}
void TextureManager::TextureInfo::SetLevelCleared(GLenum target, GLint level) {
DCHECK_GE(level, 0);
DCHECK_LT(static_cast<size_t>(GLTargetToFaceIndex(target)),
level_infos_.size());
DCHECK_LT(static_cast<size_t>(level),
level_infos_[GLTargetToFaceIndex(target)].size());
TextureInfo::LevelInfo& info =
level_infos_[GLTargetToFaceIndex(target)][level];
if (!info.cleared) {
DCHECK_NE(0, num_uncleared_mips_);
--num_uncleared_mips_;
}
info.cleared = true;
UpdateCleared();
}
void TextureManager::TextureInfo::UpdateCleared() {
if (level_infos_.empty()) {
return;
}
const TextureInfo::LevelInfo& first_face = level_infos_[0][0];
int levels_needed = ComputeMipMapCount(
first_face.width, first_face.height, first_face.depth);
cleared_ = true;
for (size_t ii = 0; ii < level_infos_.size(); ++ii) {
for (GLint jj = 0; jj < levels_needed; ++jj) {
const TextureInfo::LevelInfo& info = level_infos_[ii][jj];
if (info.width > 0 && info.height > 0 && info.depth > 0 &&
!info.cleared) {
cleared_ = false;
return;
}
}
}
}
void TextureManager::TextureInfo::SetLevelInfo(
const FeatureInfo* feature_info,
GLenum target,
GLint level,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLsizei depth,
GLint border,
GLenum format,
GLenum type,
bool cleared) {
DCHECK_GE(level, 0);
DCHECK_LT(static_cast<size_t>(GLTargetToFaceIndex(target)),
level_infos_.size());
DCHECK_LT(static_cast<size_t>(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.target = target;
info.level = level;
info.internal_format = internal_format;
info.width = width;
info.height = height;
info.depth = depth;
info.border = border;
info.format = format;
info.type = type;
estimated_size_ -= info.estimated_size;
GLES2Util::ComputeImageDataSizes(
width, height, format, type, 4, &info.estimated_size, NULL, NULL);
estimated_size_ += info.estimated_size;
if (!info.cleared) {
DCHECK_NE(0, num_uncleared_mips_);
--num_uncleared_mips_;
}
info.cleared = cleared;
if (!info.cleared) {
++num_uncleared_mips_;
}
max_level_set_ = std::max(max_level_set_, level);
Update(feature_info);
UpdateCleared();
}
bool TextureManager::TextureInfo::ValidForTexture(
GLint face,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) const {
size_t face_index = GLTargetToFaceIndex(face);
if (level >= 0 && face_index < level_infos_.size() &&
static_cast<size_t>(level) < level_infos_[face_index].size()) {
const LevelInfo& info = level_infos_[GLTargetToFaceIndex(face)][level];
int32 right;
int32 top;
return SafeAddInt32(xoffset, width, &right) &&
SafeAddInt32(yoffset, height, &top) &&
xoffset >= 0 &&
yoffset >= 0 &&
right <= info.width &&
top <= info.height &&
format == info.internal_format &&
type == info.type;
}
return false;
}
bool TextureManager::TextureInfo::GetLevelSize(
GLint face, GLint level, GLsizei* width, GLsizei* height) const {
DCHECK(width);
DCHECK(height);
size_t face_index = GLTargetToFaceIndex(face);
if (level >= 0 && face_index < level_infos_.size() &&
static_cast<size_t>(level) < level_infos_[face_index].size()) {
const LevelInfo& info = level_infos_[GLTargetToFaceIndex(face)][level];
if (info.target != 0) {
*width = info.width;
*height = info.height;
return true;
}
}
return false;
}
bool TextureManager::TextureInfo::GetLevelType(
GLint face, GLint level, GLenum* type, GLenum* internal_format) const {
DCHECK(type);
DCHECK(internal_format);
size_t face_index = GLTargetToFaceIndex(face);
if (level >= 0 && face_index < level_infos_.size() &&
static_cast<size_t>(level) < level_infos_[face_index].size()) {
const LevelInfo& info = level_infos_[GLTargetToFaceIndex(face)][level];
if (info.target != 0) {
*type = info.type;
*internal_format = info.internal_format;
return true;
}
}
return false;
}
bool TextureManager::TextureInfo::SetParameter(
const FeatureInfo* feature_info, GLenum pname, GLint param) {
DCHECK(feature_info);
if (target_ == GL_TEXTURE_EXTERNAL_OES ||
target_ == GL_TEXTURE_RECTANGLE_ARB) {
if (pname == GL_TEXTURE_MIN_FILTER &&
(param != GL_NEAREST && param != GL_LINEAR))
return false;
if ((pname == GL_TEXTURE_WRAP_S || pname == GL_TEXTURE_WRAP_T) &&
param != GL_CLAMP_TO_EDGE)
return false;
}
switch (pname) {
case GL_TEXTURE_MIN_FILTER:
if (!feature_info->validators()->texture_min_filter_mode.IsValid(param)) {
return false;
}
min_filter_ = param;
break;
case GL_TEXTURE_MAG_FILTER:
if (!feature_info->validators()->texture_mag_filter_mode.IsValid(param)) {
return false;
}
mag_filter_ = param;
break;
case GL_TEXTURE_WRAP_S:
if (!feature_info->validators()->texture_wrap_mode.IsValid(param)) {
return false;
}
wrap_s_ = param;
break;
case GL_TEXTURE_WRAP_T:
if (!feature_info->validators()->texture_wrap_mode.IsValid(param)) {
return false;
}
wrap_t_ = param;
break;
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
// Nothing to do for this case at the moment.
break;
case GL_TEXTURE_USAGE_ANGLE:
if (!feature_info->validators()->texture_usage.IsValid(param)) {
return false;
}
usage_ = param;
break;
default:
NOTREACHED();
return false;
}
Update(feature_info);
UpdateCleared();
return true;
}
void TextureManager::TextureInfo::Update(const FeatureInfo* feature_info) {
// Update npot status.
npot_ = false;
if (level_infos_.empty()) {
texture_complete_ = false;
cube_complete_ = false;
return;
}
// checks that the first mip of any face is npot.
for (size_t ii = 0; ii < level_infos_.size(); ++ii) {
const TextureInfo::LevelInfo& info = level_infos_[ii][0];
if (GLES2Util::IsNPOT(info.width) ||
GLES2Util::IsNPOT(info.height) ||
GLES2Util::IsNPOT(info.depth)) {
npot_ = true;
break;
}
}
// Update texture_complete and cube_complete status.
const TextureInfo::LevelInfo& first_face = level_infos_[0][0];
int levels_needed = ComputeMipMapCount(
first_face.width, first_face.height, first_face.depth);
texture_complete_ =
max_level_set_ >= (levels_needed - 1) && max_level_set_ >= 0;
cube_complete_ = (level_infos_.size() == 6) &&
(first_face.width == first_face.height);
if (first_face.width == 0 || first_face.height == 0) {
texture_complete_ = false;
}
if (first_face.type == GL_FLOAT &&
!feature_info->feature_flags().enable_texture_float_linear &&
(min_filter_ != GL_NEAREST_MIPMAP_NEAREST ||
mag_filter_ != GL_NEAREST)) {
texture_complete_ = false;
} else if (first_face.type == GL_HALF_FLOAT_OES &&
!feature_info->feature_flags().enable_texture_half_float_linear &&
(min_filter_ != GL_NEAREST_MIPMAP_NEAREST ||
mag_filter_ != GL_NEAREST)) {
texture_complete_ = false;
}
for (size_t ii = 0;
ii < level_infos_.size() && (cube_complete_ || texture_complete_);
++ii) {
const TextureInfo::LevelInfo& level0 = level_infos_[ii][0];
if (level0.target == 0 ||
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 < levels_needed; ++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.target == 0 ||
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;
}
}
}
}
bool TextureManager::TextureInfo::ClearRenderableLevels(GLES2Decoder* decoder) {
DCHECK(decoder);
if (SafeToRenderFrom()) {
return true;
}
const TextureInfo::LevelInfo& first_face = level_infos_[0][0];
int levels_needed = ComputeMipMapCount(
first_face.width, first_face.height, first_face.depth);
for (size_t ii = 0; ii < level_infos_.size(); ++ii) {
for (GLint jj = 0; jj < levels_needed; ++jj) {
TextureInfo::LevelInfo& info = level_infos_[ii][jj];
if (info.target != 0) {
if (!ClearLevel(decoder, info.target, jj)) {
return false;
}
}
}
}
cleared_ = true;
return true;
}
bool TextureManager::TextureInfo::IsLevelCleared(GLenum target, GLint level) {
size_t face_index = GLTargetToFaceIndex(target);
if (face_index >= level_infos_.size() ||
level >= static_cast<GLint>(level_infos_[face_index].size())) {
return true;
}
TextureInfo::LevelInfo& info = level_infos_[face_index][level];
return info.cleared;
}
bool TextureManager::TextureInfo::ClearLevel(
GLES2Decoder* decoder, GLenum target, GLint level) {
DCHECK(decoder);
size_t face_index = GLTargetToFaceIndex(target);
if (face_index >= level_infos_.size() ||
level >= static_cast<GLint>(level_infos_[face_index].size())) {
return true;
}
TextureInfo::LevelInfo& info = level_infos_[face_index][level];
DCHECK(target == info.target);
if (info.target == 0 ||
info.cleared ||
info.width == 0 ||
info.height == 0 ||
info.depth == 0) {
return true;
}
DCHECK_NE(0, num_uncleared_mips_);
--num_uncleared_mips_;
// NOTE: It seems kind of gross to call back into the decoder for this
// but only the decoder knows all the state (like unpack_alignment_) that's
// needed to be able to call GL correctly.
info.cleared = decoder->ClearLevel(
service_id_, target_, info.target, info.level, info.format, info.type,
info.width, info.height, immutable_);
if (!info.cleared) {
++num_uncleared_mips_;
}
return info.cleared;
}
TextureManager::TextureManager(
MemoryTracker* memory_tracker,
FeatureInfo* feature_info,
GLint max_texture_size,
GLint max_cube_map_texture_size)
: texture_memory_tracker_(new MemoryTypeTracker(memory_tracker)),
feature_info_(feature_info),
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)),
num_unrenderable_textures_(0),
num_unsafe_textures_(0),
num_uncleared_mips_(0),
texture_info_count_(0),
mem_represented_(0),
have_context_(true) {
for (int ii = 0; ii < kNumDefaultTextures; ++ii) {
black_texture_ids_[ii] = 0;
}
}
void TextureManager::UpdateMemRepresented() {
texture_memory_tracker_->UpdateMemRepresented(mem_represented_);
}
bool TextureManager::Initialize() {
UpdateMemRepresented();
// TODO(gman): The default textures have to be real textures, not the 0
// texture because we simulate non shared resources on top of shared
// resources and all contexts that share resource share the same default
// texture.
default_textures_[kTexture2D] = CreateDefaultAndBlackTextures(
GL_TEXTURE_2D, &black_texture_ids_[kTexture2D]);
default_textures_[kCubeMap] = CreateDefaultAndBlackTextures(
GL_TEXTURE_CUBE_MAP, &black_texture_ids_[kCubeMap]);
if (feature_info_->feature_flags().oes_egl_image_external) {
default_textures_[kExternalOES] = CreateDefaultAndBlackTextures(
GL_TEXTURE_EXTERNAL_OES, &black_texture_ids_[kExternalOES]);
}
if (feature_info_->feature_flags().arb_texture_rectangle) {
default_textures_[kRectangleARB] = CreateDefaultAndBlackTextures(
GL_TEXTURE_RECTANGLE_ARB, &black_texture_ids_[kRectangleARB]);
}
return true;
}
TextureManager::TextureInfo::Ref TextureManager::CreateDefaultAndBlackTextures(
GLenum target,
GLuint* black_texture) {
static uint8 black[] = {0, 0, 0, 255};
// Sampling a texture not associated with any EGLImage sibling will return
// black values according to the spec.
bool needs_initialization = (target != GL_TEXTURE_EXTERNAL_OES);
bool needs_faces = (target == GL_TEXTURE_CUBE_MAP);
// Make default textures and texture for replacing non-renderable textures.
GLuint ids[2];
glGenTextures(arraysize(ids), ids);
for (unsigned long ii = 0; ii < arraysize(ids); ++ii) {
glBindTexture(target, ids[ii]);
if (needs_initialization) {
if (needs_faces) {
for (int jj = 0; jj < GLES2Util::kNumFaces; ++jj) {
glTexImage2D(GLES2Util::IndexToGLFaceTarget(jj), 0, GL_RGBA, 1, 1, 0,
GL_RGBA, GL_UNSIGNED_BYTE, black);
}
} else {
glTexImage2D(target, 0, GL_RGBA, 1, 1, 0, GL_RGBA,
GL_UNSIGNED_BYTE, black);
}
}
}
glBindTexture(target, 0);
// Since we are manually setting up these textures
// we need to manually manipulate some of the their bookkeeping.
++num_unrenderable_textures_;
TextureInfo::Ref default_texture = TextureInfo::Ref(
new TextureInfo(this, ids[1]));
SetInfoTarget(default_texture, target);
if (needs_faces) {
for (int ii = 0; ii < GLES2Util::kNumFaces; ++ii) {
SetLevelInfo(
default_texture, GLES2Util::IndexToGLFaceTarget(ii),
0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
}
} else {
if (needs_initialization) {
SetLevelInfo(default_texture,
GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0,
GL_RGBA, GL_UNSIGNED_BYTE, true);
} else {
SetLevelInfo(
default_texture, GL_TEXTURE_EXTERNAL_OES, 0,
GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true);
}
}
*black_texture = ids[0];
return default_texture;
}
bool TextureManager::ValidForTarget(
GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth) {
GLsizei max_size = MaxSizeForTarget(target);
return level >= 0 &&
width >= 0 &&
height >= 0 &&
depth >= 0 &&
level < MaxLevelsForTarget(target) &&
width <= max_size &&
height <= max_size &&
depth <= max_size &&
(level == 0 || feature_info_->feature_flags().npot_ok ||
(!GLES2Util::IsNPOT(width) &&
!GLES2Util::IsNPOT(height) &&
!GLES2Util::IsNPOT(depth))) &&
(target != GL_TEXTURE_CUBE_MAP || (width == height && depth == 1)) &&
(target != GL_TEXTURE_2D || (depth == 1));
}
void TextureManager::SetInfoTarget(
TextureManager::TextureInfo* info, GLenum target) {
DCHECK(info);
if (!info->CanRender(feature_info_)) {
DCHECK_NE(0, num_unrenderable_textures_);
--num_unrenderable_textures_;
}
info->SetTarget(target, MaxLevelsForTarget(target));
if (!info->CanRender(feature_info_)) {
++num_unrenderable_textures_;
}
}
void TextureManager::SetLevelCleared(
TextureManager::TextureInfo* info, GLenum target, GLint level) {
DCHECK(info);
if (!info->SafeToRenderFrom()) {
DCHECK_NE(0, num_unsafe_textures_);
--num_unsafe_textures_;
}
num_uncleared_mips_ -= info->num_uncleared_mips();
DCHECK_GE(num_uncleared_mips_, 0);
info->SetLevelCleared(target, level);
num_uncleared_mips_ += info->num_uncleared_mips();
if (!info->SafeToRenderFrom()) {
++num_unsafe_textures_;
}
}
bool TextureManager::ClearRenderableLevels(
GLES2Decoder* decoder,TextureManager::TextureInfo* info) {
DCHECK(info);
if (info->SafeToRenderFrom()) {
return true;
}
DCHECK_NE(0, num_unsafe_textures_);
--num_unsafe_textures_;
num_uncleared_mips_ -= info->num_uncleared_mips();
DCHECK_GE(num_uncleared_mips_, 0);
bool result = info->ClearRenderableLevels(decoder);
num_uncleared_mips_ += info->num_uncleared_mips();
if (!info->SafeToRenderFrom()) {
++num_unsafe_textures_;
}
return result;
}
bool TextureManager::ClearTextureLevel(
GLES2Decoder* decoder,TextureManager::TextureInfo* info,
GLenum target, GLint level) {
DCHECK(info);
if (info->num_uncleared_mips() == 0) {
return true;
}
num_uncleared_mips_ -= info->num_uncleared_mips();
DCHECK_GE(num_uncleared_mips_, 0);
if (!info->SafeToRenderFrom()) {
DCHECK_NE(0, num_unsafe_textures_);
--num_unsafe_textures_;
}
bool result = info->ClearLevel(decoder, target, level);
info->UpdateCleared();
num_uncleared_mips_ += info->num_uncleared_mips();
if (!info->SafeToRenderFrom()) {
++num_unsafe_textures_;
}
return result;
}
void TextureManager::SetLevelInfo(
TextureManager::TextureInfo* info,
GLenum target,
GLint level,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLsizei depth,
GLint border,
GLenum format,
GLenum type,
bool cleared) {
DCHECK(info);
if (!info->CanRender(feature_info_)) {
DCHECK_NE(0, num_unrenderable_textures_);
--num_unrenderable_textures_;
}
if (!info->SafeToRenderFrom()) {
DCHECK_NE(0, num_unsafe_textures_);
--num_unsafe_textures_;
}
num_uncleared_mips_ -= info->num_uncleared_mips();
DCHECK_GE(num_uncleared_mips_, 0);
mem_represented_ -= info->estimated_size();
info->SetLevelInfo(
feature_info_, target, level, internal_format, width, height, depth,
border, format, type, cleared);
mem_represented_ += info->estimated_size();
UpdateMemRepresented();
num_uncleared_mips_ += info->num_uncleared_mips();
if (!info->CanRender(feature_info_)) {
++num_unrenderable_textures_;
}
if (!info->SafeToRenderFrom()) {
++num_unsafe_textures_;
}
}
TextureDefinition* TextureManager::Save(TextureInfo* info) {
DCHECK(info->owned_);
if (info->IsAttachedToFramebuffer())
return NULL;
TextureDefinition::LevelInfos level_infos(info->level_infos_.size());
for (size_t face = 0; face < level_infos.size(); ++face) {
GLenum target = info->target() == GL_TEXTURE_2D ?
GL_TEXTURE_2D : FaceIndexToGLTarget(face);
for (size_t level = 0; level < info->level_infos_[face].size(); ++level) {
const TextureInfo::LevelInfo& level_info =
info->level_infos_[face][level];
level_infos[face].push_back(
TextureDefinition::LevelInfo(target,
level_info.internal_format,
level_info.width,
level_info.height,
level_info.depth,
level_info.border,
level_info.format,
level_info.type,
level_info.cleared));
SetLevelInfo(info,
target,
level,
GL_RGBA,
0,
0,
0,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
true);
}
}
GLuint old_service_id = info->service_id();
bool immutable = info->IsImmutable();
GLuint new_service_id = 0;
glGenTextures(1, &new_service_id);
info->SetServiceId(new_service_id);
info->SetImmutable(false);
return new TextureDefinition(info->target(),
old_service_id,
immutable,
level_infos);
}
bool TextureManager::Restore(TextureInfo* info,
TextureDefinition* definition) {
DCHECK(info->owned_);
scoped_ptr<TextureDefinition> scoped_definition(definition);
if (info->IsAttachedToFramebuffer())
return false;
if (info->target() != definition->target())
return false;
if (info->level_infos_.size() != definition->level_infos().size())
return false;
if (info->level_infos_[0].size() != definition->level_infos()[0].size())
return false;
for (size_t face = 0; face < info->level_infos_.size(); ++face) {
GLenum target = info->target() == GL_TEXTURE_2D ?
GL_TEXTURE_2D : FaceIndexToGLTarget(face);
for (size_t level = 0; level < info->level_infos_[face].size(); ++level) {
const TextureDefinition::LevelInfo& level_info =
definition->level_infos()[face][level];
SetLevelInfo(info,
target,
level,
level_info.internal_format,
level_info.width,
level_info.height,
level_info.depth,
level_info.border,
level_info.format,
level_info.type,
level_info.cleared);
}
}
GLuint old_service_id = info->service_id();
glDeleteTextures(1, &old_service_id);
info->SetServiceId(definition->ReleaseServiceId());
info->SetImmutable(definition->immutable());
return true;
}
bool TextureManager::SetParameter(
TextureManager::TextureInfo* info, GLenum pname, GLint param) {
DCHECK(info);
if (!info->CanRender(feature_info_)) {
DCHECK_NE(0, num_unrenderable_textures_);
--num_unrenderable_textures_;
}
if (!info->SafeToRenderFrom()) {
DCHECK_NE(0, num_unsafe_textures_);
--num_unsafe_textures_;
}
bool result = info->SetParameter(feature_info_, pname, param);
if (!info->CanRender(feature_info_)) {
++num_unrenderable_textures_;
}
if (!info->SafeToRenderFrom()) {
++num_unsafe_textures_;
}
return result;
}
bool TextureManager::MarkMipmapsGenerated(TextureManager::TextureInfo* info) {
DCHECK(info);
if (!info->CanRender(feature_info_)) {
DCHECK_NE(0, num_unrenderable_textures_);
--num_unrenderable_textures_;
}
if (!info->SafeToRenderFrom()) {
DCHECK_NE(0, num_unsafe_textures_);
--num_unsafe_textures_;
}
num_uncleared_mips_ -= info->num_uncleared_mips();
DCHECK_GE(num_uncleared_mips_, 0);
mem_represented_ -= info->estimated_size();
bool result = info->MarkMipmapsGenerated(feature_info_);
mem_represented_ += info->estimated_size();
UpdateMemRepresented();
num_uncleared_mips_ += info->num_uncleared_mips();
if (!info->CanRender(feature_info_)) {
++num_unrenderable_textures_;
}
if (!info->SafeToRenderFrom()) {
++num_unsafe_textures_;
}
return result;
}
TextureManager::TextureInfo* TextureManager::CreateTextureInfo(
GLuint client_id, GLuint service_id) {
DCHECK_NE(0u, service_id);
TextureInfo::Ref info(new TextureInfo(this, service_id));
std::pair<TextureInfoMap::iterator, bool> result =
texture_infos_.insert(std::make_pair(client_id, info));
DCHECK(result.second);
if (!info->CanRender(feature_info_)) {
++num_unrenderable_textures_;
}
if (!info->SafeToRenderFrom()) {
++num_unsafe_textures_;
}
num_uncleared_mips_ += info->num_uncleared_mips();
return info.get();
}
TextureManager::TextureInfo* TextureManager::GetTextureInfo(
GLuint client_id) {
TextureInfoMap::iterator it = texture_infos_.find(client_id);
return it != texture_infos_.end() ? it->second : NULL;
}
void TextureManager::RemoveTextureInfo(GLuint client_id) {
TextureInfoMap::iterator it = texture_infos_.find(client_id);
if (it != texture_infos_.end()) {
TextureInfo* info = it->second;
info->MarkAsDeleted();
texture_infos_.erase(it);
}
}
void TextureManager::StartTracking(TextureManager::TextureInfo* /* texture */) {
++texture_info_count_;
}
void TextureManager::StopTracking(TextureManager::TextureInfo* texture) {
--texture_info_count_;
if (!texture->CanRender(feature_info_)) {
DCHECK_NE(0, num_unrenderable_textures_);
--num_unrenderable_textures_;
}
if (!texture->SafeToRenderFrom()) {
DCHECK_NE(0, num_unsafe_textures_);
--num_unsafe_textures_;
}
num_uncleared_mips_ -= texture->num_uncleared_mips();
DCHECK_GE(num_uncleared_mips_, 0);
mem_represented_ -= texture->estimated_size();
UpdateMemRepresented();
}
bool TextureManager::GetClientId(GLuint service_id, GLuint* client_id) const {
// This doesn't need to be fast. It's only used during slow queries.
for (TextureInfoMap::const_iterator it = texture_infos_.begin();
it != texture_infos_.end(); ++it) {
if (it->second->service_id() == service_id) {
*client_id = it->first;
return true;
}
}
return false;
}
GLsizei TextureManager::ComputeMipMapCount(
GLsizei width, GLsizei height, GLsizei depth) {
return 1 + base::bits::Log2Floor(std::max(std::max(width, height), depth));
}
} // namespace gles2
} // namespace gpu