Commit e697e379 by Mohan Maiya Committed by Angle LUCI CQ

Vulkan: Allow certain usecases to have non-zero stride

When glVertexAttribPointer is called with a type parameter that doesn't match the vertex attribute binding's type in shader, the vulkan backend used to force the stride to 0. This is acceptable since this usecase is unspecified in spec. To allow for better app compatibility, introduce a new extension that requires normal glVertexAttribPointer functionality to be maintained if the mismatched vertex attribute type is a mismatched integer type sign. This change also modifies the VkFormat used when a mismatch in signedness occurs to use a VkFormat with the same component width as the type parameter. Bug: angleproject:5762 Test: VertexAttributeTestES3.DrawWithRelaxedVertexAttributeType* Test: VertexAttributeTestES3.DrawWithMismatchedComponentCount* Change-Id: I7e5281500afc3d77f0775821447cabfad3ff2d66 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2765012 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 1f1e0e6c
Name
ANGLE_relaxed_vertex_attribute_type
Name Strings
GL_ANGLE_relaxed_vertex_attribute_type
Contributors
Jamie Madill, Google
Kevin Jung, Samsung
Contact
Jamie Madill (jmadill 'at' google.com)
Notice
Copyright (c) 2021 The Khronos Group Inc. Copyright terms at
http://www.khronos.org/registry/speccopyright.html
Status
Draft
Version
Version 1, March 25, 2021
Number
OpenGL ES Extension #??
Dependencies
Requires OpenGL ES 3.0
Written against the OpenGL ES 3.0 specification.
Overview
This extension defines behavior when the type parameter of
VertexAttribPointer has mismatched integer signedness with
the shader's attribute.
New Procedures and Functions
None
New Tokens
None
Additions to the OpenGL ES Specification
Add after paragraph 6 of section 2.9 "Vertex Arrays" (beginning with
"When values for a vertex shader attribute variable are sourced..."):
If the generic attribute is specified by VertexAttribIPointer with an
unsigned type for base signed integer type attributes, or by a signed
type for base unsigned integer type attributes, the values loaded
into the shader attribute are reinterpreted as the corresponding
signed/unsigned type.
New State
None
Conformance Tests
TBD
Issues
None
Revision History
Rev. Date Author Changes
---- ------------- --------- ----------------------------------------
1 Mar 25, 2021 jmadill Initial version
...@@ -1083,6 +1083,7 @@ const ExtensionInfoMap &GetExtensionInfoMap() ...@@ -1083,6 +1083,7 @@ const ExtensionInfoMap &GetExtensionInfoMap()
map["GL_EXT_clip_cull_distance"] = enableableExtension(&Extensions::clipCullDistanceEXT); map["GL_EXT_clip_cull_distance"] = enableableExtension(&Extensions::clipCullDistanceEXT);
map["GL_ANGLE_get_serialized_context_string"] = esOnlyExtension(&Extensions::getSerializedContextStringANGLE); map["GL_ANGLE_get_serialized_context_string"] = esOnlyExtension(&Extensions::getSerializedContextStringANGLE);
map["GL_EXT_primitive_bounding_box"] = esOnlyExtension(&Extensions::primitiveBoundingBoxEXT); map["GL_EXT_primitive_bounding_box"] = esOnlyExtension(&Extensions::primitiveBoundingBoxEXT);
map["GL_ANGLE_relaxed_vertex_attribute_type"] = esOnlyExtension(&Extensions::relaxedVertexAttributeTypeANGLE);
// GLES1 extensions // GLES1 extensions
map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArrayOES); map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArrayOES);
map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMapOES); map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMapOES);
......
...@@ -737,6 +737,9 @@ struct Extensions ...@@ -737,6 +737,9 @@ struct Extensions
// GL_EXT_primitive_bounding_box // GL_EXT_primitive_bounding_box
bool primitiveBoundingBoxEXT = false; bool primitiveBoundingBoxEXT = false;
// GL_ANGLE_relaxed_vertex_attribute_type
bool relaxedVertexAttributeTypeANGLE = false;
}; };
// Pointer to a boolean memeber of the Extensions struct // Pointer to a boolean memeber of the Extensions struct
......
...@@ -2597,6 +2597,182 @@ size_t GetVertexFormatSize(angle::FormatID vertexFormatID) ...@@ -2597,6 +2597,182 @@ size_t GetVertexFormatSize(angle::FormatID vertexFormatID)
} }
} }
angle::FormatID ConvertFormatSignedness(const angle::Format &format)
{
switch (format.id)
{
// 1 byte signed to unsigned
case angle::FormatID::R8_SINT:
return angle::FormatID::R8_UINT;
case angle::FormatID::R8_SNORM:
return angle::FormatID::R8_UNORM;
case angle::FormatID::R8_SSCALED:
return angle::FormatID::R8_USCALED;
case angle::FormatID::R8G8_SINT:
return angle::FormatID::R8G8_UINT;
case angle::FormatID::R8G8_SNORM:
return angle::FormatID::R8G8_UNORM;
case angle::FormatID::R8G8_SSCALED:
return angle::FormatID::R8G8_USCALED;
case angle::FormatID::R8G8B8_SINT:
return angle::FormatID::R8G8B8_UINT;
case angle::FormatID::R8G8B8_SNORM:
return angle::FormatID::R8G8B8_UNORM;
case angle::FormatID::R8G8B8_SSCALED:
return angle::FormatID::R8G8B8_USCALED;
case angle::FormatID::R8G8B8A8_SINT:
return angle::FormatID::R8G8B8A8_UINT;
case angle::FormatID::R8G8B8A8_SNORM:
return angle::FormatID::R8G8B8A8_UNORM;
case angle::FormatID::R8G8B8A8_SSCALED:
return angle::FormatID::R8G8B8A8_USCALED;
// 1 byte unsigned to signed
case angle::FormatID::R8_UINT:
return angle::FormatID::R8_SINT;
case angle::FormatID::R8_UNORM:
return angle::FormatID::R8_SNORM;
case angle::FormatID::R8_USCALED:
return angle::FormatID::R8_SSCALED;
case angle::FormatID::R8G8_UINT:
return angle::FormatID::R8G8_SINT;
case angle::FormatID::R8G8_UNORM:
return angle::FormatID::R8G8_SNORM;
case angle::FormatID::R8G8_USCALED:
return angle::FormatID::R8G8_SSCALED;
case angle::FormatID::R8G8B8_UINT:
return angle::FormatID::R8G8B8_SINT;
case angle::FormatID::R8G8B8_UNORM:
return angle::FormatID::R8G8B8_SNORM;
case angle::FormatID::R8G8B8_USCALED:
return angle::FormatID::R8G8B8_SSCALED;
case angle::FormatID::R8G8B8A8_UINT:
return angle::FormatID::R8G8B8A8_SINT;
case angle::FormatID::R8G8B8A8_UNORM:
return angle::FormatID::R8G8B8A8_SNORM;
case angle::FormatID::R8G8B8A8_USCALED:
return angle::FormatID::R8G8B8A8_SSCALED;
// 2 byte signed to unsigned
case angle::FormatID::R16_SINT:
return angle::FormatID::R16_UINT;
case angle::FormatID::R16_SNORM:
return angle::FormatID::R16_UNORM;
case angle::FormatID::R16_SSCALED:
return angle::FormatID::R16_USCALED;
case angle::FormatID::R16G16_SINT:
return angle::FormatID::R16G16_UINT;
case angle::FormatID::R16G16_SNORM:
return angle::FormatID::R16G16_UNORM;
case angle::FormatID::R16G16_SSCALED:
return angle::FormatID::R16G16_USCALED;
case angle::FormatID::R16G16B16_SINT:
return angle::FormatID::R16G16B16_UINT;
case angle::FormatID::R16G16B16_SNORM:
return angle::FormatID::R16G16B16_UNORM;
case angle::FormatID::R16G16B16_SSCALED:
return angle::FormatID::R16G16B16_USCALED;
case angle::FormatID::R16G16B16A16_SINT:
return angle::FormatID::R16G16B16A16_UINT;
case angle::FormatID::R16G16B16A16_SNORM:
return angle::FormatID::R16G16B16A16_UNORM;
case angle::FormatID::R16G16B16A16_SSCALED:
return angle::FormatID::R16G16B16A16_USCALED;
// 2 byte unsigned to signed
case angle::FormatID::R16_UINT:
return angle::FormatID::R16_SINT;
case angle::FormatID::R16_UNORM:
return angle::FormatID::R16_SNORM;
case angle::FormatID::R16_USCALED:
return angle::FormatID::R16_SSCALED;
case angle::FormatID::R16G16_UINT:
return angle::FormatID::R16G16_SINT;
case angle::FormatID::R16G16_UNORM:
return angle::FormatID::R16G16_SNORM;
case angle::FormatID::R16G16_USCALED:
return angle::FormatID::R16G16_SSCALED;
case angle::FormatID::R16G16B16_UINT:
return angle::FormatID::R16G16B16_SINT;
case angle::FormatID::R16G16B16_UNORM:
return angle::FormatID::R16G16B16_SNORM;
case angle::FormatID::R16G16B16_USCALED:
return angle::FormatID::R16G16B16_SSCALED;
case angle::FormatID::R16G16B16A16_UINT:
return angle::FormatID::R16G16B16A16_SINT;
case angle::FormatID::R16G16B16A16_UNORM:
return angle::FormatID::R16G16B16A16_SNORM;
case angle::FormatID::R16G16B16A16_USCALED:
return angle::FormatID::R16G16B16A16_SSCALED;
// 4 byte signed to unsigned
case angle::FormatID::R32_SINT:
return angle::FormatID::R32_UINT;
case angle::FormatID::R32_SNORM:
return angle::FormatID::R32_UNORM;
case angle::FormatID::R32_SSCALED:
return angle::FormatID::R32_USCALED;
case angle::FormatID::R32G32_SINT:
return angle::FormatID::R32G32_UINT;
case angle::FormatID::R32G32_SNORM:
return angle::FormatID::R32G32_UNORM;
case angle::FormatID::R32G32_SSCALED:
return angle::FormatID::R32G32_USCALED;
case angle::FormatID::R32G32B32_SINT:
return angle::FormatID::R32G32B32_UINT;
case angle::FormatID::R32G32B32_SNORM:
return angle::FormatID::R32G32B32_UNORM;
case angle::FormatID::R32G32B32_SSCALED:
return angle::FormatID::R32G32B32_USCALED;
case angle::FormatID::R32G32B32A32_SINT:
return angle::FormatID::R32G32B32A32_UINT;
case angle::FormatID::R32G32B32A32_SNORM:
return angle::FormatID::R32G32B32A32_UNORM;
case angle::FormatID::R32G32B32A32_SSCALED:
return angle::FormatID::R32G32B32A32_USCALED;
// 4 byte unsigned to signed
case angle::FormatID::R32_UINT:
return angle::FormatID::R32_SINT;
case angle::FormatID::R32_UNORM:
return angle::FormatID::R32_SNORM;
case angle::FormatID::R32_USCALED:
return angle::FormatID::R32_SSCALED;
case angle::FormatID::R32G32_UINT:
return angle::FormatID::R32G32_SINT;
case angle::FormatID::R32G32_UNORM:
return angle::FormatID::R32G32_SNORM;
case angle::FormatID::R32G32_USCALED:
return angle::FormatID::R32G32_SSCALED;
case angle::FormatID::R32G32B32_UINT:
return angle::FormatID::R32G32B32_SINT;
case angle::FormatID::R32G32B32_UNORM:
return angle::FormatID::R32G32B32_SNORM;
case angle::FormatID::R32G32B32_USCALED:
return angle::FormatID::R32G32B32_SSCALED;
case angle::FormatID::R32G32B32A32_UINT:
return angle::FormatID::R32G32B32A32_SINT;
case angle::FormatID::R32G32B32A32_UNORM:
return angle::FormatID::R32G32B32A32_SNORM;
case angle::FormatID::R32G32B32A32_USCALED:
return angle::FormatID::R32G32B32A32_SSCALED;
// 1010102 signed to unsigned
case angle::FormatID::R10G10B10A2_SINT:
return angle::FormatID::R10G10B10A2_UINT;
case angle::FormatID::R10G10B10A2_SSCALED:
return angle::FormatID::R10G10B10A2_USCALED;
case angle::FormatID::R10G10B10A2_SNORM:
return angle::FormatID::R10G10B10A2_UNORM;
// 1010102 unsigned to signed
case angle::FormatID::R10G10B10A2_UINT:
return angle::FormatID::R10G10B10A2_SINT;
case angle::FormatID::R10G10B10A2_USCALED:
return angle::FormatID::R10G10B10A2_SSCALED;
case angle::FormatID::R10G10B10A2_UNORM:
return angle::FormatID::R10G10B10A2_SNORM;
default:
UNREACHABLE();
}
#if !UNREACHABLE_IS_NORETURN
return angle::FormatID::NONE;
#endif
}
bool ValidES3InternalFormat(GLenum internalFormat) bool ValidES3InternalFormat(GLenum internalFormat)
{ {
const InternalFormatInfoMap &formatMap = GetInternalFormatMap(); const InternalFormatInfoMap &formatMap = GetInternalFormatMap();
......
...@@ -354,6 +354,7 @@ angle::FormatID GetVertexFormatID(const VertexAttribute &attrib, VertexAttribTyp ...@@ -354,6 +354,7 @@ angle::FormatID GetVertexFormatID(const VertexAttribute &attrib, VertexAttribTyp
angle::FormatID GetCurrentValueFormatID(VertexAttribType currentValueType); angle::FormatID GetCurrentValueFormatID(VertexAttribType currentValueType);
const VertexFormat &GetVertexFormatFromID(angle::FormatID vertexFormatID); const VertexFormat &GetVertexFormatFromID(angle::FormatID vertexFormatID);
size_t GetVertexFormatSize(angle::FormatID vertexFormatID); size_t GetVertexFormatSize(angle::FormatID vertexFormatID);
angle::FormatID ConvertFormatSignedness(const angle::Format &format);
ANGLE_INLINE bool IsS3TCFormat(const GLenum format) ANGLE_INLINE bool IsS3TCFormat(const GLenum format)
{ {
......
...@@ -1843,13 +1843,46 @@ angle::Result GraphicsPipelineDesc::initializePipeline( ...@@ -1843,13 +1843,46 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
gl::ComponentType programAttribType = gl::ComponentType programAttribType =
gl::GetComponentTypeMask(programAttribsTypeMask, attribIndex); gl::GetComponentTypeMask(programAttribsTypeMask, attribIndex);
// This forces stride to 0 when glVertexAttribute specifies a different type from the
// program's attribute type except when the type mismatch is a mismatched integer sign.
if (attribType != programAttribType) if (attribType != programAttribType)
{ {
// Override the format with a compatible one. if (attribType == gl::ComponentType::Float ||
programAttribType == gl::ComponentType::Float)
{
// When dealing with float to int or unsigned int or vice versa, just override the
// format with a compatible one.
vkFormat = kMismatchedComponentTypeMap[programAttribType]; vkFormat = kMismatchedComponentTypeMap[programAttribType];
}
else
{
// When converting from an unsigned to a signed format or vice versa, attempt to
// match the bit width.
angle::FormatID convertedFormatID = gl::ConvertFormatSignedness(angleFormat);
const Format &convertedFormat =
contextVk->getRenderer()->getFormat(convertedFormatID);
ASSERT(angleFormat.channelCount == convertedFormat.intendedFormat().channelCount);
ASSERT(angleFormat.redBits == convertedFormat.intendedFormat().redBits);
ASSERT(angleFormat.greenBits == convertedFormat.intendedFormat().greenBits);
ASSERT(angleFormat.blueBits == convertedFormat.intendedFormat().blueBits);
ASSERT(angleFormat.alphaBits == convertedFormat.intendedFormat().alphaBits);
vkFormat = convertedFormat.actualBufferVkFormat(packedAttrib.compressed);
}
GLenum programAttributeType =
contextVk->getState().getProgramExecutable()->getProgramInputs()[attribIndex].type;
GLuint attribSize = gl::GetVertexFormatFromID(formatID).components;
GLuint shaderVarSize =
static_cast<GLuint>(gl::VariableColumnCount(programAttributeType));
ASSERT(contextVk->getNativeExtensions().relaxedVertexAttributeTypeANGLE);
if (programAttribType == gl::ComponentType::Float ||
attribType == gl::ComponentType::Float || attribSize != shaderVarSize)
{
bindingDesc.stride = 0; // Prevent out-of-bounds accesses. bindingDesc.stride = 0; // Prevent out-of-bounds accesses.
} }
}
// The binding index could become more dynamic in ES 3.1. // The binding index could become more dynamic in ES 3.1.
attribDesc.binding = attribIndex; attribDesc.binding = attribIndex;
......
...@@ -1047,6 +1047,9 @@ void RendererVk::ensureCapsInitialized() const ...@@ -1047,6 +1047,9 @@ void RendererVk::ensureCapsInitialized() const
// GL_EXT_blend_func_extended // GL_EXT_blend_func_extended
mNativeExtensions.blendFuncExtended = (mPhysicalDeviceFeatures.dualSrcBlend == VK_TRUE); mNativeExtensions.blendFuncExtended = (mPhysicalDeviceFeatures.dualSrcBlend == VK_TRUE);
mNativeExtensions.maxDualSourceDrawBuffers = LimitToInt(limitsVk.maxFragmentDualSrcAttachments); mNativeExtensions.maxDualSourceDrawBuffers = LimitToInt(limitsVk.maxFragmentDualSrcAttachments);
// GL_ANGLE_relaxed_vertex_attribute_type
mNativeExtensions.relaxedVertexAttributeTypeANGLE = true;
} }
namespace vk namespace vk
......
...@@ -1733,6 +1733,153 @@ void main() ...@@ -1733,6 +1733,153 @@ void main()
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
} }
// Tests that rendering is fine if GL_ANGLE_relaxed_vertex_attribute_type is enabled
// and mismatched integer signedness between the program's attribute type and the
// attribute type specified by VertexAttribIPointer are used.
TEST_P(VertexAttributeTestES3, DrawWithRelaxedVertexAttributeType)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_relaxed_vertex_attribute_type"));
constexpr char kVS[] = R"(#version 300 es
precision highp float;
in highp vec4 a_position;
in highp ivec4 a_ColorTest;
out highp vec4 v_colorTest;
void main() {
v_colorTest = vec4(a_ColorTest);
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
precision highp float;
in highp vec4 v_colorTest;
out vec4 fragColor;
void main() {
if(v_colorTest.x > 0.5) {
fragColor = vec4(0.0, 1.0, 0.0, 1.0);
} else {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glBindAttribLocation(program, 0, "a_position");
glBindAttribLocation(program, 1, "a_ColorTest");
glLinkProgram(program);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
constexpr size_t kDataSize = 48;
// Interleave test data with 0's.
// This guards against a future code change that adjusts stride to 0
// clang-format off
constexpr GLuint kColorTestData[kDataSize] = {
// Vertex attribute data Unused data
0u, 0u, 0u, 0u, /*red*/ 0u, 0u, 0u, 0u,
1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u,
1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u,
1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u,
1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u,
1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u
};
// clang-format on
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLuint) * kDataSize, kColorTestData, GL_STATIC_DRAW);
glVertexAttribIPointer(1, 4, GL_UNSIGNED_INT, 8 * sizeof(GLuint),
reinterpret_cast<const void *>(0));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(1);
drawQuad(program, "a_position", 0.5f);
// Verify green was drawn. If the stride isn't adjusted to 0 this corner will be green. If it is
// adjusted to 0, the whole image will be red
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
ASSERT_GL_NO_ERROR();
}
// Test that ensures we do not send data for components not specified by glVertexAttribPointer when
// component types and sizes are mismatched
TEST_P(VertexAttributeTestES3, DrawWithMismatchedComponentCount)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_relaxed_vertex_attribute_type"));
// To ensure the test results are valid when we don't send data for every component, the
// shader's values must be defined by the backend.
// Vulkan Spec 22.3. Vertex Attribute Divisor in Instanced Rendering
// https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#_vertex_attribute_divisor_in_instanced_rendering
// If the format does not include G, B, or A components, then those are filled with (0,0,1) as
// needed (using either 1.0f or integer 1 based on the format) for attributes that are not
// 64-bit data types.
ANGLE_SKIP_TEST_IF(!IsVulkan());
constexpr char kVS[] = R"(#version 300 es
precision highp float;
in highp vec4 a_position;
in highp ivec2 a_ColorTest;
out highp vec2 v_colorTest;
void main() {
v_colorTest = vec2(a_ColorTest);
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
precision highp float;
in highp vec2 v_colorTest;
out vec4 fragColor;
void main() {
if(v_colorTest.y < 0.5) {
fragColor = vec4(0.0, 1.0, 0.0, 1.0);
} else {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glBindAttribLocation(program, 0, "a_position");
glBindAttribLocation(program, 1, "a_ColorTest");
glLinkProgram(program);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
constexpr size_t kDataSize = 24;
// Initialize vertex attribute data with 1s.
GLuint kColorTestData[kDataSize];
for (size_t dataIndex = 0; dataIndex < kDataSize; dataIndex++)
{
kColorTestData[dataIndex] = 1u;
}
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLuint) * kDataSize, kColorTestData, GL_STATIC_DRAW);
glVertexAttribIPointer(1, 1, GL_UNSIGNED_INT, 4 * sizeof(GLuint),
reinterpret_cast<const void *>(0));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(1);
drawQuad(program, "a_position", 0.5f);
// Verify green was drawn.
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::green);
ASSERT_GL_NO_ERROR();
}
class VertexAttributeTestES31 : public VertexAttributeTestES3 class VertexAttributeTestES31 : public VertexAttributeTestES3
{ {
protected: protected:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment