From 1e6108e97246fe544e39d5a6d9fb907847fd907b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 31 May 2018 08:26:23 +0200 Subject: OpenColorIO: cache multiple shaders instead of just one. Fixes constant shader and texture rebuilding when doing animation playback with multiple editors that use different view transforms. --- intern/opencolorio/ocio_impl_glsl.cc | 339 ++++++++++++++++++++--------------- 1 file changed, 190 insertions(+), 149 deletions(-) (limited to 'intern/opencolorio') diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc index e20d507b470..090422ff795 100644 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ b/intern/opencolorio/ocio_impl_glsl.cc @@ -59,13 +59,19 @@ using namespace OCIO_NAMESPACE; #include "ocio_impl.h" static const int LUT3D_EDGE_SIZE = 64; +static const int SHADER_CACHE_SIZE = 4; extern "C" char datatoc_gpu_shader_display_transform_glsl[]; extern "C" char datatoc_gpu_shader_display_transform_vertex_glsl[]; /* **** OpenGL drawing routines using GLSL for color space transform ***** */ -typedef struct OCIO_GLSLDrawState { +typedef struct OCIO_GLSLShader { + /* Cache ID */ + std::string lut3dCacheID; + std::string shaderCacheID; + + /* LUT */ bool lut3d_texture_allocated; /* boolean flag indicating whether * lut texture is allocated */ @@ -75,25 +81,29 @@ typedef struct OCIO_GLSLDrawState { float *lut3d; /* 3D LUT table */ - bool dither_used; + /* Dither */ + bool use_dither; - bool curve_mapping_used; + /* Curve Mapping */ + bool use_curve_mapping; bool curve_mapping_texture_allocated; bool curve_mapping_texture_valid; GLuint curve_mapping_texture; size_t curve_mapping_cache_id; - bool predivide_used; - - /* Cache */ - std::string lut3dcacheid; - std::string shadercacheid; + /* Alpha Predivide */ + bool use_predivide; /* GLSL stuff */ GLuint ocio_shader; GLuint vert_shader; GLuint program; Gwn_ShaderInterface *shader_interface; +} GLSLDrawState; + +typedef struct OCIO_GLSLDrawState { + /* Shader Cache */ + OCIO_GLSLShader *shader_cache[SHADER_CACHE_SIZE]; /* Previous OpenGL state. */ GLint last_texture, last_texture_unit; @@ -150,33 +160,24 @@ static GLuint linkShaders(GLuint ocio_shader, GLuint vert_shader) static OCIO_GLSLDrawState *allocateOpenGLState(void) { - OCIO_GLSLDrawState *state; - - /* Allocate memory for state. */ - state = (OCIO_GLSLDrawState *) MEM_callocN(sizeof(OCIO_GLSLDrawState), - "OCIO OpenGL State struct"); - - /* Call constructors on new memory. */ - new (&state->lut3dcacheid) std::string(""); - new (&state->shadercacheid) std::string(""); - - return state; + return (OCIO_GLSLDrawState *) MEM_callocN(sizeof(OCIO_GLSLDrawState), + "OCIO OpenGL State struct"); } /* Ensure LUT texture and array are allocated */ -static bool ensureLUT3DAllocated(OCIO_GLSLDrawState *state) +static bool ensureLUT3DAllocated(OCIO_GLSLShader *shader) { int num_3d_entries = 3 * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE; - if (state->lut3d_texture_allocated) - return state->lut3d_texture_valid; + if (shader->lut3d_texture_allocated) + return shader->lut3d_texture_valid; - glGenTextures(1, &state->lut3d_texture); + glGenTextures(1, &shader->lut3d_texture); - state->lut3d = (float *) MEM_callocN(sizeof(float) * num_3d_entries, "OCIO GPU 3D LUT"); + shader->lut3d = (float *) MEM_callocN(sizeof(float) * num_3d_entries, "OCIO GPU 3D LUT"); glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_3D, state->lut3d_texture); + glBindTexture(GL_TEXTURE_3D, shader->lut3d_texture); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -188,27 +189,27 @@ static bool ensureLUT3DAllocated(OCIO_GLSLDrawState *state) glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, - 0, GL_RGB, GL_FLOAT, state->lut3d); + 0, GL_RGB, GL_FLOAT, shader->lut3d); - state->lut3d_texture_allocated = true; + shader->lut3d_texture_allocated = true; /* GL_RGB16F_ARB could be not supported at some drivers * in this case we could not use GLSL display */ - state->lut3d_texture_valid = glGetError() == GL_NO_ERROR; + shader->lut3d_texture_valid = glGetError() == GL_NO_ERROR; - return state->lut3d_texture_valid; + return shader->lut3d_texture_valid; } -static bool ensureCurveMappingAllocated(OCIO_GLSLDrawState *state, OCIO_CurveMappingSettings *curve_mapping_settings) +static bool ensureCurveMappingAllocated(OCIO_GLSLShader *shader, OCIO_CurveMappingSettings *curve_mapping_settings) { - if (state->curve_mapping_texture_allocated) - return state->curve_mapping_texture_valid; + if (shader->curve_mapping_texture_allocated) + return shader->curve_mapping_texture_valid; - glGenTextures(1, &state->curve_mapping_texture); + glGenTextures(1, &shader->curve_mapping_texture); glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture); + glBindTexture(GL_TEXTURE_1D, shader->curve_mapping_texture); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -221,16 +222,51 @@ static bool ensureCurveMappingAllocated(OCIO_GLSLDrawState *state, OCIO_CurveMap glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16F, curve_mapping_settings->lut_size, 0, GL_RGBA, GL_FLOAT, curve_mapping_settings->lut); - state->curve_mapping_texture_allocated = true; + shader->curve_mapping_texture_allocated = true; /* GL_RGB16F_ARB could be not supported at some drivers * in this case we could not use GLSL display */ - state->curve_mapping_texture_valid = glGetError() == GL_NO_ERROR; + shader->curve_mapping_texture_valid = glGetError() == GL_NO_ERROR; - return state->curve_mapping_texture_valid; + return shader->curve_mapping_texture_valid; } +static void freeGLSLShader(OCIO_GLSLShader *shader) +{ + if (shader->curve_mapping_texture_allocated) { + glDeleteTextures(1, &shader->curve_mapping_texture); + } + + if (shader->lut3d_texture_allocated) { + glDeleteTextures(1, &shader->lut3d_texture); + } + + if (shader->lut3d) { + MEM_freeN(shader->lut3d); + } + + if (shader->program) { + glDeleteProgram(shader->program); + } + + if (shader->shader_interface) { + GWN_shaderinterface_discard(shader->shader_interface); + } + + if (shader->ocio_shader) { + glDeleteShader(shader->ocio_shader); + } + + using std::string; + shader->lut3dCacheID.~string(); + shader->shaderCacheID.~string(); + + MEM_freeN(shader); +} + + + /* Detect if we can support GLSL drawing */ bool OCIOImpl::supportGLSLDraw() { @@ -265,135 +301,154 @@ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRc glGetIntegerv(GL_TEXTURE_BINDING_2D, &state->last_texture); glGetIntegerv(GL_ACTIVE_TEXTURE, &state->last_texture_unit); - if (!ensureLUT3DAllocated(state)) { - glActiveTexture(state->last_texture_unit); - glBindTexture(GL_TEXTURE_2D, state->last_texture); + /* Compute cache IDs. */ + GpuShaderDesc shaderDesc; + shaderDesc.setLanguage(GPU_LANGUAGE_GLSL_1_3); + shaderDesc.setFunctionName("OCIODisplay"); + shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE); - return false; - } + std::string lut3dCacheID = ocio_processor->getGpuLut3DCacheID(shaderDesc); + std::string shaderCacheID = ocio_processor->getGpuShaderTextCacheID(shaderDesc); - if (use_curve_mapping) { - if (!ensureCurveMappingAllocated(state, curve_mapping_settings)) { - glActiveTexture(state->last_texture_unit); - glBindTexture(GL_TEXTURE_2D, state->last_texture); + /* Find matching cached shader. */ + OCIO_GLSLShader *shader = NULL; + for (int i = 0; i < SHADER_CACHE_SIZE; i++) { + OCIO_GLSLShader *cached_shader = state->shader_cache[i]; + if (cached_shader == NULL) { + continue; + } + + if (cached_shader->lut3dCacheID == lut3dCacheID && + cached_shader->shaderCacheID == shaderCacheID && + cached_shader->use_predivide == use_predivide && + cached_shader->use_curve_mapping == use_curve_mapping && + cached_shader->use_dither == use_dither) + { + /* LRU cache, so move to front. */ + for (int j = i; j > 0; j--) { + state->shader_cache[j] = state->shader_cache[j - 1]; + } + state->shader_cache[0] = cached_shader; - return false; + shader = cached_shader; + break; } } - else { - if (state->curve_mapping_texture_allocated) { - glDeleteTextures(1, &state->curve_mapping_texture); - state->curve_mapping_texture_allocated = false; + + if (shader == NULL) { + /* LRU cache, shift other items back so we can insert at the front. */ + OCIO_GLSLShader *last_shader = state->shader_cache[SHADER_CACHE_SIZE - 1]; + if (last_shader) { + freeGLSLShader(last_shader); + } + for (int j = SHADER_CACHE_SIZE - 1; j > 0; j--) { + state->shader_cache[j] = state->shader_cache[j - 1]; } - } - /* Step 1: Create a GPU Shader Description */ - GpuShaderDesc shaderDesc; - shaderDesc.setLanguage(GPU_LANGUAGE_GLSL_1_3); - shaderDesc.setFunctionName("OCIODisplay"); - shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE); + /* Allocate memory for shader. */ + shader = (OCIO_GLSLShader *) MEM_callocN(sizeof(OCIO_GLSLShader), + "OCIO GLSL Shader"); + state->shader_cache[0] = shader; - if (use_curve_mapping) { - if (state->curve_mapping_cache_id != curve_mapping_settings->cache_id) { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture); - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, curve_mapping_settings->lut_size, - GL_RGBA, GL_FLOAT, curve_mapping_settings->lut); - } - } + new (&shader->lut3dCacheID) std::string(); + new (&shader->shaderCacheID) std::string(); - /* Step 2: Compute the 3D LUT */ - std::string lut3dCacheID = ocio_processor->getGpuLut3DCacheID(shaderDesc); - if (lut3dCacheID != state->lut3dcacheid) { - state->lut3dcacheid = lut3dCacheID; - ocio_processor->getGpuLut3D(state->lut3d, shaderDesc); + shader->lut3dCacheID = lut3dCacheID; + shader->shaderCacheID = shaderCacheID; + shader->use_curve_mapping = use_curve_mapping; + shader->use_dither = use_dither; + shader->use_predivide = use_predivide; - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_3D, state->lut3d_texture); - glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, - LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, - GL_RGB, GL_FLOAT, state->lut3d); - } + bool valid = true; - /* Step 3: Compute the Shader */ - std::string shaderCacheID = ocio_processor->getGpuShaderTextCacheID(shaderDesc); - if (state->program == 0 || - shaderCacheID != state->shadercacheid || - use_predivide != state->predivide_used || - use_curve_mapping != state->curve_mapping_used || - use_dither != state->dither_used) - { - state->shadercacheid = shaderCacheID; + /* Compute 3D LUT. */ + if (valid && ensureLUT3DAllocated(shader)) { + ocio_processor->getGpuLut3D(shader->lut3d, shaderDesc); - if (state->program) { - glDeleteProgram(state->program); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_3D, shader->lut3d_texture); + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, + LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, + GL_RGB, GL_FLOAT, shader->lut3d); } - - if (state->ocio_shader) { - glDeleteShader(state->ocio_shader); + else { + valid = false; } - if (state->vert_shader) { - glDeleteShader(state->vert_shader); + /* Allocate curve mapping texture. */ + if (valid && use_curve_mapping) { + if (!ensureCurveMappingAllocated(shader, curve_mapping_settings)) { + valid = false; + } } - /* Vertex shader */ - std::ostringstream osv; + if (valid) { + /* Vertex shader */ + std::ostringstream osv; - osv << "#version 330\n"; - osv << datatoc_gpu_shader_display_transform_vertex_glsl; + osv << "#version 330\n"; + osv << datatoc_gpu_shader_display_transform_vertex_glsl; - state->vert_shader = compileShaderText(GL_VERTEX_SHADER, osv.str().c_str()); + shader->vert_shader = compileShaderText(GL_VERTEX_SHADER, osv.str().c_str()); - /* Fragment shader */ - std::ostringstream os; + /* Fragment shader */ + std::ostringstream os; - os << "#version 330\n"; + os << "#version 330\n"; - /* Work around OpenColorIO not supporting latest GLSL yet. */ - os << "#define texture2D texture\n"; - os << "#define texture3D texture\n"; + /* Work around OpenColorIO not supporting latest GLSL yet. */ + os << "#define texture2D texture\n"; + os << "#define texture3D texture\n"; - if (use_predivide) { - os << "#define USE_PREDIVIDE\n"; - } + if (use_predivide) { + os << "#define USE_PREDIVIDE\n"; + } - if (use_dither) { - os << "#define USE_DITHER\n"; - } + if (use_dither) { + os << "#define USE_DITHER\n"; + } - if (use_curve_mapping) { - os << "#define USE_CURVE_MAPPING\n"; - } + if (use_curve_mapping) { + os << "#define USE_CURVE_MAPPING\n"; + } - os << ocio_processor->getGpuShaderText(shaderDesc) << "\n"; - os << datatoc_gpu_shader_display_transform_glsl; + os << ocio_processor->getGpuShaderText(shaderDesc) << "\n"; + os << datatoc_gpu_shader_display_transform_glsl; - state->ocio_shader = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str()); + shader->ocio_shader = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str()); - if (state->ocio_shader && state->vert_shader) { - state->program = linkShaders(state->ocio_shader, state->vert_shader); - } + /* Program */ + if (shader->ocio_shader && shader->vert_shader) { + shader->program = linkShaders(shader->ocio_shader, shader->vert_shader); + } - if (state->program) { - if (state->shader_interface) { - GWN_shaderinterface_discard(state->shader_interface); + if (shader->program) { + if (shader->shader_interface) { + GWN_shaderinterface_discard(shader->shader_interface); + } + shader->shader_interface = GWN_shaderinterface_create(shader->program); } - state->shader_interface = GWN_shaderinterface_create(state->program); } + } - state->curve_mapping_used = use_curve_mapping; - state->dither_used = use_dither; - state->predivide_used = use_predivide; + /* Update curve mapping texture. */ + if (use_curve_mapping && shader->curve_mapping_texture_allocated) { + if (shader->curve_mapping_cache_id != curve_mapping_settings->cache_id) { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_1D, shader->curve_mapping_texture); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, curve_mapping_settings->lut_size, + GL_RGBA, GL_FLOAT, curve_mapping_settings->lut); + } } - if (state->program) { + /* Bind Shader. */ + if (shader->program) { glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_3D, state->lut3d_texture); + glBindTexture(GL_TEXTURE_3D, shader->lut3d_texture); if (use_curve_mapping) { glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture); + glBindTexture(GL_TEXTURE_1D, shader->curve_mapping_texture); } glActiveTexture(GL_TEXTURE0); @@ -409,7 +464,7 @@ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRc Gwn_VertFormat *format = immVertexFormat(); GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); - immBindProgram(state->program, state->shader_interface); + immBindProgram(shader->program, shader->shader_interface); immUniform1i("image_texture", 0); immUniform1i("lut3d_texture", 1); @@ -453,27 +508,13 @@ void OCIOImpl::finishGLSLDraw(OCIO_GLSLDrawState *state) immUnbindProgram(); } -void OCIOImpl::freeGLState(struct OCIO_GLSLDrawState *state) +void OCIOImpl::freeGLState(OCIO_GLSLDrawState *state) { - using std::string; - - if (state->lut3d_texture_allocated) - glDeleteTextures(1, &state->lut3d_texture); - - if (state->lut3d) - MEM_freeN(state->lut3d); - - if (state->program) - glDeleteProgram(state->program); - - if (state->shader_interface) - GWN_shaderinterface_discard(state->shader_interface); - - if (state->ocio_shader) - glDeleteShader(state->ocio_shader); - - state->lut3dcacheid.~string(); - state->shadercacheid.~string(); + for (int i = 0; i < SHADER_CACHE_SIZE; i++) { + if (state->shader_cache[i]) { + freeGLSLShader(state->shader_cache[i]); + } + } MEM_freeN(state); } -- cgit v1.2.3