1 #pragma once 2 3 #include <mbgl/gl/types.hpp> 4 #include <mbgl/gl/object.hpp> 5 #include <mbgl/gl/context.hpp> 6 #include <mbgl/gl/vertex_buffer.hpp> 7 #include <mbgl/gl/index_buffer.hpp> 8 #include <mbgl/gl/vertex_array.hpp> 9 #include <mbgl/gl/attribute.hpp> 10 #include <mbgl/gl/uniform.hpp> 11 12 #include <mbgl/util/io.hpp> 13 #include <mbgl/util/logging.hpp> 14 #include <mbgl/programs/binary_program.hpp> 15 #include <mbgl/programs/program_parameters.hpp> 16 #include <mbgl/shaders/shaders.hpp> 17 18 #include <string> 19 20 namespace mbgl { 21 namespace gl { 22 23 template <class P, class As, class Us> 24 class Program { 25 public: 26 using Primitive = P; 27 using Attributes = As; 28 using Uniforms = Us; 29 30 using UniformValues = typename Uniforms::Values; 31 using AttributeBindings = typename Attributes::Bindings; 32 Program(Context & context,const std::string & vertexSource,const std::string & fragmentSource)33 Program(Context& context, const std::string& vertexSource, const std::string& fragmentSource) 34 : program( 35 context.createProgram(context.createShader(ShaderType::Vertex, vertexSource), 36 context.createShader(ShaderType::Fragment, fragmentSource))), 37 uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))), 38 attributeLocations(Attributes::bindLocations(context, program)) { 39 40 // Re-link program after manually binding only active attributes in Attributes::bindLocations 41 context.linkProgram(program); 42 43 // We have to re-initialize the uniforms state from the bindings as the uniform locations 44 // get shifted on some implementations 45 uniformsState = Uniforms::bindLocations(program); 46 } 47 48 template <class BinaryProgram> Program(Context & context,const BinaryProgram & binaryProgram)49 Program(Context& context, const BinaryProgram& binaryProgram) 50 : program(context.createProgram(binaryProgram.format(), binaryProgram.code())), 51 uniformsState(Uniforms::loadNamedLocations(binaryProgram)), 52 attributeLocations(Attributes::loadNamedLocations(binaryProgram)) { 53 } 54 createProgram(gl::Context & context,const ProgramParameters & programParameters,const char * name,const char * vertexSource_,const char * fragmentSource_)55 static Program createProgram(gl::Context& context, 56 const ProgramParameters& programParameters, 57 const char* name, 58 const char* vertexSource_, 59 const char* fragmentSource_) { 60 const std::string vertexSource = shaders::vertexSource(programParameters, vertexSource_); 61 const std::string fragmentSource = shaders::fragmentSource(programParameters, fragmentSource_); 62 63 #if MBGL_HAS_BINARY_PROGRAMS 64 optional<std::string> cachePath = programParameters.cachePath(name); 65 if (cachePath && context.supportsProgramBinaries()) { 66 const std::string identifier = shaders::programIdentifier(vertexSource, fragmentSource); 67 68 try { 69 if (auto cachedBinaryProgram = util::readFile(*cachePath)) { 70 const BinaryProgram binaryProgram(std::move(*cachedBinaryProgram)); 71 if (binaryProgram.identifier() == identifier) { 72 return Program { context, binaryProgram }; 73 } else { 74 Log::Warning(Event::OpenGL, 75 "Cached program %s changed. Recompilation required.", 76 name); 77 } 78 } 79 } catch (std::runtime_error& error) { 80 Log::Warning(Event::OpenGL, "Could not load cached program: %s", 81 error.what()); 82 } 83 84 // Compile the shader 85 Program result{ context, vertexSource, fragmentSource }; 86 87 try { 88 if (const auto binaryProgram = 89 result.template get<BinaryProgram>(context, identifier)) { 90 util::write_file(*cachePath, binaryProgram->serialize()); 91 Log::Warning(Event::OpenGL, "Caching program in: %s", (*cachePath).c_str()); 92 } 93 } catch (std::runtime_error& error) { 94 Log::Warning(Event::OpenGL, "Failed to cache program: %s", error.what()); 95 } 96 97 return std::move(result); 98 } 99 #endif 100 101 (void)name; 102 return Program { context, vertexSource, fragmentSource }; 103 } 104 105 template <class BinaryProgram> get(Context & context,const std::string & identifier) const106 optional<BinaryProgram> get(Context& context, const std::string& identifier) const { 107 if (auto binaryProgram = context.getBinaryProgram(program)) { 108 return BinaryProgram{ binaryProgram->first, std::move(binaryProgram->second), 109 identifier, Attributes::getNamedLocations(attributeLocations), 110 Uniforms::getNamedLocations(uniformsState) }; 111 } 112 return {}; 113 } 114 115 template <class DrawMode> draw(Context & context,DrawMode drawMode,DepthMode depthMode,StencilMode stencilMode,ColorMode colorMode,const UniformValues & uniformValues,VertexArray & vertexArray,const AttributeBindings & attributeBindings,const IndexBuffer<DrawMode> & indexBuffer,std::size_t indexOffset,std::size_t indexLength)116 void draw(Context& context, 117 DrawMode drawMode, 118 DepthMode depthMode, 119 StencilMode stencilMode, 120 ColorMode colorMode, 121 const UniformValues& uniformValues, 122 VertexArray& vertexArray, 123 const AttributeBindings& attributeBindings, 124 const IndexBuffer<DrawMode>& indexBuffer, 125 std::size_t indexOffset, 126 std::size_t indexLength) { 127 static_assert(std::is_same<Primitive, typename DrawMode::Primitive>::value, "incompatible draw mode"); 128 129 context.setDrawMode(drawMode); 130 context.setDepthMode(depthMode); 131 context.setStencilMode(stencilMode); 132 context.setColorMode(colorMode); 133 134 context.program = program; 135 136 Uniforms::bind(uniformsState, uniformValues); 137 138 vertexArray.bind(context, 139 indexBuffer.buffer, 140 Attributes::toBindingArray(attributeLocations, attributeBindings)); 141 142 context.draw(drawMode.primitiveType, 143 indexOffset, 144 indexLength); 145 } 146 147 private: 148 UniqueProgram program; 149 150 typename Uniforms::State uniformsState; 151 typename Attributes::Locations attributeLocations; 152 }; 153 154 } // namespace gl 155 } // namespace mbgl 156