diff options
-rw-r--r-- | gyp/mbgl-ios.gypi | 1 | ||||
-rw-r--r-- | gyp/mbgl-linux.gypi | 1 | ||||
-rw-r--r-- | gyp/mbgl-osx.gypi | 1 | ||||
-rw-r--r-- | include/mbgl/platform/gl.hpp | 12 | ||||
-rw-r--r-- | include/mbgl/platform/platform.hpp | 3 | ||||
-rw-r--r-- | platform/default/glfw_view.cpp | 38 | ||||
-rw-r--r-- | platform/default/shader_cache_tmp.cpp | 12 | ||||
-rw-r--r-- | platform/ios/shader_cache_library.mm | 21 | ||||
-rw-r--r-- | platform/osx/shader_cache_application_support.mm | 31 | ||||
-rw-r--r-- | src/mbgl/platform/gl.cpp | 4 | ||||
-rw-r--r-- | src/mbgl/shader/shader.cpp | 172 | ||||
-rw-r--r-- | src/mbgl/shader/shader.hpp | 3 |
12 files changed, 253 insertions, 46 deletions
diff --git a/gyp/mbgl-ios.gypi b/gyp/mbgl-ios.gypi index dd1c9c0c20..ec31869ad9 100644 --- a/gyp/mbgl-ios.gypi +++ b/gyp/mbgl-ios.gypi @@ -32,6 +32,7 @@ 'hard_dependency': 1, 'sources': [ '../platform/ios/cache_database_library.mm', + '../platform/ios/shader_cache_library.mm', '../platform/darwin/log_nslog.mm', '../platform/darwin/string_nsstring.mm', '../platform/darwin/http_request_baton_cocoa.mm', diff --git a/gyp/mbgl-linux.gypi b/gyp/mbgl-linux.gypi index 9d979ddf13..7af08242ee 100644 --- a/gyp/mbgl-linux.gypi +++ b/gyp/mbgl-linux.gypi @@ -28,6 +28,7 @@ }, 'sources': [ '../platform/default/cache_database_tmp.cpp', + '../platform/default/shader_cache_tmp.cpp', '../platform/default/log_stderr.cpp', '../platform/default/string_stdlib.cpp', '../platform/default/http_request_baton_curl.cpp', diff --git a/gyp/mbgl-osx.gypi b/gyp/mbgl-osx.gypi index 76c22f5ad6..09c96807aa 100644 --- a/gyp/mbgl-osx.gypi +++ b/gyp/mbgl-osx.gypi @@ -7,6 +7,7 @@ 'hard_dependency': 1, 'sources': [ '../platform/osx/cache_database_application_support.mm', + '../platform/osx/shader_cache_application_support.mm', '../platform/darwin/log_nslog.mm', '../platform/darwin/string_nsstring.mm', '../platform/darwin/http_request_baton_cocoa.mm', diff --git a/include/mbgl/platform/gl.hpp b/include/mbgl/platform/gl.hpp index d16478dab9..6a35ab8006 100644 --- a/include/mbgl/platform/gl.hpp +++ b/include/mbgl/platform/gl.hpp @@ -118,6 +118,7 @@ typedef void (* PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei extern PFNGLLABELOBJECTEXTPROC LabelObjectEXT; extern PFNGLGETOBJECTLABELEXTPROC GetObjectLabelEXT; +// GL_ARB_vertex_array_object / GL_OES_vertex_array_object #define GL_VERTEX_ARRAY_BINDING 0x85B5 typedef void (* PFNGLBINDVERTEXARRAYPROC) (GLuint array); typedef void (* PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint* arrays); @@ -128,6 +129,17 @@ extern PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays; extern PFNGLGENVERTEXARRAYSPROC GenVertexArrays; extern PFNGLISVERTEXARRAYPROC IsVertexArray; +// GL_ARB_get_program_binary / GL_OES_get_program_binary +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +typedef void (* PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +typedef void (* PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (* PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +extern PFNGLGETPROGRAMBINARYPROC GetProgramBinary; +extern PFNGLPROGRAMBINARYPROC ProgramBinary; +extern PFNGLPROGRAMPARAMETERIPROC ProgramParameteri; // Debug group markers, useful for debugging on iOS #if defined(DEBUG) diff --git a/include/mbgl/platform/platform.hpp b/include/mbgl/platform/platform.hpp index b7107bb9cd..9edcc23ccd 100644 --- a/include/mbgl/platform/platform.hpp +++ b/include/mbgl/platform/platform.hpp @@ -20,6 +20,9 @@ std::string lowercase(const std::string &string); // Returns the path to the default cache database on this system. std::string defaultCacheDatabase(); +// Returns the path to the default shader cache on this system. +std::string defaultShaderCache(); + // Shows an alpha image with the specified dimensions in a named window. void show_debug_image(std::string name, const char *data, size_t width, size_t height); diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index a1f51b9f79..32b7e44ece 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -82,6 +82,17 @@ void GLFWView::initialize(mbgl::Map *map_) { gl::GetObjectLabel = (gl::PFNGLGETOBJECTLABELPROC)glfwGetProcAddress("glGetObjectLabel"); gl::ObjectPtrLabel = (gl::PFNGLOBJECTPTRLABELPROC)glfwGetProcAddress("glObjectPtrLabel"); gl::GetObjectPtrLabel = (gl::PFNGLGETOBJECTPTRLABELPROC)glfwGetProcAddress("glGetObjectPtrLabel"); + assert(gl::DebugMessageControl != nullptr); + assert(gl::DebugMessageInsert != nullptr); + assert(gl::DebugMessageCallback != nullptr); + assert(gl::GetDebugMessageLog != nullptr); + assert(gl::GetPointerv != nullptr); + assert(gl::PushDebugGroup != nullptr); + assert(gl::PopDebugGroup != nullptr); + assert(gl::ObjectLabel != nullptr); + assert(gl::GetObjectLabel != nullptr); + assert(gl::ObjectPtrLabel != nullptr); + assert(gl::GetObjectPtrLabel != nullptr); } else { if (extensions.find("GL_ARB_debug_output") != std::string::npos) { gl::DebugMessageControl = (gl::PFNGLDEBUGMESSAGECONTROLPROC)glfwGetProcAddress("glDebugMessageControlARB"); @@ -89,17 +100,27 @@ void GLFWView::initialize(mbgl::Map *map_) { gl::DebugMessageCallback = (gl::PFNGLDEBUGMESSAGECALLBACKPROC)glfwGetProcAddress("glDebugMessageCallbackARB"); gl::GetDebugMessageLog = (gl::PFNGLGETDEBUGMESSAGELOGPROC)glfwGetProcAddress("glGetDebugMessageLogARB"); gl::GetPointerv = (gl::PFNGLGETPOINTERVPROC)glfwGetProcAddress("glGetPointerv"); + assert(gl::DebugMessageControl != nullptr); + assert(gl::DebugMessageInsert != nullptr); + assert(gl::DebugMessageCallback != nullptr); + assert(gl::GetDebugMessageLog != nullptr); + assert(gl::GetPointerv != nullptr); } if (extensions.find("GL_EXT_debug_marker") != std::string::npos) { gl::InsertEventMarkerEXT = (gl::PFNGLINSERTEVENTMARKEREXTPROC)glfwGetProcAddress("glInsertEventMarkerEXT"); gl::PushGroupMarkerEXT = (gl::PFNGLPUSHGROUPMARKEREXTPROC)glfwGetProcAddress("glPushGroupMarkerEXT"); gl::PopGroupMarkerEXT = (gl::PFNGLPOPGROUPMARKEREXTPROC)glfwGetProcAddress("glPopGroupMarkerEXT"); + assert(gl::InsertEventMarkerEXT != nullptr); + assert(gl::PushGroupMarkerEXT != nullptr); + assert(gl::PopGroupMarkerEXT != nullptr); } if (extensions.find("GL_EXT_debug_label") != std::string::npos) { gl::LabelObjectEXT = (gl::PFNGLLABELOBJECTEXTPROC)glfwGetProcAddress("glLabelObjectEXT"); gl::GetObjectLabelEXT = (gl::PFNGLGETOBJECTLABELEXTPROC)glfwGetProcAddress("glGetObjectLabelEXT"); + assert(gl::LabelObjectEXT != nullptr); + assert(gl::GetObjectLabelEXT != nullptr); } } @@ -108,11 +129,28 @@ void GLFWView::initialize(mbgl::Map *map_) { gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)glfwGetProcAddress("glDeleteVertexArrays"); gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)glfwGetProcAddress("glGenVertexArrays"); gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)glfwGetProcAddress("glIsVertexArray"); + assert(gl::BindVertexArray != nullptr); + assert(gl::DeleteVertexArrays != nullptr); + assert(gl::GenVertexArrays != nullptr); + assert(gl::IsVertexArray != nullptr); } else if (extensions.find("GL_APPLE_vertex_array_object") != std::string::npos) { gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)glfwGetProcAddress("glBindVertexArrayAPPLE"); gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)glfwGetProcAddress("glDeleteVertexArraysAPPLE"); gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)glfwGetProcAddress("glGenVertexArraysAPPLE"); gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)glfwGetProcAddress("glIsVertexArrayAPPLE"); + assert(gl::BindVertexArray != nullptr); + assert(gl::DeleteVertexArrays != nullptr); + assert(gl::GenVertexArrays != nullptr); + assert(gl::IsVertexArray != nullptr); + } + + if (extensions.find("GL_ARB_get_program_binary") != std::string::npos) { + gl::GetProgramBinary = (gl::PFNGLGETPROGRAMBINARYPROC)glfwGetProcAddress("glGetProgramBinary"); + gl::ProgramBinary = (gl::PFNGLPROGRAMBINARYPROC)glfwGetProcAddress("glProgramBinary"); + gl::ProgramParameteri = (gl::PFNGLPROGRAMPARAMETERIPROC)glfwGetProcAddress("glProgramParameteri"); + assert(gl::GetProgramBinary != nullptr); + assert(gl::ProgramBinary != nullptr); + assert(gl::ProgramParameteri != nullptr); } } diff --git a/platform/default/shader_cache_tmp.cpp b/platform/default/shader_cache_tmp.cpp new file mode 100644 index 0000000000..d76f7aea4c --- /dev/null +++ b/platform/default/shader_cache_tmp.cpp @@ -0,0 +1,12 @@ +#include <mbgl/platform/platform.hpp> + +namespace mbgl { +namespace platform { + +// Returns the path to the default shader cache on this system. +std::string defaultShaderCache() { + return "/tmp/mbgl-shader-cache-"; +} + +} +} diff --git a/platform/ios/shader_cache_library.mm b/platform/ios/shader_cache_library.mm new file mode 100644 index 0000000000..68d8e959ca --- /dev/null +++ b/platform/ios/shader_cache_library.mm @@ -0,0 +1,21 @@ +#import <Foundation/Foundation.h> + +#include <mbgl/platform/platform.hpp> + +namespace mbgl { +namespace platform { + +// Returns the path to the default shader cache on this system. +std::string defaultShaderCache() { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); + if ([paths count] == 0) { + // Disable the cache if we don't have a location to write. + return ""; + } + + NSString *libraryDirectory = [paths objectAtIndex:0]; + return [[libraryDirectory stringByAppendingPathComponent:@"shader-cache-"] UTF8String]; +} + +} +} diff --git a/platform/osx/shader_cache_application_support.mm b/platform/osx/shader_cache_application_support.mm new file mode 100644 index 0000000000..459cf6535f --- /dev/null +++ b/platform/osx/shader_cache_application_support.mm @@ -0,0 +1,31 @@ +#import <Foundation/Foundation.h> + +#include <mbgl/platform/platform.hpp> + +namespace mbgl { +namespace platform { + +// Returns the path to the default shader cache on this system. +std::string defaultShaderCache() { + NSArray *paths = + NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + if ([paths count] == 0) { + // Disable the cache if we don't have a location to write. + return ""; + } + + NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Mapbox GL"]; + + if (![[NSFileManager defaultManager] createDirectoryAtPath:path + withIntermediateDirectories:YES + attributes:nil + error:nil]) { + // Disable the cache if we couldn't create the directory. + return ""; + } + + return [[path stringByAppendingPathComponent:@"shader-cache-"] UTF8String]; +} + +} +} diff --git a/src/mbgl/platform/gl.cpp b/src/mbgl/platform/gl.cpp index c0c3bb1c74..4aace913d0 100644 --- a/src/mbgl/platform/gl.cpp +++ b/src/mbgl/platform/gl.cpp @@ -70,6 +70,10 @@ PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays = nullptr; PFNGLGENVERTEXARRAYSPROC GenVertexArrays = nullptr; PFNGLISVERTEXARRAYPROC IsVertexArray = nullptr; +PFNGLGETPROGRAMBINARYPROC GetProgramBinary = nullptr; +PFNGLPROGRAMBINARYPROC ProgramBinary = nullptr; +PFNGLPROGRAMPARAMETERIPROC ProgramParameteri = nullptr; + } } diff --git a/src/mbgl/shader/shader.cpp b/src/mbgl/shader/shader.cpp index 84cb55eac4..af6ba627e1 100644 --- a/src/mbgl/shader/shader.cpp +++ b/src/mbgl/shader/shader.cpp @@ -2,9 +2,11 @@ #include <mbgl/platform/gl.hpp> #include <mbgl/util/stopwatch.hpp> #include <mbgl/platform/log.hpp> +#include <mbgl/platform/platform.hpp> #include <cstring> #include <cstdlib> +#include <cassert> using namespace mbgl; @@ -14,49 +16,97 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo program(0) { util::stopwatch stopwatch("shader compilation", Event::Shader); - GLuint vertShader; - if (!compileShader(&vertShader, GL_VERTEX_SHADER, vertSource)) { - Log::Error(Event::Shader, "Vertex shader failed to compile: %s", vertSource); - return; - } - - GLuint fragShader; - if (!compileShader(&fragShader, GL_FRAGMENT_SHADER, fragSource)) { - Log::Error(Event::Shader, "Fragment shader failed to compile: %s", fragSource); - return; - } - program = glCreateProgram(); - // Attach shaders - glAttachShader(program, vertShader); - glAttachShader(program, fragShader); + if (!mbgl::platform::defaultShaderCache().empty()) { + binaryFileName = mbgl::platform::defaultShaderCache() + name + ".bin"; + } + // Load binary shader if it exists + bool skipCompile = false; + if (!binaryFileName.empty() && (gl::ProgramBinary != nullptr)) { + FILE *binaryFile = fopen(binaryFileName.c_str(), "rb"); + if (binaryFile != nullptr) { + GLsizei binaryLength; + GLenum binaryFormat; + bool lengthOk = fread(&binaryLength, sizeof(binaryLength), 1, binaryFile) == 1; + bool formatOk = fread(&binaryFormat, sizeof(binaryFormat), 1, binaryFile) == 1; + + if (lengthOk && formatOk && binaryLength > 0) { + std::unique_ptr<char[]> binary = mbgl::util::make_unique<char[]>(binaryLength); + + if (binary != nullptr) { + bool binaryOk = fread(binary.get(), binaryLength, 1, binaryFile) == 1; + + if (binaryOk) { + gl::ProgramBinary(program, binaryFormat, binary.get(), binaryLength); + + // Check if the binary was valid + GLint status; + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (status == GL_TRUE) { + skipCompile = true; + } + } + } + } - { - // Link program - GLint status; - glLinkProgram(program); + fclose(binaryFile); + binaryFile = nullptr; + } + } - glGetProgramiv(program, GL_LINK_STATUS, &status); - if (status == 0) { - GLint logLength; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); - if (logLength > 0) { - GLchar *log = (GLchar *)malloc(logLength); - glGetProgramInfoLog(program, logLength, &logLength, log); - Log::Error(Event::Shader, "Program failed to link: %s", log); - free(log); - } + GLuint vertShader = 0; + GLuint fragShader = 0; + if (!skipCompile) { + if (!compileShader(&vertShader, GL_VERTEX_SHADER, vertSource)) { + Log::Error(Event::Shader, "Vertex shader %s failed to compile: %s", name, vertSource); + glDeleteProgram(program); + program = 0; + return; + } + if (!compileShader(&fragShader, GL_FRAGMENT_SHADER, fragSource)) { + Log::Error(Event::Shader, "Fragment shader %s failed to compile: %s", name, fragSource); glDeleteShader(vertShader); vertShader = 0; - glDeleteShader(fragShader); - fragShader = 0; glDeleteProgram(program); program = 0; return; } + + // Attach shaders + glAttachShader(program, vertShader); + glAttachShader(program, fragShader); + + { + if (!binaryFileName.empty() && (gl::ProgramParameteri != nullptr)) { + gl::ProgramParameteri(program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE); + } + + // Link program + GLint status; + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (status == 0) { + GLint logLength; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) { + std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); + glGetProgramInfoLog(program, logLength, &logLength, log.get()); + Log::Error(Event::Shader, "Program failed to link: %s", log.get()); + } + + glDeleteShader(vertShader); + vertShader = 0; + glDeleteShader(fragShader); + fragShader = 0; + glDeleteProgram(program); + program = 0; + return; + } + } } { @@ -69,10 +119,9 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo GLint logLength; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); if (logLength > 0) { - GLchar *log = (GLchar *)malloc(logLength); - glGetProgramInfoLog(program, logLength, &logLength, log); - Log::Error(Event::Shader, "Program failed to validate: %s", log); - free(log); + std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); + glGetProgramInfoLog(program, logLength, &logLength, log.get()); + Log::Error(Event::Shader, "Program failed to validate: %s", log.get()); } glDeleteShader(vertShader); @@ -82,14 +131,15 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo glDeleteProgram(program); program = 0; } - } - // Remove the compiled shaders; they are now part of the program. - glDetachShader(program, vertShader); - glDeleteShader(vertShader); - glDetachShader(program, fragShader); - glDeleteShader(fragShader); + if (!skipCompile) { + // Remove the compiled shaders; they are now part of the program. + glDetachShader(program, vertShader); + glDeleteShader(vertShader); + glDetachShader(program, fragShader); + glDeleteShader(fragShader); + } valid = true; } @@ -100,7 +150,7 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) { *shader = glCreateShader(type); const GLchar *strings[] = { source }; - const GLint lengths[] = { (GLint)strlen(source) }; + const GLsizei lengths[] = { (GLsizei)strlen(source) }; glShaderSource(*shader, 1, strings, lengths); glCompileShader(*shader); @@ -110,10 +160,9 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) { GLint logLength; glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); if (logLength > 0) { - GLchar *log = (GLchar *)malloc(logLength); - glGetShaderInfoLog(*shader, logLength, &logLength, log); - Log::Error(Event::Shader, "Shader failed to compile: %s", log); - free(log); + std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); + glGetShaderInfoLog(*shader, logLength, &logLength, log.get()); + Log::Error(Event::Shader, "Shader failed to compile: %s", log.get()); } glDeleteShader(*shader); @@ -121,10 +170,41 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) { return false; } + glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + Log::Error(Event::Shader, "Shader %s failed to compile.", name, type); + glDeleteShader(*shader); + *shader = 0; + return false; + } + return true; } Shader::~Shader() { + if (!binaryFileName.empty() && (gl::GetProgramBinary != nullptr)) { + // Retrieve the program binary + GLsizei binaryLength; + GLenum binaryFormat; + glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binaryLength); + if (binaryLength > 0) { + std::unique_ptr<char[]> binary = mbgl::util::make_unique<char[]>(binaryLength); + if (binary != nullptr) { + gl::GetProgramBinary(program, binaryLength, NULL, &binaryFormat, binary.get()); + + // Write the binary to a file + FILE *binaryFile = fopen(binaryFileName.c_str(), "wb"); + if (binaryFile != nullptr) { + fwrite(&binaryLength, sizeof(binaryLength), 1, binaryFile); + fwrite(&binaryFormat, sizeof(binaryFormat), 1, binaryFile); + fwrite(binary.get(), binaryLength, 1, binaryFile); + fclose(binaryFile); + binaryFile = nullptr; + } + } + } + } + if (program) { glDeleteProgram(program); program = 0; diff --git a/src/mbgl/shader/shader.hpp b/src/mbgl/shader/shader.hpp index 27e831a510..beaaa8b756 100644 --- a/src/mbgl/shader/shader.hpp +++ b/src/mbgl/shader/shader.hpp @@ -3,6 +3,7 @@ #include <cstdint> #include <array> +#include <string> #include <mbgl/util/noncopyable.hpp> namespace mbgl { @@ -21,6 +22,8 @@ public: private: bool compileShader(uint32_t *shader, uint32_t type, const char *source); + + std::string binaryFileName; }; } |