blob: 44264d966ca58aa53e5427c45b52f99dcd22beeb [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.
*/
#include "GL2Encoder.h"
#include "GLESv2Validation.h"
#include <string>
#include <map>
#include <assert.h>
#include <ctype.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES2/gl2platform.h>
#include <GLES3/gl3.h>
#include <GLES3/gl31.h>
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
static GLubyte *gVendorString= (GLubyte *) "Android";
static GLubyte *gRendererString= (GLubyte *) "Android HW-GLES 3.0";
static GLubyte *gVersionString= (GLubyte *) "OpenGL ES 3.0";
static GLubyte *gExtensionsString= (GLubyte *) "GL_OES_EGL_image_external ";
#define SET_ERROR_IF(condition, err) if((condition)) { \
ALOGE("%s:%s:%d GL error 0x%x\n", __FILE__, __FUNCTION__, __LINE__, err); \
ctx->setError(err); \
return; \
}
#define SET_ERROR_WITH_MESSAGE_IF(condition, err, generator, genargs) if ((condition)) { \
std::string msg = generator genargs; \
ALOGE("%s:%s:%d GL error 0x%x\n" \
"Info: %s\n", __FILE__, __FUNCTION__, __LINE__, err, msg.c_str()); \
ctx->setError(err); \
return; \
} \
#define RET_AND_SET_ERROR_IF(condition, err, ret) if((condition)) { \
ALOGE("%s:%s:%d GL error 0x%x\n", __FILE__, __FUNCTION__, __LINE__, err); \
ctx->setError(err); \
return ret; \
} \
#define RET_AND_SET_ERROR_WITH_MESSAGE_IF(condition, err, ret, generator, genargs) if((condition)) { \
std::string msg = generator genargs; \
ALOGE("%s:%s:%d GL error 0x%x\n" \
"Info: %s\n", __FILE__, __FUNCTION__, __LINE__, err, msg.c_str()); \
ctx->setError(err); \
return ret; \
} \
GL2Encoder::GL2Encoder(IOStream *stream, ChecksumCalculator *protocol)
: gl2_encoder_context_t(stream, protocol)
{
m_currMajorVersion = 2;
m_currMinorVersion = 0;
m_initialized = false;
m_noHostError = false;
m_state = NULL;
m_error = GL_NO_ERROR;
m_num_compressedTextureFormats = 0;
m_max_combinedTextureImageUnits = 0;
m_max_vertexTextureImageUnits = 0;
m_max_textureImageUnits = 0;
m_max_cubeMapTextureSize = 0;
m_max_renderBufferSize = 0;
m_max_textureSize = 0;
m_max_3d_textureSize = 0;
m_max_vertexAttribStride = 0;
m_max_transformFeedbackSeparateAttribs = 0;
m_max_uniformBufferBindings = 0;
m_max_colorAttachments = 0;
m_max_drawBuffers = 0;
m_max_atomicCounterBufferBindings = 0;
m_max_shaderStorageBufferBindings = 0;
m_max_vertexAttribBindings = 0;
m_compressedTextureFormats = NULL;
m_ssbo_offset_align = 0;
m_ubo_offset_align = 0;
m_drawCallFlushCount = 0;
m_primitiveRestartEnabled = false;
m_primitiveRestartIndex = 0;
// overrides
#define OVERRIDE(name) m_##name##_enc = this-> name ; this-> name = &s_##name
#define OVERRIDE_CUSTOM(name) this-> name = &s_##name
#define OVERRIDEWITH(name, target) do { \
m_##target##_enc = this-> target; \
this-> target = &s_##name; \
} while(0)
#define OVERRIDEOES(name) OVERRIDEWITH(name, name##OES)
OVERRIDE(glFlush);
OVERRIDE(glPixelStorei);
OVERRIDE(glGetString);
OVERRIDE(glBindBuffer);
OVERRIDE(glBufferData);
OVERRIDE(glBufferSubData);
OVERRIDE(glDeleteBuffers);
OVERRIDE(glDrawArrays);
OVERRIDE(glDrawElements);
OVERRIDE(glDrawArraysNullAEMU);
OVERRIDE(glDrawElementsNullAEMU);
OVERRIDE(glGetIntegerv);
OVERRIDE(glGetFloatv);
OVERRIDE(glGetBooleanv);
OVERRIDE(glVertexAttribPointer);
OVERRIDE(glEnableVertexAttribArray);
OVERRIDE(glDisableVertexAttribArray);
OVERRIDE(glGetVertexAttribiv);
OVERRIDE(glGetVertexAttribfv);
OVERRIDE(glGetVertexAttribPointerv);
this->glShaderBinary = &s_glShaderBinary;
this->glShaderSource = &s_glShaderSource;
this->glFinish = &s_glFinish;
OVERRIDE(glGetError);
OVERRIDE(glLinkProgram);
OVERRIDE(glDeleteProgram);
OVERRIDE(glGetUniformiv);
OVERRIDE(glGetUniformfv);
OVERRIDE(glCreateProgram);
OVERRIDE(glCreateShader);
OVERRIDE(glDeleteShader);
OVERRIDE(glAttachShader);
OVERRIDE(glDetachShader);
OVERRIDE(glGetAttachedShaders);
OVERRIDE(glGetShaderSource);
OVERRIDE(glGetShaderInfoLog);
OVERRIDE(glGetProgramInfoLog);
OVERRIDE(glGetUniformLocation);
OVERRIDE(glUseProgram);
OVERRIDE(glUniform1f);
OVERRIDE(glUniform1fv);
OVERRIDE(glUniform1i);
OVERRIDE(glUniform1iv);
OVERRIDE(glUniform2f);
OVERRIDE(glUniform2fv);
OVERRIDE(glUniform2i);
OVERRIDE(glUniform2iv);
OVERRIDE(glUniform3f);
OVERRIDE(glUniform3fv);
OVERRIDE(glUniform3i);
OVERRIDE(glUniform3iv);
OVERRIDE(glUniform4f);
OVERRIDE(glUniform4fv);
OVERRIDE(glUniform4i);
OVERRIDE(glUniform4iv);
OVERRIDE(glUniformMatrix2fv);
OVERRIDE(glUniformMatrix3fv);
OVERRIDE(glUniformMatrix4fv);
OVERRIDE(glActiveTexture);
OVERRIDE(glBindTexture);
OVERRIDE(glDeleteTextures);
OVERRIDE(glGetTexParameterfv);
OVERRIDE(glGetTexParameteriv);
OVERRIDE(glTexParameterf);
OVERRIDE(glTexParameterfv);
OVERRIDE(glTexParameteri);
OVERRIDE(glTexParameteriv);
OVERRIDE(glTexImage2D);
OVERRIDE(glTexSubImage2D);
OVERRIDE(glCopyTexImage2D);
OVERRIDE(glGenRenderbuffers);
OVERRIDE(glDeleteRenderbuffers);
OVERRIDE(glBindRenderbuffer);
OVERRIDE(glRenderbufferStorage);
OVERRIDE(glFramebufferRenderbuffer);
OVERRIDE(glGenFramebuffers);
OVERRIDE(glDeleteFramebuffers);
OVERRIDE(glBindFramebuffer);
OVERRIDE(glFramebufferTexture2D);
OVERRIDE(glFramebufferTexture3DOES);
OVERRIDE(glGetFramebufferAttachmentParameteriv);
OVERRIDE(glCheckFramebufferStatus);
OVERRIDE(glGenVertexArrays);
OVERRIDE(glDeleteVertexArrays);
OVERRIDE(glBindVertexArray);
OVERRIDEOES(glGenVertexArrays);
OVERRIDEOES(glDeleteVertexArrays);
OVERRIDEOES(glBindVertexArray);
OVERRIDE_CUSTOM(glMapBufferOES);
OVERRIDE_CUSTOM(glUnmapBufferOES);
OVERRIDE_CUSTOM(glMapBufferRange);
OVERRIDE_CUSTOM(glUnmapBuffer);
OVERRIDE_CUSTOM(glFlushMappedBufferRange);
OVERRIDE(glCompressedTexImage2D);
OVERRIDE(glCompressedTexSubImage2D);
OVERRIDE(glBindBufferRange);
OVERRIDE(glBindBufferBase);
OVERRIDE(glCopyBufferSubData);
OVERRIDE(glGetBufferParameteriv);
OVERRIDE(glGetBufferParameteri64v);
OVERRIDE(glGetBufferPointerv);
OVERRIDE_CUSTOM(glGetUniformIndices);
OVERRIDE(glUniform1ui);
OVERRIDE(glUniform2ui);
OVERRIDE(glUniform3ui);
OVERRIDE(glUniform4ui);
OVERRIDE(glUniform1uiv);
OVERRIDE(glUniform2uiv);
OVERRIDE(glUniform3uiv);
OVERRIDE(glUniform4uiv);
OVERRIDE(glUniformMatrix2x3fv);
OVERRIDE(glUniformMatrix3x2fv);
OVERRIDE(glUniformMatrix2x4fv);
OVERRIDE(glUniformMatrix4x2fv);
OVERRIDE(glUniformMatrix3x4fv);
OVERRIDE(glUniformMatrix4x3fv);
OVERRIDE(glGetUniformuiv);
OVERRIDE(glGetActiveUniformBlockiv);
OVERRIDE(glGetVertexAttribIiv);
OVERRIDE(glGetVertexAttribIuiv);
OVERRIDE_CUSTOM(glVertexAttribIPointer);
OVERRIDE(glVertexAttribDivisor);
OVERRIDE(glRenderbufferStorageMultisample);
OVERRIDE(glDrawBuffers);
OVERRIDE(glReadBuffer);
OVERRIDE(glFramebufferTextureLayer);
OVERRIDE(glTexStorage2D);
OVERRIDE_CUSTOM(glTransformFeedbackVaryings);
OVERRIDE(glBeginTransformFeedback);
OVERRIDE(glEndTransformFeedback);
OVERRIDE(glPauseTransformFeedback);
OVERRIDE(glResumeTransformFeedback);
OVERRIDE(glTexImage3D);
OVERRIDE(glTexSubImage3D);
OVERRIDE(glTexStorage3D);
OVERRIDE(glCompressedTexImage3D);
OVERRIDE(glCompressedTexSubImage3D);
OVERRIDE(glDrawArraysInstanced);
OVERRIDE_CUSTOM(glDrawElementsInstanced);
OVERRIDE_CUSTOM(glDrawRangeElements);
OVERRIDE_CUSTOM(glGetStringi);
OVERRIDE(glGetProgramBinary);
OVERRIDE(glReadPixels);
OVERRIDE(glEnable);
OVERRIDE(glDisable);
OVERRIDE(glClearBufferiv);
OVERRIDE(glClearBufferuiv);
OVERRIDE(glClearBufferfv);
OVERRIDE(glBlitFramebuffer);
OVERRIDE_CUSTOM(glGetInternalformativ);
OVERRIDE(glGenerateMipmap);
OVERRIDE(glBindSampler);
OVERRIDE_CUSTOM(glFenceSync);
OVERRIDE_CUSTOM(glClientWaitSync);
OVERRIDE_CUSTOM(glWaitSync);
OVERRIDE_CUSTOM(glDeleteSync);
OVERRIDE_CUSTOM(glIsSync);
OVERRIDE_CUSTOM(glGetSynciv);
OVERRIDE(glGetIntegeri_v);
OVERRIDE(glGetInteger64i_v);
OVERRIDE(glGetInteger64v);
OVERRIDE(glGetBooleani_v);
OVERRIDE(glGetShaderiv);
OVERRIDE(glActiveShaderProgram);
OVERRIDE_CUSTOM(glCreateShaderProgramv);
OVERRIDE(glProgramUniform1f);
OVERRIDE(glProgramUniform1fv);
OVERRIDE(glProgramUniform1i);
OVERRIDE(glProgramUniform1iv);
OVERRIDE(glProgramUniform1ui);
OVERRIDE(glProgramUniform1uiv);
OVERRIDE(glProgramUniform2f);
OVERRIDE(glProgramUniform2fv);
OVERRIDE(glProgramUniform2i);
OVERRIDE(glProgramUniform2iv);
OVERRIDE(glProgramUniform2ui);
OVERRIDE(glProgramUniform2uiv);
OVERRIDE(glProgramUniform3f);
OVERRIDE(glProgramUniform3fv);
OVERRIDE(glProgramUniform3i);
OVERRIDE(glProgramUniform3iv);
OVERRIDE(glProgramUniform3ui);
OVERRIDE(glProgramUniform3uiv);
OVERRIDE(glProgramUniform4f);
OVERRIDE(glProgramUniform4fv);
OVERRIDE(glProgramUniform4i);
OVERRIDE(glProgramUniform4iv);
OVERRIDE(glProgramUniform4ui);
OVERRIDE(glProgramUniform4uiv);
OVERRIDE(glProgramUniformMatrix2fv);
OVERRIDE(glProgramUniformMatrix2x3fv);
OVERRIDE(glProgramUniformMatrix2x4fv);
OVERRIDE(glProgramUniformMatrix3fv);
OVERRIDE(glProgramUniformMatrix3x2fv);
OVERRIDE(glProgramUniformMatrix3x4fv);
OVERRIDE(glProgramUniformMatrix4fv);
OVERRIDE(glProgramUniformMatrix4x2fv);
OVERRIDE(glProgramUniformMatrix4x3fv);
OVERRIDE(glProgramParameteri);
OVERRIDE(glUseProgramStages);
OVERRIDE(glBindProgramPipeline);
OVERRIDE(glGetProgramResourceiv);
OVERRIDE(glGetProgramResourceIndex);
OVERRIDE(glGetProgramResourceLocation);
OVERRIDE(glGetProgramResourceName);
OVERRIDE(glGetProgramPipelineInfoLog);
OVERRIDE(glVertexAttribFormat);
OVERRIDE(glVertexAttribIFormat);
OVERRIDE(glVertexBindingDivisor);
OVERRIDE(glVertexAttribBinding);
OVERRIDE(glBindVertexBuffer);
OVERRIDE_CUSTOM(glDrawArraysIndirect);
OVERRIDE_CUSTOM(glDrawElementsIndirect);
OVERRIDE(glTexStorage2DMultisample);
OVERRIDE_CUSTOM(glGetGraphicsResetStatusEXT);
OVERRIDE_CUSTOM(glReadnPixelsEXT);
OVERRIDE_CUSTOM(glGetnUniformfvEXT);
OVERRIDE_CUSTOM(glGetnUniformivEXT);
}
GL2Encoder::~GL2Encoder()
{
delete m_compressedTextureFormats;
}
GLenum GL2Encoder::s_glGetError(void * self)
{
GL2Encoder *ctx = (GL2Encoder *)self;
GLenum err = ctx->getError();
if(err != GL_NO_ERROR) {
ctx->m_glGetError_enc(ctx); // also clear host error
ctx->setError(GL_NO_ERROR);
return err;
}
if (ctx->m_noHostError) {
return GL_NO_ERROR;
} else {
return ctx->m_glGetError_enc(self);
}
}
class GL2Encoder::ErrorUpdater {
public:
ErrorUpdater(GL2Encoder* ctx) :
mCtx(ctx),
guest_error(ctx->getError()),
host_error(ctx->m_glGetError_enc(ctx)) {
// Preserve any existing GL error in the guest:
// OpenGL ES 3.0.5 spec:
// The command enum GetError( void ); is used to obtain error information.
// Each detectable error is assigned a numeric code. When an error is
// detected, a flag is set and the code is recorded. Further errors, if
// they occur, do not affect this recorded code. When GetError is called,
// the code is returned and the flag is cleared, so that a further error
// will again record its code. If a call to GetError returns NO_ERROR, then
// there has been no detectable error since the last call to GetError (or
// since the GL was initialized).
if (guest_error == GL_NO_ERROR) {
guest_error = host_error;
}
}
GLenum getHostErrorAndUpdate() {
host_error = mCtx->m_glGetError_enc(mCtx);
if (guest_error == GL_NO_ERROR) {
guest_error = host_error;
}
return host_error;
}
void updateGuestErrorState() {
mCtx->setError(guest_error);
}
private:
GL2Encoder* mCtx;
GLenum guest_error;
GLenum host_error;
};
template<class T>
class GL2Encoder::ScopedQueryUpdate {
public:
ScopedQueryUpdate(GL2Encoder* ctx, uint32_t bytes, T* target) :
mCtx(ctx),
mBuf(bytes, 0),
mTarget(target),
mErrorUpdater(ctx) {
}
T* hostStagingBuffer() {
return (T*)&mBuf[0];
}
~ScopedQueryUpdate() {
GLint hostError = mErrorUpdater.getHostErrorAndUpdate();
if (hostError == GL_NO_ERROR && mTarget) {
memcpy(mTarget, &mBuf[0], mBuf.size());
}
mErrorUpdater.updateGuestErrorState();
}
private:
GL2Encoder* mCtx;
std::vector<char> mBuf;
T* mTarget;
ErrorUpdater mErrorUpdater;
};
void GL2Encoder::safe_glGetBooleanv(GLenum param, GLboolean* val) {
ScopedQueryUpdate<GLboolean> query(this, glUtilsParamSize(param) * sizeof(GLboolean), val);
m_glGetBooleanv_enc(this, param, query.hostStagingBuffer());
}
void GL2Encoder::safe_glGetFloatv(GLenum param, GLfloat* val) {
ScopedQueryUpdate<GLfloat> query(this, glUtilsParamSize(param) * sizeof(GLfloat), val);
m_glGetFloatv_enc(this, param, query.hostStagingBuffer());
}
void GL2Encoder::safe_glGetIntegerv(GLenum param, GLint* val) {
ScopedQueryUpdate<GLint> query(this, glUtilsParamSize(param) * sizeof(GLint), val);
m_glGetIntegerv_enc(this, param, query.hostStagingBuffer());
}
void GL2Encoder::safe_glGetInteger64v(GLenum param, GLint64* val) {
ScopedQueryUpdate<GLint64> query(this, glUtilsParamSize(param) * sizeof(GLint64), val);
m_glGetInteger64v_enc(this, param, query.hostStagingBuffer());
}
void GL2Encoder::safe_glGetIntegeri_v(GLenum param, GLuint index, GLint* val) {
ScopedQueryUpdate<GLint> query(this, sizeof(GLint), val);
m_glGetIntegeri_v_enc(this, param, index, query.hostStagingBuffer());
}
void GL2Encoder::safe_glGetInteger64i_v(GLenum param, GLuint index, GLint64* val) {
ScopedQueryUpdate<GLint64> query(this, sizeof(GLint64), val);
m_glGetInteger64i_v_enc(this, param, index, query.hostStagingBuffer());
}
void GL2Encoder::safe_glGetBooleani_v(GLenum param, GLuint index, GLboolean* val) {
ScopedQueryUpdate<GLboolean> query(this, sizeof(GLboolean), val);
m_glGetBooleani_v_enc(this, param, index, query.hostStagingBuffer());
}
void GL2Encoder::s_glFlush(void *self)
{
GL2Encoder *ctx = (GL2Encoder *) self;
ctx->m_glFlush_enc(self);
ctx->m_stream->flush();
}
const GLubyte *GL2Encoder::s_glGetString(void *self, GLenum name)
{
GL2Encoder *ctx = (GL2Encoder *)self;
GLubyte *retval = (GLubyte *) "";
RET_AND_SET_ERROR_IF(
name != GL_VENDOR &&
name != GL_RENDERER &&
name != GL_VERSION &&
name != GL_EXTENSIONS,
GL_INVALID_ENUM,
retval);
switch(name) {
case GL_VENDOR:
retval = gVendorString;
break;
case GL_RENDERER:
retval = gRendererString;
break;
case GL_VERSION:
retval = gVersionString;
break;
case GL_EXTENSIONS:
retval = gExtensionsString;
break;
}
return retval;
}
void GL2Encoder::s_glPixelStorei(void *self, GLenum param, GLint value)
{
GL2Encoder *ctx = (GL2Encoder *)self;
SET_ERROR_IF(!GLESv2Validation::pixelStoreParam(ctx, param), GL_INVALID_ENUM);
SET_ERROR_IF(!GLESv2Validation::pixelStoreValue(param, value), GL_INVALID_VALUE);
ctx->m_glPixelStorei_enc(ctx, param, value);
assert(ctx->m_state != NULL);
ctx->m_state->setPixelStore(param, value);
}
void GL2Encoder::s_glBindBuffer(void *self, GLenum target, GLuint id)
{
GL2Encoder *ctx = (GL2Encoder *) self;
assert(ctx->m_state != NULL);
SET_ERROR_IF(!GLESv2Validation::bufferTarget(ctx, target), GL_INVALID_ENUM);
bool nop = ctx->m_state->isNonIndexedBindNoOp(target, id);
if (nop) return;
ctx->m_state->bindBuffer(target, id);
ctx->m_state->addBuffer(id);
ctx->m_glBindBuffer_enc(ctx, target, id);
ctx->m_state->setLastEncodedBufferBind(target, id);
}
void GL2Encoder::doBindBufferEncodeCached(GLenum target, GLuint id) {
bool encode = id != m_state->getLastEncodedBufferBind(target);
if (encode) {
m_glBindBuffer_enc(this, target, id);
}
m_state->setLastEncodedBufferBind(target, id);
}
void GL2Encoder::s_glBufferData(void * self, GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage)
{
GL2Encoder *ctx = (GL2Encoder *) self;
SET_ERROR_IF(!GLESv2Validation::bufferTarget(ctx, target), GL_INVALID_ENUM);
GLuint bufferId = ctx->m_state->getBuffer(target);
SET_ERROR_IF(bufferId==0, GL_INVALID_OPERATION);
SET_ERROR_IF(size<0, GL_INVALID_VALUE);
ctx->m_shared->updateBufferData(bufferId, size, data);
ctx->m_shared->setBufferUsage(bufferId, usage);
ctx->m_glBufferData_enc(self, target, size, data, usage);
}
void GL2Encoder::s_glBufferSubData(void * self, GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data)
{
GL2Encoder *ctx = (GL2Encoder *) self;
SET_ERROR_IF(!GLESv2Validation::bufferTarget(ctx, target), GL_INVALID_ENUM);
GLuint bufferId = ctx->m_state->getBuffer(target);
SET_ERROR_IF(bufferId==0, GL_INVALID_OPERATION);
SET_ERROR_IF(ctx->isBufferTargetMapped(target), GL_INVALID_OPERATION);
GLenum res = ctx->m_shared->subUpdateBufferData(bufferId, offset, size, data);
SET_ERROR_IF(res, res);
ctx->m_glBufferSubData_enc(self, target, offset, size, data);
}
void GL2Encoder::s_glGenBuffers(void* self, GLsizei n, GLuint* buffers) {
GL2Encoder *ctx = (GL2Encoder *) self;
SET_ERROR_IF(n<0, GL_INVALID_VALUE);
ctx->m_glGenBuffers_enc(self, n, buffers);
for (int i = 0; i < n; i++) {
ctx->m_state->addBuffer(buffers[i]);
}
}
void GL2Encoder::s_glDeleteBuffers(void * self, GLsizei n, const GLuint * buffers)
{
GL2Encoder *ctx = (GL2Encoder *) self;
SET_ERROR_IF(n<0, GL_INVALID_VALUE);
for (int i=0; i<n; i++) {
// Technically if the buffer is mapped, we should unmap it, but we won't
// use it anymore after this :)
ctx->m_shared->deleteBufferData(buffers[i]);
ctx->m_state->unBindBuffer(buffers[i]);
ctx->m_state->removeBuffer(buffers[i]);
ctx->m_glDeleteBuffers_enc(self,1,&buffers[i]);
}
}
static bool isValidVertexAttribIndex(void *self, GLuint indx)
{
GL2Encoder *ctx = (GL2Encoder *)self;
GLint maxIndex;
ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
return indx < maxIndex;
}
#define VALIDATE_VERTEX_ATTRIB_INDEX(index) \
SET_ERROR_WITH_MESSAGE_IF( \
!isValidVertexAttribIndex(self, index), GL_INVALID_VALUE, \
GLESv2Validation::vertexAttribIndexRangeErrorMsg, (ctx, index)); \
void GL2Encoder::s_glVertexAttribPointer(void *self, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * ptr)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state != NULL);
VALIDATE_VERTEX_ATTRIB_INDEX(indx);
SET_ERROR_IF((size < 1 || size > 4), GL_INVALID_VALUE);
SET_ERROR_IF(!GLESv2Validation::vertexAttribType(ctx, type), GL_INVALID_ENUM);
SET_ERROR_IF(stride < 0, GL_INVALID_VALUE);
SET_ERROR_IF((type == GL_INT_2_10_10_10_REV ||
type == GL_UNSIGNED_INT_2_10_10_10_REV) &&
size != 4,
GL_INVALID_OPERATION);
ctx->m_state->setVertexAttribBinding(indx, indx);
ctx->m_state->setVertexAttribFormat(indx, size, type, normalized, 0, false);
GLsizei effectiveStride = stride;
if (stride == 0) {
effectiveStride = glSizeof(type) * size;
switch (type) {
case GL_INT_2_10_10_10_REV:
case GL_UNSIGNED_INT_2_10_10_10_REV:
effectiveStride /= 4;
break;
default:
break;
}
}
ctx->m_state->bindIndexedBuffer(0, indx, ctx->m_state->currentArrayVbo(), (uintptr_t)ptr, 0, stride, effectiveStride);
if (ctx->m_state->currentArrayVbo() != 0) {
ctx->glVertexAttribPointerOffset(ctx, indx, size, type, normalized, stride, (uintptr_t)ptr);
} else {
SET_ERROR_IF(ctx->m_state->currentVertexArrayObject() != 0 && ptr, GL_INVALID_OPERATION);
// wait for client-array handler
}
}
void GL2Encoder::s_glGetIntegerv(void *self, GLenum param, GLint *ptr)
{
GL2Encoder *ctx = (GL2Encoder *) self;
GLClientState* state = ctx->m_state;
switch (param) {
case GL_NUM_EXTENSIONS:
*ptr = (int)ctx->m_currExtensionsArray.size();
break;
case GL_MAJOR_VERSION:
*ptr = ctx->m_deviceMajorVersion;
break;
case GL_MINOR_VERSION:
*ptr = ctx->m_deviceMinorVersion;
break;
case GL_NUM_SHADER_BINARY_FORMATS:
*ptr = 0;
break;
case GL_SHADER_BINARY_FORMATS:
// do nothing
break;
case GL_COMPRESSED_TEXTURE_FORMATS: {
GLint *compressedTextureFormats = ctx->getCompressedTextureFormats();
if (ctx->m_num_compressedTextureFormats > 0 &&
compressedTextureFormats != NULL) {
memcpy(ptr, compressedTextureFormats,
ctx->m_num_compressedTextureFormats * sizeof(GLint));
}
break;
}
case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
if (ctx->m_max_combinedTextureImageUnits != 0) {
*ptr = ctx->m_max_combinedTextureImageUnits;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_combinedTextureImageUnits = *ptr;
}
break;
case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
if (ctx->m_max_vertexTextureImageUnits != 0) {
*ptr = ctx->m_max_vertexTextureImageUnits;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_vertexTextureImageUnits = *ptr;
}
break;
case GL_MAX_TEXTURE_IMAGE_UNITS:
if (ctx->m_max_textureImageUnits != 0) {
*ptr = ctx->m_max_textureImageUnits;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_textureImageUnits = *ptr;
}
break;
case GL_TEXTURE_BINDING_2D:
SET_ERROR_IF(!state, GL_INVALID_OPERATION);
*ptr = state->getBoundTexture(GL_TEXTURE_2D);
break;
case GL_TEXTURE_BINDING_EXTERNAL_OES:
SET_ERROR_IF(!state, GL_INVALID_OPERATION);
*ptr = state->getBoundTexture(GL_TEXTURE_EXTERNAL_OES);
break;
case GL_MAX_VERTEX_ATTRIBS:
SET_ERROR_IF(!state, GL_INVALID_OPERATION);
if (!state->getClientStateParameter<GLint>(param, ptr)) {
ctx->safe_glGetIntegerv(param, ptr);
state->setMaxVertexAttribs(*ptr);
}
break;
case GL_MAX_VERTEX_ATTRIB_STRIDE:
if (ctx->m_max_vertexAttribStride != 0) {
*ptr = ctx->m_max_vertexAttribStride;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_vertexAttribStride = *ptr;
}
break;
case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
if (ctx->m_max_cubeMapTextureSize != 0) {
*ptr = ctx->m_max_cubeMapTextureSize;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_cubeMapTextureSize = *ptr;
}
break;
case GL_MAX_RENDERBUFFER_SIZE:
if (ctx->m_max_renderBufferSize != 0) {
*ptr = ctx->m_max_renderBufferSize;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_renderBufferSize = *ptr;
}
break;
case GL_MAX_TEXTURE_SIZE:
if (ctx->m_max_textureSize != 0) {
*ptr = ctx->m_max_textureSize;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_textureSize = *ptr;
}
break;
case GL_MAX_3D_TEXTURE_SIZE:
if (ctx->m_max_3d_textureSize != 0) {
*ptr = ctx->m_max_3d_textureSize;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_3d_textureSize = *ptr;
}
break;
case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT:
if (ctx->m_ssbo_offset_align != 0) {
*ptr = ctx->m_ssbo_offset_align;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_ssbo_offset_align = *ptr;
}
break;
case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
if (ctx->m_ubo_offset_align != 0) {
*ptr = ctx->m_ubo_offset_align;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_ubo_offset_align = *ptr;
}
break;
// Desktop OpenGL can allow a mindboggling # samples per pixel (such as 64).
// Limit to 4 (spec minimum) to keep dEQP tests from timing out.
case GL_MAX_SAMPLES:
case GL_MAX_COLOR_TEXTURE_SAMPLES:
case GL_MAX_INTEGER_SAMPLES:
case GL_MAX_DEPTH_TEXTURE_SAMPLES:
*ptr = 4;
break;
// Checks for version-incompatible enums.
// Not allowed in vanilla ES 2.0.
case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
SET_ERROR_IF(ctx->majorVersion() < 3, GL_INVALID_ENUM);
if (ctx->m_max_transformFeedbackSeparateAttribs != 0) {
*ptr = ctx->m_max_transformFeedbackSeparateAttribs;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_transformFeedbackSeparateAttribs = *ptr;
}
break;
case GL_MAX_UNIFORM_BUFFER_BINDINGS:
SET_ERROR_IF(ctx->majorVersion() < 3, GL_INVALID_ENUM);
if (ctx->m_max_uniformBufferBindings != 0) {
*ptr = ctx->m_max_uniformBufferBindings;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_uniformBufferBindings = *ptr;
}
break;
case GL_MAX_COLOR_ATTACHMENTS:
SET_ERROR_IF(ctx->majorVersion() < 3 &&
!ctx->hasExtension("GL_EXT_draw_buffers"), GL_INVALID_ENUM);
if (ctx->m_max_colorAttachments != 0) {
*ptr = ctx->m_max_colorAttachments;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_colorAttachments = *ptr;
}
break;
case GL_MAX_DRAW_BUFFERS:
SET_ERROR_IF(ctx->majorVersion() < 3 &&
!ctx->hasExtension("GL_EXT_draw_buffers"), GL_INVALID_ENUM);
if (ctx->m_max_drawBuffers != 0) {
*ptr = ctx->m_max_drawBuffers;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_drawBuffers = *ptr;
}
break;
// Not allowed in ES 3.0.
case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS:
SET_ERROR_IF(ctx->majorVersion() < 3 ||
(ctx->majorVersion() == 3 &&
ctx->minorVersion() == 0), GL_INVALID_ENUM);
if (ctx->m_max_atomicCounterBufferBindings != 0) {
*ptr = ctx->m_max_atomicCounterBufferBindings;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_atomicCounterBufferBindings = *ptr;
}
break;
case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS:
SET_ERROR_IF(ctx->majorVersion() < 3 ||
(ctx->majorVersion() == 3 &&
ctx->minorVersion() == 0), GL_INVALID_ENUM);
if (ctx->m_max_shaderStorageBufferBindings != 0) {
*ptr = ctx->m_max_shaderStorageBufferBindings;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_shaderStorageBufferBindings = *ptr;
}
break;
case GL_MAX_VERTEX_ATTRIB_BINDINGS:
SET_ERROR_IF(ctx->majorVersion() < 3 ||
(ctx->majorVersion() == 3 &&
ctx->minorVersion() == 0), GL_INVALID_ENUM);
if (ctx->m_max_vertexAttribBindings != 0) {
*ptr = ctx->m_max_vertexAttribBindings;
} else {
ctx->safe_glGetIntegerv(param, ptr);
ctx->m_max_vertexAttribBindings = *ptr;
}
break;
case GL_RESET_NOTIFICATION_STRATEGY_EXT:
// BUG: 121414786
*ptr = GL_LOSE_CONTEXT_ON_RESET_EXT;
break;
default:
SET_ERROR_IF(!state, GL_INVALID_OPERATION);
if (!state->getClientStateParameter<GLint>(param, ptr)) {
ctx->safe_glGetIntegerv(param, ptr);
}
break;
}
}
void GL2Encoder::s_glGetFloatv(void *self, GLenum param, GLfloat *ptr)
{
GL2Encoder *ctx = (GL2Encoder *)self;
GLClientState* state = ctx->m_state;
switch (param) {
case GL_NUM_SHADER_BINARY_FORMATS:
*ptr = 0;
break;
case GL_SHADER_BINARY_FORMATS:
// do nothing
break;
case GL_COMPRESSED_TEXTURE_FORMATS: {
GLint *compressedTextureFormats = ctx->getCompressedTextureFormats();
if (ctx->m_num_compressedTextureFormats > 0 &&
compressedTextureFormats != NULL) {
for (int i = 0; i < ctx->m_num_compressedTextureFormats; i++) {
ptr[i] = (GLfloat) compressedTextureFormats[i];
}
}
break;
}
case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
case GL_MAX_TEXTURE_IMAGE_UNITS:
case GL_MAX_VERTEX_ATTRIBS:
case GL_MAX_VERTEX_ATTRIB_STRIDE:
case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
case GL_MAX_RENDERBUFFER_SIZE:
case GL_MAX_TEXTURE_SIZE:
case GL_MAX_3D_TEXTURE_SIZE:
case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT:
case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
case GL_MAX_SAMPLES:
case GL_MAX_COLOR_TEXTURE_SAMPLES:
case GL_MAX_INTEGER_SAMPLES:
case GL_MAX_DEPTH_TEXTURE_SAMPLES:
case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
case GL_MAX_UNIFORM_BUFFER_BINDINGS:
case GL_MAX_COLOR_ATTACHMENTS:
case GL_MAX_DRAW_BUFFERS:
case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS:
case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS:
case GL_MAX_VERTEX_ATTRIB_BINDINGS:
case GL_TEXTURE_BINDING_2D:
case GL_TEXTURE_BINDING_EXTERNAL_OES: {
GLint res;
s_glGetIntegerv(ctx, param, &res);
*ptr = (GLfloat)res;
break;
}
default:
SET_ERROR_IF(!state, GL_INVALID_OPERATION);
if (!state->getClientStateParameter<GLfloat>(param, ptr)) {
ctx->safe_glGetFloatv(param, ptr);
}
break;
}
}
void GL2Encoder::s_glGetBooleanv(void *self, GLenum param, GLboolean *ptr)
{
GL2Encoder *ctx = (GL2Encoder *)self;
GLClientState* state = ctx->m_state;
switch (param) {
case GL_NUM_SHADER_BINARY_FORMATS:
*ptr = GL_FALSE;
break;
case GL_SHADER_BINARY_FORMATS:
// do nothing
break;
case GL_COMPRESSED_TEXTURE_FORMATS: {
GLint *compressedTextureFormats = ctx->getCompressedTextureFormats();
if (ctx->m_num_compressedTextureFormats > 0 &&
compressedTextureFormats != NULL) {
for (int i = 0; i < ctx->m_num_compressedTextureFormats; i++) {
ptr[i] = compressedTextureFormats[i] != 0 ? GL_TRUE : GL_FALSE;
}
}
break;
}
case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
case GL_MAX_TEXTURE_IMAGE_UNITS:
case GL_MAX_VERTEX_ATTRIBS:
case GL_MAX_VERTEX_ATTRIB_STRIDE:
case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
case GL_MAX_RENDERBUFFER_SIZE:
case GL_MAX_TEXTURE_SIZE:
case GL_MAX_3D_TEXTURE_SIZE:
case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT:
case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
case GL_MAX_SAMPLES:
case GL_MAX_COLOR_TEXTURE_SAMPLES:
case GL_MAX_INTEGER_SAMPLES:
case GL_MAX_DEPTH_TEXTURE_SAMPLES:
case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
case GL_MAX_UNIFORM_BUFFER_BINDINGS:
case GL_MAX_COLOR_ATTACHMENTS:
case GL_MAX_DRAW_BUFFERS:
case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS:
case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS:
case GL_MAX_VERTEX_ATTRIB_BINDINGS:
case GL_TEXTURE_BINDING_2D:
case GL_TEXTURE_BINDING_EXTERNAL_OES: {
GLint res;
s_glGetIntegerv(ctx, param, &res);
*ptr = res == 0 ? GL_FALSE : GL_TRUE;
break;
}
default:
SET_ERROR_IF(!state, GL_INVALID_OPERATION);
if (!state->getClientStateParameter<GLboolean>(param, ptr)) {
ctx->safe_glGetBooleanv(param, ptr);
}
*ptr = (*ptr != 0) ? GL_TRUE : GL_FALSE;
break;
}
}
void GL2Encoder::s_glEnableVertexAttribArray(void *self, GLuint index)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state);
VALIDATE_VERTEX_ATTRIB_INDEX(index);
ctx->m_glEnableVertexAttribArray_enc(ctx, index);
ctx->m_state->enable(index, 1);
}
void GL2Encoder::s_glDisableVertexAttribArray(void *self, GLuint index)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state);
VALIDATE_VERTEX_ATTRIB_INDEX(index);
ctx->m_glDisableVertexAttribArray_enc(ctx, index);
ctx->m_state->enable(index, 0);
}
void GL2Encoder::s_glGetVertexAttribiv(void *self, GLuint index, GLenum pname, GLint *params)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state);
GLint maxIndex;
ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
SET_ERROR_IF(!(index < maxIndex), GL_INVALID_VALUE);
if (!ctx->m_state->getVertexAttribParameter<GLint>(index, pname, params)) {
ctx->m_glGetVertexAttribiv_enc(self, index, pname, params);
}
}
void GL2Encoder::s_glGetVertexAttribfv(void *self, GLuint index, GLenum pname, GLfloat *params)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state);
GLint maxIndex;
ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
SET_ERROR_IF(!(index < maxIndex), GL_INVALID_VALUE);
if (!ctx->m_state->getVertexAttribParameter<GLfloat>(index, pname, params)) {
ctx->m_glGetVertexAttribfv_enc(self, index, pname, params);
}
}
void GL2Encoder::s_glGetVertexAttribPointerv(void *self, GLuint index, GLenum pname, GLvoid **pointer)
{
GL2Encoder *ctx = (GL2Encoder *)self;
if (ctx->m_state == NULL) return;
GLint maxIndex;
ctx->glGetIntegerv(self, GL_MAX_VERTEX_ATTRIBS, &maxIndex);
SET_ERROR_IF(!(index < maxIndex), GL_INVALID_VALUE);
SET_ERROR_IF(pname != GL_VERTEX_ATTRIB_ARRAY_POINTER, GL_INVALID_ENUM);
(void)pname;
*pointer = (GLvoid*)(ctx->m_state->getCurrAttributeBindingInfo(index).offset);
}
void GL2Encoder::calcIndexRange(const void* indices,
GLenum type,
GLsizei count,
int* minIndex_out,
int* maxIndex_out) {
switch(type) {
case GL_BYTE:
case GL_UNSIGNED_BYTE:
GLUtils::minmaxExcept(
(unsigned char *)indices, count,
minIndex_out, maxIndex_out,
m_primitiveRestartEnabled, GLUtils::primitiveRestartIndex<unsigned char>());
break;
case GL_SHORT:
case GL_UNSIGNED_SHORT:
GLUtils::minmaxExcept(
(unsigned short *)indices, count,
minIndex_out, maxIndex_out,
m_primitiveRestartEnabled, GLUtils::primitiveRestartIndex<unsigned short>());
break;
case GL_INT:
case GL_UNSIGNED_INT:
GLUtils::minmaxExcept(
(unsigned int *)indices, count,
minIndex_out, maxIndex_out,
m_primitiveRestartEnabled, GLUtils::primitiveRestartIndex<unsigned int>());
break;
default:
ALOGE("unsupported index buffer type %d\n", type);
}
}
void* GL2Encoder::recenterIndices(const void* src,
GLenum type,
GLsizei count,
int minIndex) {
void* adjustedIndices = (void*)src;
if (minIndex != 0) {
adjustedIndices = m_fixedBuffer.alloc(glSizeof(type) * count);
switch(type) {
case GL_BYTE:
case GL_UNSIGNED_BYTE:
GLUtils::shiftIndicesExcept(
(unsigned char *)src,
(unsigned char *)adjustedIndices,
count, -minIndex,
m_primitiveRestartEnabled,
(unsigned char)m_primitiveRestartIndex);
break;
case GL_SHORT:
case GL_UNSIGNED_SHORT:
GLUtils::shiftIndicesExcept(
(unsigned short *)src,
(unsigned short *)adjustedIndices,
count, -minIndex,
m_primitiveRestartEnabled,
(unsigned short)m_primitiveRestartIndex);
break;
case GL_INT:
case GL_UNSIGNED_INT:
GLUtils::shiftIndicesExcept(
(unsigned int *)src,
(unsigned int *)adjustedIndices,
count, -minIndex,
m_primitiveRestartEnabled,
(unsigned int)m_primitiveRestartIndex);
break;
default:
ALOGE("unsupported index buffer type %d\n", type);
}
}
return adjustedIndices;
}
void GL2Encoder::getBufferIndexRange(BufferData* buf,
const void* dataWithOffset,
GLenum type,
size_t count,
size_t offset,
int* minIndex_out,
int* maxIndex_out) {
if (buf->m_indexRangeCache.findRange(
type, offset, count,
m_primitiveRestartEnabled,
minIndex_out,
maxIndex_out)) {
return;
}
calcIndexRange(dataWithOffset, type, count, minIndex_out, maxIndex_out);
buf->m_indexRangeCache.addRange(
type, offset, count, m_primitiveRestartEnabled,
*minIndex_out, *maxIndex_out);
ALOGV("%s: got range [%u %u] pr? %d", __FUNCTION__, *minIndex_out, *maxIndex_out, m_primitiveRestartEnabled);
}
// 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) {
const GLClientState::BufferBinding& curr_binding = m_state->getCurrAttributeBindingInfo(i);
GLuint bufferObject = curr_binding.buffer;
if (bufferObject == 0 && curr_binding.offset && hasClientArrays) {
*hasClientArrays = true;
}
if (bufferObject != 0 && hasVBOs) {
*hasVBOs = true;
}
}
}
}
void GL2Encoder::sendVertexAttributes(GLint first, GLsizei count, bool hasClientArrays, GLsizei primcount)
{
assert(m_state);
m_state->updateEnableDirtyArrayForDraw();
GLuint currentVao = m_state->currentVertexArrayObject();
GLuint lastBoundVbo = m_state->currentArrayVbo();
const GLClientState::VAOState& vaoState = m_state->currentVaoState();
for (int k = 0; k < vaoState.numAttributesNeedingUpdateForDraw; k++) {
int i = vaoState.attributesNeedingUpdateForDraw[k];
const GLClientState::VertexAttribState& state = vaoState.attribState[i];
if (state.enabled) {
const GLClientState::BufferBinding& curr_binding = m_state->getCurrAttributeBindingInfo(i);
GLuint bufferObject = curr_binding.buffer;
if (hasClientArrays && lastBoundVbo != bufferObject) {
doBindBufferEncodeCached(GL_ARRAY_BUFFER, bufferObject);
lastBoundVbo = bufferObject;
}
int divisor = curr_binding.divisor;
int stride = curr_binding.stride;
int effectiveStride = curr_binding.effectiveStride;
uintptr_t offset = curr_binding.offset;
int firstIndex = effectiveStride * first;
if (firstIndex && divisor && !primcount) {
// If firstIndex != 0 according to effectiveStride * first,
// it needs to be adjusted if a divisor has been specified,
// even if we are not in glDraw***Instanced.
firstIndex = 0;
}
if (bufferObject == 0) {
unsigned int datalen = state.elementSize * count;
if (divisor) {
ALOGV("%s: divisor for att %d: %d, w/ stride %d (effective stride %d) size %d type 0x%x) datalen %u",
__FUNCTION__, i, divisor, state.stride, effectiveStride, state.elementSize, state.type, datalen);
int actual_count = std::max(1, (int)((primcount + divisor - 1) / divisor));
datalen = state.elementSize * actual_count;
ALOGV("%s: actual datalen %u", __FUNCTION__, datalen);
}
if (state.elementSize == 0) {
// The vertex attribute array is uninitialized. Abandon it.
ALOGE("a vertex attribute array is uninitialized. Skipping corresponding vertex attribute.");
this->m_glDisableVertexAttribArray_enc(this, i);
continue;
}
m_glEnableVertexAttribArray_enc(this, i);
if (datalen && (!offset || !((unsigned char*)offset + firstIndex))) {
ALOGD("%s: bad offset / len!!!!!", __FUNCTION__);
continue;
}
if (state.isInt) {
this->glVertexAttribIPointerDataAEMU(this, i, state.size, state.type, stride, (unsigned char *)offset + firstIndex, datalen);
} else {
this->glVertexAttribPointerData(this, i, state.size, state.type, state.normalized, stride, (unsigned char *)offset + firstIndex, datalen);
}
} else {
const BufferData* buf = m_shared->getBufferData(bufferObject);
// The following expression actually means bufLen = stride*count;
// But the last element doesn't have to fill up the whole stride.
// So it becomes the current form.
unsigned int bufLen = effectiveStride * (count ? (count - 1) : 0) + state.elementSize;
if (divisor) {
int actual_count = std::max(1, (int)((primcount + divisor - 1) / divisor));
bufLen = effectiveStride * (actual_count ? (actual_count - 1) : 0) + state.elementSize;
}
if (buf && firstIndex >= 0 && firstIndex + bufLen <= buf->m_size) {
if (hasClientArrays) {
m_glEnableVertexAttribArray_enc(this, i);
if (state.isInt) {
this->glVertexAttribIPointerOffsetAEMU(this, i, state.size, state.type, stride, offset + firstIndex);
} else {
this->glVertexAttribPointerOffset(this, i, state.size, state.type, state.normalized, stride, offset + firstIndex);
}
}
} else {
ALOGE("a vertex attribute index out of boundary is detected. Skipping corresponding vertex attribute. buf=%p", buf);
if (buf) {
ALOGE("Out of bounds vertex attribute info: "
"clientArray? %d attribute %d vbo %u allocedBufferSize %u bufferDataSpecified? %d wantedStart %u wantedEnd %u",
hasClientArrays, i, bufferObject, (unsigned int)buf->m_size, buf != NULL, firstIndex, firstIndex + bufLen);
}
m_glDisableVertexAttribArray_enc(this, i);
}
}
} else {
if (hasClientArrays) {
this->m_glDisableVertexAttribArray_enc(this, i);
}
}
}
if (hasClientArrays && lastBoundVbo != m_state->currentArrayVbo()) {
doBindBufferEncodeCached(GL_ARRAY_BUFFER, m_state->currentArrayVbo());
}
}
void GL2Encoder::flushDrawCall() {
// This used to be every other draw call, but
// now that we are using real GPU buffers on host,
// set this to every 200 draw calls
// (tuned on z840 linux NVIDIA Quadro K2200)
if (m_drawCallFlushCount % 200 == 0) {
m_stream->flush();
}
m_drawCallFlushCount++;
}
static bool isValidDrawMode(GLenum mode)
{
bool retval = false;
switch (mode) {
case GL_POINTS:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
case GL_LINES:
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_TRIANGLES:
retval = true;
}
return retval;
}
void GL2Encoder::s_glDrawArrays(void *self, GLenum mode, GLint first, GLsizei count)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state != NULL);
SET_ERROR_IF(!isValidDrawMode(mode), GL_INVALID_ENUM);
SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
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);
}
}
void GL2Encoder::s_glDrawElements(void *self, GLenum mode, GLsizei count, GLenum type, const void *indices)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state != NULL);
SET_ERROR_IF(!isValidDrawMode(mode), GL_INVALID_ENUM);
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);
SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
bool has_client_vertex_arrays = false;
bool has_indirect_arrays = false;
int nLocations = ctx->m_state->nLocations();
GLintptr offset = 0;
ctx->getVBOUsage(&has_client_vertex_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);
}
BufferData* buf = NULL;
int minIndex = 0, maxIndex = 0;
// For validation/immediate index array purposes,
// we need the min/max vertex index of the index array.
// If the VBO != 0, this may not be the first time we have
// used this particular index buffer. getBufferIndexRange
// can more quickly get min/max vertex index by
// caching previous results.
if (ctx->m_state->currentIndexVbo() != 0) {
buf = ctx->m_shared->getBufferData(ctx->m_state->currentIndexVbo());
offset = (GLintptr)indices;
indices = (void*)((GLintptr)buf->m_fixedBuffer.ptr() + (GLintptr)indices);
ctx->getBufferIndexRange(buf,
indices,
type,
(size_t)count,
(size_t)offset,
&minIndex, &maxIndex);
} else {
// In this case, the |indices| field holds a real
// array, so calculate the indices now. They will
// also be needed to know how much data to
// transfer to host.
ctx->calcIndexRange(indices,
type,
count,
&minIndex,
&maxIndex);
}
if (count == 0) return;
bool adjustIndices = true;
if (ctx->m_state->currentIndexVbo() != 0) {
if (!has_client_vertex_arrays) {
ctx->sendVertexAttributes(0, maxIndex + 1, false);
ctx->doBindBufferEncodeCached(GL_ELEMENT_ARRAY_BUFFER, ctx->m_state->currentIndexVbo());
ctx->glDrawElementsOffset(ctx, mode, count, type, offset);
ctx->flushDrawCall();
adjustIndices = false;
} else {
BufferData * buf = ctx->m_shared->getBufferData(ctx->m_state->currentIndexVbo());
ctx->doBindBufferEncodeCached(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
if (adjustIndices) {
void *adjustedIndices =
ctx->recenterIndices(indices,
type,
count,
minIndex);
if (has_indirect_arrays || 1) {
ctx->sendVertexAttributes(minIndex, maxIndex - minIndex + 1, true);
ctx->glDrawElementsData(ctx, mode, count, type, adjustedIndices,
count * glSizeof(type));
// XXX - OPTIMIZATION (see the other else branch) should be implemented
if(!has_indirect_arrays) {
//ALOGD("unoptimized drawelements !!!\n");
}
} else {
// we are all direct arrays and immidate mode index array -
// rebuild the arrays and the index array;
ALOGE("glDrawElements: direct index & direct buffer data - will be implemented in later versions;\n");
}
}
}
void GL2Encoder::s_glDrawArraysNullAEMU(void *self, GLenum mode, GLint first, GLsizei count)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state != NULL);
SET_ERROR_IF(!isValidDrawMode(mode), GL_INVALID_ENUM);
SET_ERROR_IF(count < 0, GL_INVALID_VALUE);
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_glDrawArraysNullAEMU_enc(ctx, mode, 0, count);
} else {
ctx->sendVertexAttributes(0, count, false);
ctx->m_glDrawArraysNullAEMU_enc(ctx, mode, first, count);
}
}
void GL2Encoder::s_glDrawElementsNullAEMU(void *self, GLenum mode, GLsizei count, GLenum type, const void *indices)
{
GL2Encoder *ctx = (GL2Encoder *)self;
assert(ctx->m_state != NULL);
SET_ERROR_IF(!isValidDrawMode(mode), GL_INVALID_ENUM);
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);
SET_ERROR_IF(ctx->m_state->getTransformFeedbackActiveUnpaused(), GL_INVALID_OPERATION);
bool has_client_vertex_arrays = false;
bool has_indirect_arrays = false;
int nLocations = ctx->m_state->nLocations();
GLintptr offset = 0;
ctx->getVBOUsage(&has_client_vertex_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);
}
BufferData* buf = NULL;
int minIndex = 0, maxIndex = 0;
// For validation/immediate index array purposes,
// we need the min/max vertex index of the index array.
// If the VBO != 0, this may not be the first time we have
// used this particular index buffer. getBufferIndexRange
// can more quickly get min/max vertex index by
// caching previous results.
if (ctx->m_state->currentIndexVbo() != 0) {
buf = ctx->m_shared->getBufferData(ctx->m_state->currentIndexVbo());
offset = (GLintptr)indices;
indices = (void*)((GLintptr)buf->m_fixedBuffer.ptr() + (GLintptr)indices);
ctx->getBufferIndexRange(buf,
indices,
type,
(size_t)count,
(size_t)offset,
&minIndex, &maxIndex);
} else {
// In this case, the |indices| field holds a real
// array, so calculate the indices now. They will
// also be needed to know how much data to
// transfer to host.
ctx->calcIndexRange(indices,
type,
count,
&minIndex,
&maxIndex);
}
if (count == 0) return;
bool adjustIndices = true;
if (ctx->m_state->currentIndexVbo() != 0) {
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->glDrawElementsOffsetNullAEMU(ctx, mode, count, type, offset);
ctx->flushDrawCall();
adjustIndices = false;
} else {
BufferData * buf = ctx->m_shared->getBufferData(ctx->m_state->currentIndexVbo());
ctx->m_glBindBuffer_enc(self, GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
if (adjustIndices) {
void *adjustedIndices =
ctx->recenterIndices(indices,
type,
count,
minIndex);
if (has_indirect_arrays || 1) {
ctx->sendVertexAttributes(minIndex, maxIndex - minIndex + 1, true);
ctx->glDrawElementsDataNullAEMU(ctx, mode, count, type, adjustedIndices,
count * glSizeof(type));
// XXX - OPTIMIZATION (see the other else branch) should be implemented
if(!has_indirect_arrays) {
//ALOGD("unoptimized drawelements !!!\n");
}
} else {
// we are all direct arrays and immidate mode index array -
// rebuild the arrays and the index array;
ALOGE("glDrawElementsNullAEMU: direct index & direct buffer data - will be implemented in later versions;\n");
}
}
}
GLint * GL2Encoder::getCompressedTextureFormats()
{
if (m_compressedTextureFormats == NULL) {
this->glGetIntegerv(this, GL_NUM_COMPRESSED_TEXTURE_FORMATS,
&m_num_compressedTextureFormats);
if (m_num_compressedTextureFormats > 0) {
// get number of texture formats;
m_compressedTextureFormats = new GLint[m_num_compressedTextureFormats];
this->glGetCompressedTextureFormats(this, m_num_compressedTextureFormats, m_compressedTextureFormats);
}
}
return m_compressedTextureFormats;
}
// Replace uses of samplerExternalOES with sampler2D, recording the names of
// modified shaders in data. Also remove
// #extension GL_OES_EGL_image_external : require
// statements.
//
// This implementation assumes the input has already been pre-processed. If not,
// a few cases will be mishandled:
//
// 1. "mySampler" will be incorrectly recorded as being a samplerExternalOES in
// the following code:
// #if 1
// uniform sampler2D mySampler;
// #else
// uniform samplerExternalOES mySampler;
// #endif
//
// 2. Comments that look like sampler declarations will be incorrectly modified
// and recorded:
// // samplerExternalOES hahaFooledYou
//
// 3. However, GLSL ES does not have a concatentation operator, so things like
// this (valid in C) are invalid and not a problem:
// #define SAMPLER(TYPE, NAME) uniform sampler#TYPE NAME
// SAMPLER(ExternalOES, mySampler);
//
static const char STR_SAMPLER_EXTERNAL_OES[] = "samplerExternalOES";
static const char STR_SAMPLER2D_SPACE[] = "sampler2D ";
static const char STR_DEFINE[] = "#define";
static std::vector<std::string> getSamplerExternalAliases(char* str) {
std::vector<std::string> res;
res.push_back(STR_SAMPLER_EXTERNAL_OES);
// -- capture #define x samplerExternalOES
char* c = str;
while ((c = strstr(c, STR_DEFINE))) {
// Don't push it if samplerExternalOES is not even there.
char* samplerExternalOES_next = strstr(c, STR_SAMPLER_EXTERNAL_OES);
if (!samplerExternalOES_next) break;
bool prevIdent = false;
std::vector<std::string> idents;
std::string curr;
while (*c != '\0') {
if (isspace(*c)) {
if (prevIdent) {
idents.push_back(curr);
curr = "";
}
}
if (*c == '\n' || idents.size() == 3) break;
if (isalpha(*c) || *c == '_') {
curr.push_back(*c);
prevIdent = true;
}
++c;
}
if (idents.size() != 3) continue;
const std::string& defineLhs = idents[1];
const std::string& defineRhs = idents[2];
if (defineRhs == STR_SAMPLER_EXTERNAL_OES) {
res.push_back(defineLhs);
}
if (*c == '\0') break;
}
return res;
}
static bool replaceExternalSamplerUniformDefinition(char* str, const std::string& samplerExternalType, ShaderData* data) {
// -- replace "samplerExternalOES" with "sampler2D" and record name
char* c = str;
while ((c = strstr(c, samplerExternalType.c_str()))) {
// Make sure "samplerExternalOES" isn't a substring of a larger token
if (c == str || !isspace(*(c-1))) {
c++;
continue;
}
char* sampler_start = c;
c += samplerExternalType.size();
if (!isspace(*c) && *c != '\0') {
continue;
}
// capture sampler name
while (isspace(*c) && *c != '\0') {
c++;
}
if (!isalpha(*c) && *c != '_') {
// not an identifier
return false;
}
char* name_start = c;
do {
c++;
} while (isalnum(*c) || *c == '_');
size_t len = (size_t)(c - name_start);
data->samplerExternalNames.push_back(
std::string(name_start, len));
// We only need to perform a string replacement for the original
// occurrence of samplerExternalOES if a #define was used.
//
// The important part was to record the name in
// |data->samplerExternalNames|.
if (samplerExternalType == STR_SAMPLER_EXTERNAL_OES) {
memcpy(sampler_start, STR_SAMPLER2D_SPACE, sizeof(STR_SAMPLER2D_SPACE)-1);
}
}
return true;
}
static bool replaceSamplerExternalWith2D(char* const str, ShaderData* const data)
{
static const char STR_HASH_EXTENSION[] = "#extension";
static const char STR_GL_OES_EGL_IMAGE_EXTERNAL[] = "GL_OES_EGL_image_external";
static const char STR_GL_OES_EGL_IMAGE_EXTERNAL_ESSL3[] = "GL_OES_EGL_image_external_essl3";
// -- overwrite all "#extension GL_OES_EGL_image_external : xxx" statements
char* c = str;
while ((c = strstr(c, STR_HASH_EXTENSION))) {
char* start = c;
c += sizeof(STR_HASH_EXTENSION)-1;
while (isspace(*c) && *c != '\0') {
c++;
}
bool hasBaseImageExternal =
!strncmp(c, STR_GL_OES_EGL_IMAGE_EXTERNAL,
sizeof(STR_GL_OES_EGL_IMAGE_EXTERNAL) - 1);
bool hasEssl3ImageExternal =
!strncmp(c, STR_GL_OES_EGL_IMAGE_EXTERNAL_ESSL3,
sizeof(STR_GL_OES_EGL_IMAGE_EXTERNAL_ESSL3) - 1);
if (hasBaseImageExternal || hasEssl3ImageExternal)
{
// #extension statements are terminated by end of line
c = start;
while (*c != '\0' && *c != '\r' && *c != '\n') {
*c++ = ' ';
}
}
}
std::vector<std::string> samplerExternalAliases =
getSamplerExternalAliases(str);
for (size_t i = 0; i < samplerExternalAliases.size(); i++) {
if (!replaceExternalSamplerUniformDefinition(
str, samplerExternalAliases[i], data))
return false;
}
return true;
}
void GL2Encoder::s_glShaderBinary(void *self, GLsizei n, const GLuint *shaders, GLenum binaryformat, const void* binary, GLsizei length)
{
GL2Encoder* ctx = (GL2Encoder*)self;
// Although it is not supported, need to set proper error code.
SET_ERROR_IF(1, GL_INVALID_ENUM);
}
void GL2Encoder::s_glShaderSource(void *self, GLuint shader, GLsizei count, const GLchar * const *string, const GLint *length)
{
GL2Encoder* ctx = (GL2Encoder*)self;
ShaderData* shaderData = ctx->m_shared->getShaderData(shader);
SET_ERROR_IF(!ctx->m_shared->isShaderOrProgramObject(shader), GL_INVALID_VALUE);
SET_ERROR_IF(!shaderData, GL_INVALID_OPERATION);
SET_ERROR_IF((count<0), GL_INVALID_VALUE);
// Track original sources---they may be translated in the backend
std::vector<std::string> orig_sources;
for (int i = 0; i < count; i++) {
orig_sources.push_back(std::string((const char*)(string[i])));
}
shaderData->sources = orig_sources;
int len = glUtilsCalcShaderSourceLen((char**)string, (GLint*)length, count);
char *str = new char[len + 1];
glUtilsPackStrings(str, (char**)string, (GLint*)length, count);
// TODO: pre-process str before calling replaceSamplerExternalWith2D().
// Perhaps we can borrow Mesa's pre-processor?
if (!replaceSamplerExternalWith2D(str, shaderData)) {
delete[] str;
ctx->setError(GL_OUT_OF_MEMORY);
return;
}
ctx->glShaderString(ctx, shader, str, len + 1);
delete[] str;
}
void GL2Encoder::s_glFinish(void *self)
{
GL2Encoder *ctx = (GL2Encoder *)self;
ctx->glFinishRoundTrip(self);
}
void GL2Encoder::s_glLinkProgram(void * self, GLuint program)
{
GL2Encoder *ctx = (GL2Encoder *)self;
bool isProgram = ctx->m_shared->isProgram(program);
SET_ERROR_IF(!isProgram && !ctx->m_shared->isShader(program), GL_INVALID_VALUE);
SET_ERROR_IF(!isProgram, GL_INVALID_OPERATION);
ctx->m_glLinkProgram_enc(self, program);
GLint linkStatus = 0;
ctx->glGetProgramiv(self, program, GL_LINK_STATUS, &linkStatus);
if (!linkStatus) {
return;
}
//get number of active uniforms in the program
GLint numUniforms=0;
ctx->glGetProgramiv(self, program, GL_ACTIVE_UNIFORMS, &numUniforms);
ctx->m_shared->initProgramData(program,numUniforms);
//get the length of the longest uniform name
GLint maxLength=0;
ctx->glGetProgramiv(self, program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
GLint size;
GLenum type;
GLchar *name = new GLchar[maxLength+1];
GLint location;
//for each active uniform, get its size and starting location.
for (GLint i=0 ; i<numUniforms ; ++i)
{
ctx->glGetActiveUniform(self, program, i, maxLength, NULL, &size, &type, name);
location = ctx->m_glGetUniformLocation_enc(self, program, name);
ctx->m_shared->setProgramIndexInfo(program, i, location, size, type, name);
}
ctx->m_shared->setupLocationShiftWAR(program);
delete[] name;
}
void GL2Encoder::s_glDeleteProgram(void *self, GLuint program)
{
GL2Encoder *ctx = (GL2Encoder*)self;
ctx->m_glDeleteProgram_enc(self, program);
ctx->m_shared->deleteProgramData(program);
}
void GL2Encoder::s_glGetUniformiv(void *self, GLuint program, GLint location, GLint* params)
{
GL2Encoder *ctx = (GL2Encoder*)self;
SET_ERROR_IF(!ctx->m_shared->isShaderOrProgramObject(program), GL_INVALID_VALUE);
SET_ERROR_IF(!ctx->m_shared->isProgram(program), GL_INVALID_OPERATION);
SET_ERROR_IF(!ctx->m_shared->isProgramInitialized(program), GL_INVALID_OPERATION);
GLint hostLoc = ctx->m_shared->locationWARAppToHost(program, location);
SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,hostLoc)==0, GL_INVALID_OPERATION);
ctx->m_glGetUniformiv_enc(self, program, hostLoc, params);
}
void GL2Encoder::s_glGetUniformfv(void *self, GLuint program, GLint location, GLfloat* params)
{
GL2Encoder *ctx = (GL2Encoder*)self;
SET_ERROR_IF(!ctx->m_shared->isShaderOrProgramObject(program), GL_INVALID_VALUE);
SET_ERROR_IF(!ctx->m_shared->isProgram(program), GL_INVALID_OPERATION);
SET_ERROR_IF(!ctx->m_shared->isProgramInitialized(program), GL_INVALID_OPERATION);
GLint hostLoc = ctx->m_shared->locationWARAppToHost(program,location);
SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,hostLoc)==0, GL_INVALID_OPERATION);
ctx->m_glGetUniformfv_enc(self, program, hostLoc, params);
}
GLuint GL2Encoder::s_glCreateProgram(void * self)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLuint program = ctx->m_glCreateProgram_enc(self);
if (program!=0)
ctx->m_shared->addProgramData(program);
return program;
}
GLuint GL2Encoder::s_glCreateShader(void *self, GLenum shaderType)
{
GL2Encoder *ctx = (GL2Encoder*)self;
RET_AND_SET_ERROR_IF(!GLESv2Validation::shaderType(ctx, shaderType), GL_INVALID_ENUM, 0);
GLuint shader = ctx->m_glCreateShader_enc(self, shaderType);
if (shader != 0) {
if (!ctx->m_shared->addShaderData(shader)) {
ctx->m_glDeleteShader_enc(self, shader);
return 0;
}
}
return shader;
}
void GL2Encoder::s_glGetAttachedShaders(void *self, GLuint program, GLsizei maxCount,
GLsizei* count, GLuint* shaders)
{
GL2Encoder *ctx = (GL2Encoder*)self;
SET_ERROR_IF(maxCount < 0, GL_INVALID_VALUE);
ctx->m_glGetAttachedShaders_enc(self, program, maxCount, count, shaders);
}
void GL2Encoder::s_glGetShaderSource(void *self, GLuint shader, GLsizei bufsize,
GLsizei* length, GLchar* source)
{
GL2Encoder *ctx = (GL2Encoder*)self;
SET_ERROR_IF(bufsize < 0, GL_INVALID_VALUE);
ctx->m_glGetShaderSource_enc(self, shader, bufsize, length, source);
ShaderData* shaderData = ctx->m_shared->getShaderData(shader);
if (shaderData) {
std::string returned;
int curr_len = 0;
for (int i = 0; i < shaderData->sources.size(); i++) {
if (curr_len + shaderData->sources[i].size() < bufsize - 1) {
returned += shaderData->sources[i];
} else {
returned += shaderData->sources[i].substr(0, bufsize - 1 - curr_len);
break;
}
}
memcpy(source, returned.substr(0, bufsize - 1).c_str(), bufsize);
}
}
void GL2Encoder::s_glGetShaderInfoLog(void *self, GLuint shader, GLsizei bufsize,
GLsizei* length, GLchar* infolog)
{
GL2Encoder *ctx = (GL2Encoder*)self;
SET_ERROR_IF(bufsize < 0, GL_INVALID_VALUE);
ctx->m_glGetShaderInfoLog_enc(self, shader, bufsize, length, infolog);
}
void GL2Encoder::s_glGetProgramInfoLog(void *self, GLuint program, GLsizei bufsize,
GLsizei* length, GLchar* infolog)
{
GL2Encoder *ctx = (GL2Encoder*)self;
SET_ERROR_IF(bufsize < 0, GL_INVALID_VALUE);
ctx->m_glGetProgramInfoLog_enc(self, program, bufsize, length, infolog);
}
void GL2Encoder::s_glDeleteShader(void *self, GLenum shader)
{
GL2Encoder *ctx = (GL2Encoder*)self;
ctx->m_glDeleteShader_enc(self,shader);
ctx->m_shared->unrefShaderData(shader);
}
void GL2Encoder::s_glAttachShader(void *self, GLuint program, GLuint shader)
{
GL2Encoder *ctx = (GL2Encoder*)self;
ctx->m_glAttachShader_enc(self, program, shader);
ctx->m_shared->attachShader(program, shader);
}
void GL2Encoder::s_glDetachShader(void *self, GLuint program, GLuint shader)
{
GL2Encoder *ctx = (GL2Encoder*)self;
ctx->m_glDetachShader_enc(self, program, shader);
ctx->m_shared->detachShader(program, shader);
}
int sArrIndexOfUniformExpr(const char* name, int* err) {
*err = 0;
int arrIndex = 0;
int namelen = strlen(name);
if (name[namelen-1] == ']') {
const char *brace = strrchr(name,'[');
if (!brace || sscanf(brace+1,"%d",&arrIndex) != 1) {
*err = 1; return 0;
}
}
return arrIndex;
}
int GL2Encoder::s_glGetUniformLocation(void *self, GLuint program, const GLchar *name)
{
if (!name) return -1;
GL2Encoder *ctx = (GL2Encoder*)self;
// if we need the uniform location WAR
// parse array index from the end of the name string
int arrIndex = 0;
bool needLocationWAR = ctx->m_shared->needUniformLocationWAR(program);
if (needLocationWAR) {
int err;
arrIndex = sArrIndexOfUniformExpr(name, &err);
if (err) return -1;
}
int hostLoc = ctx->m_glGetUniformLocation_enc(self, program, name);
if (hostLoc >= 0 && needLocationWAR) {
return ctx->m_shared->locationWARHostToApp(program, hostLoc, arrIndex);
}
return hostLoc;
}
bool GL2Encoder::updateHostTexture2DBinding(GLenum texUnit, GLenum newTarget)
{
if (newTarget != GL_TEXTURE_2D && newTarget != GL_TEXTURE_EXTERNAL_OES)
return false;
m_state->setActiveTextureUnit(texUnit);
GLenum oldTarget = m_state->getPriorityEnabledTarget(GL_TEXTURE_2D);
if (newTarget != oldTarget) {
if (newTarget == GL_TEXTURE_EXTERNAL_OES) {
m_state->disableTextureTarget(GL_TEXTURE_2D);
m_state->enableTextureTarget(GL_TEXTURE_EXTERNAL_OES);
} else {
m_state->disableTextureTarget(GL_TEXTURE_EXTERNAL_OES);
m_state->enableTextureTarget(GL_TEXTURE_2D);
}
m_glActiveTexture_enc(this, texUnit);
m_glBindTexture_enc(this, GL_TEXTURE_2D,
m_state->getBoundTexture(newTarget));
return true;
}
return false;
}
void GL2Encoder::updateHostTexture2DBindingsFromProgramData(GLuint program) {
GL2Encoder *ctx = this;
GLClientState* state = ctx->m_state;
GLSharedGroupPtr shared = ctx->m_shared;
GLenum origActiveTexture = state->getActiveTextureUnit();
GLenum hostActiveTexture = origActiveTexture;
GLint samplerIdx = -1;
GLint samplerVal;
GLenum samplerTarget;
while ((samplerIdx = shared->getNextSamplerUniform(program, samplerIdx, &samplerVal, &samplerTarget)) != -1) {
if (samplerVal < 0 || samplerVal >= GLClientState::MAX_TEXTURE_UNITS)
continue;
if (ctx->updateHostTexture2DBinding(GL_TEXTURE0 + samplerVal,
samplerTarget))
{
hostActiveTexture = GL_TEXTURE0 + samplerVal;
}
}
state->setActiveTextureUnit(origActiveTexture);
if (hostActiveTexture != origActiveTexture) {
ctx->m_glActiveTexture_enc(ctx, origActiveTexture);
}
}
void GL2Encoder::s_glUseProgram(void *self, GLuint program)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLSharedGroupPtr shared = ctx->m_shared;
SET_ERROR_IF(program && !shared->isShaderOrProgramObject(program), GL_INVALID_VALUE);
SET_ERROR_IF(program && !shared->isProgram(program), GL_INVALID_OPERATION);
ctx->m_glUseProgram_enc(self, program);
ctx->m_state->setCurrentProgram(program);
ctx->m_state->setCurrentShaderProgram(program);
ctx->updateHostTexture2DBindingsFromProgramData(program);
}
void GL2Encoder::s_glUniform1f(void *self , GLint location, GLfloat x)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform1f_enc(self, hostLoc, x);
}
void GL2Encoder::s_glUniform1fv(void *self , GLint location, GLsizei count, const GLfloat* v)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform1fv_enc(self, hostLoc, count, v);
}
void GL2Encoder::s_glUniform1i(void *self , GLint location, GLint x)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLClientState* state = ctx->m_state;
GLSharedGroupPtr shared = ctx->m_shared;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform1i_enc(self, hostLoc, x);
GLenum target;
if (shared->setSamplerUniform(state->currentShaderProgram(), location, x, &target)) {
GLenum origActiveTexture = state->getActiveTextureUnit();
if (ctx->updateHostTexture2DBinding(GL_TEXTURE0 + x, target)) {
ctx->m_glActiveTexture_enc(self, origActiveTexture);
}
state->setActiveTextureUnit(origActiveTexture);
}
}
void GL2Encoder::s_glUniform1iv(void *self , GLint location, GLsizei count, const GLint* v)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform1iv_enc(self, hostLoc, count, v);
}
void GL2Encoder::s_glUniform2f(void *self , GLint location, GLfloat x, GLfloat y)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform2f_enc(self, hostLoc, x, y);
}
void GL2Encoder::s_glUniform2fv(void *self , GLint location, GLsizei count, const GLfloat* v)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform2fv_enc(self, hostLoc, count, v);
}
void GL2Encoder::s_glUniform2i(void *self , GLint location, GLint x, GLint y)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform2i_enc(self, hostLoc, x, y);
}
void GL2Encoder::s_glUniform2iv(void *self , GLint location, GLsizei count, const GLint* v)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform2iv_enc(self, hostLoc, count, v);
}
void GL2Encoder::s_glUniform3f(void *self , GLint location, GLfloat x, GLfloat y, GLfloat z)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform3f_enc(self, hostLoc, x, y, z);
}
void GL2Encoder::s_glUniform3fv(void *self , GLint location, GLsizei count, const GLfloat* v)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform3fv_enc(self, hostLoc, count, v);
}
void GL2Encoder::s_glUniform3i(void *self , GLint location, GLint x, GLint y, GLint z)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform3i_enc(self, hostLoc, x, y, z);
}
void GL2Encoder::s_glUniform3iv(void *self , GLint location, GLsizei count, const GLint* v)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform3iv_enc(self, hostLoc, count, v);
}
void GL2Encoder::s_glUniform4f(void *self , GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform4f_enc(self, hostLoc, x, y, z, w);
}
void GL2Encoder::s_glUniform4fv(void *self , GLint location, GLsizei count, const GLfloat* v)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform4fv_enc(self, hostLoc, count, v);
}
void GL2Encoder::s_glUniform4i(void *self , GLint location, GLint x, GLint y, GLint z, GLint w)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform4i_enc(self, hostLoc, x, y, z, w);
}
void GL2Encoder::s_glUniform4iv(void *self , GLint location, GLsizei count, const GLint* v)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniform4iv_enc(self, hostLoc, count, v);
}
void GL2Encoder::s_glUniformMatrix2fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniformMatrix2fv_enc(self, hostLoc, count, transpose, value);
}
void GL2Encoder::s_glUniformMatrix3fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniformMatrix3fv_enc(self, hostLoc, count, transpose, value);
}
void GL2Encoder::s_glUniformMatrix4fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
{
GL2Encoder *ctx = (GL2Encoder*)self;
GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentShaderProgram(),location);
ctx->m_glUniformMatrix4fv_enc(self, hostLoc, count, transpose, value);
}
void GL2Encoder::s_glActiveTexture(void* self, GLenum texture)
{
GL2Encoder* ctx = (GL2Encoder*)self;
GLClientState* state = ctx->m_state;
GLenum err;
SET_ERROR_IF((err = state->setActiveTextureUnit(texture)) != GL_NO_ERROR, err);
ctx->m_glActiveTexture_enc(ctx, texture);
}
void GL2Encoder::s_glBindTexture(void* self, GLenum target, GLuint texture)
{
GL2Encoder* ctx = (GL2Encoder*)self;
GLClientState* state = ctx->m_state;
GLenum err;
GLboolean firstUse;
SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
SET_ERROR_IF((err = state->bindTexture(target, texture, &firstUse)) != GL_NO_ERROR, err);
if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
ctx->m_glBindTexture_enc(ctx, target, texture);
return;
}
GLenum priorityTarget = state->getPriorityEnabledTarget(GL_TEXTURE_2D);
if (target == GL_TEXTURE_EXTERNAL_OES && firstUse) {
ctx->m_glBindTexture_enc(ctx, GL_TEXTURE_2D, texture);
ctx->m_glTexParameteri_enc(ctx, GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER, GL_LINEAR);
ctx->m_glTexParameteri_enc(ctx, GL_TEXTURE_2D,
GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
ctx->m_glTexParameteri_enc(ctx, GL_TEXTURE_2D,
GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (target != priorityTarget) {
ctx->m_glBindTexture_enc(ctx, GL_TEXTURE_2D,
state->getBoundTexture(GL_TEXTURE_2D));
}
}
if (target == priorityTarget) {
ctx->m_glBindTexture_enc(ctx, GL_TEXTURE_2D, texture);
}
}
void GL2Encoder::s_glDeleteTextures(void* self, GLsizei n, const GLuint* textures)
{
GL2Encoder* ctx = (GL2Encoder*)self;
GLClientState* state = ctx->m_state;
state->deleteTextures(n, textures);
ctx->m_glDeleteTextures_enc(ctx, n, textures);
}
void GL2Encoder::s_glGetTexParameterfv(void* self,
GLenum target, GLenum pname, GLfloat* params)
{
GL2Encoder* ctx = (GL2Encoder*)self;
const GLClientState* state = ctx->m_state;
if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
ctx->override2DTextureTarget(target);
ctx->m_glGetTexParameterfv_enc(ctx, GL_TEXTURE_2D, pname, params);
ctx->restore2DTextureTarget(target);
} else {
ctx->m_glGetTexParameterfv_enc(ctx, target, pname, params);
}
}
void GL2Encoder::s_glGetTexParameteriv(void* self,
GLenum target, GLenum pname, GLint* params)
{
GL2Encoder* ctx = (GL2Encoder*)self;
const GLClientState* state = ctx->m_state;
switch (pname) {
case GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES:
*params = 1;
break;
default:
if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
ctx->override2DTextureTarget(target);
ctx->m_glGetTexParameteriv_enc(ctx, GL_TEXTURE_2D, pname, params);
ctx->restore2DTextureTarget(target);
} else {
ctx->m_glGetTexParameteriv_enc(ctx, target, pname, params);
}
break;
}
}
static bool isValidTextureExternalParam(GLenum pname, GLenum param)
{
switch (pname) {
case GL_TEXTURE_MIN_FILTER:
case GL_TEXTURE_MAG_FILTER:
return param == GL_NEAREST || param == GL_LINEAR;
case GL_TEXTURE_WRAP_S:
case GL_TEXTURE_WRAP_T:
return param == GL_CLAMP_TO_EDGE;
default:
return true;
}
}
void GL2Encoder::s_glTexParameterf(void* self,
GLenum target, GLenum pname, GLfloat param)
{
GL2Encoder* ctx = (GL2Encoder*)self;
const GLClientState* state = ctx->m_state;
SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES &&
!isValidTextureExternalParam(pname, (GLenum)param)),
GL_INVALID_ENUM);
if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
ctx->override2DTextureTarget(target);
ctx->m_glTexParameterf_enc(ctx, GL_TEXTURE_2D, pname, param);
ctx->restore2DTextureTarget(target);
} else {
ctx->m_glTexParameterf_enc(ctx, target, pname, param);
}
}
void GL2Encoder::s_glTexParameterfv(void* self,
GLenum target, GLenum pname, const GLfloat* params)
{
GL2Encoder* ctx = (GL2Encoder*)self;
const GLClientState* state = ctx->m_state;
SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES &&
!isValidTextureExternalParam(pname, (GLenum)params[0])),
GL_INVALID_ENUM);
if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
ctx->override2DTextureTarget(target);
ctx->m_glTexParameterfv_enc(ctx, GL_TEXTURE_2D, pname, params);
ctx->restore2DTextureTarget(target);
} else {
ctx->m_glTexParameterfv_enc(ctx, target, pname, params);
}
}
void GL2Encoder::s_glTexParameteri(void* self,
GLenum target, GLenum pname, GLint param)
{
GL2Encoder* ctx = (GL2Encoder*)self;
const GLClientState* state = ctx->m_state;
SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES &&
!isValidTextureExternalParam(pname, (GLenum)param)),
GL_INVALID_ENUM);
if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
ctx->override2DTextureTarget(target);
ctx->m_glTexParameteri_enc(ctx, GL_TEXTURE_2D, pname, param);
ctx->restore2DTextureTarget(target);
} else {
ctx->m_glTexParameteri_enc(ctx, target, pname, param);
}
}
static int ilog2(uint32_t x) {
int p = 0;
while ((1 << p) < x)
p++;
return p;
}
void GL2Encoder::s_glTexImage2D(void* self, GLenum target, GLint level,
GLint internalformat, GLsizei width, GLsizei height, GLint border,
GLenum format, GLenum type, const GLvoid* pixels)
{
GL2Encoder* ctx = (GL2Encoder*)self;
GLClientState* state = ctx->m_state;
SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
SET_ERROR_IF(!GLESv2Validation::pixelType(ctx, type), GL_INVALID_ENUM);
SET_ERROR_IF(!GLESv2Validation::pixelFormat(ctx, format), GL_INVALID_ENUM);
// If unpack buffer is nonzero, verify unmapped state.
SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_UNPACK_BUFFER), GL_INVALID_OPERATION);
GLint max_texture_size;
GLint max_cube_map_texture_size;
ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
ctx->glGetIntegerv(ctx, GL_MAX_CUBE_MAP_TEXTURE_SIZE, &max_cube_map_texture_size);
SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
SET_ERROR_IF(level > ilog2(max_texture_size), GL_INVALID_VALUE);
SET_ERROR_IF((target == GL_TEXTURE_CUBE_MAP) &&
(level > ilog2(max_cube_map_texture_size)), GL_INVALID_VALUE);
SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
SET_ERROR_IF(width > max_texture_size, GL_INVALID_VALUE);
SET_ERROR_IF(height > max_texture_size, GL_INVALID_VALUE);
SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && width > max_cube_map_texture_size, GL_INVALID_VALUE);
SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) && height > max_cube_map_texture_size, GL_INVALID_VALUE);
SET_ERROR_IF(border != 0, GL_INVALID_VALUE);
// If unpack buffer is nonzero, verify buffer data fits and is evenly divisible by the type.
SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
(ctx->m_state->pboNeededDataSize(width, height, 1, format, type, 0) >
ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size),
GL_INVALID_OPERATION);
SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
(ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size %
glSizeof(type)),
GL_INVALID_OPERATION);
SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
((uintptr_t)pixels % glSizeof(type)),
GL_INVALID_OPERATION);
SET_ERROR_IF(state->isBoundTextureImmutableFormat(target), GL_INVALID_OPERATION);
GLenum stateTarget = target;
if (target == GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y ||
target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z ||
target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X ||
target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y ||
target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
stateTarget = GL_TEXTURE_CUBE_MAP;
state->setBoundTextureInternalFormat(stateTarget, internalformat);
state->setBoundTextureFormat(stateTarget, format);
state->setBoundTextureType(stateTarget, type);
state->setBoundTextureDims(stateTarget, level, width, height, 1);
if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
ctx->override2DTextureTarget(target);
}
if (ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER)) {
ctx->glTexImage2DOffsetAEMU(
ctx, target, level, internalformat,
width, height, border,
format, type, (uintptr_t)pixels);
} else {
ctx->m_glTexImage2D_enc(
ctx, target, level, internalformat,
width, height, border,
format, type, pixels);
}
if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
ctx->restore2DTextureTarget(target);
}
}
void GL2Encoder::s_glTexSubImage2D(void* self, GLenum target, GLint level,
GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format,
GLenum type, const GLvoid* pixels)
{
GL2Encoder* ctx = (GL2Encoder*)self;
GLClientState* state = ctx->m_state;
SET_ERROR_IF(!GLESv2Validation::textureTarget(ctx, target), GL_INVALID_ENUM);
SET_ERROR_IF(!GLESv2Validation::pixelType(ctx, type), GL_INVALID_ENUM);
SET_ERROR_IF(!GLESv2Validation::pixelFormat(ctx, format), GL_INVALID_ENUM);
// If unpack buffer is nonzero, verify unmapped state.
SET_ERROR_IF(ctx->isBufferTargetMapped(GL_PIXEL_UNPACK_BUFFER), GL_INVALID_OPERATION);
GLint max_texture_size;
GLint max_cube_map_texture_size;
ctx->glGetIntegerv(ctx, GL_MAX_TEXTURE_SIZE, &max_texture_size);
ctx->glGetIntegerv(ctx, GL_MAX_CUBE_MAP_TEXTURE_SIZE, &max_cube_map_texture_size);
SET_ERROR_IF(level < 0, GL_INVALID_VALUE);
SET_ERROR_IF(level > ilog2(max_texture_size), GL_INVALID_VALUE);
SET_ERROR_IF(GLESv2Validation::isCubeMapTarget(target) &&
level > ilog2(max_cube_map_texture_size), GL_INVALID_VALUE);
SET_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
SET_ERROR_IF(xoffset < 0 || yoffset < 0, GL_INVALID_VALUE);
GLuint tex = state->getBoundTexture(target);
GLsizei neededWidth = xoffset + width;
GLsizei neededHeight = yoffset + height;
GLsizei neededDepth = 1;
if (tex && !state->queryTexEGLImageBacked(tex)) {
SET_ERROR_IF(
(neededWidth > state->queryTexWidth(level, tex) ||
neededHeight > state->queryTexHeight(level, tex) ||
neededDepth > state->queryTexDepth(level, tex)),
GL_INVALID_VALUE);
}
// If unpack buffer is nonzero, verify buffer data fits and is evenly divisible by the type.
SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
(state->pboNeededDataSize(width, height, 1, format, type, 0) >
ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size),
GL_INVALID_OPERATION);
SET_ERROR_IF(ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER) &&
ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER) &&
(ctx->getBufferData(GL_PIXEL_UNPACK_BUFFER)->m_size %
glSizeof(type)),
GL_INVALID_OPERATION);
SET_ERROR_IF(!ctx