Add CTS_EXT_polygon_offset_clamp tests implementation

This is CTS_EXT_polygon_offset_clamp tests implementation.
Implemented cases list:
PolygonOffsetClampAvailability,
PolygonOffsetClampMinMax,
PolygonOffsetClampZeroInfinity.

Affects:

KHR-GL45.polygon_offset_clamp.*
KHR-GLES31.core.polygon_offset_clamp.*

Components: OpenGL, Framework

VK-GL-CTS issue: 304

Change-Id: I638ae52b6eca608dfb4acefe06eda02c4969e436
diff --git a/external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt b/external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt
index 08a1043..8be51bf 100644
--- a/external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt
+++ b/external/openglcts/data/mustpass/gl/khronos_mustpass/4.5.5.x/gl45-master.txt
@@ -4936,3 +4936,6 @@
 KHR-GL45.limits.max_fragment_interpolation_offset
 KHR-GL45.limits.max_compute_work_group_count
 KHR-GL45.limits.max_compute_work_group_size
+KHR-GL45.polygon_offset_clamp.PolygonOffsetClampAvailabilityTestCase
+KHR-GL45.polygon_offset_clamp.PolygonOffsetClampMinMaxTestCase
+KHR-GL45.polygon_offset_clamp.PolygonOffsetClampZeroInfinityTestCase
diff --git a/external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles31-khr-master.txt b/external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles31-khr-master.txt
index ef8a145..8b3d58b 100644
--- a/external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles31-khr-master.txt
+++ b/external/openglcts/data/mustpass/gles/khronos_mustpass/3.2.4.x/gles31-khr-master.txt
@@ -1316,6 +1316,9 @@
 KHR-GLES31.core.arrays_of_arrays.InteractionInterfaceArrays2
 KHR-GLES31.core.arrays_of_arrays.InteractionInterfaceArrays3
 KHR-GLES31.core.arrays_of_arrays.InteractionInterfaceArrays4
+KHR-GLES31.core.polygon_offset_clamp.PolygonOffsetClampAvailabilityTestCase
+KHR-GLES31.core.polygon_offset_clamp.PolygonOffsetClampMinMaxTestCase
+KHR-GLES31.core.polygon_offset_clamp.PolygonOffsetClampZeroInfinityTestCase
 KHR-GLES31.core.geometry_shader.adjacency.adjacency_non_indiced_lines
 KHR-GLES31.core.geometry_shader.adjacency.adjacency_indiced_lines
 KHR-GLES31.core.geometry_shader.adjacency.adjacency_non_indiced_line_strip
diff --git a/external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles31-khr-master.txt b/external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles31-khr-master.txt
index ef8a145..8b3d58b 100644
--- a/external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles31-khr-master.txt
+++ b/external/openglcts/data/mustpass/gles/khronos_mustpass/master/gles31-khr-master.txt
@@ -1316,6 +1316,9 @@
 KHR-GLES31.core.arrays_of_arrays.InteractionInterfaceArrays2
 KHR-GLES31.core.arrays_of_arrays.InteractionInterfaceArrays3
 KHR-GLES31.core.arrays_of_arrays.InteractionInterfaceArrays4
+KHR-GLES31.core.polygon_offset_clamp.PolygonOffsetClampAvailabilityTestCase
+KHR-GLES31.core.polygon_offset_clamp.PolygonOffsetClampMinMaxTestCase
+KHR-GLES31.core.polygon_offset_clamp.PolygonOffsetClampZeroInfinityTestCase
 KHR-GLES31.core.geometry_shader.adjacency.adjacency_non_indiced_lines
 KHR-GLES31.core.geometry_shader.adjacency.adjacency_indiced_lines
 KHR-GLES31.core.geometry_shader.adjacency.adjacency_non_indiced_line_strip
diff --git a/external/openglcts/modules/common/CMakeLists.txt b/external/openglcts/modules/common/CMakeLists.txt
index f269a1f..c1a2636 100644
--- a/external/openglcts/modules/common/CMakeLists.txt
+++ b/external/openglcts/modules/common/CMakeLists.txt
@@ -32,6 +32,8 @@
 	glcNoErrorTests.hpp
 	glcRobustnessTests.cpp
 	glcRobustnessTests.hpp
+	glcPolygonOffsetClampTests.cpp
+	glcPolygonOffsetClampTests.hpp
 	glcRobustBufferAccessBehaviorTests.cpp
 	glcRobustBufferAccessBehaviorTests.hpp
 	glcShaderIndexingTests.cpp
