Index: gpu/command_buffer/service/gles2_cmd_decoder.cc |
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc |
index 6df7f6a2f816a89bb6e37149101e0a60c27a2da4..9d6959f47e80b63ff97494c6d1f4907266f52686 100644 |
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc |
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc |
@@ -64,6 +64,7 @@ |
#include "gpu/command_buffer/service/transform_feedback_manager.h" |
#include "gpu/command_buffer/service/vertex_array_manager.h" |
#include "gpu/command_buffer/service/vertex_attrib_manager.h" |
+#include "third_party/angle/src/image_util/loadimage.h" |
#include "third_party/smhasher/src/City.h" |
#include "ui/gfx/buffer_types.h" |
#include "ui/gfx/geometry/point.h" |
@@ -1900,9 +1901,13 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient { |
bool ValidateCompressedTexDimensions( |
const char* function_name, GLenum target, GLint level, |
GLsizei width, GLsizei height, GLsizei depth, GLenum format); |
- bool ValidateCompressedTexFuncData( |
- const char* function_name, GLsizei width, GLsizei height, GLsizei depth, |
- GLenum format, GLsizei size); |
+ bool ValidateCompressedTexFuncData(const char* function_name, |
+ GLsizei width, |
+ GLsizei height, |
+ GLsizei depth, |
+ GLenum format, |
+ GLsizei size, |
+ const GLvoid* data); |
bool ValidateCompressedTexSubDimensions( |
const char* function_name, |
GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, |
@@ -11482,6 +11487,146 @@ const ASTCBlockArray kASTCBlockArray[] = { |
{12, 10}, |
{12, 12}}; |
+bool CheckETCFormatSupport(const FeatureInfo& featureInfo) { |
+ const gl::GLVersionInfo& versionInfo = featureInfo.gl_version_info(); |
+ return versionInfo.IsAtLeastGL(4, 3) || versionInfo.IsAtLeastGLES(3, 0) || |
+ featureInfo.feature_flags().arb_es3_compatibility; |
+} |
+ |
+using CompressedFormatSupportCheck = bool (*)(const FeatureInfo&); |
+using CompressedFormatDecompressionFunction = void (*)(size_t width, |
+ size_t height, |
+ size_t depth, |
+ const uint8_t* input, |
+ size_t inputRowPitch, |
+ size_t inputDepthPitch, |
+ uint8_t* output, |
+ size_t outputRowPitch, |
+ size_t outputDepthPitch); |
+ |
+struct CompressedFormatInfo { |
+ GLenum format; |
+ uint32_t block_size; |
+ uint32_t bytes_per_block; |
+ CompressedFormatSupportCheck support_check; |
+ CompressedFormatDecompressionFunction decompression_function; |
+ GLenum decompressed_internal_format; |
+ GLenum decompressed_format; |
+ GLenum decompressed_type; |
+}; |
+ |
+const CompressedFormatInfo kCompressedFormatInfoArray[] = { |
+ { |
+ GL_COMPRESSED_R11_EAC, 4, 8, CheckETCFormatSupport, |
+ angle::LoadEACR11ToR8, GL_R8, GL_RED, GL_UNSIGNED_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_SIGNED_R11_EAC, 4, 8, CheckETCFormatSupport, |
+ angle::LoadEACR11SToR8, GL_R8_SNORM, GL_RED, GL_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_RG11_EAC, 4, 16, CheckETCFormatSupport, |
+ angle::LoadEACRG11ToRG8, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_SIGNED_RG11_EAC, 4, 16, CheckETCFormatSupport, |
+ angle::LoadEACRG11SToRG8, GL_RG8_SNORM, GL_RG, GL_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_RGB8_ETC2, 4, 8, CheckETCFormatSupport, |
+ angle::LoadETC2RGB8ToRGBA8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_SRGB8_ETC2, 4, 8, CheckETCFormatSupport, |
+ angle::LoadETC2SRGB8ToRGBA8, GL_SRGB8_ALPHA8, GL_SRGB_ALPHA, |
+ GL_UNSIGNED_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_RGBA8_ETC2_EAC, 4, 16, CheckETCFormatSupport, |
+ angle::LoadETC2RGBA8ToRGBA8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, 4, 8, |
+ CheckETCFormatSupport, angle::LoadETC2RGB8A1ToRGBA8, GL_RGBA8, GL_RGBA, |
+ GL_UNSIGNED_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, 4, 16, CheckETCFormatSupport, |
+ angle::LoadETC2SRGBA8ToSRGBA8, GL_SRGB8_ALPHA8, GL_SRGB_ALPHA, |
+ GL_UNSIGNED_BYTE, |
+ }, |
+ { |
+ GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 4, 8, |
+ CheckETCFormatSupport, angle::LoadETC2SRGB8A1ToRGBA8, GL_SRGB8_ALPHA8, |
+ GL_SRGB_ALPHA, GL_UNSIGNED_BYTE, |
+ }, |
+}; |
+ |
+const CompressedFormatInfo* GetCompressedFormatInfo(GLenum format) { |
+ for (size_t i = 0; i < arraysize(kCompressedFormatInfoArray); i++) { |
+ if (kCompressedFormatInfoArray[i].format == format) { |
+ return &kCompressedFormatInfoArray[i]; |
+ } |
+ } |
+ return nullptr; |
+} |
+ |
+uint32_t GetCompressedFormatRowPitch(const CompressedFormatInfo& info, |
+ uint32_t width) { |
+ uint32_t num_blocks_wide = (width + info.block_size - 1) / info.block_size; |
+ return num_blocks_wide * info.bytes_per_block; |
+} |
+ |
+uint32_t GetCompressedFormatDepthPitch(const CompressedFormatInfo& info, |
+ uint32_t width, |
+ uint32_t height) { |
+ uint32_t num_blocks_high = (height + info.block_size - 1) / info.block_size; |
+ return num_blocks_high * GetCompressedFormatRowPitch(info, width); |
+} |
+ |
+std::unique_ptr<uint8_t[]> DecompressTextureData( |
+ const ContextState& state, |
+ const CompressedFormatInfo& info, |
+ uint32_t width, |
+ uint32_t height, |
+ uint32_t depth, |
+ GLsizei image_size, |
+ const void* data) { |
+ uint32_t output_pixel_size = GLES2Util::ComputeImageGroupSize( |
+ info.decompressed_format, info.decompressed_type); |
+ std::unique_ptr<uint8_t[]> decompressed_data( |
+ new uint8_t[output_pixel_size * width * height]); |
+ |
+ // If a PBO is bound, map it to decompress the data. |
+ const void* input_data = data; |
+ if (state.bound_pixel_unpack_buffer) { |
+ input_data = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, |
+ reinterpret_cast<GLintptr>(data), image_size, |
+ GL_MAP_READ_BIT); |
+ if (input_data == nullptr) { |
+ LOG(ERROR) << "Failed to map pixel unpack buffer."; |
+ return nullptr; |
+ } |
+ } |
+ |
+ DCHECK_NE(input_data, nullptr); |
+ info.decompression_function( |
+ width, height, depth, static_cast<const uint8_t*>(input_data), |
+ GetCompressedFormatRowPitch(info, width), |
+ GetCompressedFormatDepthPitch(info, width, height), |
+ decompressed_data.get(), output_pixel_size * width, |
+ output_pixel_size * width * height); |
+ |
+ if (state.bound_pixel_unpack_buffer) { |
+ if (glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER) != GL_TRUE) { |
+ LOG(ERROR) << "glUnmapBuffer unexpectedly returned GL_FALSE"; |
+ return nullptr; |
+ } |
+ } |
+ |
+ return decompressed_data; |
+} |
+ |
bool IsValidDXTSize(GLint level, GLsizei size) { |
// TODO(zmo): Linux NVIDIA driver does allow size of 1 and 2 on level 0. |
// However, the WebGL conformance test and blink side code forbid it. |
@@ -11623,9 +11768,13 @@ bool GLES2DecoderImpl::GetCompressedTexSizeInBytes( |
return true; |
} |
-bool GLES2DecoderImpl::ValidateCompressedTexFuncData( |
- const char* function_name, GLsizei width, GLsizei height, GLsizei depth, |
- GLenum format, GLsizei size) { |
+bool GLES2DecoderImpl::ValidateCompressedTexFuncData(const char* function_name, |
+ GLsizei width, |
+ GLsizei height, |
+ GLsizei depth, |
+ GLenum format, |
+ GLsizei size, |
+ const GLvoid* data) { |
GLsizei bytes_required = 0; |
if (!GetCompressedTexSizeInBytes( |
function_name, width, height, depth, format, &bytes_required)) { |
@@ -11638,6 +11787,25 @@ bool GLES2DecoderImpl::ValidateCompressedTexFuncData( |
return false; |
} |
+ if (state_.bound_pixel_unpack_buffer.get()) { |
+ if (state_.bound_pixel_unpack_buffer->GetMappedRange() != nullptr) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
+ "pixel unpack buffer is mapped"); |
+ return false; |
+ } |
+ |
+ base::CheckedNumeric<GLintptr> pbo_bytes_required( |
+ reinterpret_cast<GLintptr>(data)); |
+ pbo_bytes_required += bytes_required; |
+ if (!pbo_bytes_required.IsValid() || |
+ pbo_bytes_required.ValueOrDie() > |
+ state_.bound_pixel_unpack_buffer->size()) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
+ "pixel unpack buffer is not large enough"); |
+ return false; |
+ } |
+ } |
+ |
return true; |
} |
@@ -11889,8 +12057,8 @@ error::Error GLES2DecoderImpl::DoCompressedTexImage2D( |
} |
if (!ValidateCompressedTexDimensions("glCompressedTexImage2D", target, level, |
width, height, 1, internal_format) || |
- !ValidateCompressedTexFuncData("glCompressedTexImage2D", width, height, |
- 1, internal_format, image_size)) { |
+ !ValidateCompressedTexFuncData("glCompressedTexImage2D", width, height, 1, |
+ internal_format, image_size, data)) { |
return error::kNoError; |
} |
@@ -11905,14 +12073,32 @@ error::Error GLES2DecoderImpl::DoCompressedTexImage2D( |
} |
std::unique_ptr<int8_t[]> zero; |
- if (!data) { |
+ if (!state_.bound_pixel_unpack_buffer && !data) { |
zero.reset(new int8_t[image_size]); |
memset(zero.get(), 0, image_size); |
data = zero.get(); |
} |
LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glCompressedTexImage2D"); |
- glCompressedTexImage2D( |
- target, level, internal_format, width, height, border, image_size, data); |
+ |
+ const CompressedFormatInfo* format_info = |
+ GetCompressedFormatInfo(internal_format); |
+ if (format_info != nullptr && !format_info->support_check(*feature_info_)) { |
+ std::unique_ptr<uint8_t[]> decompressed_data = DecompressTextureData( |
+ state_, *format_info, width, height, 1, image_size, data); |
+ if (!decompressed_data) { |
+ MarkContextLost(error::kGuilty); |
+ group_->LoseContexts(error::kInnocent); |
+ return error::kLostContext; |
+ } |
+ state_.PushTextureDecompressionUnpackState(); |
+ glTexImage2D(target, level, format_info->decompressed_internal_format, |
+ width, height, border, format_info->decompressed_format, |
+ format_info->decompressed_type, decompressed_data.get()); |
+ state_.RestoreUnpackState(); |
+ } else { |
+ glCompressedTexImage2D(target, level, internal_format, width, height, |
+ border, image_size, data); |
+ } |
GLenum error = LOCAL_PEEK_GL_ERROR("glCompressedTexImage2D"); |
if (error == GL_NO_ERROR) { |
texture_manager()->SetLevelInfo(texture_ref, target, level, internal_format, |
@@ -11962,7 +12148,8 @@ error::Error GLES2DecoderImpl::DoCompressedTexImage3D( |
if (!ValidateCompressedTexDimensions("glCompressedTexImage3D", target, level, |
width, height, depth, internal_format) || |
!ValidateCompressedTexFuncData("glCompressedTexImage3D", width, height, |
- depth, internal_format, image_size)) { |
+ depth, internal_format, image_size, |
+ data)) { |
return error::kNoError; |
} |
@@ -11977,14 +12164,31 @@ error::Error GLES2DecoderImpl::DoCompressedTexImage3D( |
} |
std::unique_ptr<int8_t[]> zero; |
- if (!data) { |
+ if (!state_.bound_pixel_unpack_buffer && !data) { |
zero.reset(new int8_t[image_size]); |
memset(zero.get(), 0, image_size); |
data = zero.get(); |
} |
LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glCompressedTexImage3D"); |
- glCompressedTexImage3D(target, level, internal_format, width, height, depth, |
- border, image_size, data); |
+ const CompressedFormatInfo* format_info = |
+ GetCompressedFormatInfo(internal_format); |
+ if (format_info != nullptr && !format_info->support_check(*feature_info_)) { |
+ std::unique_ptr<uint8_t[]> decompressed_data = DecompressTextureData( |
+ state_, *format_info, width, height, depth, image_size, data); |
+ if (!decompressed_data) { |
+ MarkContextLost(error::kGuilty); |
+ group_->LoseContexts(error::kInnocent); |
+ return error::kLostContext; |
+ } |
+ state_.PushTextureDecompressionUnpackState(); |
+ glTexImage3D(target, level, format_info->decompressed_internal_format, |
+ width, height, depth, border, format_info->decompressed_format, |
+ format_info->decompressed_type, decompressed_data.get()); |
+ state_.RestoreUnpackState(); |
+ } else { |
+ glCompressedTexImage3D(target, level, internal_format, width, height, depth, |
+ border, image_size, data); |
+ } |
GLenum error = LOCAL_PEEK_GL_ERROR("glCompressedTexImage3D"); |
if (error == GL_NO_ERROR) { |
texture_manager()->SetLevelInfo(texture_ref, target, level, internal_format, |
@@ -12035,13 +12239,11 @@ void GLES2DecoderImpl::DoCompressedTexSubImage3D( |
"bad dimensions"); |
return; |
} |
- if (!ValidateCompressedTexFuncData("glCompressedTexSubImage3D", |
- width, height, depth, format, |
- image_size) || |
- !ValidateCompressedTexSubDimensions("glCompressedTexSubImage3D", |
- target, level, xoffset, yoffset, |
- zoffset, width, height, depth, |
- format, texture)) { |
+ if (!ValidateCompressedTexFuncData("glCompressedTexSubImage3D", width, height, |
+ depth, format, image_size, data) || |
+ !ValidateCompressedTexSubDimensions( |
+ "glCompressedTexSubImage3D", target, level, xoffset, yoffset, zoffset, |
+ width, height, depth, format, texture)) { |
return; |
} |
@@ -12049,9 +12251,26 @@ void GLES2DecoderImpl::DoCompressedTexSubImage3D( |
// because the validation above means you can only get here if the level |
// is already a matching compressed format and in that case |
// CompressedTexImage3D already cleared the texture. |
- glCompressedTexSubImage3D( |
- target, level, xoffset, yoffset, zoffset, width, height, depth, format, |
- image_size, data); |
+ |
+ const CompressedFormatInfo* format_info = |
+ GetCompressedFormatInfo(internal_format); |
+ if (format_info != nullptr && !format_info->support_check(*feature_info_)) { |
+ std::unique_ptr<uint8_t[]> decompressed_data = DecompressTextureData( |
+ state_, *format_info, width, height, depth, image_size, data); |
+ if (!decompressed_data) { |
+ MarkContextLost(error::kGuilty); |
+ group_->LoseContexts(error::kInnocent); |
+ return; |
+ } |
+ state_.PushTextureDecompressionUnpackState(); |
+ glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, |
+ depth, format_info->decompressed_format, |
+ format_info->decompressed_type, decompressed_data.get()); |
+ state_.RestoreUnpackState(); |
+ } else { |
+ glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, |
+ height, depth, format, image_size, data); |
+ } |
// This may be a slow command. Exit command processing to allow for |
// context preemption and GPU watchdog checks. |
@@ -12274,11 +12493,11 @@ void GLES2DecoderImpl::DoCompressedTexSubImage2D( |
return; |
} |
- if (!ValidateCompressedTexFuncData("glCompressedTexSubImage2D", |
- width, height, 1, format, image_size) || |
- !ValidateCompressedTexSubDimensions("glCompressedTexSubImage2D", |
- target, level, xoffset, yoffset, 0, |
- width, height, 1, format, texture)) { |
+ if (!ValidateCompressedTexFuncData("glCompressedTexSubImage2D", width, height, |
+ 1, format, image_size, data) || |
+ !ValidateCompressedTexSubDimensions("glCompressedTexSubImage2D", target, |
+ level, xoffset, yoffset, 0, width, |
+ height, 1, format, texture)) { |
return; |
} |
@@ -12300,8 +12519,25 @@ void GLES2DecoderImpl::DoCompressedTexSubImage2D( |
DCHECK(texture->IsLevelCleared(target, level)); |
} |
- glCompressedTexSubImage2D( |
- target, level, xoffset, yoffset, width, height, format, image_size, data); |
+ const CompressedFormatInfo* format_info = |
+ GetCompressedFormatInfo(internal_format); |
+ if (format_info != nullptr && !format_info->support_check(*feature_info_)) { |
+ std::unique_ptr<uint8_t[]> decompressed_data = DecompressTextureData( |
+ state_, *format_info, width, height, 1, image_size, data); |
+ if (!decompressed_data) { |
+ MarkContextLost(error::kGuilty); |
+ group_->LoseContexts(error::kInnocent); |
+ return; |
+ } |
+ state_.PushTextureDecompressionUnpackState(); |
+ glTexSubImage2D(target, level, xoffset, yoffset, width, height, |
+ format_info->decompressed_format, |
+ format_info->decompressed_type, decompressed_data.get()); |
+ state_.RestoreUnpackState(); |
+ } else { |
+ glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, |
+ format, image_size, data); |
+ } |
// This may be a slow command. Exit command processing to allow for |
// context preemption and GPU watchdog checks. |
@@ -15098,13 +15334,22 @@ void GLES2DecoderImpl::TexStorageImpl(GLenum target, |
} |
} |
+ GLenum compatibility_internal_format = internal_format; |
+ const CompressedFormatInfo* format_info = |
+ GetCompressedFormatInfo(internal_format); |
+ if (format_info != nullptr && !format_info->support_check(*feature_info_)) { |
+ compatibility_internal_format = format_info->decompressed_internal_format; |
+ } |
+ |
// TODO(zmo): We might need to emulate TexStorage using TexImage or |
// CompressedTexImage on Mac OSX where we expose ES3 APIs when the underlying |
// driver is lower than 4.2 and ARB_texture_storage extension doesn't exist. |
if (dimension == ContextState::k2D) { |
- glTexStorage2DEXT(target, levels, internal_format, width, height); |
+ glTexStorage2DEXT(target, levels, compatibility_internal_format, width, |
+ height); |
} else { |
- glTexStorage3D(target, levels, internal_format, width, height, depth); |
+ glTexStorage3D(target, levels, compatibility_internal_format, width, height, |
+ depth); |
} |
{ |