diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index a84a8ba01f..dac1e37630 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -114,6 +114,7 @@ public: * Platform. The and Invocables may be called at any time and * from any thread from the time at which setBlobFunc is called until the time that Platform * is destroyed. Concurrent calls to these functions from different threads is also allowed. + * Either function can be null. * * @param insertBlob an Invocable that inserts a new value into the cache and associates * it with the given key @@ -123,9 +124,21 @@ public: void setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept; /** - * @return true if setBlobFunc was called. + * @return true if insertBlob is valid. */ - bool hasBlobFunc() const noexcept; + bool hasInsertBlobFunc() const noexcept; + + /** + * @return true if retrieveBlob is valid. + */ + bool hasRetrieveBlobFunc() const noexcept; + + /** + * @return true if either of insertBlob or retrieveBlob are valid. + */ + bool hasBlobFunc() const noexcept { + return hasInsertBlobFunc() || hasRetrieveBlobFunc(); + } /** * To insert a new binary value into the cache and associate it with a given diff --git a/filament/backend/src/Platform.cpp b/filament/backend/src/Platform.cpp index ece36477ba..db2fd0eafd 100644 --- a/filament/backend/src/Platform.cpp +++ b/filament/backend/src/Platform.cpp @@ -28,14 +28,16 @@ bool Platform::pumpEvents() noexcept { } void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept { - if (!mInsertBlob && !mRetrieveBlob) { - mInsertBlob = std::move(insertBlob); - mRetrieveBlob = std::move(retrieveBlob); - } + mInsertBlob = std::move(insertBlob); + mRetrieveBlob = std::move(retrieveBlob); } -bool Platform::hasBlobFunc() const noexcept { - return mInsertBlob && mRetrieveBlob; +bool Platform::hasInsertBlobFunc() const noexcept { + return bool(mInsertBlob); +} + +bool Platform::hasRetrieveBlobFunc() const noexcept { + return bool(mRetrieveBlob); } void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) { diff --git a/filament/backend/src/opengl/OpenGLBlobCache.cpp b/filament/backend/src/opengl/OpenGLBlobCache.cpp index 4f65c60586..f9e68384da 100644 --- a/filament/backend/src/opengl/OpenGLBlobCache.cpp +++ b/filament/backend/src/opengl/OpenGLBlobCache.cpp @@ -16,6 +16,8 @@ #include "OpenGLBlobCache.h" +#include "OpenGLContext.h" + #include #include @@ -28,17 +30,18 @@ struct OpenGLBlobCache::Blob { char data[]; }; -GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, - Program const& program) noexcept { - SYSTRACE_CALL(); +OpenGLBlobCache::OpenGLBlobCache(OpenGLContext& gl) noexcept + : mCachingSupported(gl.gets.num_program_binary_formats >= 1) { +} - if (!platform.hasBlobFunc()) { +GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, + Program const& program) const noexcept { + SYSTRACE_CALL(); + if (!mCachingSupported || !platform.hasRetrieveBlobFunc()) { // the key is never updated in that case return 0; } - SYSTRACE_CONTEXT(); - GLuint programId = 0; #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 @@ -64,8 +67,10 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, programId = glCreateProgram(); - SYSTRACE_NAME("glProgramBinary"); - glProgramBinary(programId, blob->format, blob->data, programBinarySize); + { // scope for systrace + SYSTRACE_NAME("glProgramBinary"); + glProgramBinary(programId, blob->format, blob->data, programBinarySize); + } if (UTILS_UNLIKELY(glGetError() != GL_NO_ERROR)) { // glProgramBinary can fail if for instance the driver has been updated @@ -85,46 +90,36 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, void OpenGLBlobCache::insert(Platform& platform, BlobCacheKey const& key, GLuint program) noexcept { -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 SYSTRACE_CALL(); - if (platform.hasBlobFunc()) { - SYSTRACE_CONTEXT(); - GLenum format; - GLint programBinarySize = 0; + if (!mCachingSupported || !platform.hasInsertBlobFunc()) { + // the key is never updated in that case + return; + } + +#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 + GLenum format; + GLint programBinarySize = 0; + { // scope for systrace SYSTRACE_NAME("glGetProgramiv"); glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinarySize); - if (programBinarySize) { - size_t const size = sizeof(Blob) + programBinarySize; - std::unique_ptr blob{ (Blob*)malloc(size), &::free }; - if (UTILS_LIKELY(blob)) { + } + if (programBinarySize) { + size_t const size = sizeof(Blob) + programBinarySize; + std::unique_ptr blob{ (Blob*)malloc(size), &::free }; + if (UTILS_LIKELY(blob)) { + { // scope for systrace SYSTRACE_NAME("glGetProgramBinary"); - glGetProgramBinary(program, programBinarySize, &programBinarySize, &format, - blob->data); - GLenum const error = glGetError(); - if (error == GL_NO_ERROR) { - blob->format = format; - platform.insertBlob(key.data(), key.size(), blob.get(), size); - } + glGetProgramBinary(program, programBinarySize, + &programBinarySize, &format, blob->data); + } + GLenum const error = glGetError(); + if (error == GL_NO_ERROR) { + blob->format = format; + platform.insertBlob(key.data(), key.size(), blob.get(), size); } } } #endif } -void OpenGLBlobCache::insert(Platform& platform, BlobCacheKey const& key, - GLenum format, void* data, GLsizei programBinarySize) noexcept { - SYSTRACE_CALL(); - if (platform.hasBlobFunc()) { - if (programBinarySize) { - size_t const size = sizeof(Blob) + programBinarySize; - std::unique_ptr blob{ (Blob*)malloc(size), &::free }; - if (UTILS_LIKELY(blob)) { - blob->format = format; - memcpy(blob->data, data, programBinarySize); - platform.insertBlob(key.data(), key.size(), blob.get(), size); - } - } - } -} - } // namespace filament::backend diff --git a/filament/backend/src/opengl/OpenGLBlobCache.h b/filament/backend/src/opengl/OpenGLBlobCache.h index 5569fa2034..fa98f0a449 100644 --- a/filament/backend/src/opengl/OpenGLBlobCache.h +++ b/filament/backend/src/opengl/OpenGLBlobCache.h @@ -25,20 +25,21 @@ namespace filament::backend { class Platform; class Program; +class OpenGLContext; class OpenGLBlobCache { public: - static GLuint retrieve(BlobCacheKey* key, Platform& platform, - Program const& program) noexcept; + explicit OpenGLBlobCache(OpenGLContext& gl) noexcept; - static void insert(Platform& platform, + GLuint retrieve(BlobCacheKey* key, Platform& platform, + Program const& program) const noexcept; + + void insert(Platform& platform, BlobCacheKey const& key, GLuint program) noexcept; - static void insert(Platform& platform, BlobCacheKey const& key, - GLenum format, void* data, GLsizei programBinarySize) noexcept; - private: struct Blob; + bool mCachingSupported = false; }; } // namespace filament::backend diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index 072096718b..bf4c7e1f13 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -99,38 +99,41 @@ OpenGLContext::OpenGLContext() noexcept { if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) { #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, - &gets.max_uniform_block_size); - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, - &gets.max_uniform_buffer_bindings); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, - &gets.uniform_buffer_offset_alignment); - glGetIntegerv(GL_MAX_SAMPLES, - &gets.max_samples); - glGetIntegerv(GL_MAX_DRAW_BUFFERS, - &gets.max_draw_buffers); - glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, - &gets.max_transform_feedback_separate_attribs); #ifdef GL_EXT_texture_filter_anisotropic if (ext.EXT_texture_filter_anisotropic) { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy); } #endif + glGetIntegerv(GL_MAX_DRAW_BUFFERS, + &gets.max_draw_buffers); + glGetIntegerv(GL_MAX_SAMPLES, + &gets.max_samples); + glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, + &gets.max_transform_feedback_separate_attribs); + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, + &gets.max_uniform_block_size); + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, + &gets.max_uniform_buffer_bindings); + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, + &gets.num_program_binary_formats); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, + &gets.uniform_buffer_offset_alignment); #endif } + #ifdef BACKEND_OPENGL_VERSION_GLES else { + gets.max_anisotropy = 1; + gets.max_draw_buffers = 1; + gets.max_samples = 1; + gets.max_transform_feedback_separate_attribs = 0; gets.max_uniform_block_size = 0; gets.max_uniform_buffer_bindings = 0; + gets.num_program_binary_formats = 0; gets.uniform_buffer_offset_alignment = 0; - gets.max_samples = 1; - gets.max_draw_buffers = 1; - gets.max_transform_feedback_separate_attribs = 0; - gets.max_anisotropy = 1; } #endif - slog.v << "Feature level: " << +mFeatureLevel << '\n'; slog.v << "Active workarounds: " << '\n'; UTILS_NOUNROLL @@ -143,13 +146,18 @@ OpenGLContext::OpenGLContext() noexcept { #ifndef NDEBUG // this is useful for development - slog.v << "GL_MAX_DRAW_BUFFERS = " << gets.max_draw_buffers << '\n' - << "GL_MAX_RENDERBUFFER_SIZE = " << gets.max_renderbuffer_size << '\n' - << "GL_MAX_SAMPLES = " << gets.max_samples << '\n' - << "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = " << gets.max_anisotropy << '\n' - << "GL_MAX_UNIFORM_BLOCK_SIZE = " << gets.max_uniform_block_size << '\n' - << "GL_MAX_TEXTURE_IMAGE_UNITS = " << gets.max_texture_image_units << '\n' - << "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = " << gets.uniform_buffer_offset_alignment << '\n' + slog.v + << "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT" << gets.max_anisotropy << '\n' + << "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS" << gets.max_combined_texture_image_units << '\n' + << "GL_MAX_DRAW_BUFFERS" << gets.max_draw_buffers << '\n' + << "GL_MAX_RENDERBUFFER_SIZE" << gets.max_renderbuffer_size << '\n' + << "GL_MAX_SAMPLES" << gets.max_samples << '\n' + << "GL_MAX_TEXTURE_IMAGE_UNITS" << gets.max_texture_image_units << '\n' + << "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS" << gets.max_transform_feedback_separate_attribs << '\n' + << "GL_MAX_UNIFORM_BLOCK_SIZE" << gets.max_uniform_block_size << '\n' + << "GL_MAX_UNIFORM_BUFFER_BINDINGS" << gets.max_uniform_buffer_bindings << '\n' + << "GL_NUM_PROGRAM_BINARY_FORMATS" << gets.num_program_binary_formats << '\n' + << "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT" << gets.uniform_buffer_offset_alignment << '\n' ; flush(slog.v); #endif diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index ff8b29cbd5..4ba559c78c 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -153,14 +153,15 @@ public: // glGet*() values struct Gets { GLfloat max_anisotropy; + GLint max_combined_texture_image_units; GLint max_draw_buffers; GLint max_renderbuffer_size; GLint max_samples; - GLint max_uniform_block_size; GLint max_texture_image_units; - GLint max_combined_texture_image_units; GLint max_transform_feedback_separate_attribs; - GLint max_uniform_buffer_bindings; + GLint max_uniform_block_size; + GLint max_uniform_buffer_bindings; + GLint num_program_binary_formats; GLint uniform_buffer_offset_alignment; } gets = {}; diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index 4d49cca9bf..9ab30a9bf1 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -140,6 +140,7 @@ void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept ShaderCompilerService::ShaderCompilerService(OpenGLDriver& driver) : mDriver(driver), + mBlobCache(driver.getContext()), mCallbackManager(driver), KHR_parallel_shader_compile(driver.getContext().ext.KHR_parallel_shader_compile) { } @@ -219,7 +220,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( token->attributes = std::move(program.getAttributes()); } - token->gl.program = OpenGLBlobCache::retrieve(&token->key, mDriver.mPlatform, program); + token->gl.program = mBlobCache.retrieve(&token->key, mDriver.mPlatform, program); if (token->gl.program) { return token; } @@ -264,7 +265,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( // caching must be the last thing we do if (token->key && status == GL_TRUE) { // Attempt to cache. This calls glGetProgramBinary. - OpenGLBlobCache::insert(mDriver.mPlatform, token->key, glProgram); + mBlobCache.insert(mDriver.mPlatform, token->key, glProgram); } }); @@ -317,7 +318,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( // do this later, maybe depending on CPU usage? // attempt to cache if we don't have a thread pool (otherwise it's done // by the pool). - OpenGLBlobCache::insert(mDriver.mPlatform, token->key, token->gl.program); + mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program); } return true; @@ -431,7 +432,7 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept { mCallbackManager.put(token->handle); if (token->key) { - OpenGLBlobCache::insert(mDriver.mPlatform, token->key, token->gl.program); + mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program); } } else { // if we don't have a program yet, block until we get it. diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h index bbce6a5c23..0ff9e493cb 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.h +++ b/filament/backend/src/opengl/ShaderCompilerService.h @@ -21,6 +21,7 @@ #include "CallbackManager.h" #include "CompilerThreadPool.h" +#include "OpenGLBlobCache.h" #include #include @@ -95,6 +96,7 @@ public: private: OpenGLDriver& mDriver; + OpenGLBlobCache mBlobCache; CallbackManager mCallbackManager; CompilerThreadPool mCompilerThreadPool;