diff --git a/external/openglcts/modules/common/glcPolygonOffsetClampTests.cpp b/external/openglcts/modules/common/glcPolygonOffsetClampTests.cpp
new file mode 100644
index 0000000..aaa4ef1
--- /dev/null
+++ b/external/openglcts/modules/common/glcPolygonOffsetClampTests.cpp
@@ -0,0 +1,624 @@
+/*-------------------------------------------------------------------------
+* OpenGL Conformance Test Suite
+* -----------------------------
+*
+* Copyright (c) 2017 The Khronos Group Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/ /*!
+* \file  glcPolygonOffsetClampTests.cpp
+* \brief Conformance tests for the EXT_polygon_offset_clamp functionality.
+*/ /*-------------------------------------------------------------------*/
+
+#include "glcPolygonOffsetClampTests.hpp"
+#include "gluContextInfo.hpp"
+#include "gluShaderProgram.hpp"
+#include "glwEnums.hpp"
+#include "tcuRenderTarget.hpp"
+#include "tcuTestLog.hpp"
+
+#include <stdio.h>
+
+using namespace glw;
+using namespace glu;
+
+namespace glcts
+{
+
+const char* poc_shader_version_450core = "#version 450 core\n\n";
+const char* poc_shader_version_310es   = "#version 310 es\n\n";
+
+const char* poc_vertexColor = "in highp vec3 vertex;\n"
+							  "\n"
+							  "void main()\n"
+							  "{\n"
+							  "    gl_Position = vec4(vertex, 1);\n"
+							  "}\n";
+
+const char* poc_fragmentColor = "out highp vec4 fragColor;\n"
+								"\n"
+								"void main()\n"
+								"{\n"
+								"    fragColor = vec4(1, 1, 1, 1);\n"
+								"}\n";
+
+const char* poc_vertexTexture = "in highp vec3 vertex;\n"
+								"in highp vec2 texCoord;\n"
+								"out highp vec2 varyingtexCoord;\n"
+								"\n"
+								"void main()\n"
+								"{\n"
+								"    gl_Position = vec4(vertex, 1);\n"
+								"    varyingtexCoord = texCoord;\n"
+								"}\n";
+
+const char* poc_fragmentTexture = "in highp vec2 varyingtexCoord;\n"
+								  "out highp vec4 fragColor;\n"
+								  "\n"
+								  "layout (location = 0) uniform highp sampler2D tex;\n"
+								  "\n"
+								  "void main()\n"
+								  "{\n"
+								  "    highp vec4 v = texture(tex, varyingtexCoord);\n"
+								  "    int r = int(v.r * 65536.0) % 256;\n"
+								  "    int g = int(v.r * 65536.0) / 256;\n"
+								  "    fragColor = vec4(float(r) / 255.0, float(g) / 255.0, 0.0, 1.0);\n"
+								  "}\n";
+
+/** Constructor.
+*
+*  @param context Rendering context
+*  @param name Test name
+*  @param description Test description
+*/
+PolygonOffsetClampTestCaseBase::PolygonOffsetClampTestCaseBase(deqp::Context& context, const char* name,
+															   const char* description)
+	: TestCase(context, name, description)
+{
+	m_extensionSupported = context.getContextInfo().isExtensionSupported("GL_EXT_polygon_offset_clamp");
+}
+
+tcu::TestNode::IterateResult PolygonOffsetClampTestCaseBase::iterate()
+{
+	if (!m_extensionSupported)
+	{
+		m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
+		return STOP;
+	}
+
+	test(m_context.getRenderContext().getFunctions());
+
+	return STOP;
+}
+
+/** Constructor.
+*
+*  @param context Rendering context
+*/
+PolygonOffsetClampAvailabilityTestCase::PolygonOffsetClampAvailabilityTestCase(deqp::Context& context)
+	: PolygonOffsetClampTestCaseBase(context, "PolygonOffsetClampAvailability",
+									 "Verifies if queries for GL_EXT_polygon_offset_clamp extension works properly")
+{
+}
+
+void PolygonOffsetClampAvailabilityTestCase::test(const glw::Functions& gl)
+{
+	{
+		glw::GLboolean data;
+		gl.getBooleanv(GL_POLYGON_OFFSET_CLAMP_EXT, &data);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "getBooleanv error occurred");
+	}
+	{
+		glw::GLint data;
+		gl.getIntegerv(GL_POLYGON_OFFSET_CLAMP_EXT, &data);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "getBooleanv error occurred");
+	}
+	{
+		glw::GLint64 data;
+		gl.getInteger64v(GL_POLYGON_OFFSET_CLAMP_EXT, &data);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "getBooleanv error occurred");
+	}
+	{
+		glw::GLfloat data;
+		gl.getFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &data);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "getBooleanv error occurred");
+	}
+
+	// OpenGL ES does not support getDoublev query
+	if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
+	{
+		glw::GLdouble data;
+		gl.getDoublev(GL_POLYGON_OFFSET_CLAMP_EXT, &data);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "getBooleanv error occurred");
+	}
+
+	gl.polygonOffsetClampEXT(1.0f, 1.0f, 0.5f);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "polygonOffsetClampEXT error occurred");
+
+	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
+}
+
+/** Constructor.
+*
+*  @param context Rendering context
+*/
+PolygonOffsetClampValueTestCaseBase::PolygonOffsetClampValueTestCaseBase(deqp::Context& context, const char* name,
+																		 const char* description)
+	: PolygonOffsetClampTestCaseBase(context, name, description)
+	, m_fbo(0)
+	, m_depthBuf(0)
+	, m_colorBuf(0)
+	, m_fboReadback(0)
+	, m_colorBufReadback(0)
+{
+}
+
+/** Initialization method that creates framebuffer with depth attachment
+ */
+void PolygonOffsetClampValueTestCaseBase::init()
+{
+	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+
+	gl.genTextures(1, &m_depthBuf);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "genTextures");
+	gl.bindTexture(GL_TEXTURE_2D, m_depthBuf);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "bindTexture");
+	gl.texStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, 64, 64);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "texStorage2D");
+	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");
+	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");
+
+	gl.genTextures(1, &m_colorBuf);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "genTextures");
+	gl.bindTexture(GL_TEXTURE_2D, m_colorBuf);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "bindTexture");
+	gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 64, 64);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "texStorage2D");
+	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");
+	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");
+
+	gl.genFramebuffers(1, &m_fbo);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "genFramebuffers");
+	gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "bindFramebuffer");
+	gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthBuf, 0);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "framebufferTexture2D");
+	gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuf, 0);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "framebufferTexture2D");
+
+	if (!glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
+	{
+		gl.genTextures(1, &m_colorBufReadback);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "genTextures");
+		gl.bindTexture(GL_TEXTURE_2D, m_colorBufReadback);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "bindTexture");
+		gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 64, 64);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "texStorage2D");
+		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");
+		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");
+
+		gl.genFramebuffers(1, &m_fboReadback);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "genFramebuffers");
+		gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboReadback);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "bindFramebuffer");
+		gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBufReadback, 0);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "framebufferTexture2D");
+	}
+
+	gl.viewport(0, 0, 64, 64);
+}
+
+/** De-Initialization method that releases
+ */
+void PolygonOffsetClampValueTestCaseBase::deinit()
+{
+	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+
+	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "bindFramebuffer");
+
+	if (m_fbo)
+		gl.deleteFramebuffers(1, &m_fbo);
+	if (m_depthBuf)
+		gl.deleteTextures(1, &m_depthBuf);
+	if (m_colorBuf)
+		gl.deleteTextures(1, &m_colorBuf);
+
+	if (!glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
+	{
+		if (m_colorBufReadback)
+			gl.deleteTextures(1, &m_colorBufReadback);
+		if (m_fboReadback)
+			gl.deleteFramebuffers(1, &m_fboReadback);
+	}
+}
+
+/** Testing method that verifies if depth values generated after polygon offset clamp are as expected.
+ *
+ *  @param gl   Function bindings
+ */
+void PolygonOffsetClampValueTestCaseBase::test(const glw::Functions& gl)
+{
+	const GLfloat vertices[] = { -1.0f, -1.0f, 0.5f, -1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f };
+
+	// Prepare shader program
+	std::string vertexColor;
+	std::string fragmentColor;
+	if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
+		vertexColor = std::string(poc_shader_version_450core);
+	else
+		vertexColor = std::string(poc_shader_version_310es);
+	fragmentColor   = vertexColor;
+
+	vertexColor   = vertexColor + poc_vertexColor;
+	fragmentColor = fragmentColor + poc_fragmentColor;
+
+	ProgramSources testSources = makeVtxFragSources(vertexColor, fragmentColor);
+	ShaderProgram  testProgram(gl, testSources);
+
+	if (!testProgram.isOk())
+	{
+		m_testCtx.getLog() << tcu::TestLog::Message << "TestProgram build failed.\n"
+						   << "Vertex: " << testProgram.getShaderInfo(SHADERTYPE_VERTEX).infoLog << "\n"
+						   << "Fragment: " << testProgram.getShaderInfo(SHADERTYPE_FRAGMENT).infoLog << "\n"
+						   << "Program: " << testProgram.getProgramInfo().infoLog << tcu::TestLog::EndMessage;
+
+		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
+		return;
+	}
+
+	ShaderProgram* readDepthProgram   = DE_NULL;
+	GLuint		   readDepthProgramId = 0;
+
+	// Prepare shader program for reading depth buffer indirectly
+	if (!glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
+	{
+		std::string vertexTexture   = std::string(poc_shader_version_310es) + poc_vertexTexture;
+		std::string fragmentTexture = std::string(poc_shader_version_310es) + poc_fragmentTexture;
+
+		ProgramSources readDepthSources = makeVtxFragSources(vertexTexture, fragmentTexture);
+
+		readDepthProgram = new ShaderProgram(gl, readDepthSources);
+
+		if (!readDepthProgram->isOk())
+		{
+			m_testCtx.getLog() << tcu::TestLog::Message << "ReadDepthProgram build failed.\n"
+							   << "Vertex: " << readDepthProgram->getShaderInfo(SHADERTYPE_VERTEX).infoLog << "\n"
+							   << "Fragment: " << readDepthProgram->getShaderInfo(SHADERTYPE_FRAGMENT).infoLog << "\n"
+							   << "Program: " << readDepthProgram->getProgramInfo().infoLog << tcu::TestLog::EndMessage;
+
+			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
+			return;
+		}
+
+		readDepthProgramId = readDepthProgram->getProgram();
+	}
+
+	gl.useProgram(testProgram.getProgram());
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram");
+
+	GLuint vao;
+	GLuint arrayBuffer;
+
+	// Setup depth testing
+	gl.enable(GL_DEPTH_TEST);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable");
+
+	gl.depthFunc(GL_ALWAYS);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glDepthFunc");
+
+	// Generate vertex array object
+	gl.genVertexArrays(1, &vao);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays");
+
+	gl.bindVertexArray(vao);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray");
+
+	// Setup vertex array buffer
+	gl.genBuffers(1, &arrayBuffer);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers");
+
+	gl.bindBuffer(GL_ARRAY_BUFFER, arrayBuffer);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer");
+
+	gl.bufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData");
+
+	// Setup vertex attrib pointer
+	gl.enableVertexAttribArray(0);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray");
+
+	gl.vertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer");
+
+	// Bind framebuffer for drawing
+	gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+
+	bool result = true;
+	for (uint32_t i = 0; i < m_testValues.size(); ++i)
+	{
+		// Prepare verification variables
+		GLfloat depthValue			  = 0.0f;
+		GLfloat depthValueOffset	  = 0.0f;
+		GLfloat depthValueOffsetClamp = 0.0f;
+
+		// Draw reference polygon
+		gl.disable(GL_POLYGON_OFFSET_FILL);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable");
+
+		gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glClear");
+
+		gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays");
+
+		// Get reference depth value
+		depthValue = readDepthValue(gl, readDepthProgramId);
+
+		// Draw polygon with depth offset
+		gl.enable(GL_POLYGON_OFFSET_FILL);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable");
+
+		gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+
+		gl.polygonOffset(m_testValues[i].factor, m_testValues[i].units);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glPolygonOffset");
+
+		gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays");
+
+		depthValueOffset = readDepthValue(gl, readDepthProgramId);
+
+		// Draw reference polygon
+		gl.disable(GL_POLYGON_OFFSET_FILL);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable");
+
+		gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+
+		gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glClear");
+
+		gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays");
+
+		// Draw polygon with depth offset
+		gl.enable(GL_POLYGON_OFFSET_FILL);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable");
+
+		gl.polygonOffsetClampEXT(m_testValues[i].factor, m_testValues[i].units, m_testValues[i].clamp);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glPolygonOffsetClampEXT");
+
+		gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays");
+
+		depthValueOffsetClamp = readDepthValue(gl, readDepthProgramId);
+
+		// Verify results
+		result = result && verify(i, depthValue, depthValueOffset, depthValueOffsetClamp);
+	}
+
+	// Cleanup
+	gl.disableVertexAttribArray(0);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glDisableVertexAttribArray");
+
+	gl.deleteVertexArrays(1, &arrayBuffer);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteVertexArrays");
+
+	gl.deleteVertexArrays(1, &vao);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteVertexArrays");
+
+	gl.disable(GL_POLYGON_OFFSET_FILL);
+	GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable");
+
+	if (readDepthProgram)
+		delete readDepthProgram;
+
+	if (result)
+		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
+	else
+		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
+}
+
+/** Method .
+ *
+ *  @param gl   Function bindings
+ */
+float PolygonOffsetClampValueTestCaseBase::readDepthValue(const glw::Functions& gl, const GLuint readDepthProgramId)
+{
+	GLfloat depthValue = 0.0f;
+
+	if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
+	{
+		gl.readPixels(0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depthValue);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");
+	}
+	// OpenGL ES does not support reading pixels directly from depth buffer
+	else
+	{
+		// Bind framebuffer for readback
+		gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboReadback);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+
+		gl.disable(GL_DEPTH_TEST);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable");
+
+		gl.useProgram(readDepthProgramId);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram");
+
+		gl.activeTexture(GL_TEXTURE0);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glActiveTexture");
+		gl.bindTexture(GL_TEXTURE_2D, m_depthBuf);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture");
+		gl.uniform1i(0, 0);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i");
+
+		gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays");
+
+		GLubyte pixels[4];
+		gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");
+
+		gl.enable(GL_DEPTH_TEST);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable");
+
+		// Convert read depth value to GLfloat normalized
+		depthValue = (GLfloat)(pixels[0] + pixels[1] * 256) / 0xFFFF;
+
+		// Bind framebuffer for drawing
+		gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+	}
+
+	return depthValue;
+}
+
+/** Constructor.
+*
+*  @param context Rendering context
+*/
+PolygonOffsetClampMinMaxTestCase::PolygonOffsetClampMinMaxTestCase(deqp::Context& context)
+	: PolygonOffsetClampValueTestCaseBase(
+		  context, "PolygonOffsetClampMinMax",
+		  "Verifies if polygon offset clamp works as expected for non-zero, finite clamp values")
+{
+}
+
+/** Initialization method that fills polygonOffset* testing values
+ */
+void PolygonOffsetClampMinMaxTestCase::init()
+{
+	PolygonOffsetClampValueTestCaseBase::init();
+
+	m_testValues.clear();
+	m_testValues.push_back(PolygonOffsetClampValues(0.0f, -1000.0f, -0.0001f)); // Min offset case
+	m_testValues.push_back(PolygonOffsetClampValues(0.0f, 1000.0f, 0.0001f));   // Max offset case
+}
+
+/** Verification method that determines if depth values are as expected
+ *
+ *  @param caseNo           Case iteration number
+ *  @param depth            Reference depth value
+ *  @param offsetDepth      Case iteration number
+ *  @param offsetClampDepth Case iteration number
+ */
+bool PolygonOffsetClampMinMaxTestCase::verify(uint32_t caseNo, GLfloat depth, GLfloat offsetDepth,
+											  GLfloat offsetClampDepth)
+{
+	// Min offset case
+	if (caseNo == 0)
+	{
+		if (depth <= offsetDepth || depth <= offsetClampDepth || offsetDepth >= offsetClampDepth)
+		{
+			m_testCtx.getLog() << tcu::TestLog::Message << "PolygonOffsetClampEXT failed at MIN offset test.\n"
+							   << "Expected result: "
+							   << "refDepth[" << depth << "] > "
+							   << "offsetClampDepth[" << offsetClampDepth << "] > "
+							   << "offsetDepth[" << offsetDepth << "]" << tcu::TestLog::EndMessage;
+
+			return false;
+		}
+	}
+	// Max offset case
+	else if (caseNo == 1)
+	{
+		if (depth >= offsetDepth || depth >= offsetClampDepth || offsetDepth <= offsetClampDepth)
+		{
+			m_testCtx.getLog() << tcu::TestLog::Message << "PolygonOffsetClampEXT failed at MAX offset test.\n"
+							   << "Expected result: "
+							   << "refDepth[" << depth << "] < "
+							   << "offsetClampDepth[" << offsetClampDepth << "] < "
+							   << "offsetDepth[" << offsetDepth << "]" << tcu::TestLog::EndMessage;
+
+			return false;
+		}
+	}
+	// Undefined case
+	else
+		return false;
+
+	return true;
+}
+
+/** Constructor.
+*
+*  @param context Rendering context
+*/
+PolygonOffsetClampZeroInfinityTestCase::PolygonOffsetClampZeroInfinityTestCase(deqp::Context& context)
+	: PolygonOffsetClampValueTestCaseBase(
+		  context, "PolygonOffsetClampZeroInfinity",
+		  "Verifies if polygon offset clamp works as expected for zero and infinite clamp values")
+{
+}
+
+/** Initialization method that fills polygonOffset* testing values
+ */
+void PolygonOffsetClampZeroInfinityTestCase::init()
+{
+	PolygonOffsetClampValueTestCaseBase::init();
+
+	m_testValues.clear();
+	m_testValues.push_back(PolygonOffsetClampValues(0.0f, -1000.0f, 0.0f));		 // Min offset, zero clamp case
+	m_testValues.push_back(PolygonOffsetClampValues(0.0f, -1000.0f, -INFINITY)); // Min Offset, infinity clamp case
+	m_testValues.push_back(PolygonOffsetClampValues(0.0f, 1000.0f, 0.0f));		 // Max offset, zero clamp case
+	m_testValues.push_back(PolygonOffsetClampValues(0.0f, 1000.0f, INFINITY));   // Max Offset, infinity clamp case
+}
+
+bool PolygonOffsetClampZeroInfinityTestCase::verify(uint32_t caseNo, GLfloat depth, GLfloat offsetDepth,
+													GLfloat offsetClampDepth)
+{
+	DE_UNREF(caseNo);
+
+	if (depth == offsetDepth || depth == offsetClampDepth || offsetDepth != offsetClampDepth)
+	{
+		m_testCtx.getLog() << tcu::TestLog::Message
+						   << "PolygonOffsetClampEXT failed at Zero/Infinity offset clamp test.\n"
+						   << "Expected result: "
+						   << "refDepth[" << depth << "] != "
+						   << "(offsetClampDepth[" << offsetClampDepth << "] == "
+						   << "offsetDepth[" << offsetDepth << "])" << tcu::TestLog::EndMessage;
+
+		return false;
+	}
+
+	return true;
+}
+
+/** Constructor.
+*
+*  @param context Rendering context.
+*/
+PolygonOffsetClamp::PolygonOffsetClamp(deqp::Context& context)
+	: TestCaseGroup(context, "polygon_offset_clamp",
+					"Verify conformance of CTS_EXT_polygon_offset_clamp implementation")
+{
+}
+
+/** Initializes the test group contents. */
+void PolygonOffsetClamp::init()
+{
+	addChild(new PolygonOffsetClampAvailabilityTestCase(m_context));
+	addChild(new PolygonOffsetClampMinMaxTestCase(m_context));
+	addChild(new PolygonOffsetClampZeroInfinityTestCase(m_context));
+}
+} /* glcts namespace */
diff --git a/external/openglcts/modules/common/glcPolygonOffsetClampTests.hpp b/external/openglcts/modules/common/glcPolygonOffsetClampTests.hpp
new file mode 100644
index 0000000..999957d
--- /dev/null
+++ b/external/openglcts/modules/common/glcPolygonOffsetClampTests.hpp
@@ -0,0 +1,153 @@
+#ifndef _GLCPOLYGONOFFSETCLAMPTESTS_HPP
+#define _GLCPOLYGONOFFSETCLAMPTESTS_HPP
+/*-------------------------------------------------------------------------
+* OpenGL Conformance Test Suite
+* -----------------------------
+*
+* Copyright (c) 2017 The Khronos Group Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/ /*!
+* \file  glcPolygonOffsetClampTests.hpp
+* \brief Conformance tests for the EXT_polygon_offset_clamp functionality.
+*/ /*-------------------------------------------------------------------*/
+
+#include "esextcTestCaseBase.hpp"
+#include "glcTestCase.hpp"
+#include "gluShaderProgram.hpp"
+
+#include <string>
+
+using namespace glw;
+
+namespace glcts
+{
+
+struct PolygonOffsetClampValues
+{
+	GLfloat factor;
+	GLfloat units;
+	GLfloat clamp;
+
+	PolygonOffsetClampValues(GLfloat _f, GLfloat _u, GLfloat _c) : factor(_f), units(_u), clamp(_c)
+	{
+	}
+};
+
+/** Tests base class
+**/
+class PolygonOffsetClampTestCaseBase : public deqp::TestCase
+{
+public:
+	/* Public methods */
+	PolygonOffsetClampTestCaseBase(deqp::Context& context, const char* name, const char* description);
+
+	virtual tcu::TestNode::IterateResult iterate();
+
+protected:
+	/* Protected methods */
+	virtual void test(const glw::Functions& gl) = DE_NULL;
+
+	/* Protected members */
+	bool m_extensionSupported;
+};
+
+/** Test verifies if polygon offset clamp works as expected for non-zero, finite clamp values
+**/
+class PolygonOffsetClampAvailabilityTestCase : public PolygonOffsetClampTestCaseBase
+{
+public:
+	/* Public methods */
+	PolygonOffsetClampAvailabilityTestCase(deqp::Context& context);
+
+protected:
+	/* Protected methods */
+	void test(const glw::Functions& gl);
+};
+
+/** Base class for polygon offset clamp depth values verifying
+**/
+class PolygonOffsetClampValueTestCaseBase : public PolygonOffsetClampTestCaseBase
+{
+public:
+	/* Public methods */
+	PolygonOffsetClampValueTestCaseBase(deqp::Context& context, const char* name, const char* description);
+
+	virtual void init();
+	virtual void deinit();
+
+protected:
+	/* Protected members */
+	GLuint m_fbo;
+	GLuint m_depthBuf;
+	GLuint m_colorBuf;
+	GLuint m_fboReadback;
+	GLuint m_colorBufReadback;
+
+	std::vector<PolygonOffsetClampValues> m_testValues;
+
+	/* Protected methods */
+	void test(const glw::Functions& gl);
+
+	float readDepthValue(const glw::Functions& gl, const GLuint readDepthProgramId);
+
+	virtual bool verify(uint32_t caseNo, GLfloat depth, GLfloat offsetDepth, GLfloat offsetClampDepth) = DE_NULL;
+};
+
+/** Test verifies if polygon offset clamp works as expected for zero and infinite clamp values
+**/
+class PolygonOffsetClampMinMaxTestCase : public PolygonOffsetClampValueTestCaseBase
+{
+public:
+	/* Public methods */
+	PolygonOffsetClampMinMaxTestCase(deqp::Context& context);
+
+	void init();
+
+protected:
+	/* Protected methods */
+	bool verify(uint32_t caseNo, GLfloat depth, GLfloat offsetDepth, GLfloat offsetClampDepth);
+};
+
+/** Test verifies ...
+**/
+class PolygonOffsetClampZeroInfinityTestCase : public PolygonOffsetClampValueTestCaseBase
+{
+public:
+	/* Public methods */
+	PolygonOffsetClampZeroInfinityTestCase(deqp::Context& context);
+
+	void init();
+
+protected:
+	/* Protected methods */
+	bool verify(uint32_t caseNo, GLfloat depth, GLfloat offsetDepth, GLfloat offsetClampDepth);
+};
+
+/** Test group which encapsulates all ARB_shader_group_vote conformance tests */
+class PolygonOffsetClamp : public deqp::TestCaseGroup
+{
+public:
+	/* Public methods */
+	PolygonOffsetClamp(deqp::Context& context);
+
+	void init();
+
+private:
+	PolygonOffsetClamp(const PolygonOffsetClamp& other);
+};
+
+} /* glcts namespace */
+
+#endif // _GLCPOLYGONOFFSETCLAMPTESTS_HPP
diff --git a/external/openglcts/modules/gl/gl4cTestPackages.cpp b/external/openglcts/modules/gl/gl4cTestPackages.cpp
index 904b68c..3415dd3 100644
--- a/external/openglcts/modules/gl/gl4cTestPackages.cpp
+++ b/external/openglcts/modules/gl/gl4cTestPackages.cpp
@@ -71,6 +71,7 @@
 #include "glcBlendEquationAdvancedTests.hpp"
 #include "glcExposedExtensionsTests.hpp"
 #include "glcInfoTests.hpp"
