[GLESv3][VAO] enable/pointer for vertex attribs when possible
If we don't forward enables/pointers to the host until
right before the draw, when a nonzero VAO is bound,
it defeats the purpose of VAO usage for performance.
This CL prepares the GLES2 encoder for VAO usage by
forwarding those calls whenever possible.
It gives a flag to sendVertexAttributes to not do any
changes in GL state if all attributes are backed by VBOs.
The reason we don't forward them 100% is because of
supporting either legacy or tricky behavior:
Legacy: specifying vertex arrays from host memory directly
with a host ptr passed to glVertexAttribPointer
Tricky: Using glVertexAttrib*f*(...) instead of
glVertexAttribPointer
Change-Id: I190aab86d7dcd7e01cfac0a1f60b44fac131abd0
diff --git a/system/GLESv2_enc/GL2Encoder.cpp b/system/GLESv2_enc/GL2Encoder.cpp
index 6bb78da..f502df9 100755
--- a/system/GLESv2_enc/GL2Encoder.cpp
+++ b/system/GLESv2_enc/GL2Encoder.cpp
@@ -300,6 +300,11 @@
SET_ERROR_IF(!isValidVertexAttribType(type), GL_INVALID_ENUM);
SET_ERROR_IF(stride < 0, GL_INVALID_VALUE);
ctx->m_state->setState(indx, size, type, normalized, stride, ptr);
+ if (ctx->m_state->currentArrayVbo() != 0) {
+ ctx->glVertexAttribPointerOffset(ctx, indx, size, type, normalized, stride, (uintptr_t)ptr);
+ } else {
+ // TODO: if a nonzero VAO is bound, issue GL_INVALID_OPERATION.
+ }
}
void GL2Encoder::s_glGetIntegerv(void *self, GLenum param, GLint *ptr)
@@ -481,6 +486,7 @@
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state);
SET_ERROR_IF(!isValidVertexAttribIndex(self, index), GL_INVALID_VALUE);
+ ctx->m_glEnableVertexAttribArray_enc(ctx, index);
ctx->m_state->enable(index, 1);
}
@@ -489,6 +495,7 @@
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state);
SET_ERROR_IF(!isValidVertexAttribIndex(self, index), GL_INVALID_VALUE);
+ ctx->m_glDisableVertexAttribArray_enc(ctx, index);
ctx->m_state->enable(index, 0);
}
@@ -635,7 +642,25 @@
*minIndex_out, *maxIndex_out);
}
-void GL2Encoder::sendVertexAttributes(GLint first, GLsizei count)
+// For detecting legacy usage of glVertexAttribPointer
+void GL2Encoder::getVBOUsage(bool* hasClientArrays, bool* hasVBOs) const {
+ if (hasClientArrays) *hasClientArrays = false;
+ if (hasVBOs) *hasVBOs = false;
+
+ for (int i = 0; i < m_state->nLocations(); i++) {
+ const GLClientState::VertexAttribState *state = m_state->getState(i);
+ if (state->enabled) {
+ if (state->bufferObject == 0 && state->data && hasClientArrays) {
+ *hasClientArrays = true;
+ }
+ if (state->bufferObject != 0 && hasVBOs) {
+ *hasVBOs = true;
+ }
+ }
+ }
+}
+
+void GL2Encoder::sendVertexAttributes(GLint first, GLsizei count, bool hasClientArrays)
{
assert(m_state);
@@ -653,7 +678,7 @@
}
if (state->enabled) {
- if (lastBoundVbo != state->bufferObject) {
+ if (hasClientArrays && lastBoundVbo != state->bufferObject) {
this->m_glBindBuffer_enc(this, GL_ARRAY_BUFFER, state->bufferObject);
lastBoundVbo = state->bufferObject;
}
@@ -679,20 +704,24 @@
// So it becomes the current form.
unsigned int bufLen = stride * (count - 1) + state->elementSize;
if (buf && firstIndex >= 0 && firstIndex + bufLen <= buf->m_size) {
- m_glEnableVertexAttribArray_enc(this, i);
- this->glVertexAttribPointerOffset(this, i, state->size, state->type, state->normalized, state->stride,
- (uintptr_t) state->data + firstIndex);
+ if (hasClientArrays) {
+ m_glEnableVertexAttribArray_enc(this, i);
+ this->glVertexAttribPointerOffset(this, i, state->size, state->type, state->normalized, state->stride,
+ (uintptr_t) state->data + firstIndex);
+ }
} else {
ALOGE("a vertex attribute index out of boundary is detected. Skipping corresponding vertex attribute.");
m_glDisableVertexAttribArray_enc(this, i);
}
}
} else {
- this->m_glDisableVertexAttribArray_enc(this, i);
+ if (hasClientArrays) {
+ this->m_glDisableVertexAttribArray_enc(this, i);
+ }
}
}
- if (lastBoundVbo != m_state->currentArrayVbo()) {
+ if (hasClientArrays && lastBoundVbo != m_state->currentArrayVbo()) {
this->m_glBindBuffer_enc(this, GL_ARRAY_BUFFER, m_state->currentArrayVbo());
}
}
@@ -736,8 +765,20 @@
SET_ERROR_IF(!isValidDrawMode(mode), GL_INVALID_ENUM);
SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
- ctx->sendVertexAttributes(first, count);
- ctx->m_glDrawArrays_enc(ctx, mode, 0, count);
+ bool has_client_vertex_arrays = false;
+ bool has_indirect_arrays = false;
+ ctx->getVBOUsage(&has_client_vertex_arrays,
+ &has_indirect_arrays);
+
+ if (has_client_vertex_arrays ||
+ (!has_client_vertex_arrays &&
+ !has_indirect_arrays)) {
+ ctx->sendVertexAttributes(first, count, true);
+ ctx->m_glDrawArrays_enc(ctx, mode, 0, count);
+ } else {
+ ctx->sendVertexAttributes(0, count, false);
+ ctx->m_glDrawArrays_enc(ctx, mode, first, count);
+ }
ctx->m_stream->flush();
}
@@ -751,23 +792,14 @@
SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
SET_ERROR_IF(!(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT), GL_INVALID_ENUM);
- bool has_immediate_arrays = false;
+ bool has_client_vertex_arrays = false;
bool has_indirect_arrays = false;
int nLocations = ctx->m_state->nLocations();
GLintptr offset = 0;
- for (int i = 0; i < nLocations; i++) {
- const GLClientState::VertexAttribState *state = ctx->m_state->getState(i);
- if (state->enabled) {
- if (state->bufferObject != 0) {
- has_indirect_arrays = true;
- } else if (state->data) {
- has_immediate_arrays = true;
- }
- }
- }
+ ctx->getVBOUsage(&has_client_vertex_arrays, &has_indirect_arrays);
- if (!has_immediate_arrays && !has_indirect_arrays) {
+ if (!has_client_vertex_arrays && !has_indirect_arrays) {
ALOGW("glDrawElements: no vertex arrays / buffers bound to the command\n");
GLenum status = ctx->m_glCheckFramebufferStatus_enc(self, GL_FRAMEBUFFER);
SET_ERROR_IF(status != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION);
@@ -806,8 +838,8 @@
bool adjustIndices = true;
if (ctx->m_state->currentIndexVbo() != 0) {
- if (!has_immediate_arrays) {
- ctx->sendVertexAttributes(0, maxIndex + 1);
+ if (!has_client_vertex_arrays) {
+ ctx->sendVertexAttributes(0, maxIndex + 1, false);
ctx->m_glBindBuffer_enc(self, GL_ELEMENT_ARRAY_BUFFER, ctx->m_state->currentIndexVbo());
ctx->glDrawElementsOffset(ctx, mode, count, type, offset);
ctx->flushDrawCall();
@@ -825,7 +857,7 @@
minIndex);
if (has_indirect_arrays || 1) {
- ctx->sendVertexAttributes(minIndex, maxIndex - minIndex + 1);
+ ctx->sendVertexAttributes(minIndex, maxIndex - minIndex + 1, true);
ctx->glDrawElementsData(ctx, mode, count, type, adjustedIndices,
count * glSizeof(type));
ctx->m_stream->flush();
diff --git a/system/GLESv2_enc/GL2Encoder.h b/system/GLESv2_enc/GL2Encoder.h
index 1f82a8d..dcd336d 100644
--- a/system/GLESv2_enc/GL2Encoder.h
+++ b/system/GLESv2_enc/GL2Encoder.h
@@ -77,7 +77,8 @@
void getBufferIndexRange(BufferData* buf, const void* dataWithOffset,
GLenum type, GLsizei count, GLintptr offset,
int* minIndex_out, int* maxIndex_out);
- void sendVertexAttributes(GLint first, GLsizei count);
+ void getVBOUsage(bool* hasClientArrays, bool* hasVBOs) const;
+ void sendVertexAttributes(GLint first, GLsizei count, bool hasClientArrays);
void flushDrawCall();
bool updateHostTexture2DBinding(GLenum texUnit, GLenum newTarget);