| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- // Copyright (c) 2008-2023 the Urho3D project
- // License: MIT
- #include "../../Precompiled.h"
- #include "../../Graphics/Graphics.h"
- #include "../../GraphicsAPI/ConstantBuffer.h"
- #include "../../GraphicsAPI/GraphicsImpl.h"
- #include "../../GraphicsAPI/ShaderVariation.h"
- #include "../../IO/Log.h"
- #include "OGLShaderProgram.h"
- #include "../../DebugNew.h"
- namespace Urho3D
- {
- static const char* shaderParameterGroups[] = {
- "frame",
- "camera",
- "zone",
- "light",
- "material",
- "object",
- "custom"
- };
- static unsigned NumberPostfix(const String& str)
- {
- for (unsigned i = 0; i < str.Length(); ++i)
- {
- if (IsDigit(str[i]))
- return ToU32(str.CString() + i);
- }
- return M_MAX_UNSIGNED;
- }
- i32 ShaderProgram_OGL::globalFrameNumber = 0;
- const void* ShaderProgram_OGL::globalParameterSources[MAX_SHADER_PARAMETER_GROUPS];
- ShaderProgram_OGL::ShaderProgram_OGL(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader) :
- GPUObject(graphics),
- vertexShader_(vertexShader),
- pixelShader_(pixelShader)
- {
- for (auto& parameterSource : parameterSources_)
- parameterSource = (const void*)M_MAX_UNSIGNED;
- }
- ShaderProgram_OGL::~ShaderProgram_OGL()
- {
- Release();
- }
- void ShaderProgram_OGL::OnDeviceLost()
- {
- if (object_.name_ && !graphics_->IsDeviceLost())
- glDeleteProgram(object_.name_);
- GPUObject::OnDeviceLost();
- if (graphics_ && graphics_->GetShaderProgram_OGL() == this)
- graphics_->SetShaders(nullptr, nullptr);
- linkerOutput_.Clear();
- }
- void ShaderProgram_OGL::Release()
- {
- if (object_.name_)
- {
- if (!graphics_)
- return;
- if (!graphics_->IsDeviceLost())
- {
- if (graphics_->GetShaderProgram_OGL() == this)
- graphics_->SetShaders(nullptr, nullptr);
- glDeleteProgram(object_.name_);
- }
- object_.name_ = 0;
- linkerOutput_.Clear();
- shaderParameters_.Clear();
- vertexAttributes_.Clear();
- usedVertexAttributes_ = 0;
- for (bool& useTextureUnit : useTextureUnits_)
- useTextureUnit = false;
- for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
- constantBuffers_[i].Reset();
- }
- }
- bool ShaderProgram_OGL::Link()
- {
- Release();
- if (!vertexShader_ || !pixelShader_ || !vertexShader_->GetGPUObjectName() || !pixelShader_->GetGPUObjectName())
- return false;
- object_.name_ = glCreateProgram();
- if (!object_.name_)
- {
- linkerOutput_ = "Could not create shader program";
- return false;
- }
- glAttachShader(object_.name_, vertexShader_->GetGPUObjectName());
- glAttachShader(object_.name_, pixelShader_->GetGPUObjectName());
- glLinkProgram(object_.name_);
- int linked, length;
- glGetProgramiv(object_.name_, GL_LINK_STATUS, &linked);
- if (!linked)
- {
- glGetProgramiv(object_.name_, GL_INFO_LOG_LENGTH, &length);
- linkerOutput_.Resize((unsigned)length);
- int outLength;
- glGetProgramInfoLog(object_.name_, length, &outLength, &linkerOutput_[0]);
- glDeleteProgram(object_.name_);
- object_.name_ = 0;
- }
- else
- linkerOutput_.Clear();
- if (!object_.name_)
- return false;
- const int MAX_NAME_LENGTH = 256;
- char nameBuffer[MAX_NAME_LENGTH];
- int attributeCount, uniformCount, elementCount, nameLength;
- GLenum type;
- glUseProgram(object_.name_);
- // Check for vertex attributes
- glGetProgramiv(object_.name_, GL_ACTIVE_ATTRIBUTES, &attributeCount);
- for (int i = 0; i < attributeCount; ++i)
- {
- glGetActiveAttrib(object_.name_, i, (GLsizei)MAX_NAME_LENGTH, &nameLength, &elementCount, &type, nameBuffer);
- String name = String(nameBuffer, nameLength);
- VertexElementSemantic semantic = MAX_VERTEX_ELEMENT_SEMANTICS;
- i8 semanticIndex = 0;
- // Go in reverse order so that "binormal" is detected before "normal"
- for (i32 j = MAX_VERTEX_ELEMENT_SEMANTICS - 1; j >= 0; --j)
- {
- if (name.Contains(ShaderVariation::elementSemanticNames_OGL[j], false))
- {
- semantic = (VertexElementSemantic)j;
- unsigned index = NumberPostfix(name);
- if (index != M_MAX_UNSIGNED)
- semanticIndex = (i8)index;
- break;
- }
- }
- if (semantic == MAX_VERTEX_ELEMENT_SEMANTICS)
- {
- URHO3D_LOGWARNING("Found vertex attribute " + name + " with no known semantic in shader program " +
- vertexShader_->GetFullName() + " " + pixelShader_->GetFullName());
- continue;
- }
- int location = glGetAttribLocation(object_.name_, name.CString());
- vertexAttributes_[{(i8)semantic, semanticIndex}] = location;
- usedVertexAttributes_ |= (1u << location);
- }
- // Check for constant buffers
- #ifndef URHO3D_GLES2
- HashMap<unsigned, unsigned> blockToBinding;
- if (Graphics::GetGL3Support())
- {
- int numUniformBlocks = 0;
- glGetProgramiv(object_.name_, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
- for (int i = 0; i < numUniformBlocks; ++i)
- {
- glGetActiveUniformBlockName(object_.name_, (GLuint)i, MAX_NAME_LENGTH, &nameLength, nameBuffer);
- String name(nameBuffer, (unsigned)nameLength);
- unsigned blockIndex = glGetUniformBlockIndex(object_.name_, name.CString());
- unsigned group = M_MAX_UNSIGNED;
- // Try to recognize the use of the buffer from its name
- for (unsigned j = 0; j < MAX_SHADER_PARAMETER_GROUPS; ++j)
- {
- if (name.Contains(shaderParameterGroups[j], false))
- {
- group = j;
- break;
- }
- }
- // If name is not recognized, search for a digit in the name and use that as the group index
- if (group == M_MAX_UNSIGNED)
- group = NumberPostfix(name);
- if (group >= MAX_SHADER_PARAMETER_GROUPS)
- {
- URHO3D_LOGWARNING("Skipping unrecognized uniform block " + name + " in shader program " + vertexShader_->GetFullName() +
- " " + pixelShader_->GetFullName());
- continue;
- }
- // Find total constant buffer data size
- int dataSize;
- glGetActiveUniformBlockiv(object_.name_, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize);
- if (!dataSize)
- continue;
- unsigned bindingIndex = group;
- // Vertex shader constant buffer bindings occupy slots starting from zero to maximum supported, pixel shader bindings
- // from that point onward
- ShaderType shaderType = VS;
- if (name.Contains("PS", false))
- {
- bindingIndex += MAX_SHADER_PARAMETER_GROUPS;
- shaderType = PS;
- }
- glUniformBlockBinding(object_.name_, blockIndex, bindingIndex);
- blockToBinding[blockIndex] = bindingIndex;
- constantBuffers_[bindingIndex] = graphics_->GetOrCreateConstantBuffer(shaderType, bindingIndex, (unsigned)dataSize);
- }
- }
- #endif
- // Check for shader parameters and texture units
- glGetProgramiv(object_.name_, GL_ACTIVE_UNIFORMS, &uniformCount);
- for (int i = 0; i < uniformCount; ++i)
- {
- glGetActiveUniform(object_.name_, (GLuint)i, MAX_NAME_LENGTH, nullptr, &elementCount, &type, nameBuffer);
- int location = glGetUniformLocation(object_.name_, nameBuffer);
- // Check for array index included in the name and strip it
- String name(nameBuffer);
- i32 index = name.Find('[');
- if (index != String::NPOS)
- {
- // If not the first index, skip
- if (name.Find("[0]", index) == String::NPOS)
- continue;
- name = name.Substring(0, index);
- }
- if (name[0] == 'c')
- {
- // Store constant uniform
- String paramName = name.Substring(1);
- ShaderParameter parameter{paramName, type, location};
- bool store = location >= 0;
- #ifndef URHO3D_GLES2
- // If running OpenGL 3, the uniform may be inside a constant buffer
- if (parameter.location_ < 0 && Graphics::GetGL3Support())
- {
- int blockIndex, blockOffset;
- glGetActiveUniformsiv(object_.name_, 1, (const GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &blockIndex);
- glGetActiveUniformsiv(object_.name_, 1, (const GLuint*)&i, GL_UNIFORM_OFFSET, &blockOffset);
- if (blockIndex >= 0)
- {
- parameter.offset_ = blockOffset;
- parameter.bufferPtr_ = constantBuffers_[blockToBinding[blockIndex]];
- store = true;
- }
- }
- #endif
- if (store)
- shaderParameters_[StringHash(paramName)] = parameter;
- }
- else if (location >= 0 && name[0] == 's')
- {
- // Set the samplers here so that they do not have to be set later
- unsigned unit = graphics_->GetTextureUnit(name.Substring(1));
- if (unit >= MAX_TEXTURE_UNITS)
- unit = NumberPostfix(name);
- if (unit < MAX_TEXTURE_UNITS)
- {
- useTextureUnits_[unit] = true;
- glUniform1iv(location, 1, reinterpret_cast<int*>(&unit));
- }
- }
- }
- // Rehash the parameter & vertex attributes maps to ensure minimal load factor
- vertexAttributes_.Rehash(NextPowerOfTwo(vertexAttributes_.Size()));
- shaderParameters_.Rehash(NextPowerOfTwo(shaderParameters_.Size()));
- return true;
- }
- ShaderVariation* ShaderProgram_OGL::GetVertexShader() const
- {
- return vertexShader_;
- }
- ShaderVariation* ShaderProgram_OGL::GetPixelShader() const
- {
- return pixelShader_;
- }
- bool ShaderProgram_OGL::HasParameter(StringHash param) const
- {
- return shaderParameters_.Find(param) != shaderParameters_.End();
- }
- const ShaderParameter* ShaderProgram_OGL::GetParameter(StringHash param) const
- {
- HashMap<StringHash, ShaderParameter>::ConstIterator i = shaderParameters_.Find(param);
- if (i != shaderParameters_.End())
- return &i->second_;
- else
- return nullptr;
- }
- bool ShaderProgram_OGL::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
- {
- // If global framenumber has changed, invalidate all per-program parameter sources now
- if (globalFrameNumber != frameNumber_)
- {
- for (auto& parameterSource : parameterSources_)
- parameterSource = (const void*)M_MAX_UNSIGNED;
- frameNumber_ = globalFrameNumber;
- }
- // The shader program may use a mixture of constant buffers and individual uniforms even in the same group
- #ifndef URHO3D_GLES2
- bool useBuffer = constantBuffers_[group].Get() || constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
- bool useIndividual = !constantBuffers_[group].Get() || !constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
- bool needUpdate = false;
- if (useBuffer && globalParameterSources[group] != source)
- {
- globalParameterSources[group] = source;
- needUpdate = true;
- }
- if (useIndividual && parameterSources_[group] != source)
- {
- parameterSources_[group] = source;
- needUpdate = true;
- }
- return needUpdate;
- #else
- if (parameterSources_[group] != source)
- {
- parameterSources_[group] = source;
- return true;
- }
- else
- return false;
- #endif
- }
- void ShaderProgram_OGL::ClearParameterSource(ShaderParameterGroup group)
- {
- // The shader program may use a mixture of constant buffers and individual uniforms even in the same group
- #ifndef URHO3D_GLES2
- bool useBuffer = constantBuffers_[group].Get() || constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
- bool useIndividual = !constantBuffers_[group].Get() || !constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
- if (useBuffer)
- globalParameterSources[group] = (const void*)M_MAX_UNSIGNED;
- if (useIndividual)
- parameterSources_[group] = (const void*)M_MAX_UNSIGNED;
- #else
- parameterSources_[group] = (const void*)M_MAX_UNSIGNED;
- #endif
- }
- void ShaderProgram_OGL::ClearParameterSources()
- {
- ++globalFrameNumber;
- if (!globalFrameNumber)
- ++globalFrameNumber;
- #ifndef URHO3D_GLES2
- for (auto& globalParameterSource : globalParameterSources)
- globalParameterSource = (const void*)M_MAX_UNSIGNED;
- #endif
- }
- void ShaderProgram_OGL::ClearGlobalParameterSource(ShaderParameterGroup group)
- {
- globalParameterSources[group] = (const void*)M_MAX_UNSIGNED;
- }
- }
|