// Copyright 2015 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/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h" using ::gfx::MockGLInterface; using ::testing::_; using ::testing::Return; using ::testing::SetArgPointee; namespace gpu { namespace gles2 { using namespace cmds; namespace { } // namespace anonymous TEST_P(GLES2DecoderTest, MapBufferRangeUnmapBufferReadSucceeds) { const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_READ_BIT; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; // uint32_t is Result for both MapBufferRange and UnmapBuffer commands. uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(uint32_t); DoBindBuffer(kTarget, client_buffer_id_, kServiceBufferId); std::vector<int8_t> data(kSize); for (GLsizeiptr ii = 0; ii < kSize; ++ii) { data[ii] = static_cast<int8_t>(ii % 255); } { // MapBufferRange EXPECT_CALL(*gl_, MapBufferRange(kTarget, kOffset, kSize, kAccess)) .WillOnce(Return(&data[0])) .RetiresOnSaturation(); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); decoder_->set_unsafe_es3_apis_enabled(false); *result = 0; EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); EXPECT_EQ(0u, *result); decoder_->set_unsafe_es3_apis_enabled(true); *result = 0; EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); int8_t* mem = reinterpret_cast<int8_t*>(&result[1]); EXPECT_EQ(0, memcmp(&data[0], mem, kSize)); EXPECT_EQ(1u, *result); } { // UnmapBuffer EXPECT_CALL(*gl_, UnmapBuffer(kTarget)) .WillOnce(Return(GL_TRUE)) .RetiresOnSaturation(); UnmapBuffer cmd; cmd.Init(kTarget); decoder_->set_unsafe_es3_apis_enabled(false); EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderTest, MapBufferRangeUnmapBufferWriteSucceeds) { const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_WRITE_BIT; const GLbitfield kMappedAccess = GL_MAP_WRITE_BIT | GL_MAP_READ_BIT; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; // uint32_t is Result for both MapBufferRange and UnmapBuffer commands. uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(uint32_t); DoBindBuffer(kTarget, client_buffer_id_, kServiceBufferId); std::vector<int8_t> data(kSize); for (GLsizeiptr ii = 0; ii < kSize; ++ii) { data[ii] = static_cast<int8_t>(ii % 255); } { // MapBufferRange succeeds EXPECT_CALL(*gl_, MapBufferRange(kTarget, kOffset, kSize, kMappedAccess)) .WillOnce(Return(&data[0])) .RetiresOnSaturation(); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); decoder_->set_unsafe_es3_apis_enabled(false); *result = 0; EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); EXPECT_EQ(0u, *result); decoder_->set_unsafe_es3_apis_enabled(true); *result = 0; EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); int8_t* mem = reinterpret_cast<int8_t*>(&result[1]); EXPECT_EQ(0, memcmp(&data[0], mem, kSize)); EXPECT_EQ(1u, *result); } { // UnmapBuffer succeeds EXPECT_CALL(*gl_, UnmapBuffer(kTarget)) .WillOnce(Return(GL_TRUE)) .RetiresOnSaturation(); UnmapBuffer cmd; cmd.Init(kTarget); decoder_->set_unsafe_es3_apis_enabled(false); EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } EXPECT_EQ(GL_NO_ERROR, GetGLError()); } TEST_P(GLES2DecoderTest, MapBufferRangeNotInitFails) { const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_READ_BIT; std::vector<int8_t> data(kSize); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); *result = 1; // Any value other than 0. uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(*result); MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, MapBufferRangeWriteInvalidateRangeSucceeds) { const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; // With MAP_INVALIDATE_RANGE_BIT, no need to append MAP_READ_BIT. const GLbitfield kAccess = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT; DoBindBuffer(kTarget, client_buffer_id_, kServiceBufferId); std::vector<int8_t> data(kSize); for (GLsizeiptr ii = 0; ii < kSize; ++ii) { data[ii] = static_cast<int8_t>(ii % 255); } EXPECT_CALL(*gl_, MapBufferRange(kTarget, kOffset, kSize, kAccess)) .WillOnce(Return(&data[0])) .RetiresOnSaturation(); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); *result = 0; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(*result); int8_t* mem = reinterpret_cast<int8_t*>(&result[1]); memset(mem, 72, kSize); // Init to a random value other than 0. MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, MapBufferRangeWriteInvalidateBufferSucceeds) { // Test INVALIDATE_BUFFER_BIT is mapped to INVALIDATE_RANGE_BIT. const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT; // With MAP_INVALIDATE_BUFFER_BIT, no need to append MAP_READ_BIT. const GLbitfield kFilteredAccess = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT; DoBindBuffer(kTarget, client_buffer_id_, kServiceBufferId); std::vector<int8_t> data(kSize); for (GLsizeiptr ii = 0; ii < kSize; ++ii) { data[ii] = static_cast<int8_t>(ii % 255); } EXPECT_CALL(*gl_, MapBufferRange(kTarget, kOffset, kSize, kFilteredAccess)) .WillOnce(Return(&data[0])) .RetiresOnSaturation(); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); *result = 0; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(*result); int8_t* mem = reinterpret_cast<int8_t*>(&result[1]); memset(mem, 72, kSize); // Init to a random value other than 0. MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, MapBufferRangeWriteUnsynchronizedBit) { // Test UNSYNCHRONIZED_BIT is filtered out. const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT; const GLbitfield kFilteredAccess = GL_MAP_WRITE_BIT | GL_MAP_READ_BIT; DoBindBuffer(kTarget, client_buffer_id_, kServiceBufferId); std::vector<int8_t> data(kSize); for (GLsizeiptr ii = 0; ii < kSize; ++ii) { data[ii] = static_cast<int8_t>(ii % 255); } EXPECT_CALL(*gl_, MapBufferRange(kTarget, kOffset, kSize, kFilteredAccess)) .WillOnce(Return(&data[0])) .RetiresOnSaturation(); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); *result = 0; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(*result); int8_t* mem = reinterpret_cast<int8_t*>(&result[1]); memset(mem, 72, kSize); // Init to a random value other than 0. MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(0, memcmp(&data[0], mem, kSize)); } TEST_P(GLES2DecoderTest, MapBufferRangeWithError) { const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_READ_BIT; std::vector<int8_t> data(kSize); for (GLsizeiptr ii = 0; ii < kSize; ++ii) { data[ii] = static_cast<int8_t>(ii % 255); } EXPECT_CALL(*gl_, MapBufferRange(kTarget, kOffset, kSize, kAccess)) .WillOnce(Return(nullptr)) // Return nullptr to indicate a GL error. .RetiresOnSaturation(); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); *result = 0; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(*result); int8_t* mem = reinterpret_cast<int8_t*>(&result[1]); memset(mem, 72, kSize); // Init to a random value other than 0. MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); memset(&data[0], 72, kSize); // Mem is untouched. EXPECT_EQ(0, memcmp(&data[0], mem, kSize)); EXPECT_EQ(0u, *result); } TEST_P(GLES2DecoderTest, MapBufferRangeBadSharedMemoryFails) { const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_READ_BIT; std::vector<int8_t> data(kSize); for (GLsizeiptr ii = 0; ii < kSize; ++ii) { data[ii] = static_cast<int8_t>(ii % 255); } typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); *result = 0; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(*result); decoder_->set_unsafe_es3_apis_enabled(true); MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, kInvalidSharedMemoryId, data_shm_offset, result_shm_id, result_shm_offset); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, kInvalidSharedMemoryId, result_shm_offset); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, kInvalidSharedMemoryOffset, result_shm_id, result_shm_offset); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, kInvalidSharedMemoryOffset); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, UnmapBufferWriteNotMappedFails) { const GLenum kTarget = GL_ARRAY_BUFFER; DoBindBuffer(kTarget, client_buffer_id_, kServiceBufferId); UnmapBuffer cmd; cmd.Init(kTarget); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, UnmapBufferWriteNoBoundBufferFails) { const GLenum kTarget = GL_ARRAY_BUFFER; UnmapBuffer cmd; cmd.Init(kTarget); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, BufferDataDestroysDataStore) { const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_WRITE_BIT; const GLbitfield kFilteredAccess = GL_MAP_WRITE_BIT | GL_MAP_READ_BIT; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; // uint32_t is Result for both MapBufferRange and UnmapBuffer commands. uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(uint32_t); DoBindBuffer(kTarget, client_buffer_id_, kServiceBufferId); std::vector<int8_t> data(kSize); decoder_->set_unsafe_es3_apis_enabled(true); { // MapBufferRange succeeds EXPECT_CALL(*gl_, MapBufferRange(kTarget, kOffset, kSize, kFilteredAccess)) .WillOnce(Return(&data[0])) .RetiresOnSaturation(); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); *result = 0; EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(1u, *result); } { // BufferData unmaps the data store. DoBufferData(kTarget, kSize * 2); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } { // UnmapBuffer fails. UnmapBuffer cmd; cmd.Init(kTarget); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } } TEST_P(GLES2DecoderTest, DeleteBuffersDestroysDataStore) { const GLenum kTarget = GL_ARRAY_BUFFER; const GLintptr kOffset = 10; const GLsizeiptr kSize = 64; const GLbitfield kAccess = GL_MAP_WRITE_BIT; const GLbitfield kFilteredAccess = GL_MAP_WRITE_BIT | GL_MAP_READ_BIT; uint32_t result_shm_id = kSharedMemoryId; uint32_t result_shm_offset = kSharedMemoryOffset; uint32_t data_shm_id = kSharedMemoryId; // uint32_t is Result for both MapBufferRange and UnmapBuffer commands. uint32_t data_shm_offset = kSharedMemoryOffset + sizeof(uint32_t); DoBindBuffer(kTarget, client_buffer_id_, kServiceBufferId); std::vector<int8_t> data(kSize); decoder_->set_unsafe_es3_apis_enabled(true); { // MapBufferRange succeeds EXPECT_CALL(*gl_, MapBufferRange(kTarget, kOffset, kSize, kFilteredAccess)) .WillOnce(Return(&data[0])) .RetiresOnSaturation(); typedef MapBufferRange::Result Result; Result* result = GetSharedMemoryAs<Result*>(); MapBufferRange cmd; cmd.Init(kTarget, kOffset, kSize, kAccess, data_shm_id, data_shm_offset, result_shm_id, result_shm_offset); *result = 0; EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(1u, *result); } { // DeleteBuffers unmaps the data store. DoDeleteBuffer(client_buffer_id_, kServiceBufferId); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } { // UnmapBuffer fails. UnmapBuffer cmd; cmd.Init(kTarget); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } } } // namespace gles2 } // namespace gpu