| // Copyright 2014 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/texture_uploader.h" |
| |
| #include "cc/base/util.h" |
| #include "cc/resources/prioritized_resource.h" |
| #include "gpu/command_buffer/client/gles2_interface_stub.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/khronos/GLES2/gl2.h" |
| #include "third_party/khronos/GLES2/gl2ext.h" |
| |
| namespace cc { |
| namespace { |
| |
| class TextureUploadTestContext : public gpu::gles2::GLES2InterfaceStub { |
| public: |
| TextureUploadTestContext() : result_available_(0), unpack_alignment_(4) {} |
| |
| void PixelStorei(GLenum pname, GLint param) override { |
| switch (pname) { |
| case GL_UNPACK_ALIGNMENT: |
| // Param should be a power of two <= 8. |
| EXPECT_EQ(0, param & (param - 1)); |
| EXPECT_GE(8, param); |
| switch (param) { |
| case 1: |
| case 2: |
| case 4: |
| case 8: |
| unpack_alignment_ = param; |
| break; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void GetQueryObjectuivEXT(GLuint, GLenum type, GLuint* value) override { |
| switch (type) { |
| case GL_QUERY_RESULT_AVAILABLE_EXT: |
| *value = result_available_; |
| break; |
| default: |
| *value = 0; |
| break; |
| } |
| } |
| |
| void TexSubImage2D(GLenum target, |
| GLint level, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLenum format, |
| GLenum type, |
| const void* pixels) override { |
| EXPECT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target); |
| EXPECT_EQ(0, level); |
| EXPECT_LE(0, width); |
| EXPECT_LE(0, height); |
| EXPECT_LE(0, xoffset); |
| EXPECT_LE(0, yoffset); |
| EXPECT_LE(0, width); |
| EXPECT_LE(0, height); |
| |
| // Check for allowed format/type combination. |
| unsigned int bytes_per_pixel = 0; |
| switch (format) { |
| case GL_ALPHA: |
| EXPECT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type); |
| bytes_per_pixel = 1; |
| break; |
| case GL_RGB: |
| EXPECT_NE(static_cast<unsigned>(GL_UNSIGNED_SHORT_4_4_4_4), type); |
| EXPECT_NE(static_cast<unsigned>(GL_UNSIGNED_SHORT_5_5_5_1), type); |
| switch (type) { |
| case GL_UNSIGNED_BYTE: |
| bytes_per_pixel = 3; |
| break; |
| case GL_UNSIGNED_SHORT_5_6_5: |
| bytes_per_pixel = 2; |
| break; |
| } |
| break; |
| case GL_RGBA: |
| EXPECT_NE(static_cast<unsigned>(GL_UNSIGNED_SHORT_5_6_5), type); |
| switch (type) { |
| case GL_UNSIGNED_BYTE: |
| bytes_per_pixel = 4; |
| break; |
| case GL_UNSIGNED_SHORT_4_4_4_4: |
| bytes_per_pixel = 2; |
| break; |
| case GL_UNSIGNED_SHORT_5_5_5_1: |
| bytes_per_pixel = 2; |
| break; |
| } |
| break; |
| case GL_LUMINANCE: |
| EXPECT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type); |
| bytes_per_pixel = 1; |
| break; |
| case GL_LUMINANCE_ALPHA: |
| EXPECT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type); |
| bytes_per_pixel = 2; |
| break; |
| case GL_RED_EXT: |
| EXPECT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type); |
| bytes_per_pixel = 1; |
| break; |
| case GL_RG_EXT: |
| EXPECT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type); |
| bytes_per_pixel = 2; |
| break; |
| } |
| |
| // If NULL, we aren't checking texture contents. |
| if (pixels == NULL) |
| return; |
| |
| const uint8* bytes = static_cast<const uint8*>(pixels); |
| // We'll expect the first byte of every row to be 0x1, and the last byte to |
| // be 0x2. |
| const unsigned int stride = |
| RoundUp(bytes_per_pixel * width, unpack_alignment_); |
| for (GLsizei row = 0; row < height; ++row) { |
| const uint8* row_bytes = |
| bytes + (xoffset * bytes_per_pixel + (yoffset + row) * stride); |
| EXPECT_EQ(0x1, row_bytes[0]); |
| EXPECT_EQ(0x2, row_bytes[width * bytes_per_pixel - 1]); |
| } |
| } |
| |
| void SetResultAvailable(unsigned result_available) { |
| result_available_ = result_available; |
| } |
| |
| private: |
| unsigned result_available_; |
| unsigned unpack_alignment_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TextureUploadTestContext); |
| }; |
| |
| void UploadTexture(TextureUploader* uploader, |
| ResourceFormat format, |
| const gfx::Size& size, |
| const uint8* data) { |
| uploader->Upload( |
| data, gfx::Rect(size), gfx::Rect(size), gfx::Vector2d(), format, size); |
| } |
| |
| TEST(TextureUploaderTest, NumBlockingUploads) { |
| TextureUploadTestContext context; |
| scoped_ptr<TextureUploader> uploader = TextureUploader::Create(&context); |
| |
| context.SetResultAvailable(0); |
| EXPECT_EQ(0u, uploader->NumBlockingUploads()); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| EXPECT_EQ(1u, uploader->NumBlockingUploads()); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| EXPECT_EQ(2u, uploader->NumBlockingUploads()); |
| |
| context.SetResultAvailable(1); |
| EXPECT_EQ(0u, uploader->NumBlockingUploads()); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| EXPECT_EQ(0u, uploader->NumBlockingUploads()); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| EXPECT_EQ(0u, uploader->NumBlockingUploads()); |
| } |
| |
| TEST(TextureUploaderTest, MarkPendingUploadsAsNonBlocking) { |
| TextureUploadTestContext context; |
| scoped_ptr<TextureUploader> uploader = TextureUploader::Create(&context); |
| |
| context.SetResultAvailable(0); |
| EXPECT_EQ(0u, uploader->NumBlockingUploads()); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| EXPECT_EQ(2u, uploader->NumBlockingUploads()); |
| |
| uploader->MarkPendingUploadsAsNonBlocking(); |
| EXPECT_EQ(0u, uploader->NumBlockingUploads()); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| EXPECT_EQ(1u, uploader->NumBlockingUploads()); |
| |
| context.SetResultAvailable(1); |
| EXPECT_EQ(0u, uploader->NumBlockingUploads()); |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); |
| uploader->MarkPendingUploadsAsNonBlocking(); |
| EXPECT_EQ(0u, uploader->NumBlockingUploads()); |
| } |
| |
| TEST(TextureUploaderTest, UploadContentsTest) { |
| TextureUploadTestContext context; |
| scoped_ptr<TextureUploader> uploader = TextureUploader::Create(&context); |
| |
| uint8 buffer[256 * 256 * 4]; |
| |
| // Upload a tightly packed 256x256 RGBA texture. |
| memset(buffer, 0, sizeof(buffer)); |
| for (int i = 0; i < 256; ++i) { |
| // Mark the beginning and end of each row, for the test. |
| buffer[i * 4 * 256] = 0x1; |
| buffer[(i + 1) * 4 * 256 - 1] = 0x2; |
| } |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(256, 256), buffer); |
| |
| // Upload a tightly packed 41x43 RGBA texture. |
| memset(buffer, 0, sizeof(buffer)); |
| for (int i = 0; i < 43; ++i) { |
| // Mark the beginning and end of each row, for the test. |
| buffer[i * 4 * 41] = 0x1; |
| buffer[(i + 1) * 4 * 41 - 1] = 0x2; |
| } |
| UploadTexture(uploader.get(), RGBA_8888, gfx::Size(41, 43), buffer); |
| |
| // Upload a tightly packed 41x86 ALPHA texture. |
| memset(buffer, 0, sizeof(buffer)); |
| for (int i = 0; i < 86; ++i) { |
| // Mark the beginning and end of each row, for the test. |
| buffer[i * 1 * 41] = 0x1; |
| buffer[(i + 1) * 41 - 1] = 0x2; |
| } |
| UploadTexture(uploader.get(), ALPHA_8, gfx::Size(41, 86), buffer); |
| |
| // Upload a tightly packed 82x86 LUMINANCE texture. |
| memset(buffer, 0, sizeof(buffer)); |
| for (int i = 0; i < 86; ++i) { |
| // Mark the beginning and end of each row, for the test. |
| buffer[i * 1 * 82] = 0x1; |
| buffer[(i + 1) * 82 - 1] = 0x2; |
| } |
| UploadTexture(uploader.get(), LUMINANCE_8, gfx::Size(82, 86), buffer); |
| |
| // Upload a tightly packed 82x86 RED texture. |
| memset(buffer, 0, sizeof(buffer)); |
| for (int i = 0; i < 86; ++i) { |
| // Mark the beginning and end of each row, for the test. |
| buffer[i * 1 * 82] = 0x1; |
| buffer[(i + 1) * 82 - 1] = 0x2; |
| } |
| UploadTexture(uploader.get(), RED_8, gfx::Size(82, 86), buffer); |
| } |
| |
| } // namespace |
| } // namespace cc |