blob: af6398cc69cf5f82a776bbcaaed4d79df69b12f9 [file] [log] [blame]
// 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/gles2_cmd_decoder_unittest.h"
#include <stddef.h>
#include <stdint.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/context_state.h"
#include "gpu/command_buffer/service/gl_stream_texture_image_stub.h"
#include "gpu/command_buffer/service/gl_surface_mock.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/image_manager.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/mocks.h"
#include "gpu/command_buffer/service/program_manager.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "gpu/command_buffer/service/validating_abstract_texture_impl.h"
#include "gpu/config/gpu_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_image_stub.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_mock.h"
#include "ui/gl/gl_surface_stub.h"
#include "ui/gl/gpu_timing_fake.h"
#include "ui/gl/scoped_make_current.h"
#if !defined(GL_DEPTH24_STENCIL8)
#define GL_DEPTH24_STENCIL8 0x88F0
#endif
using ::gl::MockGLInterface;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::MatcherCast;
using ::testing::Mock;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArrayArgument;
using ::testing::SetArgPointee;
using ::testing::StrEq;
using ::testing::StrictMock;
namespace gpu {
namespace gles2 {
using namespace cmds;
void GLES2DecoderRGBBackbufferTest::SetUp() {
InitState init;
init.bind_generates_resource = true;
InitDecoder(init);
SetupDefaultProgram();
}
void GLES2DecoderManualInitTest::EnableDisableTest(GLenum cap,
bool enable,
bool expect_set) {
if (expect_set) {
SetupExpectationsForEnableDisable(cap, enable);
}
if (enable) {
Enable cmd;
cmd.Init(cap);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
} else {
Disable cmd;
cmd.Init(cap);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
}
TEST_P(GLES3DecoderTest, Basic) {
// Make sure the setup is correct for ES3.
EXPECT_TRUE(feature_info()->IsWebGL2OrES3Context());
EXPECT_FALSE(feature_info()->IsWebGLContext());
EXPECT_TRUE(feature_info()->validators()->texture_bind_target.IsValid(
GL_TEXTURE_3D));
}
TEST_P(WebGL2DecoderTest, Basic) {
// Make sure the setup is correct for WebGL2.
EXPECT_TRUE(feature_info()->IsWebGL2OrES3Context());
EXPECT_TRUE(feature_info()->IsWebGLContext());
EXPECT_TRUE(feature_info()->validators()->texture_bind_target.IsValid(
GL_TEXTURE_3D));
}
TEST_P(GLES2DecoderTest, InvalidCommand) {
cmd::Noop cmd;
cmd.header.Init(gles2::kNumCommands, 1);
EXPECT_EQ(error::kUnknownCommand, ExecuteImmediateCmd(cmd, 0));
}
TEST_P(GLES2DecoderTest, GetIntegervCached) {
struct TestInfo {
GLenum pname;
GLint expected;
};
TestInfo tests[] = {
{
GL_MAX_TEXTURE_SIZE, TestHelper::kMaxTextureSize,
},
{
GL_MAX_CUBE_MAP_TEXTURE_SIZE, TestHelper::kMaxCubeMapTextureSize,
},
{
GL_MAX_RENDERBUFFER_SIZE, TestHelper::kMaxRenderbufferSize,
},
};
typedef GetIntegerv::Result Result;
for (size_t ii = 0; ii < sizeof(tests) / sizeof(tests[0]); ++ii) {
const TestInfo& test = tests[ii];
Result* result = static_cast<Result*>(shared_memory_address_);
EXPECT_CALL(*gl_, GetError())
.WillOnce(Return(GL_NO_ERROR))
.WillOnce(Return(GL_NO_ERROR))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, GetIntegerv(test.pname, _)).Times(0);
result->size = 0;
GetIntegerv cmd2;
cmd2.Init(test.pname, shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(test.pname),
result->GetNumResults());
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(test.expected, result->GetData()[0]);
}
}
TEST_P(GLES2DecoderWithShaderTest, GetMaxValueInBufferCHROMIUM) {
SetupIndexBuffer();
GetMaxValueInBufferCHROMIUM::Result* result =
static_cast<GetMaxValueInBufferCHROMIUM::Result*>(shared_memory_address_);
*result = 0;
GetMaxValueInBufferCHROMIUM cmd;
cmd.Init(client_element_buffer_id_, kValidIndexRangeCount, GL_UNSIGNED_SHORT,
kValidIndexRangeStart * 2, shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(7u, *result);
EXPECT_EQ(GL_NO_ERROR, GetGLError());
cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(100u, *result);
EXPECT_EQ(GL_NO_ERROR, GetGLError());
cmd.Init(kInvalidClientId, kValidIndexRangeCount, GL_UNSIGNED_SHORT,
kValidIndexRangeStart * 2, shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
cmd.Init(client_element_buffer_id_, kOutOfRangeIndexRangeEnd,
GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
GL_UNSIGNED_SHORT, kOutOfRangeIndexRangeEnd * 2, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
cmd.Init(client_buffer_id_, kValidIndexRangeCount + 1, GL_UNSIGNED_SHORT,
kValidIndexRangeStart * 2, shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
cmd.Init(client_element_buffer_id_,
kValidIndexRangeCount + 1,
GL_UNSIGNED_SHORT,
kValidIndexRangeStart * 2,
kInvalidSharedMemoryId,
kSharedMemoryOffset);
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, shared_memory_id_,
kInvalidSharedMemoryOffset);
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
}
TEST_P(GLES2DecoderTest, IsBuffer) {
EXPECT_FALSE(DoIsBuffer(client_buffer_id_));
DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId);
EXPECT_TRUE(DoIsBuffer(client_buffer_id_));
EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, 0))
.Times(1)
.RetiresOnSaturation();
DoDeleteBuffer(client_buffer_id_, kServiceBufferId);
EXPECT_FALSE(DoIsBuffer(client_buffer_id_));
}
TEST_P(GLES2DecoderTest, IsFramebuffer) {
EXPECT_FALSE(DoIsFramebuffer(client_framebuffer_id_));
DoBindFramebuffer(
GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
EXPECT_TRUE(DoIsFramebuffer(client_framebuffer_id_));
DoDeleteFramebuffer(client_framebuffer_id_,
kServiceFramebufferId,
true,
GL_FRAMEBUFFER,
0,
true,
GL_FRAMEBUFFER,
0);
EXPECT_FALSE(DoIsFramebuffer(client_framebuffer_id_));
}
TEST_P(GLES2DecoderTest, IsProgram) {
// IsProgram is true as soon as the program is created.
EXPECT_TRUE(DoIsProgram(client_program_id_));
EXPECT_CALL(*gl_, DeleteProgram(kServiceProgramId))
.Times(1)
.RetiresOnSaturation();
DoDeleteProgram(client_program_id_, kServiceProgramId);
EXPECT_FALSE(DoIsProgram(client_program_id_));
}
TEST_P(GLES2DecoderTest, IsRenderbuffer) {
EXPECT_FALSE(DoIsRenderbuffer(client_renderbuffer_id_));
DoBindRenderbuffer(
GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
EXPECT_TRUE(DoIsRenderbuffer(client_renderbuffer_id_));
DoDeleteRenderbuffer(client_renderbuffer_id_, kServiceRenderbufferId);
EXPECT_FALSE(DoIsRenderbuffer(client_renderbuffer_id_));
}
TEST_P(GLES2DecoderTest, IsShader) {
// IsShader is true as soon as the program is created.
EXPECT_TRUE(DoIsShader(client_shader_id_));
DoDeleteShader(client_shader_id_, kServiceShaderId);
EXPECT_FALSE(DoIsShader(client_shader_id_));
}
TEST_P(GLES2DecoderTest, IsTexture) {
EXPECT_FALSE(DoIsTexture(client_texture_id_));
DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
EXPECT_TRUE(DoIsTexture(client_texture_id_));
DoDeleteTexture(client_texture_id_, kServiceTextureId);
EXPECT_FALSE(DoIsTexture(client_texture_id_));
}
TEST_P(GLES2DecoderTest, TestImageBindingForDecoderManagement) {
const GLuint service_id = 123;
EXPECT_CALL(*gl_, GenTextures(1, _))
.Times(1)
.WillOnce(SetArgPointee<1>(service_id))
.RetiresOnSaturation();
const GLenum target = GL_TEXTURE_EXTERNAL_OES;
std::unique_ptr<AbstractTexture> abstract_texture =
GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */
256, /* height */
1, /* depth */
0, /* border */
GL_RGBA, GL_UNSIGNED_BYTE);
scoped_refptr<gl::GLImage> image(new gl::GLImageStub);
abstract_texture->BindImage(image.get(), GetParam());
ValidatingAbstractTextureImpl* validating_texture =
static_cast<ValidatingAbstractTextureImpl*>(abstract_texture.get());
TextureRef* texture_ref = validating_texture->GetTextureRefForTesting();
Texture::ImageState state;
EXPECT_EQ(texture_ref->texture()->GetLevelImage(target, 0, &state),
image.get());
EXPECT_EQ(state, GetParam() ? Texture::ImageState::BOUND
: Texture::ImageState::UNBOUND);
EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation();
abstract_texture.reset();
}
TEST_P(GLES2DecoderTest, CreateAbstractTexture) {
const GLuint service_id = 123;
EXPECT_CALL(*gl_, GenTextures(1, _))
.Times(1)
.WillOnce(SetArgPointee<1>(service_id))
.RetiresOnSaturation();
const GLenum target = GL_TEXTURE_EXTERNAL_OES;
std::unique_ptr<AbstractTexture> abstract_texture =
GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */
256, /* height */
1, /* depth */
0, /* border */
GL_RGBA, GL_UNSIGNED_BYTE);
EXPECT_EQ(abstract_texture->GetTextureBase()->target(), target);
EXPECT_EQ(abstract_texture->service_id(), service_id);
Texture* texture = Texture::CheckedCast(abstract_texture->GetTextureBase());
EXPECT_EQ(texture->SafeToRenderFrom(), false);
// Set some parameters, and verify that we set them.
// These three are for ScopedTextureBinder.
// TODO(liberato): Is there a way to make this less brittle?
EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation();
EXPECT_CALL(*gl_, BindTexture(target, _)).Times(1).RetiresOnSaturation();
EXPECT_CALL(*gl_, BindTexture(target, abstract_texture->service_id()))
.Times(1)
.RetiresOnSaturation();
// This one we actually care about.
EXPECT_CALL(*gl_, TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
abstract_texture->SetParameteri(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
EXPECT_EQ(texture->min_filter(), static_cast<GLenum>(GL_LINEAR));
// Attach an image and see if it works.
scoped_refptr<gl::GLImage> image(new gl::GLImageStub);
abstract_texture->BindImage(image.get(), true);
EXPECT_EQ(abstract_texture->GetImage(), image.get());
// Binding an image should make the texture renderable.
EXPECT_EQ(texture->SafeToRenderFrom(), true);
EXPECT_EQ(texture->GetLevelImage(target, 0), image.get());
// Unbinding should make it not renderable.
abstract_texture->BindImage(nullptr, false);
EXPECT_EQ(texture->SafeToRenderFrom(), false);
EXPECT_EQ(abstract_texture->GetImage(), nullptr);
// Attach a stream image, and verify that the image changes and the service_id
// matches the one we provide.
scoped_refptr<gpu::gles2::GLStreamTextureImage> stream_image(
new gpu::gles2::GLStreamTextureImageStub);
const GLuint surface_texture_service_id = service_id + 1;
abstract_texture->BindStreamTextureImage(stream_image.get(),
surface_texture_service_id);
EXPECT_EQ(texture->SafeToRenderFrom(), true);
EXPECT_EQ(texture->GetLevelStreamTextureImage(target, 0), stream_image.get());
EXPECT_EQ(abstract_texture->service_id(), surface_texture_service_id);
// Deleting |abstract_texture| should delete the platform texture as well,
// since we haven't make a copy of the TextureRef. Also make sure that the
// cleanup CB is called.
EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation();
bool cleanup_flag = false;
abstract_texture->SetCleanupCallback(base::BindOnce(
[](bool* flag, AbstractTexture*) { *flag = true; }, &cleanup_flag));
abstract_texture.reset();
EXPECT_TRUE(cleanup_flag);
}
TEST_P(GLES2DecoderTest, AbstractTextureIsDestroyedWithDecoder) {
// Deleting the decoder should delete the AbstractTexture's TextureRef.
const GLuint service_id = 123;
EXPECT_CALL(*gl_, GenTextures(1, _))
.Times(1)
.WillOnce(SetArgPointee<1>(service_id))
.RetiresOnSaturation();
const GLenum target = GL_TEXTURE_EXTERNAL_OES;
std::unique_ptr<AbstractTexture> abstract_texture =
GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */
256, /* height */
1, /* depth */
0, /* border */
GL_RGBA, GL_UNSIGNED_BYTE);
bool cleanup_flag = false;
abstract_texture->SetCleanupCallback(base::BindOnce(
[](bool* flag, AbstractTexture*) { *flag = true; }, &cleanup_flag));
// There is only one TextureRef, so it should delete the platform texture. It
// should also call the cleanup cb.
EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation();
ResetDecoder();
// The texture should no longer have a TextureRef.
EXPECT_EQ(abstract_texture->GetTextureBase(), nullptr);
EXPECT_TRUE(cleanup_flag);
}
TEST_P(GLES2DecoderTest, AbstractTextureIsDestroyedWhenMadeCurrent) {
// When an AbstractTexture is destroyed, the ref will be dropped by the next
// call to MakeCurrent if the context isn't already current.
const GLuint service_id = 123;
EXPECT_CALL(*gl_, GenTextures(1, _))
.Times(1)
.WillOnce(SetArgPointee<1>(service_id))
.RetiresOnSaturation();
const GLenum target = GL_TEXTURE_EXTERNAL_OES;
std::unique_ptr<AbstractTexture> abstract_texture =
GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */
256, /* height */
1, /* depth */
0, /* border */
GL_RGBA, GL_UNSIGNED_BYTE);
// Make the context not current, so that it's not destroyed immediately.
context_->ReleaseCurrent(surface_.get());
abstract_texture.reset();
// Make the context current again, |context_| overrides it with a mock.
context_->GLContextStub::MakeCurrent(surface_.get());
// Having textures to delete should signal idle work.
EXPECT_EQ(GetDecoder()->HasMoreIdleWork(), true);
EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation();
// Allow the context to be made current.
EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(true));
GetDecoder()->MakeCurrent();
}
TEST_P(GLES2DecoderTest, AbstractTextureIsDestroyedIfAlreadyCurrent) {
// When an AbstractTexture is destroyed, the ref will be dropped immediately
// if the context is current.
const GLuint service_id = 123;
EXPECT_CALL(*gl_, GenTextures(1, _))
.Times(1)
.WillOnce(SetArgPointee<1>(service_id))
.RetiresOnSaturation();
const GLenum target = GL_TEXTURE_EXTERNAL_OES;
std::unique_ptr<AbstractTexture> abstract_texture =
GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */
256, /* height */
1, /* depth */
0, /* border */
GL_RGBA, GL_UNSIGNED_BYTE);
EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation();
abstract_texture.reset();
EXPECT_EQ(GetDecoder()->HasMoreIdleWork(), false);
}
TEST_P(GLES2DecoderTest, TestAbstractTextureSetClearedWorks) {
const GLuint service_id = 123;
EXPECT_CALL(*gl_, GenTextures(1, _))
.Times(1)
.WillOnce(SetArgPointee<1>(service_id))
.RetiresOnSaturation();
const GLenum target = GL_TEXTURE_2D;
std::unique_ptr<AbstractTexture> abstract_texture =
GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */
256, /* height */
1, /* depth */
0, /* border */
GL_RGBA, GL_UNSIGNED_BYTE);
Texture* texture = Texture::CheckedCast(abstract_texture->GetTextureBase());
// Texture should start off unrenderable.
EXPECT_EQ(texture->SafeToRenderFrom(), false);
// Setting it to be cleared should make it renderable.
abstract_texture->SetCleared();
EXPECT_EQ(texture->SafeToRenderFrom(), true);
EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation();
abstract_texture.reset();
}
TEST_P(GLES3DecoderTest, GetInternalformativValidArgsSamples) {
const GLint kNumSampleCounts = 8;
typedef GetInternalformativ::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
EXPECT_CALL(*gl_, GetInternalformativ(GL_RENDERBUFFER, GL_RGBA8,
GL_NUM_SAMPLE_COUNTS, 1, _))
.WillOnce(SetArgPointee<4>(kNumSampleCounts))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, GetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES,
kNumSampleCounts, _))
.Times(1)
.RetiresOnSaturation();
result->size = 0;
GetInternalformativ cmd;
cmd.Init(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES,
shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(kNumSampleCounts, result->GetNumResults());
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, GetInternalformativValidArgsNumSampleCounts) {
const GLint kNumSampleCounts = 8;
typedef GetInternalformativ::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
EXPECT_CALL(*gl_, GetInternalformativ(GL_RENDERBUFFER, GL_RGBA8,
GL_NUM_SAMPLE_COUNTS, 1, _))
.WillOnce(SetArgPointee<4>(kNumSampleCounts))
.RetiresOnSaturation();
result->size = 0;
GetInternalformativ cmd;
cmd.Init(GL_RENDERBUFFER, GL_RGBA8, GL_NUM_SAMPLE_COUNTS,
shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(1, result->GetNumResults());
EXPECT_EQ(kNumSampleCounts, result->GetData()[0]);
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, ClientWaitSyncValid) {
typedef ClientWaitSync::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
ClientWaitSync cmd;
cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, 0,
shared_memory_id_, shared_memory_offset_);
EXPECT_CALL(*gl_,
ClientWaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
GL_SYNC_FLUSH_COMMANDS_BIT, 0))
.WillOnce(Return(GL_CONDITION_SATISFIED))
.RetiresOnSaturation();
*result = GL_WAIT_FAILED;
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), *result);
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, ClientWaitSyncNonZeroTimeoutValid) {
typedef ClientWaitSync::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
ClientWaitSync cmd;
const GLuint64 kTimeout = 0xABCDEF0123456789;
cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout,
shared_memory_id_, shared_memory_offset_);
EXPECT_CALL(*gl_,
ClientWaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout))
.WillOnce(Return(GL_CONDITION_SATISFIED))
.RetiresOnSaturation();
*result = GL_WAIT_FAILED;
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), *result);
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, ClientWaitSyncInvalidSyncFails) {
typedef ClientWaitSync::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
ClientWaitSync cmd;
cmd.Init(kInvalidClientId, GL_SYNC_FLUSH_COMMANDS_BIT, 0,
shared_memory_id_, shared_memory_offset_);
*result = GL_WAIT_FAILED;
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(static_cast<GLenum>(GL_WAIT_FAILED), *result);
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
}
TEST_P(GLES3DecoderTest, ClientWaitSyncResultNotInitFails) {
typedef ClientWaitSync::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
ClientWaitSync cmd;
cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, 0,
shared_memory_id_, shared_memory_offset_);
*result = 1; // Any value other than GL_WAIT_FAILED
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
}
TEST_P(GLES3DecoderTest, ClientWaitSyncBadSharedMemoryFails) {
typedef ClientWaitSync::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
ClientWaitSync cmd;
*result = GL_WAIT_FAILED;
cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, 0,
kInvalidSharedMemoryId, shared_memory_offset_);
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
*result = GL_WAIT_FAILED;
cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, 0,
shared_memory_id_, kInvalidSharedMemoryOffset);
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
}
TEST_P(GLES3DecoderTest, WaitSyncValidArgs) {
const GLuint64 kTimeout = GL_TIMEOUT_IGNORED;
EXPECT_CALL(*gl_, WaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
0, kTimeout))
.Times(1)
.RetiresOnSaturation();
WaitSync cmd;
cmd.Init(client_sync_id_, 0, kTimeout);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderManualInitTest, BindGeneratesResourceFalse) {
InitState init;
InitDecoder(init);
BindTexture cmd1;
cmd1.Init(GL_TEXTURE_2D, kInvalidClientId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
BindBuffer cmd2;
cmd2.Init(GL_ARRAY_BUFFER, kInvalidClientId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
BindFramebuffer cmd3;
cmd3.Init(GL_FRAMEBUFFER, kInvalidClientId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd3));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
BindRenderbuffer cmd4;
cmd4.Init(GL_RENDERBUFFER, kInvalidClientId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd4));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}
TEST_P(GLES2DecoderTest, EnableFeatureCHROMIUMBadBucket) {
const uint32_t kBadBucketId = 123;
EnableFeatureCHROMIUM cmd;
cmd.Init(kBadBucketId, shared_memory_id_, shared_memory_offset_);
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
}
TEST_P(GLES2DecoderTest, RequestExtensionCHROMIUMBadBucket) {
const uint32_t kBadBucketId = 123;
RequestExtensionCHROMIUM cmd;
cmd.Init(kBadBucketId);
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
}
TEST_P(GLES2DecoderTest, BeginQueryEXTDisabled) {
// Test something fails if off.
}
TEST_P(GLES2DecoderTest, GenQueriesEXTImmediateValidArgs) {
GenQueriesEXTImmediate* cmd =
GetImmediateAs<GenQueriesEXTImmediate>();
GLuint temp = kNewClientId;
cmd->Init(1, &temp);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(*cmd, sizeof(temp)));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
EXPECT_TRUE(query_manager->IsValidQuery(kNewClientId));
}
TEST_P(GLES2DecoderTest, GenQueriesEXTImmediateDuplicateOrNullIds) {
GenQueriesEXTImmediate* cmd =
GetImmediateAs<GenQueriesEXTImmediate>();
GLuint temp[3] = {kNewClientId, kNewClientId + 1, kNewClientId};
cmd->Init(3, temp);
EXPECT_EQ(error::kInvalidArguments, ExecuteImmediateCmd(*cmd, sizeof(temp)));
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
EXPECT_FALSE(query_manager->IsValidQuery(kNewClientId));
EXPECT_FALSE(query_manager->IsValidQuery(kNewClientId + 1));
GLuint null_id[2] = {kNewClientId, 0};
cmd->Init(2, null_id);
EXPECT_EQ(error::kInvalidArguments,
ExecuteImmediateCmd(*cmd, sizeof(temp)));
EXPECT_FALSE(query_manager->IsValidQuery(kNewClientId));
}
TEST_P(GLES2DecoderTest, GenQueriesEXTImmediateInvalidArgs) {
GenQueriesEXTImmediate* cmd =
GetImmediateAs<GenQueriesEXTImmediate>();
cmd->Init(1, &client_query_id_);
EXPECT_EQ(error::kInvalidArguments,
ExecuteImmediateCmd(*cmd, sizeof(&client_query_id_)));
}
TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXT) {
InitState init;
init.extensions = "GL_EXT_occlusion_query_boolean";
init.gl_version = "OpenGL ES 2.0";
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
InitDecoder(init);
// Test end fails if no begin.
EndQueryEXT end_cmd;
end_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
BeginQueryEXT begin_cmd;
// Test id = 0 fails.
begin_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, 0, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
GenHelper<GenQueriesEXTImmediate>(kNewClientId);
// Test valid parameters work.
EXPECT_CALL(*gl_, GenQueries(1, _))
.WillOnce(SetArgPointee<1>(kNewServiceId))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, BeginQuery(GL_ANY_SAMPLES_PASSED_EXT, kNewServiceId))
.Times(1)
.RetiresOnSaturation();
// Query object should not be created untill BeginQueriesEXT.
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
EXPECT_TRUE(query == nullptr);
// BeginQueryEXT should fail if id is not generated from GenQueriesEXT.
begin_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, kInvalidClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
begin_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, kNewClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// After BeginQueriesEXT id name should have query object associated with it.
query = query_manager->GetQuery(kNewClientId);
ASSERT_TRUE(query != nullptr);
EXPECT_FALSE(query->IsPending());
// Test trying begin again fails
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
// Test end fails with different target
end_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
// Test end succeeds
EXPECT_CALL(*gl_, EndQuery(GL_ANY_SAMPLES_PASSED_EXT))
.Times(1)
.RetiresOnSaturation();
end_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_TRUE(query->IsPending());
// Begin should fail if using a different target
begin_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, kNewClientId,
shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
// Begin should fail if using a different sync
begin_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, kNewClientId,
shared_memory_id_, kSharedMemoryOffset + sizeof(QuerySync));
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
// QueryCounter should fail if using a different target
QueryCounterEXT query_counter_cmd;
query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, shared_memory_id_,
kSharedMemoryOffset, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
// QueryCounter should fail if using a different sync
query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, shared_memory_id_,
kSharedMemoryOffset + sizeof(QuerySync), 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
EXPECT_CALL(*gl_, DeleteQueries(1, _)).Times(1).RetiresOnSaturation();
}
struct QueryType {
GLenum type;
bool is_counter;
};
const QueryType kQueryTypes[] = {
{GL_COMMANDS_ISSUED_CHROMIUM, false},
{GL_LATENCY_QUERY_CHROMIUM, false},
{GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, false},
{GL_GET_ERROR_QUERY_CHROMIUM, false},
{GL_COMMANDS_COMPLETED_CHROMIUM, false},
{GL_ANY_SAMPLES_PASSED_EXT, false},
{GL_TIME_ELAPSED, false},
{GL_TIMESTAMP, true},
};
const GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef);
static void ExecuteGenerateQueryCmd(GLES2DecoderTestBase* test,
::gl::MockGLInterface* gl,
GLenum target,
GLuint client_id,
GLuint service_id) {
test->GenHelper<GenQueriesEXTImmediate>(client_id);
if (GL_ANY_SAMPLES_PASSED_EXT == target) {
EXPECT_CALL(*gl, GenQueries(1, _))
.WillOnce(SetArgPointee<1>(service_id))
.RetiresOnSaturation();
}
}
static error::Error ExecuteBeginQueryCmd(GLES2DecoderTestBase* test,
::gl::MockGLInterface* gl,
::gl::GPUTimingFake* timing_queries,
GLenum target,
GLuint client_id,
GLuint service_id,
int32_t shm_id,
uint32_t shm_offset) {
if (GL_ANY_SAMPLES_PASSED_EXT == target) {
EXPECT_CALL(*gl, BeginQuery(target, service_id))
.Times(1)
.RetiresOnSaturation();
} else if (GL_TIME_ELAPSED == target) {
timing_queries->ExpectGPUTimerQuery(*gl, true);
}
BeginQueryEXT begin_cmd;
begin_cmd.Init(target, client_id, shm_id, shm_offset);
return test->ExecuteCmd(begin_cmd);
}
static error::Error ExecuteEndQueryCmd(GLES2DecoderTestBase* test,
::gl::MockGLInterface* gl,
GLenum target,
uint32_t submit_count) {
if (GL_ANY_SAMPLES_PASSED_EXT == target) {
EXPECT_CALL(*gl, EndQuery(target))
.Times(1)
.RetiresOnSaturation();
} else if (GL_GET_ERROR_QUERY_CHROMIUM == target) {
EXPECT_CALL(*gl, GetError())
.WillOnce(Return(GL_NO_ERROR))
.RetiresOnSaturation();
} else if (GL_COMMANDS_COMPLETED_CHROMIUM == target) {
EXPECT_CALL(*gl, Flush()).RetiresOnSaturation();
EXPECT_CALL(*gl, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
.WillOnce(Return(kGlSync))
.RetiresOnSaturation();
#if DCHECK_IS_ON()
EXPECT_CALL(*gl, IsSync(kGlSync))
.WillRepeatedly(Return(GL_TRUE));
#endif
}
EndQueryEXT end_cmd;
end_cmd.Init(target, submit_count);
return test->ExecuteCmd(end_cmd);
}
static error::Error ExecuteQueryCounterCmd(GLES2DecoderTestBase* test,
::gl::MockGLInterface* gl,
::gl::GPUTimingFake* timing_queries,
GLenum target,
GLuint client_id,
GLuint service_id,
int32_t shm_id,
uint32_t shm_offset,
uint32_t submit_count) {
if (GL_TIMESTAMP == target) {
timing_queries->ExpectGPUTimeStampQuery(*gl, false);
}
QueryCounterEXT query_counter_cmd;
query_counter_cmd.Init(client_id,
target,
shm_id,
shm_offset,
submit_count);
return test->ExecuteCmd(query_counter_cmd);
}
static void ProcessQuery(GLES2DecoderTestBase* test,
::gl::MockGLInterface* gl,
GLenum target,
GLuint service_id) {
if (GL_ANY_SAMPLES_PASSED_EXT == target) {
EXPECT_CALL(
*gl, GetQueryObjectuiv(service_id, GL_QUERY_RESULT_AVAILABLE_EXT, _))
.WillOnce(SetArgPointee<2>(1))
.RetiresOnSaturation();
EXPECT_CALL(*gl, GetQueryObjectuiv(service_id, GL_QUERY_RESULT_EXT, _))
.WillOnce(SetArgPointee<2>(1))
.RetiresOnSaturation();
} else if (GL_COMMANDS_COMPLETED_CHROMIUM == target) {
EXPECT_CALL(*gl, ClientWaitSync(kGlSync, _, _))
.WillOnce(Return(GL_ALREADY_SIGNALED))
.RetiresOnSaturation();
EXPECT_CALL(*gl, DeleteSync(kGlSync)).Times(1).RetiresOnSaturation();
}
QueryManager* query_manager = test->GetDecoder()->GetQueryManager();
ASSERT_TRUE(nullptr != query_manager);
query_manager->ProcessPendingQueries(false);
}
static void CheckBeginEndQueryBadMemoryFails(GLES2DecoderTestBase* test,
GLuint client_id,
const QueryType& query_type,
int32_t shm_id,
uint32_t shm_offset) {
// We need to reset the decoder on each iteration, because we lose the
// context every time.
GLES2DecoderTestBase::InitState init;
init.extensions = "GL_EXT_occlusion_query_boolean"
" GL_ARB_sync"
" GL_ARB_timer_query";
init.gl_version = "OpenGL ES 3.0";
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
test->InitDecoder(init);
test->GenHelper<GenQueriesEXTImmediate>(client_id);
// Test bad shared memory fails
error::Error error = error::kNoError;
if (query_type.is_counter) {
QueryCounterEXT query_counter_cmd;
query_counter_cmd.Init(client_id, query_type.type, shm_id, shm_offset, 1);
error = test->ExecuteCmd(query_counter_cmd);
} else {
BeginQueryEXT begin_cmd;
begin_cmd.Init(query_type.type, client_id, shm_id, shm_offset);
error = test->ExecuteCmd(begin_cmd);
}
EXPECT_TRUE(error != error::kNoError);
test->ResetDecoder();
}
TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTBadMemoryIdFails) {
for (size_t i = 0; i < base::size(kQueryTypes); ++i) {
CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kQueryTypes[i],
kInvalidSharedMemoryId,
kSharedMemoryOffset);
}
}
TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTBadMemoryOffsetFails) {
for (size_t i = 0; i < base::size(kQueryTypes); ++i) {
// Out-of-bounds.
CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kQueryTypes[i],
shared_memory_id_,
kInvalidSharedMemoryOffset);
// Overflow.
CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kQueryTypes[i],
shared_memory_id_, 0xfffffffcu);
}
}
TEST_P(GLES2DecoderManualInitTest, QueryReuseTest) {
for (size_t i = 0; i < base::size(kQueryTypes); ++i) {
const QueryType& query_type = kQueryTypes[i];
GLES2DecoderTestBase::InitState init;
init.extensions = "GL_EXT_occlusion_query_boolean"
" GL_ARB_sync"
" GL_ARB_timer_query";
init.gl_version = "OpenGL ES 3.0";
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
InitDecoder(init);
::testing::StrictMock<::gl::MockGLInterface>* gl = GetGLMock();
::gl::GPUTimingFake gpu_timing_queries;
ExecuteGenerateQueryCmd(this, gl, query_type.type,
kNewClientId, kNewServiceId);
// Query once.
if (query_type.is_counter) {
EXPECT_EQ(
error::kNoError,
ExecuteQueryCounterCmd(this, gl, &gpu_timing_queries, query_type.type,
kNewClientId, kNewServiceId, shared_memory_id_,
kSharedMemoryOffset, 1));
} else {
EXPECT_EQ(
error::kNoError,
ExecuteBeginQueryCmd(this, gl, &gpu_timing_queries, query_type.type,
kNewClientId, kNewServiceId, shared_memory_id_,
kSharedMemoryOffset));
EXPECT_EQ(error::kNoError, ExecuteEndQueryCmd(this, gl,
query_type.type, 1));
}
ProcessQuery(this, gl, query_type.type, kNewServiceId);
// Reuse query.
if (query_type.is_counter) {
EXPECT_EQ(
error::kNoError,
ExecuteQueryCounterCmd(this, gl, &gpu_timing_queries, query_type.type,
kNewClientId, kNewServiceId, shared_memory_id_,
kSharedMemoryOffset, 2));
} else {
EXPECT_EQ(
error::kNoError,
ExecuteBeginQueryCmd(this, gl, &gpu_timing_queries, query_type.type,
kNewClientId, kNewServiceId, shared_memory_id_,
kSharedMemoryOffset));
EXPECT_EQ(error::kNoError, ExecuteEndQueryCmd(this, gl,
query_type.type, 2));
}
ProcessQuery(this, gl, query_type.type, kNewServiceId);
if (GL_ANY_SAMPLES_PASSED_EXT == query_type.type)
EXPECT_CALL(*gl, DeleteQueries(1, _)).Times(1).RetiresOnSaturation();
ResetDecoder();
}
}
TEST_P(GLES2DecoderTest, BeginEndQueryEXTCommandsIssuedCHROMIUM) {
BeginQueryEXT begin_cmd;
GenHelper<GenQueriesEXTImmediate>(kNewClientId);
// Test valid parameters work.
begin_cmd.Init(GL_COMMANDS_ISSUED_CHROMIUM, kNewClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
ASSERT_TRUE(query != nullptr);
EXPECT_FALSE(query->IsPending());
// Test end succeeds
EndQueryEXT end_cmd;
end_cmd.Init(GL_COMMANDS_ISSUED_CHROMIUM, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_FALSE(query->IsPending());
}
TEST_P(GLES2DecoderTest, BeginEndQueryEXTGetErrorQueryCHROMIUM) {
BeginQueryEXT begin_cmd;
GenHelper<GenQueriesEXTImmediate>(kNewClientId);
// Test valid parameters work.
begin_cmd.Init(GL_GET_ERROR_QUERY_CHROMIUM, kNewClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
ASSERT_TRUE(query != nullptr);
EXPECT_FALSE(query->IsPending());
// Test end succeeds
QuerySync* sync = static_cast<QuerySync*>(shared_memory_address_);
EXPECT_CALL(*gl_, GetError())
.WillOnce(Return(GL_INVALID_VALUE))
.RetiresOnSaturation();
EndQueryEXT end_cmd;
end_cmd.Init(GL_GET_ERROR_QUERY_CHROMIUM, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_FALSE(query->IsPending());
EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE),
static_cast<GLenum>(sync->result));
}
TEST_P(GLES2DecoderTest, SetDisjointValueSync) {
SetDisjointValueSyncCHROMIUM cmd;
cmd.Init(static_cast<uint32_t>(-1), 0u);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd));
cmd.Init(kInvalidSharedMemoryId, 0u);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
cmd.Init(shared_memory_id_, kSharedBufferSize);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
cmd.Init(shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
cmd.Init(shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd));
}
TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTCommandsCompletedCHROMIUM) {
InitState init;
init.extensions = "GL_EXT_occlusion_query_boolean GL_ARB_sync";
init.gl_version = "OpenGL ES 2.0";
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
InitDecoder(init);
GenHelper<GenQueriesEXTImmediate>(kNewClientId);
BeginQueryEXT begin_cmd;
begin_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, kNewClientId,
shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
ASSERT_TRUE(query != nullptr);
EXPECT_FALSE(query->IsPending());
EXPECT_CALL(*gl_, Flush()).RetiresOnSaturation();
EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
.WillOnce(Return(kGlSync))
.RetiresOnSaturation();
#if DCHECK_IS_ON()
EXPECT_CALL(*gl_, IsSync(kGlSync))
.WillOnce(Return(GL_TRUE))
.RetiresOnSaturation();
#endif
EndQueryEXT end_cmd;
end_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_TRUE(query->IsPending());
#if DCHECK_IS_ON()
EXPECT_CALL(*gl_, IsSync(kGlSync))
.WillOnce(Return(GL_TRUE))
.RetiresOnSaturation();
#endif
EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _))
.WillOnce(Return(GL_TIMEOUT_EXPIRED))
.RetiresOnSaturation();
query_manager->ProcessPendingQueries(false);
EXPECT_TRUE(query->IsPending());
#if DCHECK_IS_ON()
EXPECT_CALL(*gl_, IsSync(kGlSync))
.WillOnce(Return(GL_TRUE))
.RetiresOnSaturation();
#endif
EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _))
.WillOnce(Return(GL_ALREADY_SIGNALED))
.RetiresOnSaturation();
query_manager->ProcessPendingQueries(false);
EXPECT_FALSE(query->IsPending());
#if DCHECK_IS_ON()
EXPECT_CALL(*gl_, IsSync(kGlSync))
.WillOnce(Return(GL_TRUE))
.RetiresOnSaturation();
#endif
EXPECT_CALL(*gl_, DeleteSync(kGlSync)).Times(1).RetiresOnSaturation();
ResetDecoder();
}
TEST_P(GLES2DecoderManualInitTest, BeginInvalidTargetQueryFails) {
InitState init;
init.extensions = "";
init.gl_version = "OpenGL ES 2.0";
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
InitDecoder(init);
GenHelper<GenQueriesEXTImmediate>(kNewClientId);
BeginQueryEXT begin_cmd;
begin_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, kNewClientId,
shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
begin_cmd.Init(GL_ANY_SAMPLES_PASSED, kNewClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
begin_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, kNewClientId,
shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
begin_cmd.Init(GL_TIME_ELAPSED, kNewClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
begin_cmd.Init(0xdeadbeef, kNewClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}
TEST_P(GLES2DecoderManualInitTest, QueryCounterEXTTimeStamp) {
InitState init;
init.extensions = "GL_ARB_timer_query";
init.gl_version = "OpenGL ES 3.0";
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
InitDecoder(init);
GenHelper<GenQueriesEXTImmediate>(kNewClientId);
EXPECT_CALL(*gl_, GenQueries(1, _))
.WillOnce(SetArgPointee<1>(kNewServiceId))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, GetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, _))
.WillOnce(SetArgPointee<2>(64))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, QueryCounter(kNewServiceId, GL_TIMESTAMP))
.Times(1)
.RetiresOnSaturation();
QueryCounterEXT query_counter_cmd;
query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, shared_memory_id_,
kSharedMemoryOffset, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
ASSERT_TRUE(query != nullptr);
EXPECT_TRUE(query->IsPending());
}
TEST_P(GLES2DecoderManualInitTest, InvalidTargetQueryCounterFails) {
InitState init;
init.extensions = "";
init.gl_version = "OpenGL ES 2.0";
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
InitDecoder(init);
GenHelper<GenQueriesEXTImmediate>(kNewClientId);
QueryCounterEXT query_counter_cmd;
query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, shared_memory_id_,
kSharedMemoryOffset, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
query_counter_cmd.Init(kNewClientId, 0xdeadbeef, shared_memory_id_,
kSharedMemoryOffset, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd));
EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}
TEST_P(GLES2DecoderTest, IsEnabledReturnsCachedValue) {
// NOTE: There are no expectations because no GL functions should be
// called for DEPTH_TEST or STENCIL_TEST
static const GLenum kStates[] = {
GL_DEPTH_TEST, GL_STENCIL_TEST,
};
for (size_t ii = 0; ii < base::size(kStates); ++ii) {
Enable enable_cmd;
GLenum state = kStates[ii];
enable_cmd.Init(state);
EXPECT_EQ(error::kNoError, ExecuteCmd(enable_cmd));
IsEnabled::Result* result =
static_cast<IsEnabled::Result*>(shared_memory_address_);
IsEnabled is_enabled_cmd;
is_enabled_cmd.Init(state, shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(is_enabled_cmd));
EXPECT_NE(0u, *result);
Disable disable_cmd;
disable_cmd.Init(state);
EXPECT_EQ(error::kNoError, ExecuteCmd(disable_cmd));
EXPECT_EQ(error::kNoError, ExecuteCmd(is_enabled_cmd));
EXPECT_EQ(0u, *result);
}
}
namespace {
class SizeOnlyMemoryTracker : public MemoryTracker {
public:
SizeOnlyMemoryTracker() {
// Account for the 7 default textures. 1 for TEXTURE_2D and 6 faces for
// TEXTURE_CUBE_MAP. Each is 1x1, with 4 bytes per channel.
pool_info_.initial_size = 28;
pool_info_.size = 0;
}
~SizeOnlyMemoryTracker() override = default;
void TrackMemoryAllocatedChange(uint64_t delta) override {
pool_info_.size += delta;
}
uint64_t GetSize() const override {
return pool_info_.size - pool_info_.initial_size;
}
uint64_t ClientTracingId() const override { return 0; }
int ClientId() const override { return 0; }
uint64_t ContextGroupTracingId() const override { return 0; }
private:
struct PoolInfo {
PoolInfo() : initial_size(0), size(0) {}
uint64_t initial_size;
uint64_t size;
};
PoolInfo pool_info_;
};
} // anonymous namespace.
TEST_P(GLES2DecoderManualInitTest, MemoryTrackerInitialSize) {
auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>();
auto* memory_tracker_ptr = memory_tracker.get();
set_memory_tracker(std::move(memory_tracker));
InitState init;
init.bind_generates_resource = true;
InitDecoder(init);
// Expect that initial size - size is 0.
EXPECT_EQ(0u, memory_tracker_ptr->GetSize());
EXPECT_EQ(0u, memory_tracker_ptr->GetSize());
}
TEST_P(GLES2DecoderManualInitTest, MemoryTrackerTexImage2D) {
auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>();
auto* memory_tracker_ptr = memory_tracker.get();
set_memory_tracker(std::move(memory_tracker));
InitState init;
init.bind_generates_resource = true;
InitDecoder(init);
DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(128u, memory_tracker_ptr->GetSize());
DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(64u, memory_tracker_ptr->GetSize());
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderManualInitTest, MemoryTrackerTexStorage2DEXT) {
auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>();
auto* memory_tracker_ptr = memory_tracker.get();
set_memory_tracker(std::move(memory_tracker));
InitState init;
init.extensions = "GL_EXT_texture_storage";
init.bind_generates_resource = true;
InitDecoder(init);
DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
EXPECT_CALL(*gl_, TexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, 8, 4))
.Times(1)
.RetiresOnSaturation();
TexStorage2DEXT cmd;
cmd.Init(GL_TEXTURE_2D, 1, GL_RGBA8, 8, 4);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(128u, memory_tracker_ptr->GetSize());
}
TEST_P(GLES2DecoderManualInitTest, MemoryTrackerCopyTexImage2D) {
GLenum target = GL_TEXTURE_2D;
GLint level = 0;
GLenum internal_format = GL_RGBA;
GLsizei width = 4;
GLsizei height = 8;
GLint border = 0;
auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>();
auto* memory_tracker_ptr = memory_tracker.get();
set_memory_tracker(std::move(memory_tracker));
InitState init;
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
InitDecoder(init);
DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
EXPECT_CALL(*gl_, GetError())
.WillOnce(Return(GL_NO_ERROR))
.WillOnce(Return(GL_NO_ERROR))
.RetiresOnSaturation();
EXPECT_CALL(*gl_,
CopyTexImage2D(
target, level, internal_format, 0, 0, width, height, border))
.Times(1)
.RetiresOnSaturation();
CopyTexImage2D cmd;
cmd.Init(target, level, internal_format, 0, 0, width, height);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(128u, memory_tracker_ptr->GetSize());
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderManualInitTest, MemoryTrackerRenderbufferStorage) {
auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>();
auto* memory_tracker_ptr = memory_tracker.get();
set_memory_tracker(std::move(memory_tracker));
InitState init;
init.bind_generates_resource = true;
InitDecoder(init);
DoBindRenderbuffer(
GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
EnsureRenderbufferBound(false);
EXPECT_CALL(*gl_, GetError())
.WillOnce(Return(GL_NO_ERROR))
.WillOnce(Return(GL_NO_ERROR))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, RenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, 8, 4))
.Times(1)
.RetiresOnSaturation();
RenderbufferStorage cmd;
cmd.Init(GL_RENDERBUFFER, GL_RGBA4, 8, 4);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(128u, memory_tracker_ptr->GetSize());
}
TEST_P(GLES2DecoderManualInitTest, MemoryTrackerBufferData) {
auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>();
auto* memory_tracker_ptr = memory_tracker.get();
set_memory_tracker(std::move(memory_tracker));
InitState init;
init.bind_generates_resource = true;
InitDecoder(init);
EXPECT_EQ(0u, memory_tracker_ptr->GetSize());
DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId);
EXPECT_CALL(*gl_, GetError())
.WillOnce(Return(GL_NO_ERROR))
.WillOnce(Return(GL_NO_ERROR))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, BufferData(GL_ARRAY_BUFFER, 128, _, GL_STREAM_DRAW))
.Times(1)
.RetiresOnSaturation();
BufferData cmd;
cmd.Init(GL_ARRAY_BUFFER, 128, 0, 0, GL_STREAM_DRAW);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(128u, memory_tracker_ptr->GetSize());
}
TEST_P(GLES2DecoderManualInitTest, ImmutableCopyTexImage2D) {
const GLenum kTarget = GL_TEXTURE_2D;
const GLint kLevel = 0;
const GLint kLevels = 2;
const GLenum kInternalFormat = GL_RGBA;
const GLenum kSizedInternalFormat = GL_RGBA8;
const GLsizei kWidth = 4;
const GLsizei kHeight = 8;
const GLint kBorder = 0;
InitState init;
init.extensions = "GL_EXT_texture_storage";
init.has_alpha = true;
init.request_alpha = true;
init.bind_generates_resource = true;
init.gl_version = "OpenGL ES 2.0"; // To avoid TexStorage emulation.
InitDecoder(init);
DoBindTexture(kTarget, client_texture_id_, kServiceTextureId);
// CopyTexImage2D will call arbitrary amount of GetErrors.
EXPECT_CALL(*gl_, GetError())
.Times(AtLeast(1));
EXPECT_CALL(*gl_,
CopyTexImage2D(
kTarget, kLevel, kInternalFormat, 0, 0, kWidth, kHeight,
kBorder))
.Times(1);
EXPECT_CALL(*gl_,
TexStorage2DEXT(
kTarget, kLevels, kSizedInternalFormat, kWidth, kHeight))
.Times(1);
CopyTexImage2D copy_cmd;
copy_cmd.Init(kTarget, kLevel, kInternalFormat, 0, 0, kWidth, kHeight);
EXPECT_EQ(error::kNoError, ExecuteCmd(copy_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
TexStorage2DEXT storage_cmd;
storage_cmd.Init(kTarget, kLevels, kSizedInternalFormat, kWidth, kHeight);
EXPECT_EQ(error::kNoError, ExecuteCmd(storage_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// This should not invoke CopyTexImage2D.
copy_cmd.Init(kTarget, kLevel, kInternalFormat, 0, 0, kWidth, kHeight);
EXPECT_EQ(error::kNoError, ExecuteCmd(copy_cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}
TEST_P(GLES2DecoderTest, LoseContextCHROMIUMGuilty) {
EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kInnocent))
.Times(1);
LoseContextCHROMIUM cmd;
cmd.Init(GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_TRUE(decoder_->WasContextLost());
EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
}
TEST_P(GLES2DecoderTest, LoseContextCHROMIUMUnkown) {
EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
.Times(1);
LoseContextCHROMIUM cmd;
cmd.Init(GL_UNKNOWN_CONTEXT_RESET_ARB, GL_UNKNOWN_CONTEXT_RESET_ARB);
EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_TRUE(decoder_->WasContextLost());
EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
}
TEST_P(GLES2DecoderTest, LoseContextCHROMIUMInvalidArgs0_0) {
EXPECT_CALL(*mock_decoder_, MarkContextLost(_))
.Times(0);
LoseContextCHROMIUM cmd;
cmd.Init(GL_NONE, GL_GUILTY_CONTEXT_RESET_ARB);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}
TEST_P(GLES2DecoderTest, LoseContextCHROMIUMInvalidArgs1_0) {
EXPECT_CALL(*mock_decoder_, MarkContextLost(_))
.Times(0);
LoseContextCHROMIUM cmd;
cmd.Init(GL_GUILTY_CONTEXT_RESET_ARB, GL_NONE);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}
class GLES2DecoderDoCommandsTest : public GLES2DecoderTest {
public:
GLES2DecoderDoCommandsTest() {
for (int i = 0; i < 3; i++) {
cmds_[i].Init(GL_BLEND);
}
entries_per_cmd_ = ComputeNumEntries(cmds_[0].ComputeSize());
}
void SetExpectationsForNCommands(int num_commands) {
for (int i = 0; i < num_commands; i++)
SetupExpectationsForEnableDisable(GL_BLEND, true);
}
protected:
Enable cmds_[3];
int entries_per_cmd_;
};
TEST_P(GLES3DecoderTest, BeginInvalidTargetQueryFails) {
BeginQueryEXT begin_cmd;
begin_cmd.Init(0xdeadbeef, kNewClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
}
TEST_P(GLES3DecoderTest, BindTransformFeedbackValidArgs) {
EXPECT_CALL(*gl_, BindTransformFeedback(GL_TRANSFORM_FEEDBACK,
kServiceTransformFeedbackId));
SpecializedSetup<BindTransformFeedback, 0>(true);
BindTransformFeedback cmd;
cmd.Init(GL_TRANSFORM_FEEDBACK, client_transformfeedback_id_);
EXPECT_CALL(*gl_, BindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0))
.Times(1)
.RetiresOnSaturation();
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, DeleteTransformFeedbacksImmediateInvalidArgs) {
DeleteTransformFeedbacksImmediate& cmd =
*GetImmediateAs<DeleteTransformFeedbacksImmediate>();
SpecializedSetup<DeleteTransformFeedbacksImmediate, 0>(false);
GLuint temp = kInvalidClientId;
cmd.Init(1, &temp);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, GetIntegeri_vValidArgs) {
EXPECT_CALL(*gl_, GetIntegeri_v(_, _, _)).Times(0);
typedef GetIntegeri_v::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
result->size = 0;
GetIntegeri_v cmd;
cmd.Init(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, 2, shared_memory_id_,
shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(
GL_TRANSFORM_FEEDBACK_BUFFER_BINDING),
result->GetNumResults());
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, GetInteger64i_vValidArgs) {
EXPECT_CALL(*gl_, GetInteger64i_v(_, _, _)).Times(0);
typedef GetInteger64i_v::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
result->size = 0;
GetInteger64i_v cmd;
cmd.Init(GL_UNIFORM_BUFFER_SIZE, 2, shared_memory_id_,
shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(
GL_UNIFORM_BUFFER_SIZE),
result->GetNumResults());
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, GetSamplerBinding) {
const GLuint kClientID = 12;
const GLuint kServiceID = 1012;
const GLuint kUnit = 0;
DoCreateSampler(kClientID, kServiceID);
DoBindSampler(kUnit, kClientID, kServiceID);
EXPECT_CALL(*gl_, GetError())
.WillOnce(Return(GL_NO_ERROR))
.WillOnce(Return(GL_NO_ERROR))
.RetiresOnSaturation();
typedef cmds::GetIntegerv::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
cmds::GetIntegerv cmd;
cmd.Init(GL_SAMPLER_BINDING, shared_memory_id_, shared_memory_offset_);
result->size = 0;
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(1, result->GetNumResults());
EXPECT_EQ(kClientID, static_cast<GLuint>(result->GetData()[0]));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES3DecoderTest, GetTransformFeedbackBinding) {
const GLuint kClientID = 12;
const GLuint kServiceID = 1012;
const GLenum kTarget = GL_TRANSFORM_FEEDBACK;
DoCreateTransformFeedback(kClientID, kServiceID);
EXPECT_CALL(*gl_, BindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0))
.Times(1)
.RetiresOnSaturation();
DoBindTransformFeedback(kTarget, kClientID, kServiceID);
EXPECT_CALL(*gl_, GetError())
.WillOnce(Return(GL_NO_ERROR))
.WillOnce(Return(GL_NO_ERROR))
.RetiresOnSaturation();
typedef cmds::GetIntegerv::Result Result;
Result* result = static_cast<Result*>(shared_memory_address_);
cmds::GetIntegerv cmd;
cmd.Init(
GL_TRANSFORM_FEEDBACK_BINDING, shared_memory_id_, shared_memory_offset_);
result->size = 0;
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(1, result->GetNumResults());
EXPECT_EQ(kClientID, static_cast<GLuint>(result->GetData()[0]));
EXPECT_CALL(*gl_, BindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0))
.Times(1)
.RetiresOnSaturation();
DoBindTransformFeedback(kTarget, 0, kServiceDefaultTransformFeedbackId);
DoDeleteTransformFeedback(kClientID, kServiceID);
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
// Test that processing with 0 entries does nothing.
TEST_P(GLES2DecoderDoCommandsTest, DoCommandsOneOfZero) {
int num_processed = -1;
SetExpectationsForNCommands(0);
EXPECT_EQ(
error::kNoError,
decoder_->DoCommands(1, &cmds_, entries_per_cmd_ * 0, &num_processed));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(0, num_processed);
}
// Test processing at granularity of single commands.
TEST_P(GLES2DecoderDoCommandsTest, DoCommandsOneOfOne) {
int num_processed = -1;
SetExpectationsForNCommands(1);
EXPECT_EQ(
error::kNoError,
decoder_->DoCommands(1, &cmds_, entries_per_cmd_ * 1, &num_processed));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(entries_per_cmd_, num_processed);
}
// Test processing at granularity of multiple commands.
TEST_P(GLES2DecoderDoCommandsTest, DoCommandsThreeOfThree) {
int num_processed = -1;
SetExpectationsForNCommands(3);
EXPECT_EQ(
error::kNoError,
decoder_->DoCommands(3, &cmds_, entries_per_cmd_ * 3, &num_processed));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(entries_per_cmd_ * 3, num_processed);
}
// Test processing a request smaller than available entries.
TEST_P(GLES2DecoderDoCommandsTest, DoCommandsTwoOfThree) {
int num_processed = -1;
SetExpectationsForNCommands(2);
EXPECT_EQ(
error::kNoError,
decoder_->DoCommands(2, &cmds_, entries_per_cmd_ * 3, &num_processed));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(entries_per_cmd_ * 2, num_processed);
}
// Test that processing stops on a command with size 0.
TEST_P(GLES2DecoderDoCommandsTest, DoCommandsZeroCmdSize) {
cmds_[1].header.size = 0;
int num_processed = -1;
SetExpectationsForNCommands(1);
EXPECT_EQ(
error::kInvalidSize,
decoder_->DoCommands(2, &cmds_, entries_per_cmd_ * 2, &num_processed));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(entries_per_cmd_, num_processed);
}
// Test that processing stops on a command with size greater than available.
TEST_P(GLES2DecoderDoCommandsTest, DoCommandsOutOfBounds) {
int num_processed = -1;
SetExpectationsForNCommands(1);
EXPECT_EQ(error::kOutOfBounds,
decoder_->DoCommands(
2, &cmds_, entries_per_cmd_ * 2 - 1, &num_processed));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(entries_per_cmd_, num_processed);
}
// Test that commands with bad argument size are skipped without processing.
TEST_P(GLES2DecoderDoCommandsTest, DoCommandsBadArgSize) {
cmds_[1].header.size += 1;
int num_processed = -1;
SetExpectationsForNCommands(1);
EXPECT_EQ(error::kInvalidArguments,
decoder_->DoCommands(
2, &cmds_, entries_per_cmd_ * 2 + 1, &num_processed));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// gpu::CommandHeader::size is a 21-bit field, so casting it to int is safe.
// Without the explicit cast, Visual Studio ends up promoting the left hand
// side to unsigned, and emits a sign mismatch warning.
EXPECT_EQ(entries_per_cmd_ + static_cast<int>(cmds_[1].header.size),
num_processed);
}
class GLES2DecoderDescheduleUntilFinishedTest : public GLES2DecoderTest {
public:
GLES2DecoderDescheduleUntilFinishedTest() = default;
void SetUp() override {
InitState init;
init.gl_version = "4.4";
init.extensions += " GL_ARB_compatibility GL_ARB_sync";
InitDecoder(init);
EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
.Times(2)
.WillOnce(Return(sync_service_id_))
.WillOnce(Return(sync_service_id2_))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, IsSync(sync_service_id_)).WillRepeatedly(Return(GL_TRUE));
EXPECT_CALL(*gl_, Flush()).Times(2).RetiresOnSaturation();
EXPECT_CALL(*gl_, DeleteSync(sync_service_id_))
.Times(1)
.RetiresOnSaturation();
EXPECT_CALL(*gl_, DeleteSync(sync_service_id2_))
.Times(1)
.RetiresOnSaturation();
}
void OnDescheduleUntilFinished() override {
deschedule_until_finished_callback_count_++;
}
void OnRescheduleAfterFinished() override {
reschedule_after_finished_callback_count_++;
}
protected:
int deschedule_until_finished_callback_count_ = 0;
int reschedule_after_finished_callback_count_ = 0;
GLsync sync_service_id_ = reinterpret_cast<GLsync>(0x15);
GLsync sync_service_id2_ = reinterpret_cast<GLsync>(0x15);
};
TEST_P(GLES2DecoderDescheduleUntilFinishedTest, AlreadySignalled) {
EXPECT_CALL(*gl_, ClientWaitSync(sync_service_id_, 0, 0))
.Times(1)
.WillOnce(Return(GL_ALREADY_SIGNALED))
.RetiresOnSaturation();
DescheduleUntilFinishedCHROMIUM cmd;
cmd.Init();
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(0, deschedule_until_finished_callback_count_);
EXPECT_EQ(0, reschedule_after_finished_callback_count_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(0, deschedule_until_finished_callback_count_);
EXPECT_EQ(0, reschedule_after_finished_callback_count_);
}
TEST_P(GLES2DecoderDescheduleUntilFinishedTest, NotYetSignalled) {
EXPECT_CALL(*gl_, ClientWaitSync(sync_service_id_, 0, 0))
.Times(1)
.WillOnce(Return(GL_TIMEOUT_EXPIRED))
.RetiresOnSaturation();
DescheduleUntilFinishedCHROMIUM cmd;
cmd.Init();
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(0, deschedule_until_finished_callback_count_);
EXPECT_EQ(0, reschedule_after_finished_callback_count_);
EXPECT_EQ(error::kDeferLaterCommands, ExecuteCmd(cmd));
EXPECT_EQ(1, deschedule_until_finished_callback_count_);
EXPECT_EQ(0, reschedule_after_finished_callback_count_);
}
void GLES3DecoderTest::SetUp() {
InitState init;
init.gl_version = "OpenGL ES 3.0";
init.bind_generates_resource = true;
init.context_type = CONTEXT_TYPE_OPENGLES3;
InitDecoder(init);
}
void WebGL2DecoderTest::SetUp() {
InitState init;
init.gl_version = "OpenGL ES 3.0";
init.bind_generates_resource = true;
init.context_type = CONTEXT_TYPE_WEBGL2;
InitDecoder(init);
}
void GLES3DecoderWithShaderTest::SetUp() {
InitState init;
init.gl_version = "OpenGL ES 3.0";
init.bind_generates_resource = true;
init.context_type = CONTEXT_TYPE_OPENGLES3;
InitDecoder(init);
SetupDefaultProgram();
}
void GLES3DecoderRGBBackbufferTest::SetUp() {
InitState init;
init.gl_version = "OpenGL ES 3.0";
init.bind_generates_resource = true;
init.context_type = CONTEXT_TYPE_OPENGLES3;
InitDecoder(init);
SetupDefaultProgram();
}
INSTANTIATE_TEST_SUITE_P(Service, GLES2DecoderTest, ::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
GLES2DecoderWithShaderTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
GLES2DecoderManualInitTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
GLES2DecoderRGBBackbufferTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
GLES2DecoderDoCommandsTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
GLES2DecoderDescheduleUntilFinishedTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service, GLES3DecoderTest, ::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service, WebGL2DecoderTest, ::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
GLES3DecoderWithShaderTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
GLES3DecoderManualInitTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
GLES3DecoderRGBBackbufferTest,
::testing::Bool());
} // namespace gles2
} // namespace gpu