CGLMShaderPair::CGLMShaderPair( GLMContext *ctx ) { m_ctx = ctx; m_ctx->MakeCurrent(); m_vertexProg = m_fragmentProg = NULL; m_program = glCreateProgramObjectARB(); GLMCheckError(); m_locVertexParams = -1; m_locVertexInteger0 = -1; // "i0" m_locVertexBool0 = -1; // "b0" m_locVertexBool1 = -1; // "b1" m_locVertexBool2 = -1; // "b2" m_locVertexBool3 = -1; // "b3" m_locFragmentParams = -1; m_locFragmentFakeSRGBEnable = -1; m_fakeSRGBEnableValue = -1.0f; memset( m_locSamplers, 0xFF, sizeof( m_locSamplers ) ); m_valid = false; m_samplersFixed = false; // fix them at draw time, and only do it once. m_revision = 0; // bumps to 1 once linked }
CGLMProgram::~CGLMProgram( ) { m_ctx->CheckCurrent(); // if there is an arb program, delete it GLMShaderDesc *arbDesc = &m_descs[ kGLMARB ]; if (arbDesc->m_object.arb) { glDeleteProgramsARB( 1, &arbDesc->m_object.arb ); GLMCheckError(); arbDesc->m_object.arb = 0; } // if there is a GLSL shader, delete it GLMShaderDesc *glslDesc = &m_descs[kGLMGLSL]; if (glslDesc->m_object.glsl) { glDeleteShader( (uintptr_t)glslDesc->m_object.glsl ); // why do I need a cast here again ? GLMCheckError(); glslDesc->m_object.glsl = 0; } #if GLMDEBUG if (m_editable) { delete m_editable; m_editable = NULL; } #endif if (m_text) { free( m_text ); m_text = NULL; } m_ctx = NULL; }
CGLMBuffer::~CGLMBuffer( ) { m_ctx->CheckCurrent(); if (m_pseudo) { free (m_pseudoBuf); m_pseudoBuf = NULL; } else { glDeleteBuffersARB( 1, &m_name ); GLMCheckError(); } m_ctx = NULL; m_name = 0; m_bound = 0; m_lastMappedAddress = NULL; }
bool CGLMShaderPair::SetProgramPair ( CGLMProgram *vp, CGLMProgram *fp ) { m_valid = false; // assume failure // true result means successful link and query bool vpgood = (vp!=NULL) && (vp->m_descs[ kGLMGLSL ].m_valid); bool fpgood = (fp!=NULL) && (fp->m_descs[ kGLMGLSL ].m_valid); if (!fpgood) { // fragment side allowed to be "null". fp = m_ctx->m_nullFragmentProgram; } if (vpgood && fpgood) { // attempt link. but first, detach any previously attached programs if (m_vertexProg) { glDetachObjectARB(m_program, m_vertexProg->m_descs[kGLMGLSL].m_object.glsl); GLMCheckError(); m_vertexProg = NULL; } if (m_fragmentProg) { glDetachObjectARB(m_program, m_fragmentProg->m_descs[kGLMGLSL].m_object.glsl); GLMCheckError(); m_fragmentProg = NULL; } // now attach glAttachObjectARB(m_program, vp->m_descs[kGLMGLSL].m_object.glsl); m_vertexProg = vp; GLMCheckError(); glAttachObjectARB(m_program, fp->m_descs[kGLMGLSL].m_object.glsl); m_fragmentProg = fp; GLMCheckError(); // force the locations for input attributes v0-vN to be at locations 0-N // use the vertex attrib map to know which slots are live or not... oy! we don't have that map yet... but it's OK. // fallback - just force v0-v15 to land in locations 0-15 as a standard. if (vp->m_descs[kGLMGLSL].m_valid) { for( int i=0; i < 16; i++) { char tmp[16]; sprintf(tmp, "v%d", i); // v0 v1 v2 ... et al glBindAttribLocationARB( m_program, i, tmp ); GLMCheckError(); } } // now link glLinkProgramARB( m_program ); GLMCheckError(); // check for success GLint result = 0; glGetObjectParameterivARB(m_program,GL_OBJECT_LINK_STATUS_ARB,&result); // want GL_TRUE if (result == GL_TRUE) { // success m_valid = true; m_revision++; } else { GLint length = 0; GLint laux = 0; // do some digging glGetObjectParameterivARB(m_program,GL_OBJECT_INFO_LOG_LENGTH_ARB,&length); GLcharARB *logString = (GLcharARB *)malloc(length * sizeof(GLcharARB)); glGetInfoLogARB(m_program, length, &laux, logString); char *vtemp = strdup(vp->m_text); vtemp[ vp->m_descs[kGLMGLSL].m_textOffset + vp->m_descs[kGLMGLSL].m_textLength ] = 0; char *ftemp = strdup(fp->m_text); ftemp[ fp->m_descs[kGLMGLSL].m_textOffset + fp->m_descs[kGLMGLSL].m_textLength ] = 0; GLMPRINTF(("-D- ----- GLSL link failed: \n %s ",logString )); GLMPRINTF(("-D- ----- GLSL vertex program selected: %08x (handle %08x)", vp, vp->m_descs[kGLMGLSL].m_object.glsl )); GLMPRINTTEXT(( vtemp + vp->m_descs[kGLMGLSL].m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES )); GLMPRINTF(("-D- ----- GLSL fragment program selected: %08x (handle %08x)", fp, vp->m_descs[kGLMGLSL].m_object.glsl )); GLMPRINTTEXT(( ftemp + fp->m_descs[kGLMGLSL].m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES )); GLMPRINTF(("-D- -----end-----" )); free( ftemp ); free( vtemp ); free( logString ); } } else { // fail Assert(!"Can't link these programs"); } if (m_valid) { m_locVertexParams = glGetUniformLocationARB( m_program, "vc"); GLMCheckError(); m_locVertexInteger0 = glGetUniformLocationARB( m_program, "i0"); GLMCheckError(); m_locVertexBool0 = glGetUniformLocationARB( m_program, "b0"); GLMCheckError(); m_locVertexBool1 = glGetUniformLocationARB( m_program, "b1"); GLMCheckError(); m_locVertexBool2 = glGetUniformLocationARB( m_program, "b2"); GLMCheckError(); m_locVertexBool3 = glGetUniformLocationARB( m_program, "b3"); GLMCheckError(); m_locFragmentParams = glGetUniformLocationARB( m_program, "pc"); GLMCheckError(); m_locFragmentFakeSRGBEnable = glGetUniformLocationARB( m_program, "flSRGBWrite"); GLMCheckError(); m_fakeSRGBEnableValue = -1.0f; for( int sampler=0; sampler<16; sampler++) { char tmp[16]; sprintf(tmp, "sampler%d", sampler); // sampler0 .. sampler1.. etc m_locSamplers[sampler] = glGetUniformLocationARB( m_program, tmp ); GLMCheckError(); } } else { m_locVertexParams = -1; m_locVertexInteger0 = -1; m_locVertexBool0 = -1; m_locVertexBool1 = -1; m_locVertexBool2 = -1; m_locVertexBool3 = -1; m_locFragmentParams = -1; m_locFragmentFakeSRGBEnable = -1; m_fakeSRGBEnableValue = -999; memset( m_locSamplers, 0xFF, sizeof( m_locSamplers ) ); m_revision = 0; } return m_valid; }
bool CGLMProgram::CheckValidity( EGLMProgramLang lang ) { static char *targnames[] = { "vertex", "fragment" }; switch(lang) { case kGLMARB: { GLMShaderDesc *arbDesc; arbDesc = &m_descs[ kGLMARB ]; GLenum arbTarget = GLMProgTypeToARBEnum( m_type ); Assert( arbDesc->m_compiled ); arbDesc->m_valid = true; // assume success til we see otherwise // assume program is bound. is there anything wrong with it ? GLint isNative=0; glGetProgramivARB( arbTarget, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &isNative ); GLMCheckError(); // If the program is over the hardware's limits, print out some information if (isNative!=1) { arbDesc->m_valid = false; // check everything we can check char checkmask = (1<<m_type); // 1 for VP, 2 for FP for( GLMShaderLimitDesc *desc = g_glmShaderLimitDescs; desc->m_valueEnum !=0; desc++ ) { if ( desc->m_flags & checkmask ) { // test it GLint value = 0; GLint limit = 0; glGetProgramivARB(arbTarget, desc->m_valueEnum, &value); GLMCheckError(); glGetProgramivARB(arbTarget, desc->m_limitEnum, &limit); GLMCheckError(); if (value > limit) { GLMPRINTF(("-D- Invalid %s program: program has %d %s; limit is %d", targnames[ m_type ], value, desc->m_debugName, limit )); } } } } // syntax error check GLint errorLine; glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errorLine ); GLMCheckError(); if ( errorLine!=-1 ) { const GLubyte* errorString = glGetString(GL_PROGRAM_ERROR_STRING_ARB); GLMPRINTF(( "-D- Syntax error in ARB %s program: %s",targnames[ m_type ], errorString )); arbDesc->m_valid = false; } if (!arbDesc->m_valid) { char *temp = strdup(m_text); temp[ arbDesc->m_textOffset + arbDesc->m_textLength ] = 0; GLMPRINTF(("-D- ----- ARB compile failed; bad source follows -----" )); GLMPRINTTEXT(( temp + arbDesc->m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES )); GLMPRINTF(("-D- -----end-----" )); free( temp ); } return arbDesc->m_valid; } break; case kGLMGLSL: { GLMShaderDesc *glslDesc; glslDesc = &m_descs[ kGLMGLSL ]; GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); Assert( glslDesc->m_compiled ); glslDesc->m_valid = true; // assume success til we see otherwise // GLSL error check int compiled = 0, length = 0, laux = 0; glGetObjectParameterivARB( (GLhandleARB)glslDesc->m_object.glsl, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); glGetObjectParameterivARB( (GLhandleARB)glslDesc->m_object.glsl, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); GLcharARB *logString = (GLcharARB *)malloc(length * sizeof(GLcharARB)); glGetInfoLogARB((GLhandleARB)glslDesc->m_object.glsl, length, &laux, logString); // we may not be able to check "native limits" stuff until link time. meh if (!compiled) { glslDesc->m_valid = false; } if (!glslDesc->m_valid) { char *temp = strdup(m_text); temp[ glslDesc->m_textOffset + glslDesc->m_textLength ] = 0; GLMPRINTF(("-D- ----- GLSL compile failed: \n %s \n",logString )); GLMPRINTTEXT(( temp + glslDesc->m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES )); GLMPRINTF(("-D- -----end-----" )); free( temp ); } free( logString ); return glslDesc->m_valid; } break; } return false; }
bool CGLMProgram::Compile( EGLMProgramLang lang ) { bool result = true; // indicating validity.. bool noisy = false; int loglevel = gl_shaderpair_cachelog/* .GetInt() */; switch( lang ) { case kGLMARB: { GLMShaderDesc *arbDesc; arbDesc = &m_descs[ kGLMARB ]; // make sure no GLSL program is set up glUseProgram(0); GLMCheckError(); // bind our program container to context GLenum arbTarget = GLMProgTypeToARBEnum( m_type ); glSetEnable( arbTarget, true ); // unclear if I need this to compile or just to draw... GLMCheckError(); glBindProgramARB( arbTarget, arbDesc->m_object.arb ); // object created or just re-bound GLMCheckError(); char *section = m_text + arbDesc->m_textOffset; char *lastCharOfSection = section + arbDesc->m_textLength; // actually it's one past the last textual character #if GLMDEBUG if(noisy) { GLMPRINTF((">-D- CGLMProgram::Compile submitting following text for ARB %s program (name %d) ---------------------", arbTarget == GL_FRAGMENT_PROGRAM_ARB ? "fragment" : "vertex", arbDesc->m_object.arb )); // we don't have a "print this many chars" call yet // just temporarily null terminate the text we want to print char saveChar = *lastCharOfSection; *lastCharOfSection= 0; GLMPRINTTEXT(( section, eDebugDump )); *lastCharOfSection= saveChar; GLMPRINTF(("<-D- CGLMProgram::Compile ARB EOT--" )); } #endif glProgramStringARB( arbTarget, GL_PROGRAM_FORMAT_ASCII_ARB, arbDesc->m_textLength, section ); GLMCheckError( true, false ); arbDesc->m_compiled = true; // compiled but not necessarily valid CheckValidity( lang ); GLMCheckError(); // leave it bound n enabled, don't care (draw will sort it all out) result = arbDesc->m_valid; } break; case kGLMGLSL: { GLMShaderDesc *glslDesc; glslDesc = &m_descs[ kGLMGLSL ]; GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); // there's no binding to do for GLSL. but make sure no ARB stuff is bound for tidiness. glSetEnable( GL_VERTEX_PROGRAM_ARB, false ); glSetEnable( GL_FRAGMENT_PROGRAM_ARB, false ); // add check errors on these glBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, 0 ); // no GLSL program either glUseProgram(0); // pump text into GLSL shader object char *section = m_text + glslDesc->m_textOffset; char *lastCharOfSection = section + glslDesc->m_textLength; // actually it's one past the last textual character #if GLMDEBUG if(noisy) { GLMPRINTF((">-D- CGLMProgram::Compile submitting following text for GLSL %s program (name %d) ---------------------", glslStage == GL_FRAGMENT_SHADER_ARB ? "fragment" : "vertex", glslDesc->m_object.glsl )); // we don't have a "print this many chars" call yet // just temporarily null terminate the text we want to print char saveChar = *lastCharOfSection; *lastCharOfSection= 0; GLMPRINTTEXT(( section, eDebugDump )); *lastCharOfSection= saveChar; GLMPRINTF(("<-D- CGLMProgram::Compile GLSL EOT--" )); } #endif glShaderSourceARB( glslDesc->m_object.glsl, 1, (const GLchar **)§ion, &glslDesc->m_textLength); GLMCheckError( true, false ); // compile glCompileShaderARB( glslDesc->m_object.glsl ); glslDesc->m_compiled = true; // compiled but not necessarily valid GLMCheckError( true, false ); CheckValidity( lang ); GLMCheckError(); if (loglevel>=2) { char tempname[128]; int tempindex = -1; int tempcombo = -1; //GetLabelIndexCombo( tempname, sizeof(tempname), &tempindex, &tempcombo ); //printf("\ncompile: - [ %s/%d/%d ] on GL name %d ", tempname, tempindex, tempcombo, glslDesc->m_object.glsl ); GetComboIndexNameString( tempname, sizeof(tempname) ); printf("\ncompile: %s on GL name %p ", tempname, glslDesc->m_object.glsl ); } result = glslDesc->m_valid; } break; } return result; }
void CGLMBuffer::Lock( GLMBuffLockParams *params, char **addressOut ) { char *resultPtr = NULL; //Assert( !m_mapped); m_ctx->CheckCurrent(); GLMCheckError(); if (params->m_offset >= m_size) Debugger(); if (params->m_offset + params->m_size > m_size) Debugger(); // bind (yes, even for pseudo - this binds name 0) m_ctx->BindBufferToCtx( this->m_type, this ); if (m_pseudo) { // discard is a no-op // async map modes are a no-op // latch last mapped address (silly..) m_lastMappedAddress = (float*)m_pseudoBuf; // calc lock address resultPtr = m_pseudoBuf + params->m_offset; // dirty range is a no-op } else { // perform discard if requested if (params->m_discard) { // observe gl_bufmode on any orphan event. // if orphaned and bufmode is nonzero, flip it to dynamic. GLenum hint = gl_bufmode /*.GetInt()*/ ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; glBufferDataARB( m_buffGLTarget, m_size, NULL, hint ); m_lastMappedAddress = NULL; m_revision++; // revision grows on orphan event } // adjust async map option appropriately, leave explicit flush unchanged this->SetModes( params->m_nonblocking, m_enableExplicitFlush ); // map char *mapPtr = (char*)glMapBufferARB( this->m_buffGLTarget, GL_READ_WRITE_ARB ); if (!mapPtr) { Debugger(); } if (m_lastMappedAddress) { // just check if it moved //Assert (m_lastMappedAddress == (float*)mapPtr); } m_lastMappedAddress = (float*)mapPtr; // calculate offset location resultPtr = mapPtr + params->m_offset; // adjust dirty range if (m_dirtyMinOffset != m_dirtyMaxOffset) { // grow range m_dirtyMinOffset = std::min( m_dirtyMinOffset, params->m_offset ); m_dirtyMaxOffset = std::min( m_dirtyMaxOffset, params->m_offset+params->m_size ); } else { // set range m_dirtyMinOffset = params->m_offset; m_dirtyMaxOffset = params->m_offset+params->m_size; } // pad and clamp dirty range to choice of boundary uint quantum = (m_ctx->Caps().m_hasPerfPackage1) ? gl_buffer_alignment_quantum_slgu /*.GetInt()*/ : gl_buffer_alignment_quantum /*.GetInt()*/ ; uint quantum_mask = quantum - 1; m_dirtyMinOffset = m_dirtyMinOffset & (~quantum_mask); m_dirtyMaxOffset = (m_dirtyMaxOffset + quantum_mask) & (~quantum_mask); m_dirtyMaxOffset = std::min( m_dirtyMaxOffset, m_size ); } m_mapped = true; *addressOut = resultPtr; }
CGLMBuffer::CGLMBuffer( GLMContext *ctx, EGLMBufferType type, uint size, uint options ) { m_ctx = ctx; m_type = type; switch(m_type) { case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break; case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break; case kGLMUniformBuffer: m_buffGLTarget = GL_UNIFORM_BUFFER_EXT; break; case kGLMPixelBuffer: m_buffGLTarget = GL_PIXEL_UNPACK_BUFFER_ARB; break; default: //Assert(!"Unknown buffer type" ); break; } m_size = size; m_bound = false; m_mapped = false; m_lastMappedAddress = NULL; m_enableAsyncMap = false; m_enableExplicitFlush = false; m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock m_ctx->CheckCurrent(); m_revision = rand(); // make a decision about pseudo mode // this looked like it didn't help much or was actually slower, so leave it available but only as opt-in. // a more clever implementation would be able to select pseudo buf storage for small batches only.. m_pseudo = false; // (m_type==kGLMIndexBuffer) && (CommandLine()->FindParm("-gl_enable_pseudobufs")); if (m_pseudo) { m_name = 0; m_pseudoBuf = (char*)malloc( size ); m_ctx->BindBufferToCtx( m_type, NULL ); // exit with no buffer bound } else { glGenBuffersARB( 1, &m_name ); GLMCheckError(); m_ctx->BindBufferToCtx( m_type, this ); // causes glBindBufferARB // buffers start out static, but if they get orphaned and gl_bufmode is non zero, // then they will get flipped to dynamic. GLenum hint = GL_STATIC_DRAW_ARB; switch(m_type) { case kGLMVertexBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; case kGLMIndexBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; case kGLMUniformBuffer: hint = GL_DYNAMIC_DRAW_ARB; break; // "fwiw" - shrug case kGLMPixelBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; default: //Assert(!"Unknown buffer type" ); break; } glBufferDataARB( m_buffGLTarget, m_size, NULL, hint ); // may ultimately need more hints to set the usage correctly (esp for streaming) this->SetModes( false, true, true ); m_ctx->BindBufferToCtx( m_type, NULL ); // unbind me } }