blob: 8d04f9978265ae55eb4a0abdfbc99f1b2daf687a [file] [log] [blame]
//
// Copyright 2023 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// PolygonModeTest.cpp: Test cases for GL_NV_polygon_mode and GL_ANGLE_polygon_mode
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
class PolygonModeTest : public ANGLETest<>
{
protected:
PolygonModeTest()
{
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
setExtensionsEnabled(false);
setWindowWidth(16);
setWindowHeight(16);
}
};
// New state queries and commands fail without the extension
TEST_P(PolygonModeTest, NoExtension)
{
{
GLint mode = 0;
glGetIntegerv(GL_POLYGON_MODE_NV, &mode);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
EXPECT_EQ(mode, 0);
glPolygonModeNV(GL_FRONT_AND_BACK, GL_FILL_NV);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_FILL_NV);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
for (GLenum state : {GL_POLYGON_OFFSET_POINT_NV, GL_POLYGON_OFFSET_LINE_NV})
{
EXPECT_FALSE(glIsEnabled(state));
EXPECT_GL_ERROR(GL_INVALID_ENUM);
GLboolean enabled = true;
glGetBooleanv(state, &enabled);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
EXPECT_TRUE(enabled);
glEnable(state);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glDisable(state);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
}
}
// Test NV_polygon_mode entrypoints
TEST_P(PolygonModeTest, ExtensionStateNV)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_NV_polygon_mode"));
// Default state
{
GLint mode = 0;
glGetIntegerv(GL_POLYGON_MODE_NV, &mode);
EXPECT_GLENUM_EQ(GL_FILL_NV, mode);
EXPECT_GL_NO_ERROR();
}
for (GLenum state : {GL_POLYGON_OFFSET_POINT_NV, GL_POLYGON_OFFSET_LINE_NV})
{
EXPECT_FALSE(glIsEnabled(state));
EXPECT_GL_NO_ERROR();
GLboolean enabled = true;
glGetBooleanv(state, &enabled);
EXPECT_FALSE(enabled);
EXPECT_GL_NO_ERROR();
}
// Polygon mode state updates
for (GLenum mode : {GL_POINT_NV, GL_LINE_NV, GL_FILL_NV})
{
glPolygonModeNV(GL_FRONT_AND_BACK, mode);
EXPECT_GL_NO_ERROR();
GLint result = 0;
glGetIntegerv(GL_POLYGON_MODE_NV, &result);
EXPECT_GLENUM_EQ(mode, result);
EXPECT_GL_NO_ERROR();
}
// Polygon offset state updates
for (GLenum state : {GL_POLYGON_OFFSET_POINT_NV, GL_POLYGON_OFFSET_LINE_NV})
{
GLboolean enabled = false;
glEnable(state);
EXPECT_GL_NO_ERROR();
EXPECT_TRUE(glIsEnabled(state));
EXPECT_GL_NO_ERROR();
glGetBooleanv(state, &enabled);
EXPECT_GL_NO_ERROR();
EXPECT_TRUE(enabled);
glDisable(state);
EXPECT_GL_NO_ERROR();
EXPECT_FALSE(glIsEnabled(state));
EXPECT_GL_NO_ERROR();
glGetBooleanv(state, &enabled);
EXPECT_GL_NO_ERROR();
EXPECT_FALSE(enabled);
}
// Errors
{
glPolygonModeNV(GL_FRONT, GL_FILL_NV);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glPolygonModeNV(GL_BACK, GL_FILL_NV);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glPolygonModeNV(GL_FRONT_AND_BACK, 0);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
}
}
// Test ANGLE_polygon_mode entrypoints
TEST_P(PolygonModeTest, ExtensionStateANGLE)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_polygon_mode"));
// Default state
{
GLint mode = 0;
glGetIntegerv(GL_POLYGON_MODE_ANGLE, &mode);
EXPECT_GLENUM_EQ(GL_FILL_ANGLE, mode);
EXPECT_GL_NO_ERROR();
}
for (GLenum state : {GL_POLYGON_OFFSET_LINE_ANGLE})
{
EXPECT_FALSE(glIsEnabled(state));
EXPECT_GL_NO_ERROR();
GLboolean enabled = true;
glGetBooleanv(state, &enabled);
EXPECT_FALSE(enabled);
EXPECT_GL_NO_ERROR();
}
// Polygon mode state updates
for (GLenum mode : {GL_LINE_ANGLE, GL_FILL_ANGLE})
{
glPolygonModeANGLE(GL_FRONT_AND_BACK, mode);
EXPECT_GL_NO_ERROR();
GLint result = 0;
glGetIntegerv(GL_POLYGON_MODE_ANGLE, &result);
EXPECT_GLENUM_EQ(mode, result);
EXPECT_GL_NO_ERROR();
}
// Polygon offset state updates
for (GLenum state : {GL_POLYGON_OFFSET_LINE_ANGLE})
{
GLboolean enabled = false;
glEnable(state);
EXPECT_GL_NO_ERROR();
EXPECT_TRUE(glIsEnabled(state));
EXPECT_GL_NO_ERROR();
glGetBooleanv(state, &enabled);
EXPECT_GL_NO_ERROR();
EXPECT_TRUE(enabled);
glDisable(state);
EXPECT_GL_NO_ERROR();
EXPECT_FALSE(glIsEnabled(state));
EXPECT_GL_NO_ERROR();
glGetBooleanv(state, &enabled);
EXPECT_GL_NO_ERROR();
EXPECT_FALSE(enabled);
}
// Errors
{
glPolygonModeANGLE(GL_FRONT, GL_FILL_ANGLE);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glPolygonModeANGLE(GL_BACK, GL_FILL_ANGLE);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glPolygonModeANGLE(GL_FRONT_AND_BACK, 0);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_POINT_NV);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glIsEnabled(GL_POLYGON_OFFSET_POINT_NV);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
GLboolean enabled = true;
glGetBooleanv(GL_POLYGON_OFFSET_POINT_NV, &enabled);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
}
}
// Test line rasterization mode
TEST_P(PolygonModeTest, DrawLines)
{
const bool extensionNV = EnsureGLExtensionEnabled("GL_NV_polygon_mode");
const bool extensionANGLE = EnsureGLExtensionEnabled("GL_ANGLE_polygon_mode");
ANGLE_SKIP_TEST_IF(!extensionNV && !extensionANGLE);
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(program);
GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
const int w = getWindowWidth();
const int h = getWindowHeight();
ASSERT(w == h);
for (bool useNV : {true, false})
{
if (useNV && !extensionNV)
{
continue;
}
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_RECT_EQ(0, 0, w, h, GLColor::red);
// Draw green quad with lines
if (useNV)
{
glPolygonModeNV(GL_FRONT_AND_BACK, GL_LINE_NV);
}
else
{
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_LINE_ANGLE);
}
glUniform4f(colorLocation, 0.0, 1.0, 0.0, 1.0);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.0);
// Nothing was drawn inside triangles
EXPECT_PIXEL_RECT_EQ(1, 1, 5, 5, GLColor::red);
EXPECT_PIXEL_RECT_EQ(9, 9, 5, 5, GLColor::red);
// Main diagonal was drawn
std::vector<GLColor> colors(w * h);
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
for (int i = 0; i < w; i++)
{
const int x = i;
const int y = w - 1 - i;
EXPECT_EQ(GLColor::green, colors[y * w + x]) << "x: " << x << " y: " << y;
}
// Draw blue quad with triangles
if (useNV)
{
glPolygonModeNV(GL_FRONT_AND_BACK, GL_FILL_NV);
}
else
{
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_FILL_ANGLE);
}
glUniform4f(colorLocation, 0.0, 0.0, 1.0, 1.0);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.0);
EXPECT_PIXEL_RECT_EQ(0, 0, w, h, GLColor::blue);
}
}
// Test line rasterization mode with depth offset
TEST_P(PolygonModeTest, DrawLinesWithDepthOffset)
{
const bool extensionNV = EnsureGLExtensionEnabled("GL_NV_polygon_mode");
const bool extensionANGLE = EnsureGLExtensionEnabled("GL_ANGLE_polygon_mode");
ANGLE_SKIP_TEST_IF(!extensionNV && !extensionANGLE);
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(program);
GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
const int w = getWindowWidth();
const int h = getWindowHeight();
ASSERT(w == h);
glEnable(GL_DEPTH_TEST);
for (bool useNV : {true, false})
{
if (useNV && !extensionNV)
{
continue;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw red quad filled
if (useNV)
{
glPolygonModeNV(GL_FRONT_AND_BACK, GL_FILL_NV);
}
else
{
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_FILL_ANGLE);
}
glUniform4f(colorLocation, 1.0, 0.0, 0.0, 1.0);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5);
// Draw green quad using lines with offset failing the depth test
if (useNV)
{
glEnable(GL_POLYGON_OFFSET_LINE_NV);
glPolygonModeNV(GL_FRONT_AND_BACK, GL_LINE_NV);
}
else
{
glEnable(GL_POLYGON_OFFSET_LINE_ANGLE);
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_LINE_ANGLE);
}
glPolygonOffset(0.0, 2.0);
glUniform4f(colorLocation, 0.0, 1.0, 0.0, 1.0);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5);
// Depth test must fail
EXPECT_PIXEL_RECT_EQ(0, 0, w, h, GLColor::red);
// Draw green quad with triangles
if (useNV)
{
glPolygonModeNV(GL_FRONT_AND_BACK, GL_FILL_NV);
}
else
{
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_FILL_ANGLE);
}
drawQuad(program, essl1_shaders::PositionAttrib(), 0.0);
// Change the offset so that depth test passes
glPolygonOffset(0.0, -2.0);
// Draw blue quad with lines
if (useNV)
{
glPolygonModeNV(GL_FRONT_AND_BACK, GL_LINE_NV);
}
else
{
glPolygonModeANGLE(GL_FRONT_AND_BACK, GL_LINE_ANGLE);
}
glUniform4f(colorLocation, 0.0, 0.0, 1.0, 1.0);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.0);
// Main diagonal was drawn
std::vector<GLColor> colors(w * h);
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
for (int i = 0; i < w; i++)
{
const int x = i;
const int y = w - 1 - i;
EXPECT_EQ(GLColor::blue, colors[y * w + x]) << "x: " << x << " y: " << y;
}
}
}
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(PolygonModeTest);