+#include "glcPolygonOffsetClampTests.hpp"
 #include "glcRobustBufferAccessBehaviorTests.hpp"
 #include "glcSampleVariablesTests.hpp"
 #include "glcShaderIntegerMixTests.hpp"
@@ -364,6 +365,7 @@
 		addChild(new gl4cts::ShaderDrawParametersTests(getContext()));
 		addChild(new gl4cts::ShaderViewportLayerArray(getContext()));
 		addChild(new gl4cts::LimitsTests(getContext()));
+		addChild(new glcts::PolygonOffsetClamp(getContext()));
 	}
 	catch (...)
 	{
diff --git a/external/openglcts/modules/gles31/es31cTestPackage.cpp b/external/openglcts/modules/gles31/es31cTestPackage.cpp
index 65fd006..c197b29 100644
--- a/external/openglcts/modules/gles31/es31cTestPackage.cpp
+++ b/external/openglcts/modules/gles31/es31cTestPackage.cpp
@@ -44,6 +44,7 @@
 #include "es31cVertexAttribBindingTests.hpp"
 #include "glcBlendEquationAdvancedTests.hpp"
 #include "glcInfoTests.hpp"
+#include "glcPolygonOffsetClampTests.hpp"
 #include "glcSampleVariablesTests.hpp"
 #include "glcShaderIntegerMixTests.hpp"
 #include "glcShaderMultisampleInterpolationTests.hpp"
@@ -187,6 +188,7 @@
 		coreGroup->addChild(new glcts::ProgramInterfaceQueryTests(getContext()));
 		coreGroup->addChild(new glcts::FramebufferNoAttachmentsTests(getContext()));
 		coreGroup->addChild(new glcts::ArrayOfArraysTestGroup(getContext()));
+		coreGroup->addChild(new glcts::PolygonOffsetClamp(getContext()));
 
 		glcts::ExtParameters extParams(glu::GLSL_VERSION_310_ES, glcts::EXTENSIONTYPE_OES);
 		coreGroup->addChild(new glcts::GeometryShaderTests(getContext(), extParams));
diff --git a/framework/opengl/gluCallLogWrapper.inl b/framework/opengl/gluCallLogWrapper.inl
index 9956c79..e9ca3cd 100644
--- a/framework/opengl/gluCallLogWrapper.inl
+++ b/framework/opengl/gluCallLogWrapper.inl
@@ -4047,6 +4047,13 @@
 	m_gl.polygonOffset(factor, units);
 }
 
