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