From d6bd1843ce6ffd18b8dd7105052572b2c3af876a Mon Sep 17 00:00:00 2001 From: Jesse Hall Date: Wed, 23 Nov 2011 10:12:05 -0800 Subject: [PATCH] EmuGL: GLESv2 support for OES_EGL_image_external Change-Id: I8911328d5dcccdf4731bd2d8fd953c12fdec5f1b --- .../OpenglCodecCommon/GLClientState.cpp | 5 + .../shared/OpenglCodecCommon/GLClientState.h | 1 + .../OpenglCodecCommon/GLSharedGroup.cpp | 211 ++++++- .../shared/OpenglCodecCommon/GLSharedGroup.h | 54 +- tools/emulator/opengl/system/GLESv2/gl2.cpp | 8 +- .../opengl/system/GLESv2_enc/GL2Encoder.cpp | 551 +++++++++++++++++- .../opengl/system/GLESv2_enc/GL2Encoder.h | 35 +- 7 files changed, 791 insertions(+), 74 deletions(-) diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/GLClientState.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/GLClientState.cpp index 87c68db2c..979549029 100644 --- a/tools/emulator/opengl/shared/OpenglCodecCommon/GLClientState.cpp +++ b/tools/emulator/opengl/shared/OpenglCodecCommon/GLClientState.cpp @@ -250,6 +250,11 @@ GLenum GLClientState::setActiveTextureUnit(GLenum texture) return GL_NO_ERROR; } +GLenum GLClientState::getActiveTextureUnit() const +{ + return GL_TEXTURE0 + (m_tex.activeUnit - &m_tex.unit[0]); +} + void GLClientState::enableTextureTarget(GLenum target) { switch (target) { diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/GLClientState.h b/tools/emulator/opengl/shared/OpenglCodecCommon/GLClientState.h index be66d6828..c86329b44 100644 --- a/tools/emulator/opengl/shared/OpenglCodecCommon/GLClientState.h +++ b/tools/emulator/opengl/shared/OpenglCodecCommon/GLClientState.h @@ -142,6 +142,7 @@ public: // glActiveTexture(GL_TEXTURE0 + i) // Sets the active texture unit. Up to MAX_TEXTURE_UNITS are supported. GLenum setActiveTextureUnit(GLenum texture); + GLenum getActiveTextureUnit() const; // glEnable(GL_TEXTURE_(2D|EXTERNAL_OES)) void enableTextureTarget(GLenum target); diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.cpp index ff48c9d6a..8504f7f78 100644 --- a/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.cpp +++ b/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.cpp @@ -1,3 +1,19 @@ +/* +* 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 "GLSharedGroup.h" /**** BufferData ****/ @@ -53,6 +69,15 @@ void ProgramData::setIndexInfo(GLuint index, GLint base, GLint size, GLenum type m_Indexes[index].appBase = 0; } m_Indexes[index].hostLocsPerElement = 1; + m_Indexes[index].flags = 0; + m_Indexes[index].samplerValue = 0; +} + +void ProgramData::setIndexFlags(GLuint index, GLuint flags) +{ + if (index >= m_numIndexes) + return; + m_Indexes[index].flags |= flags; } GLuint ProgramData::getIndexForLocation(GLint location) @@ -123,13 +148,77 @@ GLint ProgramData::locationWARAppToHost(GLint appLoc) return -1; } +GLint ProgramData::getNextSamplerUniform(GLint index, GLint* val, GLenum* target) +{ + for (GLint i = index + 1; i >= 0 && i < (GLint)m_numIndexes; i++) { + if (m_Indexes[i].type == GL_SAMPLER_2D) { + if (val) *val = m_Indexes[i].samplerValue; + if (target) { + if (m_Indexes[i].flags & INDEX_FLAG_SAMPLER_EXTERNAL) { + *target = GL_TEXTURE_EXTERNAL_OES; + } else { + *target = GL_TEXTURE_2D; + } + } + return i; + } + } + return -1; +} + +bool ProgramData::setSamplerUniform(GLint appLoc, GLint val, GLenum* target) +{ + for (GLuint i = 0; i < m_numIndexes; i++) { + GLint elemIndex = appLoc - m_Indexes[i].appBase; + if (elemIndex >= 0 && elemIndex < m_Indexes[i].size) { + if (m_Indexes[i].type == GL_TEXTURE_2D) { + m_Indexes[i].samplerValue = val; + if (target) { + if (m_Indexes[i].flags & INDEX_FLAG_SAMPLER_EXTERNAL) { + *target = GL_TEXTURE_EXTERNAL_OES; + } else { + *target = GL_TEXTURE_2D; + } + } + return true; + } + } + } + return false; +} + +bool ProgramData::attachShader(GLuint shader) +{ + size_t n = m_shaders.size(); + for (size_t i = 0; i < n; i++) { + if (m_shaders[i] == shader) { + return false; + } + } + // AKA m_shaders.push_back(), but that has an ambiguous call to insertAt() + // due to the default parameters. This is the desired insertAt() overload. + m_shaders.insertAt(shader, m_shaders.size(), 1); + return true; +} + +bool ProgramData::detachShader(GLuint shader) +{ + size_t n = m_shaders.size(); + for (size_t i = 0; i < n; i++) { + if (m_shaders[i] == shader) { + m_shaders.removeAt(i); + return true; + } + } + return false; +} /***** GLSharedGroup ****/ GLSharedGroup::GLSharedGroup() : m_buffers(android::DefaultKeyedVector(NULL)), m_programs(android::DefaultKeyedVector(NULL)), - m_shaders(android::List()) + m_shaders(android::DefaultKeyedVector(NULL)) { } @@ -217,13 +306,55 @@ void GLSharedGroup::deleteProgramData(GLuint program) m_programs.removeItem(program); } -void GLSharedGroup::setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type) +void GLSharedGroup::attachShader(GLuint program, GLuint shader) +{ + android::AutoMutex _lock(m_lock); + ProgramData* programData = m_programs.valueFor(program); + ssize_t idx = m_shaders.indexOfKey(shader); + if (programData && idx >= 0) { + if (programData->attachShader(shader)) { + refShaderDataLocked(idx); + } + } +} + +void GLSharedGroup::detachShader(GLuint program, GLuint shader) +{ + android::AutoMutex _lock(m_lock); + ProgramData* programData = m_programs.valueFor(program); + ssize_t idx = m_shaders.indexOfKey(shader); + if (programData && idx >= 0) { + if (programData->detachShader(shader)) { + unrefShaderDataLocked(idx); + } + } +} + +void GLSharedGroup::setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type, const char* name) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) { pData->setIndexInfo(index,base,size,type); + + if (type == GL_SAMPLER_2D) { + size_t n = pData->getNumShaders(); + for (size_t i = 0; i < n; i++) { + GLuint shaderId = pData->getShader(i); + ShaderData* shader = m_shaders.valueFor(shaderId); + if (!shader) continue; + ShaderData::StringList::iterator nameIter = shader->samplerExternalNames.begin(); + ShaderData::StringList::iterator nameEnd = shader->samplerExternalNames.end(); + while (nameIter != nameEnd) { + if (*nameIter == name) { + pData->setIndexFlags(index, ProgramData::INDEX_FLAG_SAMPLER_EXTERNAL); + break; + } + ++nameIter; + } + } + } } } @@ -277,38 +408,62 @@ bool GLSharedGroup::needUniformLocationWAR(GLuint program) return false; } +GLint GLSharedGroup::getNextSamplerUniform(GLuint program, GLint index, GLint* val, GLenum* target) const +{ + android::AutoMutex _lock(m_lock); + ProgramData* pData = m_programs.valueFor(program); + return pData ? pData->getNextSamplerUniform(index, val, target) : -1; +} -void GLSharedGroup::addShaderData(GLuint shader) +bool GLSharedGroup::setSamplerUniform(GLuint program, GLint appLoc, GLint val, GLenum* target) { android::AutoMutex _lock(m_lock); - m_shaders.push_front(shader); - + ProgramData* pData = m_programs.valueFor(program); + return pData ? pData->setSamplerUniform(appLoc, val, target) : false; } -bool GLSharedGroup::isShader(GLuint shader) + +bool GLSharedGroup::addShaderData(GLuint shader) { android::AutoMutex _lock(m_lock); - android::List::iterator iter; - iter = m_shaders.begin(); - while (iter!=m_shaders.end()) - { - if (*iter==shader) - return true; - iter++; - } - return false; -} -void GLSharedGroup::deleteShaderData(GLuint shader) -{ - android::AutoMutex _lock(m_lock); - android::List::iterator iter; - iter = m_shaders.begin(); - while (iter!=m_shaders.end()) - { - if (*iter==shader) - { - m_shaders.erase(iter); - return; + ShaderData* data = new ShaderData; + if (data) { + if (m_shaders.add(shader, data) < 0) { + delete data; + data = NULL; } - iter++; + data->refcount = 1; + } + return data != NULL; +} + +ShaderData* GLSharedGroup::getShaderData(GLuint shader) +{ + android::AutoMutex _lock(m_lock); + return m_shaders.valueFor(shader); +} + +void GLSharedGroup::unrefShaderData(GLuint shader) +{ + android::AutoMutex _lock(m_lock); + ssize_t idx = m_shaders.indexOfKey(shader); + if (idx >= 0) { + unrefShaderDataLocked(idx); + } +} + +void GLSharedGroup::refShaderDataLocked(ssize_t shaderIdx) +{ + assert(shaderIdx >= 0 && shaderIdx <= m_shaders.size()); + ShaderData* data = m_shaders.valueAt(shaderIdx); + data->refcount++; +} + +void GLSharedGroup::unrefShaderDataLocked(ssize_t shaderIdx) +{ + assert(shaderIdx >= 0 && shaderIdx <= m_shaders.size()); + ShaderData* data = m_shaders.valueAt(shaderIdx); + if (--data->refcount == 0) { + delete data; + m_shaders.removeItemsAt(shaderIdx); } } diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.h b/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.h index 7104550d1..61b8f0056 100644 --- a/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.h +++ b/tools/emulator/opengl/shared/OpenglCodecCommon/GLSharedGroup.h @@ -31,8 +31,9 @@ #include #include "ErrorLog.h" #include -#include #include +#include +#include #include "FixedBuffer.h" #include "SmartPtr.h" @@ -51,18 +52,28 @@ private: GLenum type; GLint appBase; GLint hostLocsPerElement; + GLuint flags; + GLint samplerValue; // only set for sampler uniforms } IndexInfo; GLuint m_numIndexes; IndexInfo* m_Indexes; bool m_initialized; bool m_locShiftWAR; + + android::Vector m_shaders; + public: + enum { + INDEX_FLAG_SAMPLER_EXTERNAL = 0x00000001, + }; + ProgramData(); void initProgramData(GLuint numIndexes); bool isInitialized(); virtual ~ProgramData(); void setIndexInfo(GLuint index, GLint base, GLint size, GLenum type); + void setIndexFlags(GLuint index, GLuint flags); GLuint getIndexForLocation(GLint location); GLenum getTypeForLocation(GLint location); @@ -70,15 +81,32 @@ public: void setupLocationShiftWAR(); GLint locationWARHostToApp(GLint hostLoc, GLint arrIndex); GLint locationWARAppToHost(GLint appLoc); - + + GLint getNextSamplerUniform(GLint index, GLint* val, GLenum* target); + bool setSamplerUniform(GLint appLoc, GLint val, GLenum* target); + + bool attachShader(GLuint shader); + bool detachShader(GLuint shader); + size_t getNumShaders() const { return m_shaders.size(); } + GLuint getShader(size_t i) const { return m_shaders[i]; } +}; + +struct ShaderData { + typedef android::List StringList; + StringList samplerExternalNames; + int refcount; }; class GLSharedGroup { private: - android::DefaultKeyedVector m_buffers; - android::DefaultKeyedVector m_programs; - android::List m_shaders; - mutable android::Mutex m_lock; + android::DefaultKeyedVector m_buffers; + android::DefaultKeyedVector m_programs; + android::DefaultKeyedVector m_shaders; + mutable android::Mutex m_lock; + + void refShaderDataLocked(ssize_t shaderIdx); + void unrefShaderDataLocked(ssize_t shaderIdx); + public: GLSharedGroup(); ~GLSharedGroup(); @@ -92,18 +120,22 @@ public: bool isProgramInitialized(GLuint program); void addProgramData(GLuint program); void initProgramData(GLuint program, GLuint numIndexes); + void attachShader(GLuint program, GLuint shader); + void detachShader(GLuint program, GLuint shader); void deleteProgramData(GLuint program); - void setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type); + void setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type, const char* name); GLenum getProgramUniformType(GLuint program, GLint location); void setupLocationShiftWAR(GLuint program); GLint locationWARHostToApp(GLuint program, GLint hostLoc, GLint arrIndex); GLint locationWARAppToHost(GLuint program, GLint appLoc); bool needUniformLocationWAR(GLuint program); + GLint getNextSamplerUniform(GLuint program, GLint index, GLint* val, GLenum* target) const; + bool setSamplerUniform(GLuint program, GLint appLoc, GLint val, GLenum* target); - void addShaderData(GLuint shader); - bool isShader(GLuint shader); - void deleteShaderData(GLuint shader); - + bool addShaderData(GLuint shader); + // caller must hold a reference to the shader as long as it holds the pointer + ShaderData* getShaderData(GLuint shader); + void unrefShaderData(GLuint shader); }; typedef SmartPtr GLSharedGroupPtr; diff --git a/tools/emulator/opengl/system/GLESv2/gl2.cpp b/tools/emulator/opengl/system/GLESv2/gl2.cpp index a014c1a46..f852a9b64 100644 --- a/tools/emulator/opengl/system/GLESv2/gl2.cpp +++ b/tools/emulator/opengl/system/GLESv2/gl2.cpp @@ -9,7 +9,7 @@ #include "ThreadInfo.h" //XXX: fix this macro to get the context from fast tls path -#define GET_CONTEXT gl2_client_context_t * ctx = getEGLThreadInfo()->hostConn->gl2Encoder(); +#define GET_CONTEXT GL2Encoder * ctx = getEGLThreadInfo()->hostConn->gl2Encoder(); #include "gl2_entry.cpp" @@ -35,7 +35,7 @@ static EGLClient_glesInterface * s_gl = NULL; //GL extensions void glEGLImageTargetTexture2DOES(void * self, GLenum target, GLeglImageOES image) { - DBG("glEGLImageTargetTexture2DOES v2 img=%p\n", image); + DBG("glEGLImageTargetTexture2DOES v2 target=%#x img=%p\n", target, image); //TODO: check error - we don't have a way to set gl error android_native_buffer_t* native_buffer = (android_native_buffer_t*)image; @@ -47,8 +47,12 @@ void glEGLImageTargetTexture2DOES(void * self, GLenum target, GLeglImageOES imag return; } + GET_CONTEXT; DEFINE_AND_VALIDATE_HOST_CONNECTION(); + + ctx->override2DTextureTarget(target); rcEnc->rcBindTexture(rcEnc, ((cb_handle_t *)(native_buffer->handle))->hostHandle); + ctx->restore2DTextureTarget(); return; } diff --git a/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.cpp b/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.cpp index 59fe1a238..c9fb3966a 100644 --- a/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.cpp +++ b/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.cpp @@ -1,6 +1,27 @@ -#include "GL2Encoder.h" -#include +/* +* 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 +#include +#include + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif static GLubyte *gVendorString= (GLubyte *) "Android"; static GLubyte *gRendererString= (GLubyte *) "Android HW-GLES 2.0"; @@ -57,6 +78,8 @@ GL2Encoder::GL2Encoder(IOStream *stream) : gl2_encoder_context_t(stream) m_glCreateProgram_enc = set_glCreateProgram(s_glCreateProgram); m_glCreateShader_enc = set_glCreateShader(s_glCreateShader); m_glDeleteShader_enc = set_glDeleteShader(s_glDeleteShader); + m_glAttachShader_enc = set_glAttachShader(s_glAttachShader); + m_glDetachShader_enc = set_glDetachShader(s_glDetachShader); m_glGetUniformLocation_enc = set_glGetUniformLocation(s_glGetUniformLocation); m_glUseProgram_enc = set_glUseProgram(s_glUseProgram); @@ -79,6 +102,16 @@ GL2Encoder::GL2Encoder(IOStream *stream) : gl2_encoder_context_t(stream) m_glUniformMatrix2fv_enc = set_glUniformMatrix2fv(s_glUniformMatrix2fv); m_glUniformMatrix3fv_enc = set_glUniformMatrix3fv(s_glUniformMatrix3fv); m_glUniformMatrix4fv_enc = set_glUniformMatrix4fv(s_glUniformMatrix4fv); + + m_glActiveTexture_enc = set_glActiveTexture(s_glActiveTexture); + m_glBindTexture_enc = set_glBindTexture(s_glBindTexture); + m_glDeleteTextures_enc = set_glDeleteTextures(s_glDeleteTextures); + m_glGetTexParameterfv_enc = set_glGetTexParameterfv(s_glGetTexParameterfv); + m_glGetTexParameteriv_enc = set_glGetTexParameteriv(s_glGetTexParameteriv); + m_glTexParameterf_enc = set_glTexParameterf(s_glTexParameterf); + m_glTexParameterfv_enc = set_glTexParameterfv(s_glTexParameterfv); + m_glTexParameteri_enc = set_glTexParameteri(s_glTexParameteri); + m_glTexParameteriv_enc = set_glTexParameteriv(s_glTexParameteriv); } GL2Encoder::~GL2Encoder() @@ -184,21 +217,49 @@ void GL2Encoder::s_glVertexAtrribPointer(void *self, GLuint indx, GLint size, GL ctx->m_state->setState(indx, size, type, normalized, stride, ptr); } -void GL2Encoder::s_glGetIntegerv(void *self, GLenum param, GLint *params) +void GL2Encoder::s_glGetIntegerv(void *self, GLenum param, GLint *ptr) { GL2Encoder *ctx = (GL2Encoder *) self; assert(ctx->m_state != NULL); - if (param == GL_NUM_SHADER_BINARY_FORMATS) { - *params = 0; - } else if (param == GL_SHADER_BINARY_FORMATS) { + GLClientState* state = ctx->m_state; + + switch (param) { + case GL_NUM_SHADER_BINARY_FORMATS: + *ptr = 0; + break; + case GL_SHADER_BINARY_FORMATS: // do nothing - } else if (param == GL_COMPRESSED_TEXTURE_FORMATS) { + break; + + case GL_COMPRESSED_TEXTURE_FORMATS: { GLint *compressedTextureFormats = ctx->getCompressedTextureFormats(); - if (ctx->m_num_compressedTextureFormats > 0 && compressedTextureFormats != NULL) { - memcpy(params, compressedTextureFormats, ctx->m_num_compressedTextureFormats * sizeof(GLint)); + if (ctx->m_num_compressedTextureFormats > 0 && + compressedTextureFormats != NULL) { + memcpy(ptr, compressedTextureFormats, + ctx->m_num_compressedTextureFormats * sizeof(GLint)); } - } else if (!ctx->m_state->getClientStateParameter(param, params)) { - ctx->m_glGetIntegerv_enc(self, param, params); + break; + } + + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GL_MAX_TEXTURE_IMAGE_UNITS: + ctx->m_glGetIntegerv_enc(self, param, ptr); + *ptr = MIN(*ptr, GLClientState::MAX_TEXTURE_UNITS); + break; + + case GL_TEXTURE_BINDING_2D: + *ptr = state->getBoundTexture(GL_TEXTURE_2D); + break; + case GL_TEXTURE_BINDING_EXTERNAL_OES: + *ptr = state->getBoundTexture(GL_TEXTURE_EXTERNAL_OES); + break; + + default: + if (!ctx->m_state->getClientStateParameter(param, ptr)) { + ctx->m_glGetIntegerv_enc(self, param, ptr); + } + break; } } @@ -207,20 +268,46 @@ void GL2Encoder::s_glGetFloatv(void *self, GLenum param, GLfloat *ptr) { GL2Encoder *ctx = (GL2Encoder *)self; assert(ctx->m_state != NULL); - if (param == GL_NUM_SHADER_BINARY_FORMATS) { + GLClientState* state = ctx->m_state; + + switch (param) { + case GL_NUM_SHADER_BINARY_FORMATS: *ptr = 0; - } else if (param == GL_SHADER_BINARY_FORMATS) { - // do nothing; - } else if (param == GL_COMPRESSED_TEXTURE_FORMATS) { - GLint * compressedTextureFormats = ctx->getCompressedTextureFormats(); - if (ctx->m_num_compressedTextureFormats > 0 && compressedTextureFormats != NULL) { + 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; } - else if (!ctx->m_state->getClientStateParameter(param,ptr)) { + + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GL_MAX_TEXTURE_IMAGE_UNITS: ctx->m_glGetFloatv_enc(self, param, ptr); + *ptr = MIN(*ptr, (GLfloat)GLClientState::MAX_TEXTURE_UNITS); + break; + + case GL_TEXTURE_BINDING_2D: + *ptr = (GLfloat)state->getBoundTexture(GL_TEXTURE_2D); + break; + case GL_TEXTURE_BINDING_EXTERNAL_OES: + *ptr = (GLfloat)state->getBoundTexture(GL_TEXTURE_EXTERNAL_OES); + break; + + default: + if (!ctx->m_state->getClientStateParameter(param, ptr)) { + ctx->m_glGetFloatv_enc(self, param, ptr); + } + break; } } @@ -229,11 +316,40 @@ void GL2Encoder::s_glGetBooleanv(void *self, GLenum param, GLboolean *ptr) { GL2Encoder *ctx = (GL2Encoder *)self; assert(ctx->m_state != NULL); - if (param == GL_COMPRESSED_TEXTURE_FORMATS) { - // ignore the command, although we should have generated a GLerror; + 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; } - else if (!ctx->m_state->getClientStateParameter(param,ptr)) { - ctx->m_glGetBooleanv_enc(self, param, ptr); + + case GL_TEXTURE_BINDING_2D: + *ptr = state->getBoundTexture(GL_TEXTURE_2D) != 0 ? GL_TRUE : GL_FALSE; + break; + case GL_TEXTURE_BINDING_EXTERNAL_OES: + *ptr = state->getBoundTexture(GL_TEXTURE_EXTERNAL_OES) != 0 + ? GL_TRUE : GL_FALSE; + break; + + default: + if (!ctx->m_state->getClientStateParameter(param, ptr)) { + ctx->m_glGetBooleanv_enc(self, param, ptr); + } + break; } } @@ -431,13 +547,112 @@ GLint * GL2Encoder::getCompressedTextureFormats() 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 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_SAMPLER_EXTERNAL_OES[] = "samplerExternalOES"; + static const char STR_SAMPLER2D_SPACE[] = "sampler2D "; + + // -- 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++; + } + if (strncmp(c, STR_GL_OES_EGL_IMAGE_EXTERNAL, + sizeof(STR_GL_OES_EGL_IMAGE_EXTERNAL)-1) == 0) + { + // #extension statements are terminated by end of line + c = start; + while (*c != '\0' && *c != '\r' && *c != '\n') { + *c++ = ' '; + } + } + } + + // -- replace "samplerExternalOES" with "sampler2D" and record name + c = str; + while ((c = strstr(c, STR_SAMPLER_EXTERNAL_OES))) { + // Make sure "samplerExternalOES" isn't a substring of a larger token + if (c == str || !isspace(*(c-1))) { + c++; + continue; + } + char* sampler_start = c; + c += sizeof(STR_SAMPLER_EXTERNAL_OES)-1; + 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 == '_'); + data->samplerExternalNames.push_back( + android::String8(name_start, c - name_start)); + + // memcpy instead of strcpy since we don't want the NUL terminator + memcpy(sampler_start, STR_SAMPLER2D_SPACE, sizeof(STR_SAMPLER2D_SPACE)-1); + } + + return true; +} + void GL2Encoder::s_glShaderSource(void *self, GLuint shader, GLsizei count, const GLchar **string, const GLint *length) { + GL2Encoder* ctx = (GL2Encoder*)self; + ShaderData* shaderData = ctx->m_shared->getShaderData(shader); + SET_ERROR_IF(!shaderData, GL_INVALID_VALUE); + int len = glUtilsCalcShaderSourceLen((char**)string, (GLint*)length, count); char *str = new char[len + 1]; glUtilsPackStrings(str, (char**)string, (GLint*)length, count); - GL2Encoder *ctx = (GL2Encoder *)self; + // 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; } @@ -476,7 +691,7 @@ void GL2Encoder::s_glLinkProgram(void * self, GLuint program) { 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); + ctx->m_shared->setProgramIndexInfo(program, i, location, size, type, name); } ctx->m_shared->setupLocationShiftWAR(program); @@ -494,7 +709,7 @@ void GL2Encoder::s_glDeleteProgram(void *self, GLuint program) void GL2Encoder::s_glGetUniformiv(void *self, GLuint program, GLint location, GLint* params) { GL2Encoder *ctx = (GL2Encoder*)self; - SET_ERROR_IF(!(ctx->m_shared->isProgram(program) || ctx->m_shared->isShader(program)), GL_INVALID_VALUE); + SET_ERROR_IF(!ctx->m_shared->isProgram(program), GL_INVALID_VALUE); SET_ERROR_IF(!ctx->m_shared->isProgramInitialized(program), GL_INVALID_OPERATION); GLint hostLoc = ctx->m_shared->locationWARAppToHost(program, location); SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,hostLoc)==0, GL_INVALID_OPERATION); @@ -503,7 +718,7 @@ void GL2Encoder::s_glGetUniformiv(void *self, GLuint program, GLint location, GL void GL2Encoder::s_glGetUniformfv(void *self, GLuint program, GLint location, GLfloat* params) { GL2Encoder *ctx = (GL2Encoder*)self; - SET_ERROR_IF(!(ctx->m_shared->isProgram(program) || ctx->m_shared->isShader(program)), GL_INVALID_VALUE); + SET_ERROR_IF(!ctx->m_shared->isProgram(program), GL_INVALID_VALUE); SET_ERROR_IF(!ctx->m_shared->isProgramInitialized(program), GL_INVALID_OPERATION); GLint hostLoc = ctx->m_shared->locationWARAppToHost(program,location); SET_ERROR_IF(ctx->m_shared->getProgramUniformType(program,hostLoc)==0, GL_INVALID_OPERATION); @@ -523,8 +738,12 @@ GLuint GL2Encoder::s_glCreateShader(void *self, GLenum shaderType) { GL2Encoder *ctx = (GL2Encoder*)self; GLuint shader = ctx->m_glCreateShader_enc(self, shaderType); - if (shader!=0) - ctx->m_shared->addShaderData(shader); + if (shader != 0) { + if (!ctx->m_shared->addShaderData(shader)) { + ctx->m_glDeleteShader_enc(self, shader); + return 0; + } + } return shader; } @@ -532,7 +751,21 @@ void GL2Encoder::s_glDeleteShader(void *self, GLenum shader) { GL2Encoder *ctx = (GL2Encoder*)self; ctx->m_glDeleteShader_enc(self,shader); - ctx->m_shared->deleteShaderData(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 GL2Encoder::s_glGetUniformLocation(void *self, GLuint program, const GLchar *name) @@ -563,11 +796,58 @@ int GL2Encoder::s_glGetUniformLocation(void *self, GLuint program, const GLchar return hostLoc; } +bool GL2Encoder::updateHostTexture2DBinding(GLenum texUnit, GLenum newTarget) +{ + if (newTarget != GL_TEXTURE_2D && newTarget != GL_TEXTURE_EXTERNAL_OES) + return false; + + m_state->setActiveTextureUnit(texUnit); + + GLenum oldTarget = m_state->getPriorityEnabledTarget(GL_TEXTURE_2D); + if (newTarget != oldTarget) { + if (newTarget == GL_TEXTURE_EXTERNAL_OES) { + m_state->disableTextureTarget(GL_TEXTURE_2D); + m_state->enableTextureTarget(GL_TEXTURE_EXTERNAL_OES); + } else { + m_state->disableTextureTarget(GL_TEXTURE_EXTERNAL_OES); + m_state->enableTextureTarget(GL_TEXTURE_2D); + } + m_glActiveTexture_enc(this, texUnit); + m_glBindTexture_enc(this, GL_TEXTURE_2D, + m_state->getBoundTexture(newTarget)); + return true; + } + + return false; +} + void GL2Encoder::s_glUseProgram(void *self, GLuint program) { GL2Encoder *ctx = (GL2Encoder*)self; + GLClientState* state = ctx->m_state; + GLSharedGroupPtr shared = ctx->m_shared; + ctx->m_glUseProgram_enc(self, program); ctx->m_state->setCurrentProgram(program); + + 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(self, origActiveTexture); + } } void GL2Encoder::s_glUniform1f(void *self , GLint location, GLfloat x) @@ -587,8 +867,20 @@ void GL2Encoder::s_glUniform1fv(void *self , GLint location, GLsizei count, cons void GL2Encoder::s_glUniform1i(void *self , GLint location, GLint x) { GL2Encoder *ctx = (GL2Encoder*)self; + GLClientState* state = ctx->m_state; + GLSharedGroupPtr shared = ctx->m_shared; + GLint hostLoc = ctx->m_shared->locationWARAppToHost(ctx->m_state->currentProgram(),location); ctx->m_glUniform1i_enc(self, hostLoc, x); + + GLenum target; + if (shared->setSamplerUniform(state->currentProgram(), 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) @@ -703,3 +995,204 @@ void GL2Encoder::s_glUniformMatrix4fv(void *self , GLint location, GLsizei count 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((err = state->bindTexture(target, texture, &firstUse)) != GL_NO_ERROR, err); + + if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) { + ctx->m_glBindTexture_enc(ctx, target, texture); + return; + } + + GLenum priorityTarget = state->getPriorityEnabledTarget(GL_TEXTURE_2D); + + if (target == GL_TEXTURE_EXTERNAL_OES && firstUse) { + ctx->m_glBindTexture_enc(ctx, GL_TEXTURE_2D, texture); + ctx->m_glTexParameteri_enc(ctx, GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ctx->m_glTexParameteri_enc(ctx, GL_TEXTURE_2D, + GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + ctx->m_glTexParameteri_enc(ctx, GL_TEXTURE_2D, + GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (target != priorityTarget) { + ctx->m_glBindTexture_enc(ctx, GL_TEXTURE_2D, + state->getBoundTexture(GL_TEXTURE_2D)); + } + } + + if (target == priorityTarget) { + ctx->m_glBindTexture_enc(ctx, GL_TEXTURE_2D, texture); + } +} + +void GL2Encoder::s_glDeleteTextures(void* self, GLsizei n, const GLuint* textures) +{ + GL2Encoder* ctx = (GL2Encoder*)self; + GLClientState* state = ctx->m_state; + + state->deleteTextures(n, textures); + ctx->m_glDeleteTextures_enc(ctx, n, textures); +} + +void GL2Encoder::s_glGetTexParameterfv(void* self, + GLenum target, GLenum pname, GLfloat* params) +{ + GL2Encoder* ctx = (GL2Encoder*)self; + const GLClientState* state = ctx->m_state; + + if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) { + ctx->override2DTextureTarget(target); + ctx->m_glGetTexParameterfv_enc(ctx, GL_TEXTURE_2D, pname, params); + ctx->restore2DTextureTarget(); + } else { + ctx->m_glGetTexParameterfv_enc(ctx, target, pname, params); + } +} + +void GL2Encoder::s_glGetTexParameteriv(void* self, + GLenum target, GLenum pname, GLint* params) +{ + GL2Encoder* ctx = (GL2Encoder*)self; + const GLClientState* state = ctx->m_state; + + switch (pname) { + case GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES: + *params = 1; + break; + + default: + if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) { + ctx->override2DTextureTarget(target); + ctx->m_glGetTexParameteriv_enc(ctx, GL_TEXTURE_2D, pname, params); + ctx->restore2DTextureTarget(); + } else { + ctx->m_glGetTexParameteriv_enc(ctx, target, pname, params); + } + break; + } +} + +static bool isValidTextureExternalParam(GLenum pname, GLenum param) +{ + switch (pname) { + case GL_TEXTURE_MIN_FILTER: + case GL_TEXTURE_MAG_FILTER: + return param == GL_NEAREST || param == GL_LINEAR; + + case GL_TEXTURE_WRAP_S: + case GL_TEXTURE_WRAP_T: + return param == GL_CLAMP_TO_EDGE; + + default: + return true; + } +} + +void GL2Encoder::s_glTexParameterf(void* self, + GLenum target, GLenum pname, GLfloat param) +{ + GL2Encoder* ctx = (GL2Encoder*)self; + const GLClientState* state = ctx->m_state; + + SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES && + !isValidTextureExternalParam(pname, (GLenum)param)), + GL_INVALID_ENUM); + + if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) { + ctx->override2DTextureTarget(target); + ctx->m_glTexParameterf_enc(ctx, GL_TEXTURE_2D, pname, param); + ctx->restore2DTextureTarget(); + } else { + ctx->m_glTexParameterf_enc(ctx, target, pname, param); + } +} + +void GL2Encoder::s_glTexParameterfv(void* self, + GLenum target, GLenum pname, const GLfloat* params) +{ + GL2Encoder* ctx = (GL2Encoder*)self; + const GLClientState* state = ctx->m_state; + + SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES && + !isValidTextureExternalParam(pname, (GLenum)params[0])), + GL_INVALID_ENUM); + + if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) { + ctx->override2DTextureTarget(target); + ctx->m_glTexParameterfv_enc(ctx, GL_TEXTURE_2D, pname, params); + ctx->restore2DTextureTarget(); + } else { + ctx->m_glTexParameterfv_enc(ctx, target, pname, params); + } +} + +void GL2Encoder::s_glTexParameteri(void* self, + GLenum target, GLenum pname, GLint param) +{ + GL2Encoder* ctx = (GL2Encoder*)self; + const GLClientState* state = ctx->m_state; + + SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES && + !isValidTextureExternalParam(pname, (GLenum)param)), + GL_INVALID_ENUM); + + if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) { + ctx->override2DTextureTarget(target); + ctx->m_glTexParameteri_enc(ctx, GL_TEXTURE_2D, pname, param); + ctx->restore2DTextureTarget(); + } else { + ctx->m_glTexParameteri_enc(ctx, target, pname, param); + } +} + +void GL2Encoder::s_glTexParameteriv(void* self, + GLenum target, GLenum pname, const GLint* params) +{ + GL2Encoder* ctx = (GL2Encoder*)self; + const GLClientState* state = ctx->m_state; + + SET_ERROR_IF((target == GL_TEXTURE_EXTERNAL_OES && + !isValidTextureExternalParam(pname, (GLenum)params[0])), + GL_INVALID_ENUM); + + if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) { + ctx->override2DTextureTarget(target); + ctx->m_glTexParameteriv_enc(ctx, GL_TEXTURE_2D, pname, params); + ctx->restore2DTextureTarget(); + } else { + ctx->m_glTexParameteriv_enc(ctx, target, pname, params); + } +} + +void GL2Encoder::override2DTextureTarget(GLenum target) +{ + if ((target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) && + target != m_state->getPriorityEnabledTarget(GL_TEXTURE_2D)) { + m_glBindTexture_enc(this, GL_TEXTURE_2D, + m_state->getBoundTexture(target)); + } +} + +void GL2Encoder::restore2DTextureTarget() +{ + GLenum priorityTarget = m_state->getPriorityEnabledTarget(GL_TEXTURE_2D); + m_glBindTexture_enc(this, GL_TEXTURE_2D, + m_state->getBoundTexture(priorityTarget)); +} diff --git a/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.h b/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.h index 2914fa905..f9235d747 100644 --- a/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.h +++ b/tools/emulator/opengl/system/GLESv2_enc/GL2Encoder.h @@ -17,7 +17,6 @@ #define _GL2_ENCODER_H_ #include "gl2_enc.h" -#include "IOStream.h" #include "GLClientState.h" #include "GLSharedGroup.h" #include "FixedBuffer.h" @@ -33,9 +32,7 @@ public: void setSharedGroup(GLSharedGroupPtr shared){ m_shared = shared; } const GLClientState *state() { return m_state; } const GLSharedGroupPtr shared() { return m_shared; } - void flush() { - gl2_encoder_context_t::m_stream->flush(); - } + void flush() { m_stream->flush(); } void setInitialized(){ m_initialized = true; }; bool isInitialized(){ return m_initialized; }; @@ -43,6 +40,9 @@ public: virtual void setError(GLenum error){ m_error = error; }; virtual GLenum getError() { return m_error; }; + void override2DTextureTarget(GLenum target); + void restore2DTextureTarget(); + private: bool m_initialized; @@ -57,6 +57,7 @@ private: FixedBuffer m_fixedBuffer; void sendVertexAttributes(GLint first, GLsizei count); + bool updateHostTexture2DBinding(GLenum texUnit, GLenum newTarget); glGetError_client_proc_t m_glGetError_enc; static GLenum s_glGetError(void * self); @@ -141,6 +142,12 @@ private: glDeleteShader_client_proc_t m_glDeleteShader_enc; static void s_glDeleteShader(void *self, GLuint shader); + glAttachShader_client_proc_t m_glAttachShader_enc; + static void s_glAttachShader(void *self, GLuint program, GLuint shader); + + glDetachShader_client_proc_t m_glDetachShader_enc; + static void s_glDetachShader(void *self, GLuint program, GLuint shader); + glGetUniformLocation_client_proc_t m_glGetUniformLocation_enc; static int s_glGetUniformLocation(void *self, GLuint program, const GLchar *name); glUseProgram_client_proc_t m_glUseProgram_enc; @@ -185,5 +192,25 @@ private: static void s_glUniformMatrix2fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); static void s_glUniformMatrix3fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); static void s_glUniformMatrix4fv(void *self , GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + + glActiveTexture_client_proc_t m_glActiveTexture_enc; + glBindTexture_client_proc_t m_glBindTexture_enc; + glDeleteTextures_client_proc_t m_glDeleteTextures_enc; + glGetTexParameterfv_client_proc_t m_glGetTexParameterfv_enc; + glGetTexParameteriv_client_proc_t m_glGetTexParameteriv_enc; + glTexParameterf_client_proc_t m_glTexParameterf_enc; + glTexParameterfv_client_proc_t m_glTexParameterfv_enc; + glTexParameteri_client_proc_t m_glTexParameteri_enc; + glTexParameteriv_client_proc_t m_glTexParameteriv_enc; + + static void s_glActiveTexture(void* self, GLenum texture); + static void s_glBindTexture(void* self, GLenum target, GLuint texture); + static void s_glDeleteTextures(void* self, GLsizei n, const GLuint* textures); + static void s_glGetTexParameterfv(void* self, GLenum target, GLenum pname, GLfloat* params); + static void s_glGetTexParameteriv(void* self, GLenum target, GLenum pname, GLint* params); + static void s_glTexParameterf(void* self, GLenum target, GLenum pname, GLfloat param); + static void s_glTexParameterfv(void* self, GLenum target, GLenum pname, const GLfloat* params); + static void s_glTexParameteri(void* self, GLenum target, GLenum pname, GLint param); + static void s_glTexParameteriv(void* self, GLenum target, GLenum pname, const GLint* params); }; #endif