+void CallLogWrapper::glPolygonOffsetClampEXT (glw::GLfloat factor, glw::GLfloat units, glw::GLfloat clamp)
+{
+	if (m_enableLog)
+		m_log << TestLog::Message << "glPolygonOffsetClampEXT(" << factor << ", " << units << ", " << clamp << ");" << TestLog::EndMessage;
+	m_gl.polygonOffsetClampEXT(factor, units, clamp);
+}
+
 void CallLogWrapper::glPopDebugGroup (void)
 {
 	if (m_enableLog)
diff --git a/framework/opengl/gluCallLogWrapperApi.inl b/framework/opengl/gluCallLogWrapperApi.inl
index 9911fe4..fa824f5 100644
--- a/framework/opengl/gluCallLogWrapperApi.inl
+++ b/framework/opengl/gluCallLogWrapperApi.inl
@@ -547,6 +547,7 @@
 void					glPointSize											(glw::GLfloat size);
 void					glPolygonMode										(glw::GLenum face, glw::GLenum mode);
 void					glPolygonOffset										(glw::GLfloat factor, glw::GLfloat units);
+void					glPolygonOffsetClampEXT								(glw::GLfloat factor, glw::GLfloat units, glw::GLfloat clamp);
 void					glPopDebugGroup										(void);
 void					glPopGroupMarkerEXT									(void);
 void					glPrimitiveBoundingBox								(glw::GLfloat minX, glw::GLfloat minY, glw::GLfloat minZ, glw::GLfloat minW, glw::GLfloat maxX, glw::GLfloat maxY, glw::GLfloat maxZ, glw::GLfloat maxW);
diff --git a/framework/opengl/wrapper/glwApi.inl b/framework/opengl/wrapper/glwApi.inl
index 086fafc..1000cf0 100644
--- a/framework/opengl/wrapper/glwApi.inl
+++ b/framework/opengl/wrapper/glwApi.inl
@@ -547,6 +547,7 @@
 #define			glPointSize											glwPointSize
 #define			glPolygonMode										glwPolygonMode
 #define			glPolygonOffset										glwPolygonOffset
+#define			glPolygonOffsetClampEXT								glwPolygonOffsetClampEXT
 #define			glPopDebugGroup										glwPopDebugGroup
 #define			glPopGroupMarkerEXT									glwPopGroupMarkerEXT
 #define			glPrimitiveBoundingBox								glwPrimitiveBoundingBox
@@ -1435,6 +1436,7 @@
 void			glwPointSize										(GLfloat size);
 void			glwPolygonMode										(GLenum face, GLenum mode);
 void			glwPolygonOffset									(GLfloat factor, GLfloat units);
+void			glwPolygonOffsetClampEXT							(GLfloat factor, GLfloat units, GLfloat clamp);
 void			glwPopDebugGroup									();
 void			glwPopGroupMarkerEXT								();
 void			glwPrimitiveBoundingBox								(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW);
diff --git a/framework/opengl/wrapper/glwEnums.inl b/framework/opengl/wrapper/glwEnums.inl
index 758b691..933ea17 100644
--- a/framework/opengl/wrapper/glwEnums.inl
+++ b/framework/opengl/wrapper/glwEnums.inl
@@ -1198,6 +1198,7 @@
 #define GL_QUERY_NO_WAIT_INVERTED										0x8E18
 #define GL_QUERY_BY_REGION_WAIT_INVERTED								0x8E19
 #define GL_QUERY_BY_REGION_NO_WAIT_INVERTED								0x8E1A
+#define GL_POLYGON_OFFSET_CLAMP_EXT										0x8E1B
 #define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS					0x8E1E
 #define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT				0x8E1E
 #define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS				0x8E1F
diff --git a/framework/opengl/wrapper/glwFunctionTypes.inl b/framework/opengl/wrapper/glwFunctionTypes.inl
index e3832c6..9aedbc2 100644
--- a/framework/opengl/wrapper/glwFunctionTypes.inl
+++ b/framework/opengl/wrapper/glwFunctionTypes.inl
@@ -547,6 +547,7 @@
 typedef GLW_APICALL void			(GLW_APIENTRY* glPointSizeFunc)											(GLfloat size);
 typedef GLW_APICALL void			(GLW_APIENTRY* glPolygonModeFunc)										(GLenum face, GLenum mode);
 typedef GLW_APICALL void			(GLW_APIENTRY* glPolygonOffsetFunc)										(GLfloat factor, GLfloat units);
+typedef GLW_APICALL void			(GLW_APIENTRY* glPolygonOffsetClampEXTFunc)								(GLfloat factor, GLfloat units, GLfloat clamp);
 typedef GLW_APICALL void			(GLW_APIENTRY* glPopDebugGroupFunc)										(void);
 typedef GLW_APICALL void			(GLW_APIENTRY* glPopGroupMarkerEXTFunc)									(void);
 typedef GLW_APICALL void			(GLW_APIENTRY* glPrimitiveBoundingBoxFunc)								(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW);
diff --git a/framework/opengl/wrapper/glwFunctions.inl b/framework/opengl/wrapper/glwFunctions.inl
index 996752d..2ae9a2b 100644
--- a/framework/opengl/wrapper/glwFunctions.inl
+++ b/framework/opengl/wrapper/glwFunctions.inl
@@ -547,6 +547,7 @@
 glPointSizeFunc											pointSize;
 glPolygonModeFunc										polygonMode;
 glPolygonOffsetFunc										polygonOffset;
+glPolygonOffsetClampEXTFunc								polygonOffsetClampEXT;
 glPopDebugGroupFunc										popDebugGroup;
 glPopGroupMarkerEXTFunc									popGroupMarkerEXT;
 glPrimitiveBoundingBoxFunc								primitiveBoundingBox;
diff --git a/framework/opengl/wrapper/glwImpl.inl b/framework/opengl/wrapper/glwImpl.inl
index 86a6b84..fecc729 100644
--- a/framework/opengl/wrapper/glwImpl.inl
+++ b/framework/opengl/wrapper/glwImpl.inl
@@ -4356,6 +4356,14 @@
 	gl->polygonOffset(factor, units);
 }
 
