Commit ed23dc84 by Le Hoang Quyen Committed by Commit Bot

Metal: default integer attribs & offset mod for idx conv.

- Support default integer attributes. - When converting index buffer, use offset modulo instead of offset to reduce number of conversions if application uses many different offsets to the index buffer. Bug: angleproject:2634 Change-Id: I97aa9ea1826ffc9dbe5784fe5b5af2f99df63e2c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2494524 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 83539557
......@@ -503,8 +503,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
struct DefaultAttribute
{
// NOTE(hqle): Support integer default attributes in ES 3.0
float values[4];
uint8_t values[sizeof(float) * 4];
};
mtl::OcclusionQueryPool mOcclusionQueryPool;
......
......@@ -61,6 +61,10 @@ class VertexArrayMtl : public VertexArrayImpl
private:
void reset(ContextMtl *context);
void getVertexAttribFormatAndArraySize(const sh::ShaderVariable &var,
MTLVertexFormat *formatOut,
uint32_t *arraySizeOut);
angle::Result syncDirtyAttrib(const gl::Context *glContext,
const gl::VertexAttribute &attrib,
const gl::VertexBinding &binding,
......@@ -105,6 +109,10 @@ class VertexArrayMtl : public VertexArrayImpl
gl::AttribArray<GLuint> mCurrentArrayBufferStrides;
gl::AttribArray<const mtl::VertexFormat *> mCurrentArrayBufferFormats;
const mtl::VertexFormat &mDefaultFloatVertexFormat;
const mtl::VertexFormat &mDefaultIntVertexFormat;
const mtl::VertexFormat &mDefaultUIntVertexFormat;
mtl::BufferPool mDynamicVertexData;
mtl::BufferPool mDynamicIndexData;
......
......@@ -149,14 +149,15 @@ inline void SetDefaultVertexBufferLayout(mtl::VertexBufferLayoutDesc *layout)
// VertexArrayMtl implementation
VertexArrayMtl::VertexArrayMtl(const gl::VertexArrayState &state, ContextMtl *context)
: VertexArrayImpl(state),
// Due to Metal's strict requirement for offset and stride, we need to always allocate new
// buffer for every conversion.
mDynamicVertexData(true)
mDefaultFloatVertexFormat(
context->getVertexFormat(angle::FormatID::R32G32B32A32_FLOAT, false)),
mDefaultIntVertexFormat(context->getVertexFormat(angle::FormatID::R32G32B32A32_SINT, false)),
mDefaultUIntVertexFormat(context->getVertexFormat(angle::FormatID::R32G32B32A32_UINT, false))
{
reset(context);
mDynamicVertexData.initialize(context, 0, mtl::kVertexAttribBufferStrideAlignment,
mtl::kMaxVertexAttribs);
/** maxBuffers */ 10 * mtl::kMaxVertexAttribs);
mDynamicIndexData.initialize(context, kDynamicIndexDataSize, mtl::kIndexBufferOffsetAlignment,
0);
......@@ -189,7 +190,7 @@ void VertexArrayMtl::reset(ContextMtl *context)
}
for (const mtl::VertexFormat *&format : mCurrentArrayBufferFormats)
{
format = &context->getVertexFormat(angle::FormatID::R32G32B32A32_FLOAT, false);
format = &mDefaultFloatVertexFormat;
}
mVertexArrayDirty = true;
......@@ -251,6 +252,53 @@ angle::Result VertexArrayMtl::syncState(const gl::Context *context,
return angle::Result::Continue;
}
ANGLE_INLINE void VertexArrayMtl::getVertexAttribFormatAndArraySize(const sh::ShaderVariable &var,
MTLVertexFormat *formatOut,
uint32_t *arraySizeOut)
{
uint32_t arraySize = var.getArraySizeProduct();
MTLVertexFormat format;
switch (var.type)
{
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
format = mDefaultIntVertexFormat.metalFormat;
break;
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_VEC2:
case GL_UNSIGNED_INT_VEC3:
case GL_UNSIGNED_INT_VEC4:
format = mDefaultUIntVertexFormat.metalFormat;
break;
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT2x4:
arraySize *= 2;
format = mDefaultFloatVertexFormat.metalFormat;
break;
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT3x2:
case GL_FLOAT_MAT3x4:
arraySize *= 3;
format = mDefaultFloatVertexFormat.metalFormat;
break;
case GL_FLOAT_MAT4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
arraySize *= 4;
format = mDefaultFloatVertexFormat.metalFormat;
break;
default:
format = mDefaultFloatVertexFormat.metalFormat;
}
*arraySizeOut = arraySize;
*formatOut = format;
}
// vertexDescChanged is both input and output, the input value if is true, will force new
// mtl::VertexDesc to be returned via vertexDescOut. Otherwise, it is only returned when the
// vertex array is dirty
......@@ -265,6 +313,7 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
{
mVertexArrayDirty = false;
const gl::ProgramState &programState = glContext->getState().getProgram()->getState();
const gl::AttributesMask &programActiveAttribsMask =
glContext->getState().getProgram()->getExecutable().getActiveAttribLocationsMask();
......@@ -295,9 +344,6 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
const auto &attrib = attribs[v];
const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
const angle::Format &angleFormat = mCurrentArrayBufferFormats[v]->actualAngleFormat();
desc.attributes[v].format = mCurrentArrayBufferFormats[v]->metalFormat;
bool attribEnabled = attrib.enabled;
if (attribEnabled &&
(!mCurrentArrayBuffers[v] || !mCurrentArrayBuffers[v]->getCurrentBuffer()))
......@@ -306,11 +352,46 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
attribEnabled = false;
}
if (attribEnabled)
if (!attribEnabled)
{
// Use default attribute
// Need to find the attribute having the exact binding location = v in the program
// inputs list to retrieve its coresponding data type:
const std::vector<sh::ShaderVariable> &programInputs =
programState.getProgramInputs();
std::vector<sh::ShaderVariable>::const_iterator attribInfoIte = std::find_if(
begin(programInputs), end(programInputs), [v](const sh::ShaderVariable &sv) {
return static_cast<uint32_t>(sv.location) == v;
});
if (attribInfoIte == end(programInputs))
{
// Most likely this is array element with index > 0.
// Already handled when encounter first element.
continue;
}
uint32_t arraySize;
MTLVertexFormat format;
getVertexAttribFormatAndArraySize(*attribInfoIte, &format, &arraySize);
for (uint32_t vaIdx = v; vaIdx < v + arraySize; ++vaIdx)
{
desc.attributes[vaIdx].bufferIndex = mtl::kDefaultAttribsBindingIndex;
desc.attributes[vaIdx].offset = vaIdx * mtl::kDefaultAttributeSize;
desc.attributes[vaIdx].format = format;
}
}
else
{
uint32_t bufferIdx = mtl::kVboBindingIndexStart + v;
uint32_t bufferOffset = static_cast<uint32_t>(mCurrentArrayBufferOffsets[v]);
const angle::Format &angleFormat =
mCurrentArrayBufferFormats[v]->actualAngleFormat();
desc.attributes[v].format = mCurrentArrayBufferFormats[v]->metalFormat;
desc.attributes[v].bufferIndex = bufferIdx;
desc.attributes[v].offset = 0;
ASSERT((bufferOffset % angleFormat.pixelBytes) == 0);
......@@ -331,11 +412,6 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
cmdEncoder->setVertexBuffer(mCurrentArrayBuffers[v]->getCurrentBuffer(),
bufferOffset, bufferIdx);
}
else
{
desc.attributes[v].bufferIndex = mtl::kDefaultAttribsBindingIndex;
desc.attributes[v].offset = v * mtl::kDefaultAttributeSize;
}
}
}
......@@ -400,6 +476,7 @@ angle::Result VertexArrayMtl::updateClientAttribs(const gl::Context *context,
src += startElement * binding.getStride();
size_t destOffset = startElement * stride;
mDynamicVertexData.updateAlignment(contextMtl, vertexFormat.actualAngleFormat().pixelBytes);
ANGLE_TRY(StreamVertexData(
contextMtl, &mDynamicVertexData, src, bytesToAllocate, destOffset, elementCount,
binding.getStride(), vertexFormat.vertexLoadFunction,
......@@ -458,16 +535,12 @@ angle::Result VertexArrayMtl::syncDirtyAttrib(const gl::Context *glContext,
}
else
{
// Tell ContextMtl to update default attribute value
contextMtl->invalidateDefaultAttribute(attribIndex);
// Use default attribute value. Handled in setupDraw().
mCurrentArrayBuffers[attribIndex] = nullptr;
mCurrentArrayBufferOffsets[attribIndex] = 0;
mCurrentArrayBufferStrides[attribIndex] = 0;
// NOTE(hqle): We only support ES 2.0 atm. So default attribute type should always
// be float.
mCurrentArrayBufferFormats[attribIndex] =
&contextMtl->getVertexFormat(angle::FormatID::R32G32B32A32_FLOAT, false);
&contextMtl->getVertexFormat(angle::FormatID::NONE, false);
}
return angle::Result::Continue;
......@@ -523,31 +596,38 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext,
mtl::BufferRef *idxBufferOut,
size_t *idxBufferOffsetOut)
{
ASSERT((offset % mtl::kIndexBufferOffsetAlignment) != 0 ||
indexType == gl::DrawElementsType::UnsignedByte);
size_t offsetModulo = offset % mtl::kIndexBufferOffsetAlignment;
ASSERT(offsetModulo != 0 || indexType == gl::DrawElementsType::UnsignedByte);
size_t alignedOffset = offset - offsetModulo;
if (indexType == gl::DrawElementsType::UnsignedByte)
{
// Unsigned byte index will be promoted to unsigned short, thus double its offset.
alignedOffset = alignedOffset << 1;
}
ContextMtl *contextMtl = mtl::GetImpl(glContext);
BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer());
IndexConversionBufferMtl *conversion =
idxBuffer->getIndexConversionBuffer(contextMtl, indexType, offset);
idxBuffer->getIndexConversionBuffer(contextMtl, indexType, offsetModulo);
// Has the content of the buffer has changed since last conversion?
if (!conversion->dirty)
{
// reuse the converted buffer
*idxBufferOut = conversion->convertedBuffer;
*idxBufferOffsetOut = conversion->convertedOffset;
*idxBufferOffsetOut = conversion->convertedOffset + alignedOffset;
return angle::Result::Continue;
}
size_t indexCount = GetIndexCount(idxBuffer, offset, indexType);
size_t indexCount = GetIndexCount(idxBuffer, offsetModulo, indexType);
ANGLE_TRY(
convertIndexBufferGPU(glContext, indexType, idxBuffer, offset, indexCount, conversion));
ANGLE_TRY(convertIndexBufferGPU(glContext, indexType, idxBuffer, offsetModulo, indexCount,
conversion));
*idxBufferOut = conversion->convertedBuffer;
*idxBufferOffsetOut = conversion->convertedOffset;
*idxBufferOffsetOut = conversion->convertedOffset + alignedOffset;
return angle::Result::Continue;
}
......
......@@ -861,7 +861,13 @@ void Buffer::flush(ContextMtl *context, size_t offsetWritten, size_t sizeWritten
{
if (get().storageMode == MTLStorageModeManaged)
{
[get() didModifyRange:NSMakeRange(offsetWritten, sizeWritten)];
size_t startOffset = std::min(offsetWritten, size());
size_t endOffset = std::min(offsetWritten + sizeWritten, size());
size_t clampedSize = endOffset - startOffset;
if (clampedSize > 0)
{
[get() didModifyRange:NSMakeRange(startOffset, clampedSize)];
}
}
}
#endif
......
......@@ -2856,6 +2856,108 @@ void main()
checkPixels();
}
// Test that default integer attribute works correctly even if there is a gap in
// attribute locations.
TEST_P(VertexAttributeTestES3, DefaultIntAttribWithGap)
{
constexpr char kVertexShader[] = R"(#version 300 es
layout(location = 0) in vec2 position;
layout(location = 3) in int actualValue;
uniform int expectedValue;
out float result;
void main()
{
result = (actualValue == expectedValue) ? 1.0 : 0.0;
gl_Position = vec4(position, 0, 1);
})";
constexpr char kFragmentShader[] = R"(#version 300 es
in mediump float result;
layout(location = 0) out lowp vec4 out_color;
void main()
{
out_color = result > 0.0 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVertexShader, kFragmentShader);
// Re-link the program to update the attribute locations
glLinkProgram(program);
ASSERT_TRUE(CheckLinkStatusAndReturnProgram(program, true));
glUseProgram(program);
GLint uniLoc = glGetUniformLocation(program, "expectedValue");
ASSERT_NE(-1, uniLoc);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
setupQuadVertexBuffer(0.5f, 1.0f);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
std::array<GLint, 4> testValues = {{1, 2, 3, 4}};
for (GLfloat testValue : testValues)
{
glUniform1i(uniLoc, testValue);
glVertexAttribI4i(3, testValue, 0, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
}
// Test that default unsigned integer attribute works correctly even if there is a gap in
// attribute locations.
TEST_P(VertexAttributeTestES3, DefaultUIntAttribWithGap)
{
constexpr char kVertexShader[] = R"(#version 300 es
layout(location = 0) in vec2 position;
layout(location = 3) in uint actualValue;
uniform uint expectedValue;
out float result;
void main()
{
result = (actualValue == expectedValue) ? 1.0 : 0.0;
gl_Position = vec4(position, 0, 1);
})";
constexpr char kFragmentShader[] = R"(#version 300 es
in mediump float result;
layout(location = 0) out lowp vec4 out_color;
void main()
{
out_color = result > 0.0 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVertexShader, kFragmentShader);
// Re-link the program to update the attribute locations
glLinkProgram(program);
ASSERT_TRUE(CheckLinkStatusAndReturnProgram(program, true));
glUseProgram(program);
GLint uniLoc = glGetUniformLocation(program, "expectedValue");
ASSERT_NE(-1, uniLoc);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
setupQuadVertexBuffer(0.5f, 1.0f);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
std::array<GLuint, 4> testValues = {{1, 2, 3, 4}};
for (GLfloat testValue : testValues)
{
glUniform1ui(uniLoc, testValue);
glVertexAttribI4ui(3, testValue, 0, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
}
// Tests that large strides that read past the end of the buffer work correctly.
// Requires ES 3.1 to query MAX_VERTEX_ATTRIB_STRIDE.
TEST_P(VertexAttributeTestES31, LargeStride)
......@@ -3455,7 +3557,7 @@ ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(VertexAttributeTest);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(VertexAttributeOORTest);
ANGLE_INSTANTIATE_TEST_ES3(VertexAttributeTestES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(VertexAttributeTestES3, ES3_METAL());
ANGLE_INSTANTIATE_TEST_ES31(VertexAttributeTestES31);
......
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