// // 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 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 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);