+void glwPolygonOffsetClampEXT (GLfloat factor, GLfloat units, GLfloat clamp)
+{
+	const glw::Functions* gl = glw::getCurrentThreadFunctions();
+	if (!gl)
+		return;
+	gl->polygonOffsetClampEXT(factor, units, clamp);
+}
+
 void glwPopDebugGroup (void)
 {
 	const glw::Functions* gl = glw::getCurrentThreadFunctions();
diff --git a/framework/opengl/wrapper/glwInitExtES.inl b/framework/opengl/wrapper/glwInitExtES.inl
index bdfa5f1..df6c1e4 100644
--- a/framework/opengl/wrapper/glwInitExtES.inl
+++ b/framework/opengl/wrapper/glwInitExtES.inl
@@ -116,6 +116,11 @@
 	gl->pushGroupMarkerEXT		= (glPushGroupMarkerEXTFunc)	loader->get("glPushGroupMarkerEXT");
 }
 
+if (de::contains(extSet, "GL_EXT_polygon_offset_clamp"))
+{
+	gl->polygonOffsetClampEXT	= (glPolygonOffsetClampEXTFunc)	loader->get("glPolygonOffsetClampEXT");
+}
+
 if (de::contains(extSet, "GL_OES_EGL_image"))
 {
 	gl->eglImageTargetRenderbufferStorageOES	= (glEGLImageTargetRenderbufferStorageOESFunc)	loader->get("glEGLImageTargetRenderbufferStorageOES");
diff --git a/framework/opengl/wrapper/glwInitExtGL.inl b/framework/opengl/wrapper/glwInitExtGL.inl
index 2ed48f0..1da2c34 100644
--- a/framework/opengl/wrapper/glwInitExtGL.inl
+++ b/framework/opengl/wrapper/glwInitExtGL.inl
@@ -557,6 +557,11 @@
 	gl->pushGroupMarkerEXT		= (glPushGroupMarkerEXTFunc)	loader->get("glPushGroupMarkerEXT");
 }
 
