//----------------------------------------------------------------------------- GLES2FrameBufferObject::GLES2FrameBufferObject(GLES2FBOManager *manager, uint fsaa): mManager(manager), mNumSamples(fsaa) { // Generate framebuffer object OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mFB)); if(getGLES2SupportRef()->checkExtension("GL_EXT_debug_label")) { OGRE_IF_IOS_VERSION_IS_GREATER_THAN(5.0) OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_BUFFER_OBJECT_EXT, mFB, 0, ("FBO #" + StringConverter::toString(mFB)).c_str())); } mNumSamples = 0; mMultisampleFB = 0; // Check multisampling if supported if(gleswIsSupported(3, 0)) { // Check samples supported OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB)); GLint maxSamples; OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamples)); OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); mNumSamples = std::min(mNumSamples, (GLsizei)maxSamples); } // Will we need a second FBO to do multisampling? if (mNumSamples) { OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mMultisampleFB)); if(getGLES2SupportRef()->checkExtension("GL_EXT_debug_label")) { OGRE_IF_IOS_VERSION_IS_GREATER_THAN(5.0) OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_BUFFER_OBJECT_EXT, mMultisampleFB, 0, ("MSAA FBO #" + StringConverter::toString(mMultisampleFB)).c_str())); } } else { mMultisampleFB = 0; } // Initialise state mDepth.buffer = 0; mStencil.buffer = 0; for(size_t x = 0; x < OGRE_MAX_MULTIPLE_RENDER_TARGETS; ++x) { mColour[x].buffer=0; } }
//------------------------------------------------------------------ void GLES2HardwareOcclusionQuery::beginOcclusionQuery() { if(getGLES2SupportRef()->checkExtension("GL_EXT_occlusion_query_boolean") || gleswIsSupported(3, 0)) { OGRE_CHECK_GL_ERROR(glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, mQueryID)); } }
//------------------------------------------------------------------ void GLES2HardwareOcclusionQuery::destroyQuery() { if(getGLES2SupportRef()->checkExtension("GL_EXT_occlusion_query_boolean") || gleswIsSupported(3, 0)) { OGRE_CHECK_GL_ERROR(glDeleteQueriesEXT(1, &mQueryID)); } }
//----------------------------------------------------------------------------- GLES2FrameBufferObject::GLES2FrameBufferObject(GLES2FBOManager *manager, uint fsaa): mManager(manager), mNumSamples(fsaa) { /// Generate framebuffer object OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mFB)); mNumSamples = 0; mMultisampleFB = 0; // Check multisampling if supported if(getGLES2SupportRef()->checkExtension("GL_APPLE_framebuffer_multisample") || gleswIsSupported(3, 0)) { // Check samples supported OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB)); GLint maxSamples; OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamples)); OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); mNumSamples = std::min(mNumSamples, (GLsizei)maxSamples); } // Will we need a second FBO to do multisampling? if (mNumSamples) { OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mMultisampleFB)); } /// Initialise state mDepth.buffer=0; mStencil.buffer=0; for(size_t x=0; x<OGRE_MAX_MULTIPLE_RENDER_TARGETS; ++x) { mColour[x].buffer=0; } }
void GLES2FrameBufferObject::swapBuffers() { if (mMultisampleFB) { #if OGRE_NO_GLES3_SUPPORT == 1 if(getGLES2SupportRef()->checkExtension("GL_APPLE_framebuffer_multisample")) { // Blit from multisample buffer to final buffer, triggers resolve OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, mMultisampleFB)); OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, mFB)); } #else GLint oldfb = 0; OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb)); // Blit from multisample buffer to final buffer, triggers resolve uint32 width = mColour[0].buffer->getWidth(); uint32 height = mColour[0].buffer->getHeight(); OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mMultisampleFB)); OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFB)); OGRE_CHECK_GL_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); // Unbind OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, oldfb)); #endif } }
//------------------------------------------------------------------ bool GLES2HardwareOcclusionQuery::pullOcclusionQuery( unsigned int* NumOfFragments ) { if(getGLES2SupportRef()->checkExtension("GL_EXT_occlusion_query_boolean") || gleswIsSupported(3, 0)) { OGRE_CHECK_GL_ERROR(glGetQueryObjectuivEXT(mQueryID, GL_QUERY_RESULT_EXT, (GLuint*)NumOfFragments)); mPixelCount = *NumOfFragments; return true; } else return false; }
//------------------------------------------------------------------ bool GLES2HardwareOcclusionQuery::isStillOutstanding(void) { GLuint available = GL_FALSE; if(getGLES2SupportRef()->checkExtension("GL_EXT_occlusion_query_boolean") || gleswIsSupported(3, 0)) { OGRE_CHECK_GL_ERROR(glGetQueryObjectuivEXT(mQueryID, GL_QUERY_RESULT_AVAILABLE_EXT, &available)); } // GL_TRUE means a wait would occur return !(available == GL_TRUE); }
//------------------------------------------------------------------ void GLES2HardwareOcclusionQuery::createQuery() { // Check for hardware occlusion support if(getGLES2SupportRef()->checkExtension("GL_EXT_occlusion_query_boolean") || gleswIsSupported(3, 0)) { OGRE_CHECK_GL_ERROR(glGenQueriesEXT(1, &mQueryID)); } else { OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Cannot allocate a Hardware query. This video card doesn't support it, sorry.", "GLES2HardwareOcclusionQuery::GLES2HardwareOcclusionQuery" ); } }
HardwareIndexBufferSharedPtr GLES2HardwareBufferManagerBase::createIndexBuffer(HardwareIndexBuffer::IndexType itype, size_t numIndexes, HardwareBuffer::Usage usage, bool useShadowBuffer) { GLES2HardwareIndexBuffer* buf = 0; if(getGLES2SupportRef()->checkExtension("GL_EXT_map_buffer_range") || gleswIsSupported(3, 0)) buf = OGRE_NEW GLES2HardwareIndexBuffer(this, itype, numIndexes, usage, useShadowBuffer); else // always use shadowBuffer buf = OGRE_NEW GLES2HardwareIndexBuffer(this, itype, numIndexes, usage, true); { OGRE_LOCK_MUTEX(mIndexBuffersMutex); mIndexBuffers.insert(buf); } return HardwareIndexBufferSharedPtr(buf); }
void GLES2HardwareVertexBuffer::createBuffer() { OGRE_CHECK_GL_ERROR(glGenBuffers(1, &mBufferId)); if(getGLES2SupportRef()->checkExtension("GL_EXT_debug_label")) { OGRE_IF_IOS_VERSION_IS_GREATER_THAN(5.0) OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_BUFFER_OBJECT_EXT, mBufferId, 0, ("Vertex Buffer #" + StringConverter::toString(mBufferId)).c_str())); } if (!mBufferId) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Cannot create GL ES vertex buffer", "GLES2HardwareVertexBuffer::GLES2HardwareVertexBuffer"); } static_cast<GLES2HardwareBufferManagerBase*>(mMgr)->getStateCacheManager()->bindGLBuffer(GL_ARRAY_BUFFER, mBufferId); OGRE_CHECK_GL_ERROR(glBufferData(GL_ARRAY_BUFFER, mSizeInBytes, NULL, GLES2HardwareBufferManager::getGLUsage(mUsage))); }
//----------------------------------------------------------------------- void GLSLESProgramCommon::getMicrocodeFromCache(void) { GpuProgramManager::Microcode cacheMicrocode = GpuProgramManager::getSingleton().getMicrocodeFromCache(getCombinedName()); // add to the microcode to the cache String name; name = getCombinedName(); // turns out we need this param when loading GLenum binaryFormat = 0; cacheMicrocode->seek(0); // get size of binary cacheMicrocode->read(&binaryFormat, sizeof(GLenum)); if(getGLES2SupportRef()->checkExtension("GL_OES_get_program_binary") || gleswIsSupported(3, 0)) { GLint binaryLength = static_cast<GLint>(cacheMicrocode->size() - sizeof(GLenum)); // load binary OGRE_CHECK_GL_ERROR(glProgramBinaryOES(mGLProgramHandle, binaryFormat, cacheMicrocode->getPtr(), binaryLength)); } GLint success = 0; OGRE_CHECK_GL_ERROR(glGetProgramiv(mGLProgramHandle, GL_LINK_STATUS, &success)); if (!success) { // // Something must have changed since the program binaries // were cached away. Fallback to source shader loading path, // and then retrieve and cache new program binaries once again. // compileAndLink(); } }
void GLES2HardwareIndexBuffer::readData(size_t offset, size_t length, void* pDest) { if(mUseShadowBuffer) { // Get data from the shadow buffer void* srcData = mShadowBuffer->lock(offset, length, HBL_READ_ONLY); memcpy(pDest, srcData, length); mShadowBuffer->unlock(); } else { if(getGLES2SupportRef()->checkExtension("GL_EXT_map_buffer_range") || gleswIsSupported(3, 0)) { // Map the buffer range then copy out of it into our destination buffer void* srcData; OGRE_CHECK_GL_ERROR(srcData = glMapBufferRangeEXT(GL_ELEMENT_ARRAY_BUFFER, offset, length, GL_MAP_READ_BIT_EXT)); memcpy(pDest, srcData, length); // Unmap the buffer since we are done. GLboolean mapped; OGRE_CHECK_GL_ERROR(mapped = glUnmapBufferOES(GL_ELEMENT_ARRAY_BUFFER)); if(!mapped) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Buffer data corrupted, please reload", "GLES2HardwareIndexBuffer::readData"); } } else { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Reading hardware buffer is not supported", "GLES2HardwareIndexBuffer::readData"); } } }
void GLES2Texture::_createGLTexResource() { if(!Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_NON_POWER_OF_2_TEXTURES) || (Root::getSingleton().getRenderSystem()->getCapabilities()->getNonPOW2TexturesLimited() && mNumRequestedMipmaps > 0)) { // Convert to nearest power-of-two size if required mWidth = GLES2PixelUtil::optionalPO2(mWidth); mHeight = GLES2PixelUtil::optionalPO2(mHeight); mDepth = GLES2PixelUtil::optionalPO2(mDepth); } // Adjust format if required mFormat = TextureManager::getSingleton().getNativeFormat(mTextureType, mFormat, mUsage); GLenum texTarget = getGLES2TextureTarget(); // Check requested number of mipmaps size_t maxMips = GLES2PixelUtil::getMaxMipmaps(mWidth, mHeight, mDepth, mFormat); if(PixelUtil::isCompressed(mFormat) && (mNumMipmaps == 0)) mNumRequestedMipmaps = 0; mNumMipmaps = mNumRequestedMipmaps; if (mNumMipmaps > maxMips) mNumMipmaps = maxMips; // Generate texture name OGRE_CHECK_GL_ERROR(glGenTextures(1, &mTextureID)); // Set texture type mGLSupport.getStateCacheManager()->bindGLTexture(texTarget, mTextureID); // If we can do automip generation and the user desires this, do so mMipmapsHardwareGenerated = Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_AUTOMIPMAP) && !PixelUtil::isCompressed(mFormat); if(!Bitwise::isPO2(mWidth) || !Bitwise::isPO2(mHeight)) mMipmapsHardwareGenerated = false; // glGenerateMipmap require all mip levels to be prepared. So override how many this texture has. if((mUsage & TU_AUTOMIPMAP) && mMipmapsHardwareGenerated && mNumRequestedMipmaps) mNumMipmaps = maxMips; if(getGLES2SupportRef()->checkExtension("GL_APPLE_texture_max_level") || gleswIsSupported(3, 0)) mGLSupport.getStateCacheManager()->setTexParameteri(texTarget, GL_TEXTURE_MAX_LEVEL_APPLE, mNumRequestedMipmaps ? mNumMipmaps + 1 : 0); // Set some misc default parameters, these can of course be changed later mGLSupport.getStateCacheManager()->setTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, ((mUsage & TU_AUTOMIPMAP) && mNumRequestedMipmaps) ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST); mGLSupport.getStateCacheManager()->setTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); mGLSupport.getStateCacheManager()->setTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); mGLSupport.getStateCacheManager()->setTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set up texture swizzling #if OGRE_NO_GLES3_SUPPORT == 0 if(gleswIsSupported(3, 0)) { OGRE_CHECK_GL_ERROR(glTexParameteri(texTarget, GL_TEXTURE_SWIZZLE_R, GL_RED)); OGRE_CHECK_GL_ERROR(glTexParameteri(texTarget, GL_TEXTURE_SWIZZLE_G, GL_GREEN)); OGRE_CHECK_GL_ERROR(glTexParameteri(texTarget, GL_TEXTURE_SWIZZLE_B, GL_BLUE)); OGRE_CHECK_GL_ERROR(glTexParameteri(texTarget, GL_TEXTURE_SWIZZLE_A, GL_ALPHA)); if(mFormat == PF_L8 || mFormat == PF_L16) { OGRE_CHECK_GL_ERROR(glTexParameteri(texTarget, GL_TEXTURE_SWIZZLE_R, GL_RED)); OGRE_CHECK_GL_ERROR(glTexParameteri(texTarget, GL_TEXTURE_SWIZZLE_G, GL_RED)); OGRE_CHECK_GL_ERROR(glTexParameteri(texTarget, GL_TEXTURE_SWIZZLE_B, GL_RED)); OGRE_CHECK_GL_ERROR(glTexParameteri(texTarget, GL_TEXTURE_SWIZZLE_A, GL_RED)); } } #endif // Allocate internal buffer so that glTexSubImageXD can be used // Internal format GLenum format = GLES2PixelUtil::getGLOriginFormat(mFormat); GLenum internalformat = GLES2PixelUtil::getClosestGLInternalFormat(mFormat, mHwGamma); uint32 width = mWidth; uint32 height = mHeight; uint32 depth = mDepth; if (PixelUtil::isCompressed(mFormat)) { // Compressed formats GLsizei size = static_cast<GLsizei>(PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat)); // Provide temporary buffer filled with zeroes as glCompressedTexImageXD does not // accept a 0 pointer like normal glTexImageXD // Run through this process for every mipmap to pregenerate mipmap pyramid uint8* tmpdata = new uint8[size]; memset(tmpdata, 0, size); for (GLint mip = 0; mip <= mNumMipmaps; mip++) { #if OGRE_DEBUG_MODE LogManager::getSingleton().logMessage("GLES2Texture::create - Mip: " + StringConverter::toString(mip) + " Width: " + StringConverter::toString(width) + " Height: " + StringConverter::toString(height) + " Internal Format: " + StringConverter::toString(internalformat, 0, ' ', std::ios::hex) + " Format: " + StringConverter::toString(format, 0, ' ', std::ios::hex) ); #endif size = static_cast<GLsizei>(PixelUtil::getMemorySize(width, height, depth, mFormat)); switch(mTextureType) { case TEX_TYPE_1D: case TEX_TYPE_2D: OGRE_CHECK_GL_ERROR(glCompressedTexImage2D(GL_TEXTURE_2D, mip, internalformat, width, height, 0, size, tmpdata)); break; case TEX_TYPE_CUBE_MAP: for(int face = 0; face < 6; face++) { OGRE_CHECK_GL_ERROR(glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mip, internalformat, width, height, 0, size, tmpdata)); } break; #if OGRE_NO_GLES3_SUPPORT == 0 case TEX_TYPE_2D_ARRAY: case TEX_TYPE_3D: glCompressedTexImage3D(getGLES2TextureTarget(), mip, format, width, height, depth, 0, size, tmpdata); break; #endif default: break; }; if(width > 1) { width = width / 2; } if(height > 1) { height = height / 2; } if(depth > 1 && mTextureType != TEX_TYPE_2D_ARRAY) { depth = depth / 2; } } delete [] tmpdata; } else { #if OGRE_NO_GLES3_SUPPORT == 0 #if OGRE_DEBUG_MODE LogManager::getSingleton().logMessage("GLES2Texture::create - Name: " + mName + " ID: " + StringConverter::toString(mTextureID) + " Width: " + StringConverter::toString(width) + " Height: " + StringConverter::toString(height) + " Internal Format: " + StringConverter::toString(internalformat, 0, ' ', std::ios::hex)); #endif switch(mTextureType) { case TEX_TYPE_1D: case TEX_TYPE_2D: case TEX_TYPE_2D_RECT: OGRE_CHECK_GL_ERROR(glTexStorage2D(GL_TEXTURE_2D, GLsizei(mNumMipmaps+1), internalformat, GLsizei(width), GLsizei(height))); break; case TEX_TYPE_CUBE_MAP: OGRE_CHECK_GL_ERROR(glTexStorage2D(GL_TEXTURE_CUBE_MAP, GLsizei(mNumMipmaps+1), internalformat, GLsizei(width), GLsizei(height))); break; case TEX_TYPE_2D_ARRAY: OGRE_CHECK_GL_ERROR(glTexStorage3D(GL_TEXTURE_2D_ARRAY, GLsizei(mNumMipmaps+1), internalformat, GLsizei(width), GLsizei(height), GLsizei(depth))); break; case TEX_TYPE_3D: OGRE_CHECK_GL_ERROR(glTexStorage3D(GL_TEXTURE_3D, GLsizei(mNumMipmaps+1), internalformat, GLsizei(width), GLsizei(height), GLsizei(depth))); break; } #else GLenum datatype = GLES2PixelUtil::getGLOriginDataType(mFormat); // Run through this process to pregenerate mipmap pyramid for(GLint mip = 0; mip <= mNumMipmaps; mip++) { #if OGRE_DEBUG_MODE LogManager::getSingleton().logMessage("GLES2Texture::create - Mip: " + StringConverter::toString(mip) + " Name: " + mName + " ID: " + StringConverter::toString(mTextureID) + " Width: " + StringConverter::toString(width) + " Height: " + StringConverter::toString(height) + " Internal Format: " + StringConverter::toString(internalformat, 0, ' ', std::ios::hex) + " Format: " + StringConverter::toString(format, 0, ' ', std::ios::hex) + " Datatype: " + StringConverter::toString(datatype, 0, ' ', std::ios::hex) ); #endif // Normal formats switch(mTextureType) { case TEX_TYPE_1D: case TEX_TYPE_2D: #if OGRE_PLATFORM == OGRE_PLATFORM_NACL if(internalformat != format) { LogManager::getSingleton().logMessage("glTexImage2D: format != internalFormat, " "format=" + StringConverter::toString(format) + ", internalFormat=" + StringConverter::toString(internalformat)); } #endif OGRE_CHECK_GL_ERROR(glTexImage2D(GL_TEXTURE_2D, mip, internalformat, width, height, 0, format, datatype, 0)); break; case TEX_TYPE_CUBE_MAP: for(int face = 0; face < 6; face++) { OGRE_CHECK_GL_ERROR(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mip, internalformat, width, height, 0, format, datatype, 0)); } break; default: break; }; if (width > 1) { width = Bitwise::firstPO2From(width / 2); } if (height > 1) { height = Bitwise::firstPO2From(height / 2); } } #endif } }
bool GLSLESProgram::compile(const bool checkErrors) { if (mCompiled == 1) { return true; } // Only create a shader object if glsl es is supported if (isSupported()) { // Create shader object GLenum shaderType = 0x0000; if (mType == GPT_VERTEX_PROGRAM) { shaderType = GL_VERTEX_SHADER; } else if (mType == GPT_FRAGMENT_PROGRAM) { shaderType = GL_FRAGMENT_SHADER; } OGRE_CHECK_GL_ERROR(mGLShaderHandle = glCreateShader(shaderType)); #if OGRE_PLATFORM != OGRE_PLATFORM_NACL if(getGLES2SupportRef()->checkExtension("GL_EXT_debug_label")) { OGRE_IF_IOS_VERSION_IS_GREATER_THAN(5.0) glLabelObjectEXT(GL_SHADER_OBJECT_EXT, mGLShaderHandle, 0, mName.c_str()); } #endif if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS)) { OGRE_CHECK_GL_ERROR(mGLProgramHandle = glCreateProgram()); #if OGRE_PLATFORM != OGRE_PLATFORM_NACL if(getGLES2SupportRef()->checkExtension("GL_EXT_debug_label")) { OGRE_IF_IOS_VERSION_IS_GREATER_THAN(5.0) glLabelObjectEXT(GL_PROGRAM_OBJECT_EXT, mGLProgramHandle, 0, mName.c_str()); } #endif } } // Add preprocessor extras and main source if (!mSource.empty()) { // Fix up the source in case someone forgot to redeclare gl_Position if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS) && mType == GPT_VERTEX_PROGRAM) { size_t versionPos = mSource.find("#version"); int shaderVersion = StringConverter::parseInt(mSource.substr(versionPos+9, 3)); // Check that it's missing and that this shader has a main function, ie. not a child shader. if(mSource.find("out highp vec4 gl_Position") == String::npos) { if(shaderVersion >= 300) mSource.insert(versionPos+16, "out highp vec4 gl_Position;\nout highp float gl_PointSize;\n"); } if(mSource.find("#extension GL_EXT_separate_shader_objects : require") == String::npos) { if(shaderVersion >= 300) mSource.insert(versionPos+16, "#extension GL_EXT_separate_shader_objects : require\n"); } } #if !OGRE_NO_GLES2_GLSL_OPTIMISER const char *source = (getOptimiserEnabled() && getIsOptimised()) ? mOptimisedSource.c_str() : mSource.c_str(); #else const char *source = mSource.c_str(); #endif OGRE_CHECK_GL_ERROR(glShaderSource(mGLShaderHandle, 1, &source, NULL)); } if (checkErrors) logObjectInfo("GLSL ES compiling: " + mName, mGLShaderHandle); OGRE_CHECK_GL_ERROR(glCompileShader(mGLShaderHandle)); // Check for compile errors OGRE_CHECK_GL_ERROR(glGetShaderiv(mGLShaderHandle, GL_COMPILE_STATUS, &mCompiled)); if(!mCompiled && checkErrors) { String message = logObjectInfo("GLSL ES compile log: " + mName, mGLShaderHandle); checkAndFixInvalidDefaultPrecisionError(message); } // Log a message that the shader compiled successfully. if (mCompiled && checkErrors) logObjectInfo("GLSL ES compiled: " + mName, mGLShaderHandle); if(!mCompiled) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, ((mType == GPT_VERTEX_PROGRAM) ? "Vertex Program " : "Fragment Program ") + mName + " failed to compile. See compile log above for details.", "GLSLESProgram::compile"); } return (mCompiled == 1); }