| /* |
| * 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_hasAsyncUnmapBuffer = false; |
| 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_drawCallFlushInterval = 800; |
| 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); |
| |
| OVERRIDE(glInvalidateFramebuffer); |
| OVERRIDE(glInvalidateSubFramebuffer); |
| } |
| |
| 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) { |
| m_fixedBuffer.resize(glSizeof(type) * count); |
| adjustedIndices = m_fixedBuffer.data(); |
| 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; |
| |
| m_state->getVBOUsage(hasClientArrays, hasVBOs); |
| } |
| |
| void GL2Encoder::sendVertexAttributes(GLint first, GLsizei count, bool hasClientArrays, GLsizei primcount) |
| { |
| assert(m_state); |
| |
| m_state->updateEnableDirtyArrayForDraw(); |
| |
| 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() { |
| if (m_drawCallFlushCount % m_drawCallFlushInterval == 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->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; |
| 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 = &buf->m_fixedBuffer[offset]; |
| 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->doBindBufferEncodeCached(GL_ELEMENT_ARRAY_BUFFER, ctx->m_state->currentIndexVbo()); |
| ctx->glDrawElementsOffset(ctx, mode, count, type, offset); |
| ctx->flushDrawCall(); |
| adjustIndices = false; |
| } else { |
| 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->m_glDrawArraysNullAEMU_enc(ctx, mode, first, count); |
| } |
| ctx->flushDrawCall(); |
| } |
| |
| 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; |
| GLintptr offset = (GLintptr)indices; |
| |
| 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) { |
| if (!has_client_vertex_arrays && has_indirect_arrays) { |
| // Don't do anything |
| } else { |
| buf = ctx->m_shared->getBufferData(ctx->m_state->currentIndexVbo()); |
| offset = (GLintptr)indices; |
| indices = &buf->m_fixedBuffer[offset]; |
| 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->doBindBufferEncodeCached(GL_ELEMENT_ARRAY_BUFFER, ctx->m_state->currentIndexVbo()); |
| ctx->glDrawElementsOffsetNullAEMU(ctx, mode, count, type, offset); |
| ctx->flushDrawCall(); |
| adjustIndices = false; |
| } else { |
| 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, const GLuint *, GLenum, const void*, GLsizei) |
| { |
| // Although it is not supported, need to set proper error code. |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| 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; |
| if (length) { |
| for (int i = 0; i < count; i++) { |
| // Each element in the length array may contain the length of the corresponding |
| // string (the null character is not counted as part of the string length) or a |
| // value less than 0 to indicate that the string is null terminated. |
| if (length[i] >= 0) { |
| orig_sources.push_back(std::string((const char*)(string[i]), |
| (const char*)(string[i]) + length[i])); |
| } else { |
| orig_sources.push_back(std::string((const char*)(string[i]))); |
| } |
| } |
| } else { |
| 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); |
| } |
| |
| 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 = 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 = 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; |
| } |
| } |
| std::string ret = returned.substr(0, bufsize - 1); |
| |
| size_t toCopy = bufsize < (ret.size() + 1) ? bufsize : ret.size() + 1; |
| memcpy(source, ret.c_str(), toCopy); |
| } |
| } |
| |
| 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; |
| return ctx->m_glGetUniformLocation_enc(self, program, name); |
| } |
| |
| 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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; |
| |
| 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; |
| |
| 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; |
| |
| 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; |
| |
| 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; |
| |
| 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->boundBuffer(GL_PIXEL_UNPACK_BUFFER) && !pixels, GL_INVALID_OPERATION); |
| |
| if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) { |
| ctx->override2DTextureTarget(target); |
| } |
| |
| if (ctx->boundBuffer(GL_PIXEL_UNPACK_BUFFER)) { |
| ctx->glTexSubImage2DOffsetAEMU( |
| ctx, target, level, |
| xoffset, yoffset, width, height, |
| format, type, (uintptr_t)pixels); |
| } else { |
| ctx->m_glTexSubImage2D_enc(ctx, target, level, xoffset, yoffset, width, |
| height, format, type, pixels); |
| } |
| |
| if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) { |
| ctx->restore2DTextureTarget(target); |
| } |
| } |
| |
| void GL2Encoder::s_glCopyTexImage2D(void* self, GLenum target, GLint level, |
| GLenum internalformat, GLint x, GLint y, |
| GLsizei width, GLsizei height, GLint border) |
| { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(ctx->glCheckFramebufferStatus(ctx, GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, |
| GL_INVALID_FRAMEBUFFER_OPERATION); |
| // This is needed to work around underlying OpenGL drivers |
| // (such as those feeding some some AMD GPUs) that expect |
| // positive components of cube maps to be defined _before_ |
| // the negative components (otherwise a segfault occurs). |
| GLenum extraTarget = |
| state->copyTexImageLuminanceCubeMapAMDWorkaround |
| (target, level, internalformat); |
| |
| if (extraTarget) { |
| ctx->m_glCopyTexImage2D_enc(ctx, extraTarget, level, internalformat, |
| x, y, width, height, border); |
| } |
| |
| ctx->m_glCopyTexImage2D_enc(ctx, target, level, internalformat, |
| x, y, width, height, border); |
| |
| state->setBoundTextureInternalFormat(target, internalformat); |
| state->setBoundTextureDims(target, level, width, height, 1); |
| } |
| |
| void GL2Encoder::s_glTexParameteriv(void* self, |
| GLenum target, GLenum pname, const GLint* params) |
| { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| |
| 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_glTexParameteriv_enc(ctx, GL_TEXTURE_2D, pname, params); |
| ctx->restore2DTextureTarget(target); |
| } else { |
| ctx->m_glTexParameteriv_enc(ctx, target, pname, params); |
| } |
| } |
| |
| bool GL2Encoder::texture2DNeedsOverride(GLenum target) const { |
| return (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) && |
| target != m_state->getPriorityEnabledTarget(GL_TEXTURE_2D); |
| } |
| |
| void GL2Encoder::override2DTextureTarget(GLenum target) |
| { |
| if (texture2DNeedsOverride(target)) { |
| m_glBindTexture_enc(this, GL_TEXTURE_2D, |
| m_state->getBoundTexture(target)); |
| } |
| } |
| |
| void GL2Encoder::restore2DTextureTarget(GLenum target) |
| { |
| if (texture2DNeedsOverride(target)) { |
| GLuint priorityEnabledBoundTexture = |
| m_state->getBoundTexture( |
| m_state->getPriorityEnabledTarget(GL_TEXTURE_2D)); |
| GLuint texture2DBoundTexture = |
| m_state->getBoundTexture(GL_TEXTURE_2D); |
| if (!priorityEnabledBoundTexture) { |
| m_glBindTexture_enc(this, GL_TEXTURE_2D, texture2DBoundTexture); |
| } else { |
| m_glBindTexture_enc(this, GL_TEXTURE_2D, priorityEnabledBoundTexture); |
| } |
| } |
| } |
| |
| void GL2Encoder::associateEGLImage(GLenum target, GLeglImageOES eglImage) { |
| m_state->setBoundEGLImage(target, eglImage); |
| } |
| |
| |
| GLuint GL2Encoder::boundBuffer(GLenum target) const { |
| return m_state->getBuffer(target); |
| } |
| |
| BufferData* GL2Encoder::getBufferData(GLenum target) const { |
| GLuint bufferId = m_state->getBuffer(target); |
| if (!bufferId) return NULL; |
| return m_shared->getBufferData(bufferId); |
| } |
| |
| BufferData* GL2Encoder::getBufferDataById(GLuint bufferId) const { |
| if (!bufferId) return NULL; |
| return m_shared->getBufferData(bufferId); |
| } |
| |
| bool GL2Encoder::isBufferMapped(GLuint buffer) const { |
| return m_shared->getBufferData(buffer)->m_mapped; |
| } |
| |
| bool GL2Encoder::isBufferTargetMapped(GLenum target) const { |
| BufferData* buf = getBufferData(target); |
| if (!buf) return false; |
| return buf->m_mapped; |
| } |
| |
| void GL2Encoder::s_glGenRenderbuffers(void* self, |
| GLsizei n, GLuint* renderbuffers) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(n < 0, GL_INVALID_VALUE); |
| |
| ctx->m_glGenFramebuffers_enc(self, n, renderbuffers); |
| state->addRenderbuffers(n, renderbuffers); |
| } |
| |
| void GL2Encoder::s_glDeleteRenderbuffers(void* self, |
| GLsizei n, const GLuint* renderbuffers) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(n < 0, GL_INVALID_VALUE); |
| |
| ctx->m_glDeleteRenderbuffers_enc(self, n, renderbuffers); |
| |
| // Nope, lets just leak those for now. |
| // The spec has an *amazingly* convoluted set of conditions for when |
| // render buffers are actually deleted: |
| // glDeleteRenderbuffers deletes the n renderbuffer objects whose names are stored in the array addressed by renderbuffers. Unused names in renderbuffers that have been marked as used for the purposes of glGenRenderbuffers are marked as unused again. The name zero is reserved by the GL and is silently ignored, should it occur in renderbuffers, as are other unused names. Once a renderbuffer object is deleted, its name is again unused and it has no contents. If a renderbuffer that is currently bound to the target GL_RENDERBUFFER is deleted, it is as though glBindRenderbuffer had been executed with a target of GL_RENDERBUFFER and a name of zero. |
| // |
| // If a renderbuffer object is attached to one or more attachment points in the currently bound framebuffer, then it as if glFramebufferRenderbuffer had been called, with a renderbuffer of zero for each attachment point to which this image was attached in the currently bound framebuffer. In other words, this renderbuffer object is first detached from all attachment ponits in the currently bound framebuffer. ***Note that the renderbuffer image is specifically not detached from any non-bound framebuffers*** |
| // |
| // So, just detach this one from the bound FBO, and ignore the rest. |
| for (int i = 0; i < n; i++) { |
| state->detachRbo(renderbuffers[i]); |
| } |
| // state->removeRenderbuffers(n, renderbuffers); |
| } |
| |
| void GL2Encoder::s_glBindRenderbuffer(void* self, |
| GLenum target, GLuint renderbuffer) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF((target != GL_RENDERBUFFER), |
| GL_INVALID_ENUM); |
| |
| ctx->m_glBindRenderbuffer_enc(self, target, renderbuffer); |
| state->bindRenderbuffer(target, renderbuffer); |
| } |
| |
| void GL2Encoder::s_glRenderbufferStorage(void* self, |
| GLenum target, GLenum internalformat, |
| GLsizei width, GLsizei height) { |
| GL2Encoder* ctx = (GL2Encoder*) self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(target != GL_RENDERBUFFER, GL_INVALID_ENUM); |
| SET_ERROR_IF( |
| !GLESv2Validation::rboFormat(ctx, internalformat), |
| GL_INVALID_ENUM); |
| |
| state->setBoundRenderbufferFormat(internalformat); |
| state->setBoundRenderbufferSamples(0); |
| |
| ctx->m_glRenderbufferStorage_enc(self, target, internalformat, |
| width, height); |
| } |
| |
| void GL2Encoder::s_glFramebufferRenderbuffer(void* self, |
| GLenum target, GLenum attachment, |
| GLenum renderbuffertarget, GLuint renderbuffer) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(!GLESv2Validation::framebufferTarget(ctx, target), GL_INVALID_ENUM); |
| SET_ERROR_IF(!GLESv2Validation::framebufferAttachment(ctx, attachment), GL_INVALID_ENUM); |
| state->attachRbo(target, attachment, renderbuffer); |
| |
| ctx->m_glFramebufferRenderbuffer_enc(self, target, attachment, renderbuffertarget, renderbuffer); |
| } |
| |
| void GL2Encoder::s_glGenFramebuffers(void* self, |
| GLsizei n, GLuint* framebuffers) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(n < 0, GL_INVALID_VALUE); |
| |
| ctx->m_glGenFramebuffers_enc(self, n, framebuffers); |
| state->addFramebuffers(n, framebuffers); |
| } |
| |
| void GL2Encoder::s_glDeleteFramebuffers(void* self, |
| GLsizei n, const GLuint* framebuffers) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(n < 0, GL_INVALID_VALUE); |
| |
| ctx->m_glDeleteFramebuffers_enc(self, n, framebuffers); |
| state->removeFramebuffers(n, framebuffers); |
| } |
| |
| void GL2Encoder::s_glBindFramebuffer(void* self, |
| GLenum target, GLuint framebuffer) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(!GLESv2Validation::framebufferTarget(ctx, target), GL_INVALID_ENUM); |
| |
| state->bindFramebuffer(target, framebuffer); |
| |
| ctx->m_glBindFramebuffer_enc(self, target, framebuffer); |
| } |
| |
| void GL2Encoder::s_glFramebufferTexture2D(void* self, |
| GLenum target, GLenum attachment, |
| GLenum textarget, GLuint texture, GLint level) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| SET_ERROR_IF(!GLESv2Validation::framebufferTarget(ctx, target), GL_INVALID_ENUM); |
| SET_ERROR_IF(!GLESv2Validation::framebufferAttachment(ctx, attachment), GL_INVALID_ENUM); |
| state->attachTextureObject(target, attachment, texture); |
| |
| ctx->m_glFramebufferTexture2D_enc(self, target, attachment, textarget, texture, level); |
| } |
| |
| void GL2Encoder::s_glFramebufferTexture3DOES(void* self, |
| GLenum target, GLenum attachment, |
| GLenum textarget, GLuint texture, GLint level, GLint zoffset) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| GLClientState* state = ctx->m_state; |
| |
| state->attachTextureObject(target, attachment, texture); |
| |
| ctx->m_glFramebufferTexture3DOES_enc(self, target, attachment, textarget, texture, level, zoffset); |
| } |
| |
| void GL2Encoder::s_glGetFramebufferAttachmentParameteriv(void* self, |
| GLenum target, GLenum attachment, GLenum pname, GLint* params) { |
| GL2Encoder* ctx = (GL2Encoder*)self; |
| const GLClientState* state = ctx->m_state; |
| SET_ERROR_IF(!GLESv2Validation::framebufferTarget(ctx, target), GL_INVALID_ENUM); |
| SET_ERROR_IF(pname != GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME && |
| pname != GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE && |
| !state->attachmentHasObject(target, attachment), |
| GL_INVALID_OPERATION); |
| SET_ERROR_IF((pname == GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL || |
| pname == GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE || |
| pname == GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER) && |
| (!state->attachmentHasObject(target, attachment) || |
| state->getBoundFramebufferAttachmentType(target, attachment) != |
| FBO_ATTACHMENT_TEXTURE), |
| !state->attachmentHasObject(target, attachment) ? |
| GL_INVALID_OPERATION : GL_INVALID_ENUM); |
| SET_ERROR_IF(attachment == GL_DEPTH_STENCIL_ATTACHMENT && |
| pname == GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME && |
| (state->objectOfAttachment(target, GL_DEPTH_ATTACHMENT) != |
| state->objectOfAttachment(target, GL_STENCIL_ATTACHMENT)), |
| GL_INVALID_OPERATION); |
| SET_ERROR_IF(state->boundFramebuffer(target) && |
| (attachment == GL_BACK || |
| attachment == GL_FRONT), |
| GL_INVALID_OPERATION); |
| ctx->m_glGetFramebufferAttachmentParameteriv_enc(self, target, attachment, pname, params); |
| } |
| |
| bool GL2Encoder::isCompleteFbo(GLenum target, const GLClientState* state, |
| GLenum attachment) const { |
| FboFormatInfo fbo_format_info; |
| state->getBoundFramebufferFormat(target, attachment, &fbo_format_info); |
| |
| bool res; |
| switch (fbo_format_info.type) { |
| |