blob: c6fd00633a30678a7a6b9ba28996efaf3959bfd0 [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.
//
// PolygonOffsetClampTest.cpp: Test cases for GL_EXT_polygon_offset_clamp
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
#include "util/test_utils.h"
using namespace angle;
class PolygonOffsetClampTest : public ANGLETest<>
{
protected:
PolygonOffsetClampTest()
{
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
setExtensionsEnabled(false);
}
};
// Test state queries and updates
TEST_P(PolygonOffsetClampTest, State)
{
// New state query fails without the extension
{
GLfloat clamp = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
EXPECT_EQ(clamp, -1.0f);
ASSERT_GL_NO_ERROR();
}
// New function does nothing without enabling the extension
{
glPolygonOffsetClampEXT(1.0f, 2.0f, 3.0f);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
GLfloat factor = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
EXPECT_EQ(factor, 0.0f);
GLfloat units = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
EXPECT_EQ(units, 0.0f);
ASSERT_GL_NO_ERROR();
}
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_polygon_offset_clamp"));
// Default state
{
GLfloat factor = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
EXPECT_EQ(factor, 0.0f);
GLfloat units = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
EXPECT_EQ(units, 0.0f);
GLfloat clamp = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
EXPECT_EQ(clamp, 0.0f);
ASSERT_GL_NO_ERROR();
}
// Full state update using the new function
{
glPolygonOffsetClampEXT(1.0f, 2.0f, 3.0f);
GLfloat factor = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
EXPECT_EQ(factor, 1.0f);
GLfloat units = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
EXPECT_EQ(units, 2.0f);
GLfloat clamp = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
EXPECT_EQ(clamp, 3.0f);
ASSERT_GL_NO_ERROR();
}
// Core function resets the clamp value to zero
{
glPolygonOffset(4.0f, 5.0f);
GLfloat factor = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
EXPECT_EQ(factor, 4.0f);
GLfloat units = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
EXPECT_EQ(units, 5.0f);
GLfloat clamp = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
EXPECT_EQ(clamp, 0.0f);
ASSERT_GL_NO_ERROR();
}
// NaNs must be accepted and replaced with zeros
{
glPolygonOffsetClampEXT(6.0f, 7.0f, 8.0f);
float nan = std::numeric_limits<float>::quiet_NaN();
glPolygonOffsetClampEXT(nan, nan, nan);
GLfloat factor = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
EXPECT_EQ(factor, 0.0f);
GLfloat units = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
EXPECT_EQ(units, 0.0f);
GLfloat clamp = -1.0f;
glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
EXPECT_EQ(clamp, 0.0f);
ASSERT_GL_NO_ERROR();
}
}
// Test polygon offset clamping behavior. Ported from dEQP.
TEST_P(PolygonOffsetClampTest, Operation)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_polygon_offset_clamp"));
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_depth_texture"));
GLTexture depthTexture;
glBindTexture(GL_TEXTURE_2D, depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 64, 64, 0, GL_DEPTH_COMPONENT,
GL_UNSIGNED_SHORT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D, colorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
GLTexture colorReadbackTexture;
glBindTexture(GL_TEXTURE_2D, colorReadbackTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLFramebuffer fboReadback;
glBindFramebuffer(GL_FRAMEBUFFER, fboReadback);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
colorReadbackTexture, 0);
ANGLE_GL_PROGRAM(testProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
constexpr char kFS[] = R"(
varying highp vec2 v_texCoord;
uniform highp sampler2D tex;
void main()
{
// Store as unorm24
highp float d = floor(texture2D(tex, v_texCoord).r * 16777215.0);
highp float r = floor(d / 65536.0);
highp float g = floor(mod(d, 65536.0) / 256.0);
highp float b = mod(d, 256.0);
gl_FragColor = vec4(r, g, b, 1.0) / 255.0;
})";
ANGLE_GL_PROGRAM(readDepthProgram, essl1_shaders::vs::Texture2D(), kFS);
// Setup depth testing
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
// Bind framebuffer for drawing
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Offset units and clamp values. Should work with all depth buffer formats.
const std::vector<std::array<GLfloat, 2>> testValues = {
{-5000.0f, -0.0001f},
{+5000.0f, +0.0001f},
{-5000.0f, 0.0f},
{+5000.0f, 0.0f},
{-5000.0f, -std::numeric_limits<float>::infinity()},
{+5000.0f, +std::numeric_limits<float>::infinity()}};
auto readDepthValue = [&]() {
glBindFramebuffer(GL_FRAMEBUFFER, fboReadback);
glDisable(GL_DEPTH_TEST);
glUseProgram(readDepthProgram);
glUniform1i(glGetUniformLocation(readDepthProgram, "tex"), 0);
glBindTexture(GL_TEXTURE_2D, depthTexture);
drawQuad(readDepthProgram, "a_position", 0.5);
GLubyte pixels[4];
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Convert read depth value to GLfloat normalized
GLfloat depthValue = (GLfloat)(pixels[0] * 65536 + pixels[1] * 256 + pixels[2]) / 0xFFFFFF;
// Restore state
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glEnable(GL_DEPTH_TEST);
glUseProgram(testProgram);
return depthValue;
};
for (auto testValue : testValues)
{
const GLfloat units = testValue[0];
const GLfloat clamp = testValue[1];
// Draw reference polygon
glDisable(GL_POLYGON_OFFSET_FILL);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawQuad(testProgram, "a_position", 0.0);
// Get reference depth value
const GLfloat depthValue = readDepthValue();
// Draw polygon with depth offset
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0f, units);
drawQuad(testProgram, "a_position", 0.0);
// Get depth value with offset
const GLfloat depthValueOffset = readDepthValue();
// Draw reference polygon
glDisable(GL_POLYGON_OFFSET_FILL);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawQuad(testProgram, "a_position", 0.0);
// Draw polygon with depth offset
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffsetClampEXT(0.0f, units, clamp);
drawQuad(testProgram, "a_position", 0.0);
// Get depth value with clamped offset
const GLfloat depthValueOffsetClamp = readDepthValue();
// Check depth values
{
if (clamp == 0.0f || isinf(clamp))
{
ASSERT_NE(units, 0.0f);
// Ensure that offset works
if (units > 0.0f)
{
EXPECT_LT(depthValue, depthValueOffset);
EXPECT_LT(depthValue, depthValueOffsetClamp);
}
else if (units < 0.0f)
{
EXPECT_GT(depthValue, depthValueOffset);
EXPECT_GT(depthValue, depthValueOffsetClamp);
}
// Clamping must have no effect
EXPECT_EQ(depthValueOffset, depthValueOffsetClamp);
}
else if (clamp < 0.0f)
{
ASSERT_LT(units, 0);
// Negative clamp value sets effective offset to max(offset, clamp)
EXPECT_GT(depthValue, depthValueOffset);
EXPECT_GT(depthValue, depthValueOffsetClamp);
EXPECT_LT(depthValueOffset, depthValueOffsetClamp);
}
else if (clamp > 0.0f)
{
ASSERT_GT(units, 0);
// Positive clamp value sets effective offset to min(offset, clamp)
EXPECT_LT(depthValue, depthValueOffset);
EXPECT_LT(depthValue, depthValueOffsetClamp);
EXPECT_GT(depthValueOffset, depthValueOffsetClamp);
}
}
}
}
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(PolygonOffsetClampTest);