blob: f700b991d6bfdef4f91d55bfbe90ef7160c76065 [file] [log] [blame]
// 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 "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include <stddef.h>
#include <stdint.h>
#include "base/command_line.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"
using ::gl::MockGLInterface;
using ::testing::_;
using ::testing::Return;
namespace gpu {
namespace gles2 {
// Class to use to test that functions which need feature flags or
// extensions always return INVALID_OPERATION if the feature flags is not
// enabled or extension is not present.
class GLES2DecoderTestDisabledExtensions : public GLES2DecoderTest {
public:
GLES2DecoderTestDisabledExtensions() = default;
};
INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestDisabledExtensions,
::testing::Bool());
TEST_P(GLES2DecoderTestDisabledExtensions, CHROMIUMPathRenderingDisabled) {
const GLuint kClientPathId = 0;
{
cmds::MatrixLoadfCHROMIUMImmediate& cmd =
*GetImmediateAs<cmds::MatrixLoadfCHROMIUMImmediate>();
GLfloat temp[16] = {
0,
};
cmd.Init(GL_PATH_MODELVIEW_CHROMIUM, temp);
EXPECT_EQ(error::kUnknownCommand, ExecuteImmediateCmd(cmd, sizeof(temp)));
}
{
cmds::MatrixLoadIdentityCHROMIUM cmd;
cmd.Init(GL_PATH_PROJECTION_CHROMIUM);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::GenPathsCHROMIUM cmd;
cmd.Init(0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::DeletePathsCHROMIUM cmd;
cmd.Init(0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::IsPathCHROMIUM cmd;
cmd.Init(kClientPathId, shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::PathCommandsCHROMIUM cmd;
cmd.Init(kClientPathId, 0, 0, 0, 0, GL_FLOAT, 0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::PathParameterfCHROMIUM cmd;
cmd.Init(kClientPathId, GL_PATH_STROKE_WIDTH_CHROMIUM, 1.0f);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::PathParameteriCHROMIUM cmd;
cmd.Init(kClientPathId, GL_PATH_STROKE_WIDTH_CHROMIUM, 1);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::PathStencilFuncCHROMIUM cmd;
cmd.Init(GL_NEVER, 2, 3);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::StencilFillPathCHROMIUM cmd;
cmd.Init(kClientPathId, GL_COUNT_UP_CHROMIUM, 1);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::StencilStrokePathCHROMIUM cmd;
cmd.Init(kClientPathId, 1, 2);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::CoverFillPathCHROMIUM cmd;
cmd.Init(kClientPathId, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::CoverStrokePathCHROMIUM cmd;
cmd.Init(kClientPathId, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::StencilThenCoverFillPathCHROMIUM cmd;
cmd.Init(kClientPathId, GL_COUNT_UP_CHROMIUM, 1, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::StencilThenCoverStrokePathCHROMIUM cmd;
cmd.Init(kClientPathId, 1, 2, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::StencilFillPathInstancedCHROMIUM cmd;
GLuint* paths = GetSharedMemoryAs<GLuint*>();
paths[0] = kClientPathId;
cmd.Init(1, GL_UNSIGNED_INT, shared_memory_id_, shared_memory_offset_, 0,
GL_COUNT_UP_CHROMIUM, 1, GL_NONE, 0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::StencilStrokePathInstancedCHROMIUM cmd;
GLuint* paths = GetSharedMemoryAs<GLuint*>();
paths[0] = kClientPathId;
cmd.Init(1, GL_UNSIGNED_INT, shared_memory_id_, shared_memory_offset_, 0,
0x80, 0x80, GL_NONE, 0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::CoverFillPathInstancedCHROMIUM cmd;
GLuint* paths = GetSharedMemoryAs<GLuint*>();
paths[0] = kClientPathId;
cmd.Init(1, GL_UNSIGNED_INT, shared_memory_id_, shared_memory_offset_, 0,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, GL_NONE, 0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::CoverStrokePathInstancedCHROMIUM cmd;
GLuint* paths = GetSharedMemoryAs<GLuint*>();
paths[0] = kClientPathId;
cmd.Init(1, GL_UNSIGNED_INT, shared_memory_id_, shared_memory_offset_, 0,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, GL_NONE, 0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::StencilThenCoverFillPathInstancedCHROMIUM cmd;
GLuint* paths = GetSharedMemoryAs<GLuint*>();
paths[0] = kClientPathId;
cmd.Init(1, GL_UNSIGNED_INT, shared_memory_id_, shared_memory_offset_, 0,
GL_COUNT_UP_CHROMIUM, 1,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, GL_NONE, 0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::StencilThenCoverStrokePathInstancedCHROMIUM cmd;
GLuint* paths = GetSharedMemoryAs<GLuint*>();
paths[0] = kClientPathId;
cmd.Init(1, GL_UNSIGNED_INT, shared_memory_id_, shared_memory_offset_, 0,
0x80, 0x80, GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, GL_NONE, 0,
0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::BindFragmentInputLocationCHROMIUMBucket cmd;
const uint32_t kBucketId = 123;
const GLint kLocation = 2;
const char* kName = "testing";
SetBucketAsCString(kBucketId, kName);
cmd.Init(client_program_id_, kLocation, kBucketId);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
{
cmds::ProgramPathFragmentInputGenCHROMIUM cmd;
const GLint kLocation = 2;
cmd.Init(client_program_id_, kLocation, 0, GL_NONE, 0, 0);
EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
}
}
class GLES2DecoderTestWithCHROMIUMPathRendering : public GLES2DecoderTest {
public:
GLES2DecoderTestWithCHROMIUMPathRendering() : client_path_id_(125) {}
void SetUp() override {
InitState init;
init.gl_version = "OpenGL ES 3.1";
init.has_alpha = true;
init.has_depth = true;
init.request_alpha = true;
init.request_depth = true;
init.bind_generates_resource = true;
init.extensions = "GL_NV_path_rendering GL_NV_framebuffer_mixed_samples";
InitDecoder(init);
EXPECT_CALL(*gl_, GenPathsNV(1))
.WillOnce(Return(kServicePathId))
.RetiresOnSaturation();
cmds::GenPathsCHROMIUM cmd;
cmd.Init(client_path_id_, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
// The tests use client_path_id_ to test all sorts of drawing. The NVPR API
// behaves differently with a path name that is "used" but not which does
// not "allocate path object state" and a path name that is a name of a real
// path object. The drawing with former causes GL error while latter works
// ok, even if there is nothing in the actual path object. To remain
// compatible with the API, we allocate path object state even when using
// the mock API.
EXPECT_CALL(
*gl_, PathCommandsNV(kServicePathId, 0, nullptr, 0, GL_FLOAT, nullptr))
.RetiresOnSaturation();
cmds::PathCommandsCHROMIUM pcmd;
pcmd.Init(client_path_id_, 0, 0, 0, 0, GL_FLOAT, 0, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(pcmd));
}
protected:
template <typename TypeParam>
void TestPathCommandsCHROMIUMCoordTypes();
struct InstancedTestcase {
GLsizei num_paths;
GLenum path_name_type;
const void* paths;
GLuint path_base;
GLenum fill_mode;
GLuint reference;
GLuint mask;
GLenum transform_type;
const GLfloat* transform_values;
size_t sizeof_paths; // Used for copying to shm buffer.
size_t sizeof_transform_values; // Used for copying to shm buffer.
error::Error expected_error;
GLint expected_gl_error;
bool expect_gl_call;
};
void CallAllInstancedCommands(const InstancedTestcase& testcase) {
// Note: for testcases that expect a call, We do not compare the 'paths'
// array during EXPECT_CALL due to it being void*. Instead, we rely on the
// fact that if the path base was not added correctly, the paths wouldn't
// exists and the call wouldn't come through.
bool copy_paths = false; // Paths are copied for each call that has paths,
// since the implementation modifies the memory
// area.
void* paths = nullptr;
uint32_t paths_shm_id = 0;
uint32_t paths_shm_offset = 0;
GLfloat* transforms = nullptr;
uint32_t transforms_shm_id = 0;
uint32_t transforms_shm_offset = 0;
if (testcase.transform_values) {
transforms = GetSharedMemoryAs<GLfloat*>();
transforms_shm_id = shared_memory_id_;
transforms_shm_offset = shared_memory_offset_;
memcpy(transforms, testcase.transform_values,
testcase.sizeof_transform_values);
} else {
DCHECK(testcase.sizeof_transform_values == 0);
}
if (testcase.paths) {
paths =
GetSharedMemoryAsWithOffset<void*>(testcase.sizeof_transform_values);
paths_shm_id = shared_memory_id_;
paths_shm_offset =
shared_memory_offset_ + testcase.sizeof_transform_values;
copy_paths = true;
} else {
DCHECK(testcase.sizeof_paths == 0);
}
if (testcase.expect_gl_call) {
EXPECT_CALL(*gl_, StencilFillPathInstancedNV(
testcase.num_paths, GL_UNSIGNED_INT, _, 0,
testcase.fill_mode, testcase.mask,
testcase.transform_type, transforms))
.RetiresOnSaturation();
}
if (copy_paths) {
memcpy(paths, testcase.paths, testcase.sizeof_paths);
}
{
cmds::StencilFillPathInstancedCHROMIUM sfi_cmd;
sfi_cmd.Init(testcase.num_paths, testcase.path_name_type, paths_shm_id,
paths_shm_offset, testcase.path_base, testcase.fill_mode,
testcase.mask, testcase.transform_type, transforms_shm_id,
transforms_shm_offset);
EXPECT_EQ(testcase.expected_error, ExecuteCmd(sfi_cmd));
EXPECT_EQ(testcase.expected_gl_error, GetGLError());
}
if (testcase.expect_gl_call) {
EXPECT_CALL(*gl_, StencilStrokePathInstancedNV(
testcase.num_paths, GL_UNSIGNED_INT, _, 0,
testcase.reference, testcase.mask,
testcase.transform_type, transforms))
.RetiresOnSaturation();
}
if (copy_paths) {
memcpy(paths, testcase.paths, testcase.sizeof_paths);
}
{
cmds::StencilStrokePathInstancedCHROMIUM ssi_cmd;
ssi_cmd.Init(testcase.num_paths, testcase.path_name_type, paths_shm_id,
paths_shm_offset, testcase.path_base, testcase.reference,
testcase.mask, testcase.transform_type, transforms_shm_id,
transforms_shm_offset);
EXPECT_EQ(testcase.expected_error, ExecuteCmd(ssi_cmd));
EXPECT_EQ(testcase.expected_gl_error, GetGLError());
}
if (testcase.expect_gl_call) {
EXPECT_CALL(*gl_, CoverFillPathInstancedNV(
testcase.num_paths, GL_UNSIGNED_INT, _, 0,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV,
testcase.transform_type, transforms))
.RetiresOnSaturation();
}
if (copy_paths) {
memcpy(paths, testcase.paths, testcase.sizeof_paths);
}
{
cmds::CoverFillPathInstancedCHROMIUM cfi_cmd;
cfi_cmd.Init(testcase.num_paths, testcase.path_name_type, paths_shm_id,
paths_shm_offset, testcase.path_base,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM,
testcase.transform_type, transforms_shm_id,
transforms_shm_offset);
EXPECT_EQ(testcase.expected_error, ExecuteCmd(cfi_cmd));
EXPECT_EQ(testcase.expected_gl_error, GetGLError());
}
if (testcase.expect_gl_call) {
EXPECT_CALL(*gl_, CoverStrokePathInstancedNV(
testcase.num_paths, GL_UNSIGNED_INT, _, 0,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV,
testcase.transform_type, transforms))
.RetiresOnSaturation();
}
if (copy_paths) {
memcpy(paths, testcase.paths, testcase.sizeof_paths);
}
{
cmds::CoverStrokePathInstancedCHROMIUM csi_cmd;
csi_cmd.Init(testcase.num_paths, testcase.path_name_type, paths_shm_id,
paths_shm_offset, testcase.path_base,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM,
testcase.transform_type, transforms_shm_id,
transforms_shm_offset);
EXPECT_EQ(testcase.expected_error, ExecuteCmd(csi_cmd));
EXPECT_EQ(testcase.expected_gl_error, GetGLError());
}
if (testcase.expect_gl_call) {
EXPECT_CALL(*gl_, StencilThenCoverFillPathInstancedNV(
testcase.num_paths, GL_UNSIGNED_INT, _, 0,
testcase.fill_mode, testcase.mask,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV,
testcase.transform_type, transforms))
.RetiresOnSaturation();
}
if (copy_paths) {
memcpy(paths, testcase.paths, testcase.sizeof_paths);
}
{
cmds::StencilThenCoverFillPathInstancedCHROMIUM stcfi_cmd;
stcfi_cmd.Init(testcase.num_paths, testcase.path_name_type, paths_shm_id,
paths_shm_offset, testcase.path_base, testcase.fill_mode,
testcase.mask, GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM,
testcase.transform_type, transforms_shm_id,
transforms_shm_offset);
EXPECT_EQ(testcase.expected_error, ExecuteCmd(stcfi_cmd));
EXPECT_EQ(testcase.expected_gl_error, GetGLError());
}
if (testcase.expect_gl_call) {
EXPECT_CALL(*gl_, StencilThenCoverStrokePathInstancedNV(
testcase.num_paths, GL_UNSIGNED_INT, _, 0,
testcase.reference, testcase.mask,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV,
testcase.transform_type, transforms))
.RetiresOnSaturation();
}
if (copy_paths) {
memcpy(paths, testcase.paths, testcase.sizeof_paths);
}
{
cmds::StencilThenCoverStrokePathInstancedCHROMIUM stcsi_cmd;
stcsi_cmd.Init(testcase.num_paths, testcase.path_name_type, paths_shm_id,
paths_shm_offset, testcase.path_base, testcase.reference,
testcase.mask, GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM,
testcase.transform_type, transforms_shm_id,
transforms_shm_offset);
EXPECT_EQ(testcase.expected_error, ExecuteCmd(stcsi_cmd));
EXPECT_EQ(testcase.expected_gl_error, GetGLError());
}
}
void CallAllInstancedCommandsWithInvalidSHM(GLsizei num_paths,
const GLuint* paths,
GLuint* paths_shm,
uint32_t paths_shm_id,
uint32_t paths_shm_offset,
uint32_t transforms_shm_id,
uint32_t transforms_shm_offset) {
const GLuint kPathBase = 0;
const GLenum kFillMode = GL_INVERT;
const GLuint kMask = 0x80;
const GLuint kReference = 0xFF;
const GLuint kTransformType = GL_AFFINE_3D_CHROMIUM;
memcpy(paths_shm, paths, sizeof(GLuint) * num_paths);
{
cmds::StencilFillPathInstancedCHROMIUM sfi_cmd;
sfi_cmd.Init(num_paths, GL_UNSIGNED_INT, paths_shm_id, paths_shm_offset,
kPathBase, kFillMode, kMask, kTransformType,
transforms_shm_id, transforms_shm_offset);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(sfi_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
memcpy(paths_shm, paths, sizeof(GLuint) * num_paths);
{
cmds::StencilStrokePathInstancedCHROMIUM ssi_cmd;
ssi_cmd.Init(num_paths, GL_UNSIGNED_INT, paths_shm_id, paths_shm_offset,
kPathBase, kReference, kMask, kTransformType,
transforms_shm_id, transforms_shm_offset);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(ssi_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
memcpy(paths_shm, paths, sizeof(GLuint) * num_paths);
{
cmds::CoverFillPathInstancedCHROMIUM cfi_cmd;
cfi_cmd.Init(num_paths, GL_UNSIGNED_INT, paths_shm_id, paths_shm_offset,
kPathBase, GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM,
kTransformType, transforms_shm_id, transforms_shm_offset);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cfi_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
memcpy(paths_shm, paths, sizeof(GLuint) * num_paths);
{
cmds::CoverStrokePathInstancedCHROMIUM csi_cmd;
csi_cmd.Init(num_paths, GL_UNSIGNED_INT, paths_shm_id, paths_shm_offset,
kPathBase, GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM,
kTransformType, transforms_shm_id, transforms_shm_offset);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(csi_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
memcpy(paths_shm, paths, sizeof(GLuint) * num_paths);
{
cmds::StencilThenCoverFillPathInstancedCHROMIUM stcfi_cmd;
stcfi_cmd.Init(num_paths, GL_UNSIGNED_INT, paths_shm_id, paths_shm_offset,
kPathBase, kFillMode, kMask,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, kTransformType,
transforms_shm_id, transforms_shm_offset);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(stcfi_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
memcpy(paths_shm, paths, sizeof(GLuint) * num_paths);
{
cmds::StencilThenCoverStrokePathInstancedCHROMIUM stcsi_cmd;
stcsi_cmd.Init(num_paths, GL_UNSIGNED_INT, paths_shm_id, paths_shm_offset,
kPathBase, kReference, kMask,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, kTransformType,
transforms_shm_id, transforms_shm_offset);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(stcsi_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
}
GLuint client_path_id_;
static const GLuint kServicePathId = 311;
};
INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithCHROMIUMPathRendering,
::testing::Bool());
class GLES2DecoderTestWithBlendEquationAdvanced : public GLES2DecoderTest {
public:
GLES2DecoderTestWithBlendEquationAdvanced() = default;
void SetUp() override {
InitState init;
init.gl_version = "OpenGL ES 2.0";
init.has_alpha = true;
init.has_depth = true;
init.request_alpha = true;
init.request_depth = true;
init.bind_generates_resource = true;
init.extensions = "GL_KHR_blend_equation_advanced";
InitDecoder(init);
}
};
INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithBlendEquationAdvanced,
::testing::Bool());
class GLES2DecoderTestWithEXTMultisampleCompatibility
: public GLES2DecoderTest {
public:
GLES2DecoderTestWithEXTMultisampleCompatibility() = default;
void SetUp() override {
InitState init;
init.gl_version = "OpenGL ES 3.1";
init.has_alpha = true;
init.has_depth = true;
init.request_alpha = true;
init.request_depth = true;
init.bind_generates_resource = true;
init.extensions = "GL_EXT_multisample_compatibility ";
InitDecoder(init);
}
};
INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithEXTMultisampleCompatibility,
::testing::Bool());
class GLES2DecoderTestWithBlendFuncExtended : public GLES2DecoderTest {
public:
GLES2DecoderTestWithBlendFuncExtended() = default;
void SetUp() override {
InitState init;
init.gl_version = "OpenGL ES 3.0";
init.has_alpha = true;
init.has_depth = true;
init.request_alpha = true;
init.request_depth = true;
init.bind_generates_resource = true;
init.extensions = "GL_EXT_blend_func_extended";
InitDecoder(init);
}
};
INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithBlendFuncExtended,
::testing::Bool());
class GLES2DecoderTestWithCHROMIUMFramebufferMixedSamples
: public GLES2DecoderTest {
public:
GLES2DecoderTestWithCHROMIUMFramebufferMixedSamples() = default;
void SetUp() override {
InitState init;
init.gl_version = "OpenGL ES 3.1";
init.has_alpha = true;
init.has_depth = true;
init.request_alpha = true;
init.request_depth = true;
init.bind_generates_resource = true;
init.extensions = "GL_NV_path_rendering GL_NV_framebuffer_mixed_samples ";
InitDecoder(init);
}
};
INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithCHROMIUMFramebufferMixedSamples,
::testing::Bool());
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, GenDeletePaths) {
static GLuint kFirstClientID = client_path_id_ + 88;
static GLsizei kPathCount = 58;
static GLuint kFirstCreatedServiceID = 8000;
// GenPaths range 0 causes no calls.
cmds::GenPathsCHROMIUM gen_cmd;
gen_cmd.Init(kFirstClientID, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// DeletePaths range 0 causes no calls.
cmds::DeletePathsCHROMIUM delete_cmd;
delete_cmd.Init(kFirstClientID, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// DeletePaths client id 0 causes no calls and no errors.
delete_cmd.Init(0, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// DeletePaths with a big range should not cause any deletes.
delete_cmd.Init(client_path_id_ + 1,
std::numeric_limits<GLsizei>::max() - client_path_id_ - 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
delete_cmd.Init(static_cast<GLuint>(std::numeric_limits<GLsizei>::max()) + 1,
std::numeric_limits<GLsizei>::max());
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Normal Gen and Delete should cause the normal calls.
EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
.WillOnce(Return(kFirstCreatedServiceID))
.RetiresOnSaturation();
gen_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
.RetiresOnSaturation();
delete_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, GenDeleteRanges) {
static GLuint kFirstClientID = client_path_id_ + 77;
static GLsizei kPathCount = 5800;
static GLuint kFirstCreatedServiceID = 8000;
// Create a range of path names, delete one in middle and then
// the rest. Expect 3 DeletePath calls.
EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
.WillOnce(Return(kFirstCreatedServiceID))
.RetiresOnSaturation();
cmds::GenPathsCHROMIUM gen_cmd;
gen_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID + (kPathCount / 2), 1))
.RetiresOnSaturation();
cmds::DeletePathsCHROMIUM delete_cmd;
delete_cmd.Init(kFirstClientID + (kPathCount / 2), 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, (kPathCount / 2)))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID + (kPathCount / 2) + 1,
(kPathCount / 2) - 1)).RetiresOnSaturation();
delete_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, GenDeleteManyPaths) {
static GLuint kFirstClientID = client_path_id_ + 1;
static GLsizei kPathCount = std::numeric_limits<GLsizei>::max();
static GLuint kFirstCreatedServiceID = 8000;
EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
.WillOnce(Return(kFirstCreatedServiceID))
.RetiresOnSaturation();
// GenPaths with big range.
cmds::GenPathsCHROMIUM gen_cmd;
gen_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Path range wraps, so we get connection error.
gen_cmd.Init(kFirstClientID + kPathCount, kPathCount);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
.RetiresOnSaturation();
cmds::DeletePathsCHROMIUM delete_cmd;
delete_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Delete every possible path.
// We run into the one created for client_path_id_.
EXPECT_CALL(*gl_, DeletePathsNV(kServicePathId, 1)).RetiresOnSaturation();
delete_cmd.Init(1u, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
delete_cmd.Init(static_cast<GLuint>(kPathCount) + 1u, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Allocate every possible path, delete few, allocate them back and
// expect minimum amount of calls.
EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
.WillOnce(Return(static_cast<GLuint>(1u)))
.WillOnce(Return(static_cast<GLuint>(kPathCount) + 1u))
.RetiresOnSaturation();
gen_cmd.Init(1u, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
gen_cmd.Init(static_cast<GLuint>(kPathCount) + 1u, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
gen_cmd.Init(static_cast<GLuint>(kPathCount) * 2u + 2u, kPathCount);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, DeletePathsNV(kFirstClientID, 4)).RetiresOnSaturation();
delete_cmd.Init(kFirstClientID, 4);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, DeletePathsNV(kFirstClientID * 3, 1)).RetiresOnSaturation();
delete_cmd.Init(kFirstClientID * 3, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, GenPathsNV(1))
.WillOnce(Return(kFirstClientID))
.WillOnce(Return(kFirstClientID + 1))
.WillOnce(Return(kFirstClientID + 2))
.WillOnce(Return(kFirstClientID + 3))
.RetiresOnSaturation();
for (int i = 0; i < 4; ++i) {
gen_cmd.Init(kFirstClientID + i, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
EXPECT_CALL(*gl_, GenPathsNV(1))
.WillOnce(Return(kFirstClientID * 3))
.RetiresOnSaturation();
gen_cmd.Init(kFirstClientID * 3, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, DeletePathsNV(1u, kPathCount)).RetiresOnSaturation();
EXPECT_CALL(*gl_, DeletePathsNV(static_cast<GLuint>(kPathCount) + 1u,
kPathCount)).RetiresOnSaturation();
delete_cmd.Init(1u, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
delete_cmd.Init(static_cast<GLuint>(kPathCount) + 1u, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Cleanup: return the client_path_id_ as a path.
EXPECT_CALL(*gl_, GenPathsNV(1))
.WillOnce(Return(static_cast<GLuint>(kServicePathId)))
.RetiresOnSaturation();
gen_cmd.Init(client_path_id_, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
GenPathsCHROMIUMInvalidCalls) {
static GLuint kFirstClientID = client_path_id_ + 88;
static GLsizei kPathCount = 5800;
static GLuint kFirstCreatedServiceID = 8000;
// Range < 0 is causes gl error.
cmds::GenPathsCHROMIUM gen_cmd;
gen_cmd.Init(kFirstClientID, -1);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
// Path 0 is invalid client id, connection error.
gen_cmd.Init(0, kPathCount);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Too big range causes client id to wrap, connection error.
gen_cmd.Init(static_cast<GLuint>(std::numeric_limits<GLsizei>::max()) + 3,
std::numeric_limits<GLsizei>::max());
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Creating duplicate client_ids cause connection error.
EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
.WillOnce(Return(kFirstCreatedServiceID))
.RetiresOnSaturation();
gen_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Create duplicate by executing the same cmd.
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Create duplicate by creating a range that contains
// an already existing client path id.
gen_cmd.Init(kFirstClientID - 1, 2);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Cleanup.
EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
.RetiresOnSaturation();
cmds::DeletePathsCHROMIUM delete_cmd;
delete_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
DeletePathsCHROMIUMInvalidCalls) {
static GLuint kFirstClientID = client_path_id_ + 88;
// Range < 0 is causes gl error.
cmds::DeletePathsCHROMIUM delete_cmd;
delete_cmd.Init(kFirstClientID, -1);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
// Too big range causes client id to wrap, connection error.
delete_cmd.Init(static_cast<GLuint>(std::numeric_limits<GLsizei>::max()) + 3,
std::numeric_limits<GLsizei>::max());
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
PathCommandsCHROMIUMInvalidCalls) {
static const GLsizei kCorrectCoordCount = 19;
static const GLsizei kCorrectCommandCount = 6;
static const GLenum kInvalidCoordType = GL_NONE;
GLfloat* coords = GetSharedMemoryAs<GLfloat*>();
unsigned commands_offset = sizeof(GLfloat) * kCorrectCoordCount;
GLubyte* commands = GetSharedMemoryAsWithOffset<GLubyte*>(commands_offset);
for (int i = 0; i < kCorrectCoordCount; ++i) {
coords[i] = 5.0f * i;
}
commands[0] = GL_MOVE_TO_CHROMIUM;
commands[1] = GL_CLOSE_PATH_CHROMIUM;
commands[2] = GL_LINE_TO_CHROMIUM;
commands[3] = GL_QUADRATIC_CURVE_TO_CHROMIUM;
commands[4] = GL_CUBIC_CURVE_TO_CHROMIUM;
commands[5] = GL_CONIC_CURVE_TO_CHROMIUM;
EXPECT_CALL(*gl_, PathCommandsNV(kServicePathId, kCorrectCommandCount, _,
kCorrectCoordCount, GL_FLOAT, coords))
.RetiresOnSaturation();
cmds::PathCommandsCHROMIUM cmd;
// Reference call -- this succeeds.
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_ + commands_offset, kCorrectCoordCount,
GL_FLOAT, shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, PathCommandsNV(_, _, _, _, _, _)).Times(0);
// Invalid client id fails.
cmd.Init(client_path_id_ - 1, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_, kCorrectCoordCount, GL_FLOAT,
shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
// The numCommands < 0.
cmd.Init(client_path_id_, -1, shared_memory_id_, shared_memory_offset_,
kCorrectCoordCount, GL_FLOAT, shared_memory_id_,
shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
// The numCoords < 0.
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_, -1, GL_FLOAT, shared_memory_id_,
shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
// Invalid coordType fails.
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_, kCorrectCoordCount, kInvalidCoordType,
shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
// Big command counts.
cmd.Init(client_path_id_, std::numeric_limits<GLsizei>::max(),
shared_memory_id_, shared_memory_offset_ + commands_offset,
kCorrectCoordCount, GL_FLOAT, shared_memory_id_,
shared_memory_offset_);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Invalid SHM cases.
cmd.Init(client_path_id_, kCorrectCommandCount, kInvalidSharedMemoryId,
shared_memory_offset_ + commands_offset, kCorrectCoordCount,
GL_FLOAT, shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
kInvalidSharedMemoryOffset, kCorrectCoordCount, GL_FLOAT,
shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_ + commands_offset, kCorrectCoordCount,
GL_FLOAT, kInvalidSharedMemoryId, shared_memory_offset_);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_ + commands_offset, kCorrectCoordCount,
GL_FLOAT, shared_memory_id_, kInvalidSharedMemoryOffset);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// NULL shm command id with non-zero command count.
cmd.Init(client_path_id_, kCorrectCommandCount, 0, 0, kCorrectCoordCount,
GL_FLOAT, shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// NULL shm coord id with non-zero coord count.
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_ + commands_offset, kCorrectCoordCount,
GL_FLOAT, 0, 0);
EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// The coordCount not matching what is in commands.
// Expects kCorrectCoordCount+2 coords.
commands[1] = GL_MOVE_TO_CHROMIUM;
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_ + commands_offset, kCorrectCoordCount,
GL_FLOAT, shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
// The coordCount not matching what is in commands.
// Expects kCorrectCoordCount-2 coords.
commands[0] = GL_CLOSE_PATH_CHROMIUM;
commands[1] = GL_CLOSE_PATH_CHROMIUM;
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
// NULL shm coord ids. Currently causes gl error, though client should not let
// this through.
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_ + commands_offset, kCorrectCoordCount,
GL_FLOAT, 0, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
PathCommandsCHROMIUMEmptyCommands) {
EXPECT_CALL(*gl_,
PathCommandsNV(kServicePathId, 0, nullptr, 0, GL_FLOAT, nullptr))
.RetiresOnSaturation();
cmds::PathCommandsCHROMIUM cmd;
cmd.Init(client_path_id_, 0, 0, 0, 0, GL_FLOAT, 0, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
PathCommandsCHROMIUMInvalidCommands) {
EXPECT_CALL(*gl_, PathCommandsNV(_, _, _, _, _, _)).Times(0);
cmds::PathCommandsCHROMIUM cmd;
{
const GLsizei kCoordCount = 2;
const GLsizei kCommandCount = 2;
GLfloat* coords = GetSharedMemoryAs<GLfloat*>();
unsigned commands_offset = sizeof(GLfloat) * kCoordCount;
GLubyte* commands = GetSharedMemoryAsWithOffset<GLubyte*>(commands_offset);
coords[0] = 5.0f;
coords[1] = 5.0f;
commands[0] = 0x3; // Token MOVE_TO_RELATIVE in NV_path_rendering.
commands[1] = GL_CLOSE_PATH_CHROMIUM;
cmd.Init(client_path_id_ - 1, kCommandCount, shared_memory_id_,
shared_memory_offset_, kCoordCount, GL_FLOAT, shared_memory_id_,
shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}
{
const GLsizei kCoordCount = 8;
const GLsizei kCommandCount = 4;
GLfloat* coords = GetSharedMemoryAs<GLfloat*>();
unsigned commands_offset = sizeof(GLfloat) * kCoordCount;
GLubyte* commands = GetSharedMemoryAsWithOffset<GLubyte*>(commands_offset);
for (int i = 0; i < kCoordCount; ++i) {
coords[i] = 5.0f * i;
}
commands[0] = GL_MOVE_TO_CHROMIUM;
commands[1] = GL_MOVE_TO_CHROMIUM;
commands[2] = 'M'; // Synonym to MOVE_TO in NV_path_rendering.
commands[3] = GL_MOVE_TO_CHROMIUM;
cmd.Init(client_path_id_ - 1, kCommandCount, shared_memory_id_,
shared_memory_offset_, kCoordCount, GL_FLOAT, shared_memory_id_,
shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, PathParameterXCHROMIUM) {
static GLuint kFirstClientID = client_path_id_ + 88;
static GLsizei kPathCount = 2;
static GLuint kFirstCreatedServiceID = 8000;
// Create a paths so that we do not modify client_path_id_
EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
.WillOnce(Return(kFirstCreatedServiceID))
.RetiresOnSaturation();
cmds::GenPathsCHROMIUM gen_cmd;
gen_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
cmds::PathParameterfCHROMIUM fcmd;
cmds::PathParameteriCHROMIUM icmd;
const struct {
GLenum pname;
GLfloat value;
GLfloat expected_value;
} kTestcases[] = {
{GL_PATH_STROKE_WIDTH_CHROMIUM, 1.0f, 1.0f},
{GL_PATH_STROKE_WIDTH_CHROMIUM, 0.0f, 0.0f},
{GL_PATH_MITER_LIMIT_CHROMIUM, 500.0f, 500.0f},
{GL_PATH_STROKE_BOUND_CHROMIUM, .80f, .80f},
{GL_PATH_STROKE_BOUND_CHROMIUM, 1.80f, 1.0f},
{GL_PATH_STROKE_BOUND_CHROMIUM, -1.0f, 0.0f},
{GL_PATH_END_CAPS_CHROMIUM, GL_FLAT_CHROMIUM, GL_FLAT_CHROMIUM},
{GL_PATH_END_CAPS_CHROMIUM, GL_SQUARE_CHROMIUM, GL_SQUARE_CHROMIUM},
{GL_PATH_JOIN_STYLE_CHROMIUM,
GL_MITER_REVERT_CHROMIUM,
GL_MITER_REVERT_CHROMIUM},
};
for (auto& testcase : kTestcases) {
EXPECT_CALL(*gl_, PathParameterfNV(kFirstCreatedServiceID, testcase.pname,
testcase.expected_value))
.Times(1)
.RetiresOnSaturation();
fcmd.Init(kFirstClientID, testcase.pname, testcase.value);
EXPECT_EQ(error::kNoError, ExecuteCmd(fcmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_,
PathParameteriNV(kFirstCreatedServiceID + 1, testcase.pname,
static_cast<GLint>(testcase.expected_value)))
.Times(1)
.RetiresOnSaturation();
icmd.Init(kFirstClientID + 1, testcase.pname,
static_cast<GLint>(testcase.value));
EXPECT_EQ(error::kNoError, ExecuteCmd(icmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
// Cleanup.
EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
.RetiresOnSaturation();
cmds::DeletePathsCHROMIUM delete_cmd;
delete_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
PathParameterXCHROMIUMInvalidArgs) {
static GLuint kFirstClientID = client_path_id_ + 88;
static GLsizei kPathCount = 2;
static GLuint kFirstCreatedServiceID = 8000;
// Create a paths so that we do not modify client_path_id_
EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
.WillOnce(Return(kFirstCreatedServiceID))
.RetiresOnSaturation();
cmds::GenPathsCHROMIUM gen_cmd;
gen_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
cmds::PathParameterfCHROMIUM fcmd;
cmds::PathParameteriCHROMIUM icmd;
const struct {
GLenum pname;
GLfloat value;
bool try_int_version;
GLint error;
} kTestcases[] = {
{GL_PATH_STROKE_WIDTH_CHROMIUM, -1.0f, true, GL_INVALID_VALUE},
{GL_PATH_MITER_LIMIT_CHROMIUM,
std::numeric_limits<float>::infinity(),
false,
GL_INVALID_VALUE},
{GL_PATH_MITER_LIMIT_CHROMIUM,
std::numeric_limits<float>::quiet_NaN(),
false,
GL_INVALID_VALUE},
{GL_PATH_END_CAPS_CHROMIUM, 0x4, true, GL_INVALID_VALUE},
{GL_PATH_END_CAPS_CHROMIUM,
GL_MITER_REVERT_CHROMIUM,
true,
GL_INVALID_VALUE},
{GL_PATH_JOIN_STYLE_CHROMIUM, GL_FLAT_CHROMIUM, true, GL_INVALID_VALUE},
{GL_PATH_MODELVIEW_CHROMIUM, GL_FLAT_CHROMIUM, true, GL_INVALID_ENUM},
};
EXPECT_CALL(*gl_, PathParameterfNV(_, _, _)).Times(0);
EXPECT_CALL(*gl_, PathParameteriNV(_, _, _)).Times(0);
for (auto& testcase : kTestcases) {
fcmd.Init(kFirstClientID, testcase.pname, testcase.value);
EXPECT_EQ(error::kNoError, ExecuteCmd(fcmd));
EXPECT_EQ(testcase.error, GetGLError());
if (!testcase.try_int_version)
continue;
icmd.Init(kFirstClientID + 1, testcase.pname,
static_cast<GLint>(testcase.value));
EXPECT_EQ(error::kNoError, ExecuteCmd(icmd));
EXPECT_EQ(testcase.error, GetGLError());
}
// Cleanup.
EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
.RetiresOnSaturation();
cmds::DeletePathsCHROMIUM delete_cmd;
delete_cmd.Init(kFirstClientID, kPathCount);
EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, StencilFillPathCHROMIUM) {
SetupExpectationsForApplyingDefaultDirtyState();
cmds::StencilFillPathCHROMIUM cmd;
cmds::StencilThenCoverFillPathCHROMIUM tcmd;
static const GLenum kFillModes[] = {
GL_INVERT, GL_COUNT_UP_CHROMIUM, GL_COUNT_DOWN_CHROMIUM};
static const GLuint kMask = 0x7F;
for (auto& fill_mode : kFillModes) {
EXPECT_CALL(*gl_, StencilFillPathNV(kServicePathId, fill_mode, kMask))
.RetiresOnSaturation();
cmd.Init(client_path_id_, fill_mode, kMask);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, StencilThenCoverFillPathNV(kServicePathId, fill_mode,
kMask, GL_BOUNDING_BOX_NV))
.RetiresOnSaturation();
tcmd.Init(client_path_id_, fill_mode, kMask, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
// Non-existent path: no error, no call.
cmd.Init(client_path_id_ - 1, GL_INVERT, 0x80);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
tcmd.Init(client_path_id_ - 1, GL_INVERT, 0x80, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
StencilFillPathCHROMIUMInvalidArgs) {
EXPECT_CALL(*gl_, StencilFillPathNV(_, _, _)).Times(0);
EXPECT_CALL(*gl_, StencilThenCoverFillPathNV(_, _, _, GL_BOUNDING_BOX_NV))
.Times(0);
cmds::StencilFillPathCHROMIUM cmd;
cmds::StencilThenCoverFillPathCHROMIUM tcmd;
cmd.Init(client_path_id_, GL_INVERT - 1, 0x80);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
tcmd.Init(client_path_id_, GL_INVERT - 1, 0x80, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
// The /mask/+1 is not power of two -> invalid value.
cmd.Init(client_path_id_, GL_COUNT_UP_CHROMIUM, 0x80);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
tcmd.Init(client_path_id_, GL_COUNT_UP_CHROMIUM, 0x80,
GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
cmd.Init(client_path_id_, GL_COUNT_DOWN_CHROMIUM, 5);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
tcmd.Init(client_path_id_, GL_COUNT_DOWN_CHROMIUM, 5,
GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, StencilStrokePathCHROMIUM) {
SetupExpectationsForApplyingDefaultDirtyState();
EXPECT_CALL(*gl_, StencilStrokePathNV(kServicePathId, 1, 0x80))
.RetiresOnSaturation();
EXPECT_CALL(*gl_, StencilThenCoverStrokePathNV(kServicePathId, 1, 0x80,
GL_BOUNDING_BOX_NV))
.RetiresOnSaturation();
cmds::StencilStrokePathCHROMIUM cmd;
cmds::StencilThenCoverStrokePathCHROMIUM tcmd;
cmd.Init(client_path_id_, 1, 0x80);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
tcmd.Init(client_path_id_, 1, 0x80, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, StencilThenCoverStrokePathNV(kServicePathId, 1, 0x80,
GL_CONVEX_HULL_NV))
.RetiresOnSaturation();
tcmd.Init(client_path_id_, 1, 0x80, GL_CONVEX_HULL_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Non-existent path: no error, no call.
cmd.Init(client_path_id_ - 1, 1, 0x80);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
tcmd.Init(client_path_id_ - 1, 1, 0x80, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, CoverFillPathCHROMIUM) {
SetupExpectationsForApplyingDefaultDirtyState();
EXPECT_CALL(*gl_, CoverFillPathNV(kServicePathId, GL_BOUNDING_BOX_NV))
.RetiresOnSaturation();
cmds::CoverFillPathCHROMIUM cmd;
cmd.Init(client_path_id_, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, CoverFillPathNV(kServicePathId, GL_CONVEX_HULL_NV))
.RetiresOnSaturation();
cmd.Init(client_path_id_, GL_CONVEX_HULL_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Non-existent path: no error, no call.
cmd.Init(client_path_id_ - 1, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, CoverStrokePathCHROMIUM) {
SetupExpectationsForApplyingDefaultDirtyState();
EXPECT_CALL(*gl_, CoverStrokePathNV(kServicePathId, GL_BOUNDING_BOX_NV))
.RetiresOnSaturation();
cmds::CoverStrokePathCHROMIUM cmd;
cmd.Init(client_path_id_, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_CALL(*gl_, CoverStrokePathNV(kServicePathId, GL_CONVEX_HULL_NV))
.RetiresOnSaturation();
cmd.Init(client_path_id_, GL_CONVEX_HULL_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Non-existent path: no error, no call.
cmd.Init(client_path_id_ - 1, GL_BOUNDING_BOX_CHROMIUM);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
namespace {
template <typename T>
struct gl_type_enum {};
template <>
struct gl_type_enum<GLbyte> {
enum { kGLType = GL_BYTE };
};
template <>
struct gl_type_enum<GLubyte> {
enum { kGLType = GL_UNSIGNED_BYTE };
};
template <>
struct gl_type_enum<GLshort> {
enum { kGLType = GL_SHORT };
};
template <>
struct gl_type_enum<GLushort> {
enum { kGLType = GL_UNSIGNED_SHORT };
};
template <>
struct gl_type_enum<GLfloat> {
enum { kGLType = GL_FLOAT };
};
}
template <typename TypeParam>
void GLES2DecoderTestWithCHROMIUMPathRendering::
TestPathCommandsCHROMIUMCoordTypes() {
static const GLsizei kCorrectCoordCount = 19;
static const GLsizei kCorrectCommandCount = 6;
TypeParam* coords = GetSharedMemoryAs<TypeParam*>();
unsigned commands_offset = sizeof(TypeParam) * kCorrectCoordCount;
GLubyte* commands = GetSharedMemoryAsWithOffset<GLubyte*>(commands_offset);
for (int i = 0; i < kCorrectCoordCount; ++i) {
coords[i] = static_cast<TypeParam>(5 * i);
}
commands[0] = GL_MOVE_TO_CHROMIUM;
commands[1] = GL_CLOSE_PATH_CHROMIUM;
commands[2] = GL_LINE_TO_CHROMIUM;
commands[3] = GL_QUADRATIC_CURVE_TO_CHROMIUM;
commands[4] = GL_CUBIC_CURVE_TO_CHROMIUM;
commands[5] = GL_CONIC_CURVE_TO_CHROMIUM;
EXPECT_CALL(*gl_, PathCommandsNV(kServicePathId, kCorrectCommandCount, _,
kCorrectCoordCount,
gl_type_enum<TypeParam>::kGLType, coords))
.RetiresOnSaturation();
cmds::PathCommandsCHROMIUM cmd;
cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
shared_memory_offset_ + commands_offset, kCorrectCoordCount,
gl_type_enum<TypeParam>::kGLType, shared_memory_id_,
shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
PathCommandsCHROMIUMCoordTypes) {
// Not using a typed test case, because the base class is already parametrized
// test case and uses GetParam.
TestPathCommandsCHROMIUMCoordTypes<GLbyte>();
TestPathCommandsCHROMIUMCoordTypes<GLubyte>();
TestPathCommandsCHROMIUMCoordTypes<GLshort>();
TestPathCommandsCHROMIUMCoordTypes<GLushort>();
TestPathCommandsCHROMIUMCoordTypes<GLfloat>();
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
StencilXFillPathInstancedCHROMIUMInvalidArgs) {
cmds::StencilFillPathInstancedCHROMIUM sfi_cmd;
cmds::StencilThenCoverFillPathInstancedCHROMIUM stcfi_cmd;
const GLuint kPaths[] = {client_path_id_, client_path_id_ + 5,
client_path_id_, client_path_id_ + 18};
const GLsizei kPathCount = arraysize(kPaths);
struct {
GLenum fill_mode;
GLuint mask;
GLint expected_error;
} testcases[] = {
// Using invalid fill mode produces invalid enum.
{GL_COUNT_UP_CHROMIUM - 1, 0x7F, GL_INVALID_ENUM},
{GL_COUNT_DOWN_CHROMIUM + 1, 0x7F, GL_INVALID_ENUM},
// Using /mask/+1 which is not power of two produces invalid value.
{GL_COUNT_UP_CHROMIUM, 0x80, GL_INVALID_VALUE},
{GL_COUNT_DOWN_CHROMIUM, 4, GL_INVALID_VALUE}};
GLuint* paths = GetSharedMemoryAs<GLuint*>();
for (size_t i = 0; i < arraysize(testcases); ++i) {
memcpy(paths, kPaths, sizeof(kPaths));
sfi_cmd.Init(kPathCount, GL_UNSIGNED_INT, shared_memory_id_,
shared_memory_offset_, 0, testcases[i].fill_mode,
testcases[i].mask, GL_NONE, 0, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(sfi_cmd));
EXPECT_EQ(testcases[i].expected_error, GetGLError());
memcpy(paths, kPaths, sizeof(kPaths));
stcfi_cmd.Init(kPathCount, GL_UNSIGNED_INT, shared_memory_id_,
shared_memory_offset_, 0, testcases[i].fill_mode,
testcases[i].mask,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, GL_NONE, 0, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(stcfi_cmd));
EXPECT_EQ(testcases[i].expected_error, GetGLError());
}
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
StencilXFillPathInstancedCHROMIUMFillMode) {
SetupExpectationsForApplyingDefaultDirtyState();
// Test different fill modes.
cmds::StencilFillPathInstancedCHROMIUM sfi_cmd;
cmds::StencilThenCoverFillPathInstancedCHROMIUM stcfi_cmd;
const GLuint kPaths[] = {client_path_id_, client_path_id_ + 5,
client_path_id_, client_path_id_ + 18};
const GLsizei kPathCount = arraysize(kPaths);
static const GLenum kFillModes[] = {GL_INVERT, GL_COUNT_UP_CHROMIUM,
GL_COUNT_DOWN_CHROMIUM};
const GLuint kMask = 0x7F;
GLuint* paths = GetSharedMemoryAs<GLuint*>();
for (size_t i = 0; i < arraysize(kFillModes); ++i) {
memcpy(paths, kPaths, sizeof(kPaths));
EXPECT_CALL(*gl_, StencilFillPathInstancedNV(kPathCount, GL_UNSIGNED_INT, _,
0, kFillModes[i], kMask,
GL_NONE, nullptr))
.RetiresOnSaturation();
sfi_cmd.Init(kPathCount, GL_UNSIGNED_INT, shared_memory_id_,
shared_memory_offset_, 0, kFillModes[i], kMask, GL_NONE, 0, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(sfi_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
memcpy(paths, kPaths, sizeof(kPaths));
EXPECT_CALL(*gl_,
StencilThenCoverFillPathInstancedNV(
kPathCount, GL_UNSIGNED_INT, _, 0, kFillModes[i], kMask,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV, GL_NONE, nullptr))
.RetiresOnSaturation();
stcfi_cmd.Init(kPathCount, GL_UNSIGNED_INT, shared_memory_id_,
shared_memory_offset_, 0, kFillModes[i], kMask,
GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, GL_NONE, 0, 0);
EXPECT_EQ(error::kNoError, ExecuteCmd(stcfi_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, InstancedCalls) {
SetupExpectationsForApplyingDefaultDirtyState();
const GLuint kPaths[] = {0, client_path_id_, 15, client_path_id_};
const GLsizei kPathCount = arraysize(kPaths);
// The path base will be client_path_id_, and so 0 is a
// valid path.
const GLuint kPathBase = client_path_id_;
const GLuint kPathsWithBase[] = {0, 5, 0, 18};
const GLshort kShortPathBase = client_path_id_ * 2;
const GLshort kShortPathsWithBase[] = {
-static_cast<GLshort>(client_path_id_), 5,
-static_cast<GLshort>(client_path_id_), 18};
const GLenum kFillMode = GL_INVERT;
const GLuint kMask = 0x80;
const GLuint kReference = 0xFF;
GLfloat transform_values[12 * kPathCount];
for (GLsizei i = 0; i < kPathCount; ++i) {
for (int j = 0; j < 12; ++j) {
transform_values[i * 12 + j] = 0.1f * j + i;
}
}
// Path name overflows to correct path.
const GLuint kBigPathBase = std::numeric_limits<GLuint>::max();
const GLuint kPathsWithBigBase[] = {client_path_id_ + 1, 5,
client_path_id_ + 1, 18};
// Path name underflows. As a technical limitation, we can not get to correct
// path,
// so test just tests that there is no GL error.
const GLuint kNegativePathBase = 1;
const GLbyte kNegativePathsWithBaseByte[] = {-1, -2, -5, -18};
const GLint kNegativePathsWithBaseInt[] = {-2, -3, -4, -5};
InstancedTestcase testcases[] = {
// Test a normal call.
{kPathCount, GL_UNSIGNED_INT, kPaths, 0, kFillMode, kReference, kMask,
GL_NONE, nullptr, sizeof(kPaths), 0, error::kNoError, GL_NO_ERROR, true},
// Test that the path base is applied correctly for each instanced call.
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask, GL_NONE, nullptr, sizeof(kPaths), 0, error::kNoError,
GL_NO_ERROR, true},
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask,
// Test all possible transform types. The float array is big enough for
// all the variants. The contents of the array in call is not checked,
// though.
GL_TRANSLATE_X_CHROMIUM, transform_values, sizeof(kPaths),
sizeof(transform_values), error::kNoError, GL_NO_ERROR, true},
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask, GL_TRANSLATE_Y_CHROMIUM, transform_values,
sizeof(kPaths), sizeof(transform_values), error::kNoError, GL_NO_ERROR,
true},
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask, GL_TRANSLATE_2D_CHROMIUM, transform_values,
sizeof(kPaths), sizeof(transform_values), error::kNoError, GL_NO_ERROR,
true},
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask, GL_TRANSLATE_3D_CHROMIUM, transform_values,
sizeof(kPaths), sizeof(transform_values), error::kNoError, GL_NO_ERROR,
true},
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask, GL_AFFINE_2D_CHROMIUM, transform_values,
sizeof(kPaths), sizeof(transform_values), error::kNoError, GL_NO_ERROR,
true},
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask, GL_AFFINE_3D_CHROMIUM, transform_values,
sizeof(kPaths), sizeof(transform_values), error::kNoError, GL_NO_ERROR,
true},
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask, GL_TRANSPOSE_AFFINE_2D_CHROMIUM, transform_values,
sizeof(kPaths), sizeof(transform_values), error::kNoError, GL_NO_ERROR,
true},
{kPathCount, GL_UNSIGNED_INT, kPathsWithBase, kPathBase, kFillMode,
kReference, kMask, GL_TRANSPOSE_AFFINE_3D_CHROMIUM, transform_values,
sizeof(kPaths), sizeof(transform_values), error::kNoError, GL_NO_ERROR,
true},
{kPathCount, GL_SHORT, kShortPathsWithBase, kShortPathBase, kFillMode,
kReference, kMask, GL_TRANSPOSE_AFFINE_3D_CHROMIUM, transform_values,
sizeof(kShortPathsWithBase), sizeof(transform_values), error::kNoError,
GL_NO_ERROR, true},
// Test that if using path base causes path id to overflow, we get no
// error.
{kPathCount, GL_UNSIGNED_INT, kPathsWithBigBase, kBigPathBase, kFillMode,
kReference, kMask, GL_TRANSLATE_X_CHROMIUM, transform_values,
sizeof(kPathsWithBigBase), sizeof(transform_values), error::kNoError,
GL_NO_ERROR, true},
// Test that if using path base causes path id to underflow, we get no
// error.
{kPathCount, GL_BYTE, kNegativePathsWithBaseByte, kNegativePathBase,
kFillMode, kReference, kMask, GL_TRANSLATE_X_CHROMIUM, transform_values,
sizeof(kNegativePathsWithBaseByte), sizeof(transform_values),
error::kNoError, GL_NO_ERROR, false},
{kPathCount, GL_INT, kNegativePathsWithBaseInt, kNegativePathBase,
kFillMode, kReference, kMask, GL_TRANSLATE_X_CHROMIUM, transform_values,
sizeof(kNegativePathsWithBaseInt), sizeof(transform_values),
error::kNoError, GL_NO_ERROR, false},
};
for (size_t i = 0; i < arraysize(testcases); ++i) {
SCOPED_TRACE(testing::Message() << "InstancedCalls testcase " << i);
CallAllInstancedCommands(testcases[i]);
}
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, InstancedNoCalls) {
const GLuint kPaths[] = {1, client_path_id_, 5, client_path_id_};
const GLsizei kPathCount = arraysize(kPaths);
const GLenum kFillMode = GL_INVERT;
const GLuint kMask = 0x80;
const GLuint kReference = 0xFF;
GLfloat transform_values[12 * kPathCount];
for (GLsizei i = 0; i < kPathCount; ++i) {
for (int j = 0; j < 12; ++j) {
transform_values[i * 12 + j] = 0.1f * j + i;
}
}
// The path base will be client_path_id_, and so 0 is a valid path and others
// should be invalid.
const GLuint kInvalidPathBase = client_path_id_;
const GLuint kInvalidPathsWithBase[] = {1, client_path_id_, 5, 18};
InstancedTestcase testcases[] = {
// Zero path count produces no error, no call.
{0, GL_UNSIGNED_INT, nullptr, 0, kFillMode, kReference, kMask, GL_NONE,
nullptr, 0, 0, error::kNoError, GL_NO_ERROR, false},
// Zero path count, even with path data, produces no error, no call.
{0, GL_UNSIGNED_INT, kPaths, 0, kFillMode, kReference, kMask,
GL_TRANSLATE_X_CHROMIUM, transform_values, sizeof(kPaths),
sizeof(transform_values), error::kNoError, GL_NO_ERROR, false},
// Negative path count produces error.
{-1, GL_UNSIGNED_INT, kPaths, 0, kFillMode, kReference, kMask,
GL_TRANSLATE_X_CHROMIUM, transform_values, sizeof(kPaths),
sizeof(transform_values), error::kNoError, GL_INVALID_VALUE, false},
// Passing paths count but not having the shm data is a connection error.
{kPathCount, GL_UNSIGNED_INT, nullptr, 0, kFillMode, kReference, kMask,
GL_TRANSLATE_X_CHROMIUM, transform_values, 0, sizeof(transform_values),
error::kOutOfBounds, GL_NO_ERROR, false},
// Huge path count would cause huge transfer buffer, it does not go
// through.
{std::numeric_limits<GLsizei>::max() - 3, GL_UNSIGNED_INT, kPaths, 0,
kFillMode, kReference, kMask, GL_TRANSLATE_X_CHROMIUM, transform_values,
sizeof(kPaths), sizeof(transform_values), error::kOutOfBounds,
GL_NO_ERROR, false},
// Test that the path base is applied correctly for each instanced call.
// In this case no path is marked as used, and so no GL function should be
// called and no error should be generated.
{kPathCount, GL_UNSIGNED_INT, kInvalidPathsWithBase, kInvalidPathBase,
kFillMode, kReference, kMask, GL_TRANSLATE_X_CHROMIUM, transform_values,
sizeof(kInvalidPathsWithBase), sizeof(transform_values), error::kNoError,
GL_NO_ERROR, false},
// Test that using correct paths but invalid transform type produces
// invalid enum.
{kPathCount, GL_UNSIGNED_INT, kPaths, 0, kFillMode, kReference, kMask,
GL_TRANSLATE_X_CHROMIUM - 1, transform_values, sizeof(kPaths),
sizeof(transform_values), error::kNoError, GL_INVALID_ENUM, false},
// Test that if we have transform, not having the shm data is a connection
// error.
{kPathCount, GL_UNSIGNED_INT, kPaths, 0, kFillMode, kReference, kMask,
GL_TRANSLATE_X_CHROMIUM, nullptr, sizeof(kPaths), 0, error::kOutOfBounds,
GL_NO_ERROR, false},
};
for (size_t i = 0; i < arraysize(testcases); ++i) {
SCOPED_TRACE(testing::Message() << "InstancedNoCalls testcase " << i);
CallAllInstancedCommands(testcases[i]);
}
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, InstancedInvalidSHMValues) {
const GLuint kPaths[] = {1, client_path_id_, 5, client_path_id_};
const GLsizei kPathCount = arraysize(kPaths);
GLfloat transform_values[12 * kPathCount];
for (GLsizei i = 0; i < kPathCount; ++i) {
for (int j = 0; j < 12; ++j) {
transform_values[i * 12 + j] = 0.1f * j + i;
}
}
enum {
kPathsSHMIdInvalid = 1,
kPathsSHMOffsetInvalid = 1 << 1,
kTransformsHMIdInvalid = 1 << 2,
kTransformsHMOffsetInvalid = 1 << 3,
kFirstTestcase = kPathsSHMIdInvalid,
kLastTestcase = kTransformsHMOffsetInvalid
};
for (int testcase = kFirstTestcase; testcase <= kLastTestcase; ++testcase) {
GLfloat* transforms = GetSharedMemoryAs<GLfloat*>();
uint32_t transforms_shm_id = shared_memory_id_;
uint32_t transforms_shm_offset = shared_memory_offset_;
memcpy(transforms, transform_values, sizeof(transform_values));
GLuint* paths =
GetSharedMemoryAsWithOffset<GLuint*>(sizeof(transform_values));
uint32_t paths_shm_id = shared_memory_id_;
uint32_t paths_shm_offset =
shared_memory_offset_ + sizeof(transform_values);
if (testcase & kPathsSHMIdInvalid) {
paths_shm_id = kInvalidSharedMemoryId;
}
if (testcase & kPathsSHMOffsetInvalid) {
paths_shm_offset = kInvalidSharedMemoryOffset;
}
if (testcase & kTransformsHMIdInvalid) {
transforms_shm_id = kInvalidSharedMemoryId;
}
if (testcase & kTransformsHMOffsetInvalid) {
transforms_shm_offset = kInvalidSharedMemoryOffset;
}
SCOPED_TRACE(testing::Message() << "InstancedInvalidSHMValues testcase "
<< testcase);
CallAllInstancedCommandsWithInvalidSHM(
kPathCount, kPaths, paths, paths_shm_id, paths_shm_offset,
transforms_shm_id, transforms_shm_offset);
}
}
TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
BindFragmentInputLocationCHROMIUM) {
const uint32_t kBucketId = 123;
const GLint kLocation = 2;
const char* kName = "testing";
const char* kBadName1 = "gl_testing";
SetBucketAsCString(kBucketId, kName);
cmds::BindFragmentInputLocationCHROMIUMBucket cmd;
cmd.Init(client_program_id_, kLocation, kBucketId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Check negative location.
SetBucketAsCString(kBucketId, kName);
cmd.Init(client_program_id_, -1, kBucketId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
// Check the highest location.
SetBucketAsCString(kBucketId, kName);
const GLint kMaxLocation = kMaxVaryingVectors * 4 - 1;
cmd.Init(client_program_id_, kMaxLocation, kBucketId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
// Check too high location.
SetBucketAsCString(kBucketId, kName);
cmd.Init(client_program_id_, kMaxLocation + 1, kBucketId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
// Check bad name "gl_...".
SetBucketAsCString(kBucketId, kBadName1);
cmd.Init(client_program_id_, kLocation, kBucketId);
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}
class GLES2DecoderTestWithCHROMIUMRasterTransport : public GLES2DecoderTest {
public:
GLES2DecoderTestWithCHROMIUMRasterTransport() = default;
void SetUp() override {
InitState init;
init.gl_version = "OpenGL ES 2.0";
init.has_alpha = true;
init.has_depth = true;
init.request_alpha = true;
init.request_depth = true;
init.bind_generates_resource = true;
init.extensions = "chromium_raster_transport";
InitDecoder(init);
}
};
INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithCHROMIUMRasterTransport,
::testing::Bool());
class GLES3DecoderTestWithEXTWindowRectangles : public GLES3DecoderTest {
public:
GLES3DecoderTestWithEXTWindowRectangles() = default;
void SetUp() override {
InitState init;
init.context_type = CONTEXT_TYPE_OPENGLES3;
init.gl_version = "OpenGL ES 3.0";
init.has_alpha = true;
init.has_depth = true;
init.request_alpha = true;
init.request_depth = true;
init.bind_generates_resource = true;
init.extensions = "GL_EXT_window_rectangles";
InitDecoder(init);
}
};
INSTANTIATE_TEST_CASE_P(Service,
GLES3DecoderTestWithEXTWindowRectangles,
::testing::Bool());
TEST_P(GLES3DecoderTestWithEXTWindowRectangles,
WindowRectanglesEXTImmediateValidArgs) {
cmds::WindowRectanglesEXTImmediate& cmd =
*GetImmediateAs<cmds::WindowRectanglesEXTImmediate>();
SpecializedSetup<cmds::WindowRectanglesEXTImmediate, 0>(true);
GLint temp[4 * 2] = {};
// The backbuffer is still bound, so the expected result is actually a reset
// to the default state. (Window rectangles don't affect the backbuffer.)
EXPECT_CALL(*gl_, WindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr));
cmd.Init(GL_INCLUSIVE_EXT, 2, &temp[0]);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h"
} // namespace gles2
} // namespace gpu