// // Copyright (c) 2016 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // TranslatorVulkan: // A GLSL-based translator that outputs shaders that fit GL_KHR_vulkan_glsl. // The shaders are then fed into glslang to spit out SPIR-V (libANGLE-side). // See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt // #include "compiler/translator/TranslatorVulkan.h" #include "angle_gl.h" #include "common/utilities.h" #include "compiler/translator/OutputVulkanGLSL.h" #include "compiler/translator/util.h" namespace sh { class DeclareDefaultUniformsTraverser : public TIntermTraverser { public: DeclareDefaultUniformsTraverser(TInfoSinkBase *sink, ShHashFunction64 hashFunction, NameMap *nameMap) : TIntermTraverser(true, true, true), mSink(sink), mHashFunction(hashFunction), mNameMap(nameMap), mInDefaultUniform(false) { } bool visitDeclaration(Visit visit, TIntermDeclaration *node) override { const TIntermSequence &sequence = *(node->getSequence()); // TODO(jmadill): Compound declarations. ASSERT(sequence.size() == 1); TIntermTyped *variable = sequence.front()->getAsTyped(); const TType &type = variable->getType(); bool isUniform = (type.getQualifier() == EvqUniform) && !IsOpaqueType(type.getBasicType()); if (visit == PreVisit) { if (isUniform) { (*mSink) << " " << GetTypeName(type, mHashFunction, mNameMap) << " "; mInDefaultUniform = true; } } else if (visit == InVisit) { mInDefaultUniform = isUniform; } else if (visit == PostVisit) { if (isUniform) { (*mSink) << ";\n"; // Remove the uniform declaration from the tree so it isn't parsed again. TIntermSequence emptyReplacement; mMultiReplacements.push_back(NodeReplaceWithMultipleEntry( getParentNode()->getAsBlock(), node, emptyReplacement)); } mInDefaultUniform = false; } return true; } void visitSymbol(TIntermSymbol *symbol) override { if (mInDefaultUniform) { const TName &name = symbol->getName(); ASSERT(name.getString().substr(0, 3) != "gl_"); (*mSink) << HashName(name, mHashFunction, mNameMap); } } private: TInfoSinkBase *mSink; ShHashFunction64 mHashFunction; NameMap *mNameMap; bool mInDefaultUniform; }; TranslatorVulkan::TranslatorVulkan(sh::GLenum type, ShShaderSpec spec) : TCompiler(type, spec, SH_GLSL_450_CORE_OUTPUT) { } void TranslatorVulkan::translate(TIntermBlock *root, ShCompileOptions compileOptions, PerformanceDiagnostics * /*perfDiagnostics*/) { TInfoSinkBase &sink = getInfoSink().obj; sink << "#version 450 core\n"; // Write out default uniforms into a uniform block assigned to a specific set/binding. int defaultUniformCount = 0; for (const auto &uniform : getUniforms()) { if (!uniform.isBuiltIn() && uniform.staticUse && !gl::IsOpaqueType(uniform.type)) { ++defaultUniformCount; } } if (defaultUniformCount > 0) { sink << "\nlayout(@@ DEFAULT-UNIFORMS-SET-BINDING @@) uniform defaultUniforms\n{\n"; DeclareDefaultUniformsTraverser defaultTraverser(&sink, getHashFunction(), &getNameMap()); root->traverse(&defaultTraverser); defaultTraverser.updateTree(); sink << "};\n"; } // Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData // if it's core profile shaders and they are used. if (getShaderType() == GL_FRAGMENT_SHADER) { bool hasGLFragColor = false; bool hasGLFragData = false; for (const auto &outputVar : outputVariables) { if (outputVar.name == "gl_FragColor") { ASSERT(!hasGLFragColor); hasGLFragColor = true; continue; } else if (outputVar.name == "gl_FragData") { ASSERT(!hasGLFragData); hasGLFragData = true; continue; } } ASSERT(!(hasGLFragColor && hasGLFragData)); if (hasGLFragColor) { sink << "layout(location = 0) out vec4 webgl_FragColor;\n"; } if (hasGLFragData) { sink << "layout(location = 0) out vec4 webgl_FragData[gl_MaxDrawBuffers];\n"; } } // Write translated shader. TOutputVulkanGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), &getSymbolTable(), getShaderType(), getShaderVersion(), getOutputType(), compileOptions); root->traverse(&outputGLSL); } bool TranslatorVulkan::shouldFlattenPragmaStdglInvariantAll() { // Not necessary. return false; } } // namespace sh