Index: gpu/command_buffer/service/texture_manager.cc |
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc |
index 4f856d6ae234b326ac0154ed9c650aa2e57a8efb..bd93329f00e14d9c7888a0cca4695b26229ef1e0 100644 |
--- a/gpu/command_buffer/service/texture_manager.cc |
+++ b/gpu/command_buffer/service/texture_manager.cc |
@@ -22,6 +22,7 @@ |
#include "gpu/command_buffer/service/mailbox_manager.h" |
#include "gpu/command_buffer/service/memory_tracking.h" |
#include "ui/gl/gl_implementation.h" |
+#include "ui/gl/gl_version_info.h" |
#include "ui/gl/trace_util.h" |
namespace gpu { |
@@ -2002,6 +2003,138 @@ void TextureManager::ValidateAndDoTexImage( |
function_name, texture_ref, args); |
} |
+bool TextureManager::ValidateTexSubImage(ContextState* state, |
+ const char* function_name, |
+ const DoTexSubImageArguments& args, |
+ TextureRef** texture_ref) { |
+ ErrorState* error_state = state->GetErrorState(); |
+ const Validators* validators = feature_info_->validators(); |
+ |
+ if (!validators->texture_target.IsValid(args.target)) { |
+ ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(error_state, function_name, |
+ args.target, "target"); |
+ return false; |
+ } |
+ if (args.width < 0) { |
+ ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_VALUE, function_name, |
+ "width < 0"); |
+ return false; |
+ } |
+ if (args.height < 0) { |
+ ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_VALUE, function_name, |
+ "height < 0"); |
+ return false; |
+ } |
+ TextureRef* local_texture_ref = GetTextureInfoForTarget(state, args.target); |
+ if (!local_texture_ref) { |
+ ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, function_name, |
+ "unknown texture for target"); |
+ return false; |
+ } |
+ Texture* texture = local_texture_ref->texture(); |
+ GLenum current_type = 0; |
+ GLenum internal_format = 0; |
+ if (!texture->GetLevelType(args.target, args.level, ¤t_type, |
+ &internal_format)) { |
+ ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, function_name, |
+ "level does not exist."); |
+ return false; |
+ } |
+ if (!ValidateTextureParameters(error_state, function_name, args.format, |
+ args.type, internal_format, args.level)) { |
+ return false; |
+ } |
+ if (args.type != current_type && !feature_info_->IsES3Enabled()) { |
+ ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, function_name, |
+ "type does not match type of texture."); |
+ return false; |
+ } |
+ if (!texture->ValidForTexture(args.target, args.level, args.xoffset, |
+ args.yoffset, 0, args.width, args.height, 1)) { |
+ ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_VALUE, function_name, |
+ "bad dimensions."); |
+ return false; |
+ } |
+ if ((GLES2Util::GetChannelsForFormat(args.format) & |
+ (GLES2Util::kDepth | GLES2Util::kStencil)) != 0 && |
+ !feature_info_->IsES3Enabled()) { |
+ ERRORSTATE_SET_GL_ERROR( |
+ error_state, GL_INVALID_OPERATION, function_name, |
+ "can not supply data for depth or stencil textures"); |
+ return false; |
+ } |
+ DCHECK(args.pixels); |
+ *texture_ref = local_texture_ref; |
+ return true; |
+} |
+ |
+void TextureManager::ValidateAndDoTexSubImage( |
+ GLES2Decoder* decoder, |
+ DecoderTextureState* texture_state, |
+ ContextState* state, |
+ DecoderFramebufferState* framebuffer_state, |
+ const char* function_name, |
+ const DoTexSubImageArguments& args) { |
+ ErrorState* error_state = state->GetErrorState(); |
+ TextureRef* texture_ref; |
+ if (!ValidateTexSubImage(state, function_name, args, &texture_ref)) { |
+ return; |
+ } |
+ |
+ Texture* texture = texture_ref->texture(); |
+ GLsizei tex_width = 0; |
+ GLsizei tex_height = 0; |
+ bool ok = texture->GetLevelSize(args.target, args.level, &tex_width, |
+ &tex_height, nullptr); |
+ DCHECK(ok); |
+ if (args.xoffset != 0 || args.yoffset != 0 || args.width != tex_width || |
+ args.height != tex_height) { |
+ gfx::Rect cleared_rect; |
+ if (CombineAdjacentRects( |
+ texture->GetLevelClearedRect(args.target, args.level), |
+ gfx::Rect(args.xoffset, args.yoffset, args.width, args.height), |
+ &cleared_rect)) { |
+ DCHECK_GE(cleared_rect.size().GetArea(), |
+ texture->GetLevelClearedRect(args.target, args.level) |
+ .size() |
+ .GetArea()); |
+ SetLevelClearedRect(texture_ref, args.target, args.level, cleared_rect); |
+ } else { |
+ // Otherwise clear part of texture level that is not already cleared. |
+ if (!ClearTextureLevel(decoder, texture_ref, args.target, args.level)) { |
+ ERRORSTATE_SET_GL_ERROR(error_state, GL_OUT_OF_MEMORY, |
+ "glTexSubImage2D", "dimensions too big"); |
+ return; |
+ } |
+ } |
+ ScopedTextureUploadTimer timer(texture_state); |
+ glTexSubImage2D(args.target, args.level, args.xoffset, args.yoffset, |
+ args.width, args.height, AdjustTexFormat(args.format), |
+ args.type, args.pixels); |
+ return; |
+ } |
+ |
+ if (!texture_state->texsubimage_faster_than_teximage && |
+ !texture->IsImmutable() && !texture->HasImages()) { |
+ ScopedTextureUploadTimer timer(texture_state); |
+ GLenum internal_format; |
+ GLenum tex_type; |
+ texture->GetLevelType(args.target, args.level, &tex_type, &internal_format); |
+ // NOTE: In OpenGL ES 2.0 border is always zero. If that changes we'll need |
+ // to look it up. |
+ glTexImage2D(args.target, args.level, internal_format, args.width, |
+ args.height, 0, AdjustTexFormat(args.format), args.type, |
+ args.pixels); |
+ } else { |
+ ScopedTextureUploadTimer timer(texture_state); |
+ glTexSubImage2D(args.target, args.level, args.xoffset, args.yoffset, |
+ args.width, args.height, AdjustTexFormat(args.format), |
+ args.type, args.pixels); |
+ } |
+ SetLevelCleared(texture_ref, args.target, args.level, true); |
+ return; |
+} |
+ |
GLenum TextureManager::AdjustTexFormat(GLenum format) const { |
// TODO(bajones): GLES 3 allows for internal format and format to differ. |
// This logic may need to change as a result. |
@@ -2089,6 +2222,31 @@ void TextureManager::DoTexImage( |
} |
} |
+bool TextureManager::CombineAdjacentRects(const gfx::Rect& rect1, |
+ const gfx::Rect& rect2, |
+ gfx::Rect* result) { |
+ // Return |rect2| if |rect1| is empty or |rect2| contains |rect1|. |
+ if (rect1.IsEmpty() || rect2.Contains(rect1)) { |
+ *result = rect2; |
+ return true; |
+ } |
+ |
+ // Return |rect1| if |rect2| is empty or |rect1| contains |rect2|. |
+ if (rect2.IsEmpty() || rect1.Contains(rect2)) { |
+ *result = rect1; |
+ return true; |
+ } |
+ |
+ // Return the union of |rect1| and |rect2| if they share an edge. |
+ if (rect1.SharesEdgeWith(rect2)) { |
+ *result = gfx::UnionRects(rect1, rect2); |
+ return true; |
+ } |
+ |
+ // Return false if it's not possible to combine |rect1| and |rect2|. |
+ return false; |
+} |
+ |
ScopedTextureUploadTimer::ScopedTextureUploadTimer( |
DecoderTextureState* texture_state) |
: texture_state_(texture_state), |