+if (de::contains(extSet, "GL_EXT_polygon_offset_clamp"))
+{
+	gl->polygonOffsetClampEXT	= (glPolygonOffsetClampEXTFunc)	loader->get("glPolygonOffsetClampEXT");
+}
+
 if (de::contains(extSet, "GL_ARB_clip_control"))
 {
 	gl->clipControl	= (glClipControlFunc)	loader->get("glClipControl");
diff --git a/framework/platform/null/tcuNullRenderContextFuncs.inl b/framework/platform/null/tcuNullRenderContextFuncs.inl
index 08f15c6..cb470f2 100644
--- a/framework/platform/null/tcuNullRenderContextFuncs.inl
+++ b/framework/platform/null/tcuNullRenderContextFuncs.inl
@@ -4669,6 +4669,14 @@
 
 }
 
+GLW_APICALL void GLW_APIENTRY glPolygonOffsetClampEXT (GLfloat factor, GLfloat units, GLfloat clamp)
+{
+	DE_UNREF(factor);
+	DE_UNREF(units);
+	DE_UNREF(clamp);
+
+}
+
 GLW_APICALL void GLW_APIENTRY glPopDebugGroup (void)
 {
 
diff --git a/framework/platform/null/tcuNullRenderContextInitFuncs.inl b/framework/platform/null/tcuNullRenderContextInitFuncs.inl
index a4ed40f..1ce3664 100644
--- a/framework/platform/null/tcuNullRenderContextInitFuncs.inl
+++ b/framework/platform/null/tcuNullRenderContextInitFuncs.inl
@@ -547,6 +547,7 @@
 gl->pointSize										= glPointSize;
 gl->polygonMode										= glPolygonMode;
 gl->polygonOffset									= glPolygonOffset;
+gl->polygonOffsetClampEXT							= glPolygonOffsetClampEXT;
 gl->popDebugGroup									= glPopDebugGroup;
 gl->popGroupMarkerEXT								= glPopGroupMarkerEXT;
 gl->primitiveBoundingBox							= glPrimitiveBoundingBox;
diff --git a/scripts/opengl/src_util.py b/scripts/opengl/src_util.py
index 56df340..a74b3a2 100644
--- a/scripts/opengl/src_util.py
+++ b/scripts/opengl/src_util.py
@@ -73,6 +73,7 @@
 	'GL_EXT_texture_sRGB_R8',
 	'GL_EXT_texture_sRGB_RG8',
 	'GL_EXT_debug_marker',
+	'GL_EXT_polygon_offset_clamp',
 	'GL_IMG_texture_compression_pvrtc',
 	'GL_OES_EGL_image',
 	'GL_OES_EGL_image_external',