void GlslGeneratorInstance::PrintUniforms() { // uniform-буферы и переменные // отсортировать их struct Sorter { bool operator()(const std::pair<UniformGroup*, UniformNode*>& a, const std::pair<UniformGroup*, UniformNode*>& b) const { int slotA = a.first->GetSlot(); int slotB = b.first->GetSlot(); return slotA < slotB || (slotA == slotB && a.second->GetOffset() < b.second->GetOffset()); } }; std::sort(uniforms.begin(), uniforms.end(), Sorter()); // удалить дубликаты uniforms.resize(std::unique(uniforms.begin(), uniforms.end()) - uniforms.begin()); // вывести буферы for(size_t i = 0; i < uniforms.size(); ) { size_t j; for(j = i + 1; j < uniforms.size() && uniforms[j].first == uniforms[i].first; ++j); int slot = uniforms[i].first->GetSlot(); // print header, if needed if(supportUniformBuffers) text << "layout(std140) uniform " << uniformBufferPrefix << slot << "\n{\n"; // текущее смещение от начала буфера int currentOffset = 0; // вывести переменные for(size_t k = i; k < j; ++k) { UniformNode* uniformNode = uniforms[k].second; DataType valueType = uniformNode->GetValueType(); int offset = uniformNode->GetOffset(); int count = uniformNode->GetCount(); int valueSize = GetDataTypeSize(valueType); // переменная должна лежать на границе float'а, как минимум if(offset % sizeof(float)) THROW("Wrong variable offset: should be on 4-byte boundary"); // если по умолчанию переменная попадёт в неправильное место, делаем пустые переменные, чтобы занять место // автоматический сдвиг if(currentOffset % sizeof(vec4) + valueSize > sizeof(vec4)) currentOffset = (currentOffset + sizeof(vec4) - 1) & ~(sizeof(vec4) - 1); // оставшийся сдвиг добиваем пустыми переменными (только если uniform буферы) if(supportUniformBuffers) { while(currentOffset < offset) { int newOffset = (currentOffset + sizeof(vec4)) & ~(sizeof(vec4) - 1); if(newOffset > offset) newOffset = offset; int size = (newOffset - currentOffset) / sizeof(float); static const char* dumpTypes[] = { "float", "vec2", "vec3", "vec4" }; text << '\t' << dumpTypes[size - 1] << " dump" << slot << '_' << currentOffset << '_' << size << ";\n"; currentOffset = newOffset; } } else currentOffset = offset; // печатаем определение переменной text << (supportUniformBuffers ? "\t" : "uniform "); PrintDataType(valueType); // имя переменной text << ' ' << uniformPrefix << slot << '_' << offset; // размер массива if(count > 1) text << '[' << count << ']'; // если массив, размер элемента должен быть кратен размеру vec4 if(count > 1 && valueSize % sizeof(vec4)) THROW("Size of element of array should be multiply of vec4 size"); // конец переменной text << ";\n"; // смещение для следующей переменной currentOffset += valueSize * count; } // ending of buffer if(supportUniformBuffers) text << "};\n"; i = j; } }
ptr<ShaderSource> GlslGeneratorInstance::Generate() { // first stage: register all nodes RegisterNode(rootNode); // заголовок файла switch(glslVersion) { case GlslVersions::opengl33: text << "#version 330\n"; break; case GlslVersions::webgl: // version numbers in WebGL are not supported text << "#extension GL_OES_standard_derivatives : enable\n" "#extension GL_EXT_draw_buffers : enable\n" "#ifdef GL_ES\n" "precision highp float;\n" "#endif\n"; break; default: THROW("Unknown GLSL version"); } // вывести атрибуты в качестве входных переменных, если это вершинный шейдер if(shaderType == ShaderTypes::vertex) { const char* prefixStr; switch(glslVersion) { case GlslVersions::opengl33: prefixStr = "in "; break; case GlslVersions::webgl: prefixStr = "attribute "; break; default: THROW("Unknown GLSL version"); } std::sort(attributes.begin(), attributes.end()); for(size_t i = 0; i < attributes.size(); ++i) { AttributeNode* node = attributes[i]; text << prefixStr; DataType valueType = node->GetValueType(); // hack for lack of supporting integer attributes in WebGL // change them to float if(glslVersion == GlslVersions::webgl) valueType = EnforceFloatDataType(valueType); PrintDataType(valueType); text << " a" << node->GetElementIndex() << ";\n"; } } // print transformed nodes if(!transformedNodes.empty()) { if(shaderType != ShaderTypes::pixel) THROW("Only pixel shader may have transformed nodes"); const char* prefixStr; switch(glslVersion) { case GlslVersions::opengl33: prefixStr = "in "; break; case GlslVersions::webgl: prefixStr = "varying "; break; default: THROW("Unknown GLSL version"); } for(size_t i = 0; i < transformedNodes.size(); ++i) { TransformedNode* node = transformedNodes[i]; text << prefixStr; PrintDataType(node->GetValueType()); text << " v" << node->GetSemantic() << ";\n"; } } // print interpolate nodes if(!interpolateNodes.empty()) { if(shaderType != ShaderTypes::vertex) THROW("Only vertex shader may have interpolate nodes"); const char* prefixStr; switch(glslVersion) { case GlslVersions::opengl33: prefixStr = "out "; break; case GlslVersions::webgl: prefixStr = "varying "; break; default: THROW("Unknown GLSL version"); } for(size_t i = 0; i < interpolateNodes.size(); ++i) { InterpolateNode* node = interpolateNodes[i]; text << prefixStr; PrintDataType(node->GetValueType()); text << " v" << node->GetSemantic() << ";\n"; } } // вывести пиксельные переменные, если это пиксельный шейдер, и это нужно if(shaderType == ShaderTypes::pixel && glslVersion == GlslVersions::opengl33) { for(int i = 0; i < fragmentTargetsCount; ++i) { text << "out "; PrintDataType(DataTypes::_vec4); text << " r" << i << ";\n"; } } // print uniforms PrintUniforms(); // samplers for(size_t i = 0; i < samplers.size(); ++i) { SamplerNode* samplerNode = samplers[i]; // строка, описывающая основной тип семпла const char* valueTypeStr; switch(samplerNode->GetValueType()) { case DataTypes::_float: case DataTypes::_vec2: case DataTypes::_vec3: case DataTypes::_vec4: valueTypeStr = ""; break; case DataTypes::_uint: case DataTypes::_uvec2: case DataTypes::_uvec3: case DataTypes::_uvec4: valueTypeStr = glslVersion == GlslVersions::webgl ? "i" : "u"; break; case DataTypes::_int: case DataTypes::_ivec2: case DataTypes::_ivec3: case DataTypes::_ivec4: valueTypeStr = "i"; break; default: THROW("Invalid sampler value type"); } // строка, описывающая размерность const char* dimensionStr; switch(samplerNode->GetCoordType()) { case SamplerNode::_1D: dimensionStr = "1D"; break; case SamplerNode::_2D: dimensionStr = "2D"; break; case SamplerNode::_3D: dimensionStr = "3D"; break; case SamplerNode::_Cube: dimensionStr = "Cube"; break; default: THROW("Invalid sampler coord type"); } // вывести семплер int slot = samplerNode->GetSlot(); text << "uniform " << valueTypeStr << "sampler" << dimensionStr << ' ' << samplerPrefix << slot << ";\n"; } //** заголовок функции шейдера text << "void main()\n{\n"; // shader code for(size_t i = 0; i < nodeInits.size(); ++i) PrintNodeInit(i); // завершение шейдера text << "}\n"; // make list of uniform variable bindings GlShaderBindings::UniformBindings uniformBindings; if(!supportUniformBuffers) { uniformBindings.resize(uniforms.size()); for(size_t i = 0; i < uniforms.size(); ++i) { UniformNode* node = uniforms[i].second; GlShaderBindings::UniformBinding& uniformBinding = uniformBindings[i]; uniformBinding.dataType = node->GetValueType(); uniformBinding.count = node->GetCount(); uniformBinding.slot = uniforms[i].first->GetSlot(); uniformBinding.offset = node->GetOffset(); std::ostringstream ss; ss << uniformPrefix << uniformBinding.slot << '_' << uniformBinding.offset; uniformBinding.name = ss.str(); } } // make list of uniform block bindings GlShaderBindings::Bindings uniformBlockBindings; if(supportUniformBuffers) { uniformBlockBindings.resize(uniforms.size()); for(size_t i = 0; i < uniforms.size(); ++i) { int slot = uniforms[i].first->GetSlot(); char name[16]; sprintf(name, "%s%d", uniformBufferPrefix, slot); uniformBlockBindings[i].first = name; uniformBlockBindings[i].second = slot; } // remove duplicates std::sort(uniformBlockBindings.begin(), uniformBlockBindings.end()); uniformBlockBindings.resize(std::unique(uniformBlockBindings.begin(), uniformBlockBindings.end()) - uniformBlockBindings.begin()); } // сформировать список привязок семплеров GlShaderBindings::Bindings samplerBindings(samplers.size()); for(size_t i = 0; i < samplers.size(); ++i) { int slot = samplers[i]->GetSlot(); char name[16]; sprintf(name, "%s%d", samplerPrefix, slot); samplerBindings[i].first = name; samplerBindings[i].second = slot; } // сформировать список привязок атрибутов GlShaderBindings::Bindings attributeBindings(attributes.size()); for(size_t i = 0; i < attributes.size(); ++i) { int index = attributes[i]->GetElementIndex(); char name[16]; sprintf(name, "a%d", index); attributeBindings[i].first = name; attributeBindings[i].second = index; } // make a target variables list GlShaderBindings::Bindings targetBindings; switch(glslVersion) { case GlslVersions::opengl33: targetBindings.resize(fragmentTargetsCount); for(int i = 0; i < fragmentTargetsCount; ++i) { char name[16]; sprintf(name, "r%d", i); targetBindings[i].first = name; targetBindings[i].second = i; } break; case GlslVersions::webgl: // no bindings needed, because of use of gl_FragColor/gl_FragData break; } return NEW(GlslSource( Strings::String2File(text.str()), NEW(GlShaderBindings(uniformBindings, uniformBlockBindings, samplerBindings, attributeBindings, targetBindings, dualFragmentTarget)) )); }
void Hlsl11GeneratorInstance::PrintUniforms() { // uniform-буферы и переменные // отсортировать их struct Sorter { bool operator()(const std::pair<UniformGroup*, UniformNode*>& a, const std::pair<UniformGroup*, UniformNode*>& b) const { int slotA = a.first->GetSlot(); int slotB = b.first->GetSlot(); return slotA < slotB || slotA == slotB && a.second->GetOffset() < b.second->GetOffset(); } }; std::sort(uniforms.begin(), uniforms.end(), Sorter()); // удалить дубликаты uniforms.resize(std::unique(uniforms.begin(), uniforms.end()) - uniforms.begin()); // вывести буферы for(size_t i = 0; i < uniforms.size(); ) { size_t j; for(j = i + 1; j < uniforms.size() && uniforms[j].first == uniforms[i].first; ++j); // вывести заголовок int slot = uniforms[i].first->GetSlot(); text << "cbuffer CB" << slot << " : register(b" << slot << ")\n{\n"; // вывести переменные for(size_t k = i; k < j; ++k) { UniformNode* uniformNode = uniforms[k].second; DataType valueType = uniformNode->GetValueType(); int offset = uniformNode->GetOffset(); int count = uniformNode->GetCount(); // переменная должна лежать на границе float'а, как минимум if(offset % sizeof(float)) THROW("Wrong variable offset: should be on 4-byte boundary"); // печатаем определение переменной text << '\t'; PrintDataType(valueType); // имя переменной text << " u" << slot << '_' << offset; // размер массива if(count > 1) text << '[' << count << ']'; // если массив, размер элемента должен быть кратен размеру vec4 if(count > 1 && GetDataTypeSize(valueType) % sizeof(vec4)) THROW("Size of element of array should be multiply of vec4 size"); // регистр и положение в нём переменной text << " : packoffset(c" << (offset / sizeof(vec4)); // если переменная не начинается ровно на границе регистра, нужно дописать ещё первую компоненту регистра int registerOffset = offset % sizeof(vec4); if(registerOffset) { // получить размер данных int variableSize = GetDataTypeSize(valueType); // переменная не должна пересекать границу регистра if(registerOffset + variableSize > sizeof(vec4)) THROW("Variable should not intersect a register boundary"); // выложить нужную букву registerOffset /= sizeof(float); text << '.' << "xyzw"[registerOffset]; } // конец упаковки text << ")"; // конец переменной text << ";\n"; } // окончание text << "};\n"; i = j; } }