58 int C4Shader::GetSourceFileId(
const char *file)
const
60 auto it = std::find(SourceFiles.begin(), SourceFiles.end(), file);
61 if (it == SourceFiles.end())
return -1;
62 return std::distance(SourceFiles.begin(), it);
74 AddSlice(VertexSlices, iPos, szText,
nullptr, 0, 0);
79 AddSlice(FragmentSlices, iPos, szText,
nullptr, 0, 0);
84 AddSlices(VertexSlices, szWhat, szText, szSource, iSourceTime);
89 AddSlices(FragmentSlices, szWhat, szText, szSource, iSourceTime);
94 return LoadSlices(FragmentSlices, pGroups, szFile);
99 return LoadSlices(VertexSlices, pGroups, szFile);
104 assert(!ScriptSlicesLoaded &&
"Can't change shader categories after initialization");
105 Categories = categories;
108 void C4Shader::LoadScriptSlices()
111 for (
auto&
id : ScriptShaders)
115 ScriptSlicesLoaded =
true;
118 void C4Shader::LoadScriptSlice(
int id)
132 void C4Shader::AddSlice(ShaderSliceList& slices,
int iPos,
const char *szText,
const char *szSource,
int line,
int iSourceTime)
135 Slice.Position = iPos;
136 Slice.Text.Copy(szText);
137 Slice.Source = szSource;
138 Slice.SourceTime = iSourceTime;
139 Slice.SourceLine = line;
140 slices.push_back(Slice);
143 void C4Shader::AddSlices(ShaderSliceList& slices,
const char *szWhat,
const char *szText,
const char *szSource,
int iSourceTime)
145 if (std::find(SourceFiles.cbegin(), SourceFiles.cend(), szSource) == SourceFiles.cend())
146 SourceFiles.emplace_back(szSource);
148 const char *pStart = szText, *pPos = szText;
151 bool fGotContent =
false;
153 #define SKIP_WHITESPACE do { while(isspace(*pPos)) { ++pPos; } } while (0)
158 if (*pPos ==
'/' && *(pPos + 1) ==
'/') {
160 while (*pPos && *pPos !=
'\n') pPos++;
163 if (*pPos ==
'/' && *(pPos + 1) ==
'*') {
165 while (*pPos && (*pPos !=
'*' || *(pPos + 1) !=
'/'))
169 if (*pPos) pPos += 2;
180 if (iPosition != -1 && !iDepth) {
186 AddSlice(slices, iPosition, Str.
getData(), szSource,
SGetLine(szText, pStart), iSourceTime);
203 if (
SEqual2(pPos+1,
"slice") && !isalnum(*(pPos+6))) {
204 const char *pSliceEnd = pPos; pPos += 6;
206 if(*pPos !=
'(') { pPos++;
continue; }
210 iPosition = ParsePosition(szWhat, &pPos);
211 if (iPosition != -1) {
214 if(*pPos !=
')') { pPos++;
continue; }
225 AddSlice(slices, -1, Str.
getData(), szSource,
SGetLine(szText, pSliceEnd), iSourceTime);
232 ShaderLogF(
" gl: Missing opening brace in %s!", szWhat);
241 if (!isspace(*pPos)) fGotContent =
true;
249 AddSlice(slices, iPosition, Str.
getData(), szSource,
SGetLine(szText, pStart), iSourceTime);
251 #undef SKIP_WHITESPACE
254 int C4Shader::ParsePosition(
const char *szWhat,
const char **ppPos)
256 const char *pPos = *ppPos;
257 while (isspace(*pPos)) pPos++;
260 const char *pStart = pPos;
261 while (isalnum(*pPos)) pPos++;
268 iPosition = PosName.Position;
272 if (iPosition == -1) {
278 while (isspace(*pPos)) pPos++;
281 if (!sscanf(pPos+1,
"%d%n", &iMod, &iModLen)) {
282 ShaderLogF(
" gl: Invalid slice modifier in %s", szWhat);
290 if (!sscanf(pPos+1,
"%d%n", &iMod, &iModLen)) {
291 ShaderLogF(
" gl: Invalid slice modifier in %s", szWhat);
303 bool C4Shader::LoadSlices(ShaderSliceList& slices,
C4GroupSet *pGroups,
const char *szFile)
307 if(!pGroup)
return false;
326 VertexSlices.clear();
327 FragmentSlices.clear();
330 ScriptSlicesLoaded =
false;
332 ScriptShaders.clear();
340 glDeleteProgram(hProg);
348 bool C4Shader::Init(
const char *szWhat,
const char **szUniforms,
const char **szAttributes)
353 if (!ScriptSlicesLoaded)
355 Categories.emplace_back(szWhat);
359 StdStrBuf VertexShader = Build(VertexSlices,
true),
360 FragmentShader = Build(FragmentSlices,
true);
365 ShaderLogF(
"******** Vertex shader for %s:", szWhat);
367 ShaderLogF(
"******** Fragment shader for %s:", szWhat);
373 const GLuint hVert = Create(GL_VERTEX_SHADER,
376 const GLuint hFrag = Create(GL_FRAGMENT_SHADER,
382 if (hFrag) glDeleteShader(hFrag);
383 if (hVert) glDeleteShader(hVert);
388 const GLuint hNewProg = glCreateProgram();
390 glAttachShader(hNewProg, hVert);
391 glAttachShader(hNewProg, hFrag);
392 glLinkProgram(hNewProg);
394 glDeleteShader(hFrag);
395 glDeleteShader(hVert);
398 DumpInfoLog(
FormatString(
"%s shader program", szWhat).getData(), hNewProg,
true);
400 glGetProgramiv(hNewProg, GL_LINK_STATUS, &status);
401 if(status != GL_TRUE) {
402 glDeleteProgram(hNewProg);
403 ShaderLogF(
" gl: Failed to link %s shader!", szWhat);
406 ShaderLogF(
" gl: %s shader linked successfully", szWhat);
409 if (hProg != 0) glDeleteProgram(hProg);
413 int iUniformCount = 0;
414 if (szUniforms !=
nullptr)
415 while (szUniforms[iUniformCount])
417 Uniforms.resize(iUniformCount);
419 int iAttributeCount = 0;
420 if (szAttributes !=
nullptr)
421 while (szAttributes[iAttributeCount])
423 Attributes.resize(iAttributeCount);
427 for (
int i = 0; i < iUniformCount; i++) {
428 Uniforms[i].address = glGetUniformLocation(hProg, szUniforms[i]);
429 Uniforms[i].name = szUniforms[i];
430 ShaderLogF(
"Uniform %s = %d", szUniforms[i], Uniforms[i].address);
433 for (
int i = 0; i < iAttributeCount; i++) {
434 Attributes[i].address = glGetAttribLocation(hProg, szAttributes[i]);
435 Attributes[i].name = szAttributes[i];
436 ShaderLogF(
"Attribute %s = %d", szAttributes[i], Attributes[i].address);
451 std::set<int> toAdd, toRemove;
452 std::set_difference(ScriptShaders.begin(), ScriptShaders.end(), next.begin(), next.end(), std::inserter(toRemove, toRemove.end()));
453 std::set_difference(next.begin(), next.end(), ScriptShaders.begin(), ScriptShaders.end(), std::inserter(toAdd, toAdd.end()));
454 ScriptShaders = next;
456 auto removeSlices = [&](ShaderSliceList::iterator& pSlice)
461 ShaderSliceList::iterator pNext;
462 for (; pSlice != FragmentSlices.end(); pSlice = pNext)
464 pNext = pSlice; pNext++;
466 FragmentSlices.erase(pSlice);
471 std::vector<StdCopyStrBuf> sourcesToUpdate;
472 for (ShaderSliceList::iterator pSlice = FragmentSlices.begin(); pSlice != FragmentSlices.end(); pSlice++)
473 if (pSlice->Source.getLength())
475 if (pSlice->Source.BeginsWith(
"[script "))
479 sscanf(pSlice->Source.getData(),
"[script %d", &sid);
480 if (toRemove.find(sid) != toRemove.end())
481 removeSlices(pSlice);
484 else if (
FileExists(pSlice->Source.getData()) &&
485 FileTime(pSlice->Source.getData()) > pSlice->SourceTime)
487 sourcesToUpdate.push_back(pSlice->Source);
488 removeSlices(pSlice);
493 if (toAdd.size() == 0 && toRemove.size() == 0 && sourcesToUpdate.size() == 0)
497 for (
auto& Source : sourcesToUpdate)
502 if(!Group.
Open(szParentPath) ||
523 std::vector<const char*> UniformNames(Uniforms.size() + 1);
524 for (std::size_t i = 0; i < Uniforms.size(); ++i)
525 UniformNames[i] = Uniforms[i].name;
526 UniformNames[Uniforms.size()] =
nullptr;
528 std::vector<const char*> AttributeNames(Attributes.size() + 1);
529 for (std::size_t i = 0; i < Attributes.size(); ++i)
530 AttributeNames[i] = Attributes[i].name;
531 AttributeNames[Attributes.size()] =
nullptr;
550 StdStrBuf C4Shader::Build(
const ShaderSliceList &Slices,
bool fDebug)
556 GLint iMaxFrags = 0, iMaxVerts = 0;
557 glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &iMaxFrags);
558 glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &iMaxVerts);
560 int iMaxFrags = INT_MAX, iMaxVerts = INT_MAX;
562 Buf.
Format(
"#version %d\n"
563 "#define MAX_FRAGMENT_UNIFORM_COMPONENTS %d\n"
564 "#define MAX_VERTEX_UNIFORM_COMPONENTS %d\n",
568 int iPos = -1, iNextPos = -1;
573 if (fDebug && iPos > 0)
575 for (
const auto & Slice : Slices)
577 if (Slice.Position < iPos)
continue;
578 if (Slice.Position > iPos)
580 iNextPos = std::min(iNextPos, Slice.Position);
586 if (Slice.Source.getLength())
589 Buf.
AppendFormat(
"\t// Slice from %s:\n#line %d %d\n", Slice.Source.getData(), Slice.SourceLine - (
C4Shader_Version < 330), GetSourceFileId(Slice.Source.getData()) + 1);
592 Buf.
Append(
"\t// Built-in slice:\n#line 1 0\n");
600 Buf.
Append(
"void main() {\n");
608 Buf.
Append(
"// File number to name mapping:\n//\t 0: <built-in shader code>\n");
609 for (
int i = 0; i < SourceFiles.size(); ++i)
610 Buf.
AppendFormat(
"//\t%3d: %s\n", i + 1, SourceFiles[i].c_str());
615 GLuint C4Shader::Create(GLenum iShaderType,
const char *szWhat,
const char *szShader)
618 GLuint hShader = glCreateShader(iShaderType);
622 glShaderSource(hShader, 1, &szShader,
nullptr);
623 glCompileShader(hShader);
626 DumpInfoLog(szWhat, hShader,
false);
630 glGetShaderiv(hShader, GL_COMPILE_STATUS, &status);
631 if (status == GL_TRUE)
635 glDeleteShader(hShader);
639 void C4Shader::DumpInfoLog(
const char *szWhat, GLuint hShader,
bool forProgram)
644 glGetProgramiv(hShader, GL_INFO_LOG_LENGTH, &iLength);
646 glGetShaderiv(hShader, GL_INFO_LOG_LENGTH, &iLength);
647 if(iLength <= 1)
return;
650 std::vector<char> buf(iLength + 1);
651 int iActualLength = 0;
653 glGetProgramInfoLog(hShader, iLength, &iActualLength, &buf[0]);
655 glGetShaderInfoLog(hShader, iLength, &iActualLength, &buf[0]);
656 if(iActualLength > iLength || iActualLength <= 0)
return;
659 buf[iActualLength] =
'\0';
685 GLint hTex = GL_TEXTURE0 + iUnits;
686 glActiveTexture(hTex);
694 assert(pShader->hProg != 0);
698 const_cast<C4Shader *
>(pShader)->Refresh();
701 glUseProgram(pShader->hProg);
723 std::set<int> result;
724 for (
auto& cat : cats)
725 for (
auto&
id : categories[cat])
736 auto nsource =
"\n" + source;
737 shaders.emplace(std::make_pair(
id, ShaderInstance{
type, nsource}));
738 categories[shaderName].emplace(
id);
746 if (shaders.erase(
id))
748 for (
auto& kv : categories)
749 if (kv.second.erase(
id))
760 return std::unique_ptr<C4ScriptUniform::Popper>();
764 return std::unique_ptr<C4ScriptUniform::Popper>();
766 uniformStack.emplace();
767 auto& uniforms = uniformStack.top();
771 if (!prop->Key)
continue;
772 switch (prop->Value.GetType())
776 u.intVec[0] = prop->Value._getInt();
780 auto array = prop->Value._getArray();
781 switch (array->GetSize())
783 case 1: u.type = GL_INT;
break;
784 case 2: u.type = GL_INT_VEC2;
break;
785 case 3: u.type = GL_INT_VEC3;
break;
786 case 4: u.type = GL_INT_VEC4;
break;
789 for (int32_t i = 0; i < array->GetSize(); i++)
791 auto& item = array->_GetItem(i);
792 switch (item.GetType())
795 u.intVec[i] = item._getInt();
808 uniforms.insert({prop->Key->GetCStr(), u});
818 return std::make_unique<C4ScriptUniform::Popper>(
this);
824 uniformStack = std::stack<UniformMap>();
825 uniformStack.emplace();
831 for (
auto& p : uniformStack.top())
836 GLint loc = glGetUniformLocation(call.pShader->hProg, p.first.c_str());
838 if (loc == -1)
continue;
839 auto& intVec = p.second.intVec;
840 switch (p.second.type)
842 case GL_INT: glUniform1iv(loc, 1, intVec);
break;
843 case GL_INT_VEC2: glUniform2iv(loc, 1, intVec);
break;
844 case GL_INT_VEC3: glUniform3iv(loc, 1, intVec);
break;
845 case GL_INT_VEC4: glUniform4iv(loc, 1, intVec);
break;
847 assert(
false &&
"unsupported uniform type");
C4Application Application
bool ShaderLog(const char *szMessage)
bool ShaderLogF(const char *strMessage ...)
const uint32_t C4Shader_RefreshInterval
C4ShaderPosName C4SH_PosNames[]
C4ScriptShader ScriptShader
const int C4Shader_PositionMaterial
const int C4Shader_Vertex_NormalPos
const int C4Shader_PositionTexture
const int C4Shader_Vertex_ColorPos
const int C4Shader_Vertex_TexCoordPos
const int C4Shader_PositionFinish
const int C4Shader_LastPosition
const int C4ShaderCall_MaxUnits
const int C4Shader_PositionNormal
const int C4Shader_Vertex_PositionPos
const int C4Shader_PositionColor
const int C4Shader_PositionCoordinate
const int C4Shader_PositionInit
const int C4Shader_Version
const int C4Shader_PositionLight
bool SEqual2(const char *szStr1, const char *szStr2)
int SGetLine(const char *szText, const char *cpPosition)
bool SEqual(const char *szStr1, const char *szStr2)
StdStrBuf FormatString(const char *szFmt,...)
bool GetParentPath(const char *szFilename, char *szBuffer)
char * GetFilename(char *szPath)
int FileTime(const char *fname)
bool FileExists(const char *szFileName)
const char * AtRelativePath(const char *filename)
C4ConfigGraphics Graphics
StdStrBuf GetFullName() const
bool LoadEntryString(const char *entry_name, StdStrBuf *buffer)
bool Open(const char *group_name, bool do_create=false)
C4Group * FindEntry(const char *szWildcard, int32_t *pPriority=nullptr, int32_t *pID=nullptr)
bool GetProperty(C4PropertyName k, C4Value *pResult) const
int Add(const std::string &shaderName, ShaderType type, const std::string &source)
std::set< int > GetShaderIDs(const std::vector< std::string > &cats)
GLint AllocTexUnit(int iUniform)
void SetUniform1i(int iUniform, int iX) const
bool LoadVertexSlices(C4GroupSet *pGroupSet, const char *szFile)
bool LoadFragmentSlices(C4GroupSet *pGroupSet, const char *szFile)
void AddFragmentSlice(int iPos, const char *szText)
void AddDefine(const char *name)
bool HaveUniform(int iUniform) const
void AddVertexSlice(int iPos, const char *szText)
void AddVertexSlices(const char *szWhat, const char *szText, const char *szSource="", int iFileTime=0)
void SetScriptCategories(const std::vector< std::string > &categories)
bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes)
void AddFragmentSlices(const char *szWhat, const char *szText, const char *szSource="", int iFileTime=0)
static C4TimeMilliseconds Now()
C4PropList * getPropList() const
void ObjectLabel(uint32_t identifier, uint32_t name, int32_t length, const char *label)
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
const char * getData() const
void AppendChar(char cChar)
void Append(const char *pnData, size_t iChars)
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O