diff options
Diffstat (limited to 'intern/opencolorio/ocio_impl_glsl.cc')
-rw-r--r-- | intern/opencolorio/ocio_impl_glsl.cc | 787 |
1 files changed, 432 insertions, 355 deletions
diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc index 0213646345c..841f1386af1 100644 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ b/intern/opencolorio/ocio_impl_glsl.cc @@ -31,8 +31,10 @@ */ #include <limits> +#include <list> #include <sstream> #include <string.h> +#include <vector> #ifdef _MSC_VER # pragma warning(push) @@ -53,24 +55,24 @@ using namespace OCIO_NAMESPACE; #include "ocio_impl.h" -static const int LUT3D_EDGE_SIZE = 64; -static const int LUT3D_TEXTURE_SIZE = sizeof(float) * 3 * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE * - LUT3D_EDGE_SIZE; -static const int SHADER_CACHE_SIZE = 4; - -#define UBO_BIND_LOC 0 - 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 ***** */ +enum OCIO_GPUTextureSlots { + TEXTURE_SLOT_IMAGE = 0, + TEXTURE_SLOT_OVERLAY = 1, + TEXTURE_SLOT_CURVE_MAPPING = 2, + TEXTURE_SLOT_LUTS_OFFSET = 3, +}; + /* Curve mapping parameters * * See documentation for OCIO_CurveMappingSettings to get fields descriptions. * (this ones pretty much copies stuff from C structure.) */ -struct OCIO_GLSLCurveMappingParameters { +struct OCIO_GPUCurveMappingParameters { float curve_mapping_mintable[4]; float curve_mapping_range[4]; float curve_mapping_ext_in_x[4]; @@ -89,79 +91,108 @@ struct OCIO_GLSLCurveMappingParameters { /** WARNING: Needs to be 16byte aligned. Used as UBO data. */ }; -struct OCIO_GLSLShader { - /** Cache IDs */ - std::string cacheId; +struct OCIO_GPUShader { + /* GPU shader. */ + struct GPUShader *shader = nullptr; - struct GPUShader *shader; /** Uniform locations. */ - int dither_loc; - int overlay_loc; - int predivide_loc; - int curve_mapping_loc; - int ubo_bind; - /** Error checking. */ - bool valid; + int scale_loc = 0; + int exponent_loc = 0; + int dither_loc = 0; + int overlay_loc = 0; + int predivide_loc = 0; + int ubo_bind = 0; + + /* Destructor. */ + ~OCIO_GPUShader() + { + if (shader) { + GPU_shader_free(shader); + } + } }; -struct OCIO_GLSLLut3d { - /** Cache IDs */ - std::string cacheId; - /** OpenGL Texture handles. NULL if not allocated. */ - GPUTexture *texture; - GPUTexture *texture_display; - GPUTexture *texture_dummy; - /** Error checking. */ - bool valid; +struct OCIO_GPULutTexture { + GPUTexture *texture = nullptr; + std::string sampler_name; +}; + +struct OCIO_GPUUniform { + GpuShaderDesc::UniformData data; + std::string name; +}; + +struct OCIO_GPUTextures { + /** LUT Textures */ + std::vector<OCIO_GPULutTexture> luts; + + /* Dummy in case of no overlay. */ + GPUTexture *dummy = nullptr; + + /* Uniforms */ + std::vector<OCIO_GPUUniform> uniforms; + + /* Destructor. */ + ~OCIO_GPUTextures() + { + for (OCIO_GPULutTexture &lut : luts) { + GPU_texture_free(lut.texture); + } + if (dummy) { + GPU_texture_free(dummy); + } + } }; -struct OCIO_GLSLCurveMappping { - /** Cache IDs */ - size_t cacheId; +struct OCIO_GPUCurveMappping { /** GPU Uniform Buffer handle. 0 if not allocated. */ - GPUUniformBuf *buffer; + GPUUniformBuf *buffer = nullptr; /** OpenGL Texture handles. 0 if not allocated. */ - GPUTexture *texture; - /** Error checking. */ - bool valid; -}; + GPUTexture *texture = nullptr; + /* To detect when to update the uniforms and textures. */ + size_t cache_id = 0; -struct OCIO_GLSLCacheHandle { - size_t cache_id; - void *data; + /* Destructor. */ + ~OCIO_GPUCurveMappping() + { + if (texture) { + GPU_texture_free(texture); + } + if (buffer) { + GPU_uniformbuf_free(buffer); + } + } }; -struct OCIO_GLSLDrawState { - /* Shader Cache */ - OCIO_GLSLCacheHandle shader_cache[SHADER_CACHE_SIZE]; - OCIO_GLSLCacheHandle lut3d_cache[SHADER_CACHE_SIZE]; - OCIO_GLSLCacheHandle curvemap_cache[SHADER_CACHE_SIZE]; +struct OCIO_GPUDisplayShader { + OCIO_GPUShader shader; + OCIO_GPUTextures textures; + OCIO_GPUCurveMappping curvemap; + + /* Cache variables. */ + std::string input; + std::string view; + std::string display; + std::string look; + bool use_curve_mapping = false; + + /** Error checking. */ + bool valid = false; }; -static OCIO_GLSLDrawState *allocateOpenGLState(void) -{ - return (OCIO_GLSLDrawState *)MEM_callocN(sizeof(OCIO_GLSLDrawState), "OCIO OpenGL State struct"); -} +static const int SHADER_CACHE_MAX_SIZE = 4; +std::list<OCIO_GPUDisplayShader> SHADER_CACHE; /* -------------------------------------------------------------------- */ /** \name Shader * \{ */ -static void updateGLSLShader(OCIO_GLSLShader *shader, - ConstProcessorRcPtr *processor_scene_to_ui, - ConstProcessorRcPtr *processpr_ui_to_display, - GpuShaderDesc *shader_desc, - const std::string &cache_id) +static bool createGPUShader(OCIO_GPUShader &shader, + OCIO_GPUTextures &textures, + const GpuShaderDescRcPtr &shaderdesc_to_scene_linear, + const GpuShaderDescRcPtr &shaderdesc_to_display, + const bool use_curve_mapping) { - if (shader->cacheId == cache_id) { - return; - } - - /* Delete any previous shader. */ - if (shader->shader) { - GPU_shader_free(shader->shader); - } - std::ostringstream os; { /* Fragment shader */ @@ -170,146 +201,222 @@ static void updateGLSLShader(OCIO_GLSLShader *shader, os << "#define texture2D texture\n"; os << "#define texture3D texture\n"; - shader_desc->setFunctionName("OCIO_to_display_linear_with_look"); - os << (*processor_scene_to_ui)->getGpuShaderText(*shader_desc) << "\n"; + if (use_curve_mapping) { + os << "#define USE_CURVE_MAPPING\n"; + } - shader_desc->setFunctionName("OCIO_to_display_encoded"); - os << (*processpr_ui_to_display)->getGpuShaderText(*shader_desc) << "\n"; + os << shaderdesc_to_scene_linear->getShaderText() << "\n"; + os << shaderdesc_to_display->getShaderText() << "\n"; os << datatoc_gpu_shader_display_transform_glsl; } - shader->shader = GPU_shader_create(datatoc_gpu_shader_display_transform_vertex_glsl, - os.str().c_str(), - NULL, - NULL, - NULL, - "OCIOShader"); + shader.shader = GPU_shader_create(datatoc_gpu_shader_display_transform_vertex_glsl, + os.str().c_str(), + nullptr, + nullptr, + nullptr, + "OCIOShader"); - if (shader->shader) { - shader->dither_loc = GPU_shader_get_uniform(shader->shader, "dither"); - shader->overlay_loc = GPU_shader_get_uniform(shader->shader, "overlay"); - shader->predivide_loc = GPU_shader_get_uniform(shader->shader, "predivide"); - shader->curve_mapping_loc = GPU_shader_get_uniform(shader->shader, "curve_mapping"); - shader->ubo_bind = GPU_shader_get_uniform_block_binding(shader->shader, - "OCIO_GLSLCurveMappingParameters"); + if (shader.shader == nullptr) { + return false; + } - GPU_shader_bind(shader->shader); + shader.scale_loc = GPU_shader_get_uniform(shader.shader, "scale"); + shader.exponent_loc = GPU_shader_get_uniform(shader.shader, "exponent"); + shader.dither_loc = GPU_shader_get_uniform(shader.shader, "dither"); + shader.overlay_loc = GPU_shader_get_uniform(shader.shader, "overlay"); + shader.predivide_loc = GPU_shader_get_uniform(shader.shader, "predivide"); + shader.ubo_bind = GPU_shader_get_uniform_block_binding(shader.shader, + "OCIO_GPUCurveMappingParameters"); - /* Set texture bind point uniform once. This is saved by the shader. */ - GPUShader *sh = shader->shader; - GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "image_texture"), 0); - GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "overlay_texture"), 1); - GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "lut3d_texture"), 2); - GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "lut3d_display_texture"), 3); - GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "curve_mapping_texture"), 4); - } + GPU_shader_bind(shader.shader); - shader->cacheId = cache_id; - shader->valid = (shader->shader != NULL); -} + /* Set texture bind point uniform once. This is saved by the shader. */ + GPUShader *sh = shader.shader; + GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "image_texture"), TEXTURE_SLOT_IMAGE); + GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "overlay_texture"), TEXTURE_SLOT_OVERLAY); -static void ensureGLSLShader(OCIO_GLSLShader **shader_ptr, - ConstProcessorRcPtr *processor_scene_to_ui, - ConstProcessorRcPtr *processpr_ui_to_display, - GpuShaderDesc *shader_desc, - const std::string &cache_id) -{ - if (*shader_ptr != NULL) { - return; + if (use_curve_mapping) { + GPU_shader_uniform_int( + sh, GPU_shader_get_uniform(sh, "curve_mapping_texture"), TEXTURE_SLOT_CURVE_MAPPING); } - OCIO_GLSLShader *shader = OBJECT_GUARDED_NEW(OCIO_GLSLShader); - - updateGLSLShader(shader, processor_scene_to_ui, processpr_ui_to_display, shader_desc, cache_id); + /* Set LUT textures. */ + for (int i = 0; i < textures.luts.size(); i++) { + GPU_shader_uniform_int(sh, + GPU_shader_get_uniform(sh, textures.luts[i].sampler_name.c_str()), + TEXTURE_SLOT_LUTS_OFFSET + i); + } - *shader_ptr = shader; -} + /* Set uniforms. */ + for (OCIO_GPUUniform &uniform : textures.uniforms) { + const GpuShaderDesc::UniformData &data = uniform.data; + const char *name = name; -static void freeGLSLShader(OCIO_GLSLShader *shader) -{ - if (shader->shader) { - GPU_shader_free(shader->shader); + if (data.m_getDouble) { + GPU_shader_uniform_1f(sh, name, (float)data.m_getDouble()); + } + else if (data.m_getBool) { + GPU_shader_uniform_1f(sh, name, (float)(data.m_getBool() ? 1.0f : 0.0f)); + } + else if (data.m_getFloat3) { + GPU_shader_uniform_3f(sh, + name, + (float)data.m_getFloat3()[0], + (float)data.m_getFloat3()[1], + (float)data.m_getFloat3()[2]); + } + else if (data.m_vectorFloat.m_getSize && data.m_vectorFloat.m_getVector) { + GPU_shader_uniform_vector(sh, + GPU_shader_get_uniform(sh, name), + (int)data.m_vectorFloat.m_getSize(), + 1, + (float *)data.m_vectorFloat.m_getVector()); + } + else if (data.m_vectorInt.m_getSize && data.m_vectorInt.m_getVector) { + GPU_shader_uniform_vector_int(sh, + GPU_shader_get_uniform(sh, name), + (int)data.m_vectorInt.m_getSize(), + 1, + (int *)data.m_vectorInt.m_getVector()); + } } - OBJECT_GUARDED_DELETE(shader, OCIO_GLSLShader); + return true; } /** \} */ /* -------------------------------------------------------------------- */ -/** \name Lut3D +/** \name Textures * \{ */ -static void updateGLSLLut3d(OCIO_GLSLLut3d *lut3d, - ConstProcessorRcPtr *processor_scene_to_ui, - ConstProcessorRcPtr *processpr_ui_to_display, - GpuShaderDesc *shader_desc, - const std::string &cache_id) +static bool addGPUUniform(OCIO_GPUTextures &textures, + const GpuShaderDescRcPtr &shader_desc, + int index) { - if (lut3d->cacheId == cache_id) - return; - - float *lut_data = (float *)MEM_mallocN(LUT3D_TEXTURE_SIZE, __func__); + OCIO_GPUUniform uniform; + uniform.name = shader_desc->getUniform(index, uniform.data); + if (uniform.data.m_type == UNIFORM_UNKNOWN) { + return false; + } - ConstProcessorRcPtr *ocio_processors[2] = {processor_scene_to_ui, processpr_ui_to_display}; + textures.uniforms.push_back(uniform); + return true; +} - for (int i = 0; i < 2; i++) { - ConstProcessorRcPtr *processor = ocio_processors[i]; - GPUTexture *texture = (&lut3d->texture)[i]; +static bool addGPULut2D(OCIO_GPUTextures &textures, + const GpuShaderDescRcPtr &shader_desc, + int index) +{ + const char *texture_name = nullptr; + const char *sampler_name = nullptr; + unsigned int width = 0; + unsigned int height = 0; + GpuShaderCreator::TextureType channel = GpuShaderCreator::TEXTURE_RGB_CHANNEL; + Interpolation interpolation = INTERP_LINEAR; + shader_desc->getTexture( + index, texture_name, sampler_name, width, height, channel, interpolation); + + const float *values; + shader_desc->getTextureValues(index, values); + if (texture_name == nullptr || sampler_name == nullptr || width == 0 || height == 0 || + values == nullptr) { + return false; + } - (*processor)->getGpuLut3D(lut_data, *shader_desc); + eGPUTextureFormat format = (channel == GpuShaderCreator::TEXTURE_RGB_CHANNEL) ? GPU_RGB16F : + GPU_R16F; - int offset[3] = {0, 0, 0}; - int extent[3] = {LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE}; - GPU_texture_update_sub(texture, GPU_DATA_FLOAT, lut_data, UNPACK3(offset), UNPACK3(extent)); + OCIO_GPULutTexture lut; + lut.texture = GPU_texture_create_2d(texture_name, width, height, 0, format, values); + if (lut.texture == nullptr) { + return false; } - MEM_freeN(lut_data); + GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST); + GPU_texture_wrap_mode(lut.texture, false, true); + + lut.sampler_name = sampler_name; - lut3d->cacheId = cache_id; + textures.luts.push_back(lut); + return true; } -static void ensureGLSLLut3d(OCIO_GLSLLut3d **lut3d_ptr, - ConstProcessorRcPtr *processor_scene_to_ui, - ConstProcessorRcPtr *processpr_ui_to_display, - GpuShaderDesc *shaderDesc, - const std::string &cache_id) +static bool addGPULut3D(OCIO_GPUTextures &textures, + const GpuShaderDescRcPtr &shader_desc, + int index) { - if (*lut3d_ptr != NULL) { - return; + const char *texture_name = nullptr; + const char *sampler_name = nullptr; + unsigned int edgelen = 0; + Interpolation interpolation = INTERP_LINEAR; + shader_desc->get3DTexture(index, texture_name, sampler_name, edgelen, interpolation); + + const float *values; + shader_desc->get3DTextureValues(index, values); + if (texture_name == nullptr || sampler_name == nullptr || edgelen == 0 || values == nullptr) { + return false; } - OCIO_GLSLLut3d *lut3d = OBJECT_GUARDED_NEW(OCIO_GLSLLut3d); - - int extent[3] = {LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE}; - - lut3d->texture = GPU_texture_create_3d( - "OCIOLut", UNPACK3(extent), 1, GPU_RGB16F, GPU_DATA_FLOAT, NULL); - GPU_texture_filter_mode(lut3d->texture, true); - GPU_texture_wrap_mode(lut3d->texture, false, true); - - lut3d->texture_display = GPU_texture_create_3d( - "OCIOLutDisplay", UNPACK3(extent), 1, GPU_RGB16F, GPU_DATA_FLOAT, NULL); - GPU_texture_filter_mode(lut3d->texture_display, true); - GPU_texture_wrap_mode(lut3d->texture_display, false, true); - - lut3d->texture_dummy = GPU_texture_create_error(2, false); + OCIO_GPULutTexture lut; + lut.texture = GPU_texture_create_3d( + texture_name, edgelen, edgelen, edgelen, 0, GPU_RGB16F, GPU_DATA_FLOAT, values); + if (lut.texture == nullptr) { + return false; + } - updateGLSLLut3d(lut3d, processor_scene_to_ui, processpr_ui_to_display, shaderDesc, cache_id); + GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST); + GPU_texture_wrap_mode(lut.texture, false, true); - lut3d->valid = (lut3d->texture && lut3d->texture_display); + lut.sampler_name = sampler_name; - *lut3d_ptr = lut3d; + textures.luts.push_back(lut); + return true; } -static void freeGLSLLut3d(OCIO_GLSLLut3d *lut3d) +static bool createGPUTextures(OCIO_GPUTextures &textures, + const GpuShaderDescRcPtr &shaderdesc_to_scene_linear, + const GpuShaderDescRcPtr &shaderdesc_to_display) { - GPU_texture_free(lut3d->texture); - GPU_texture_free(lut3d->texture_display); - GPU_texture_free(lut3d->texture_dummy); + textures.dummy = GPU_texture_create_error(2, false); + + textures.luts.clear(); + textures.uniforms.clear(); + + for (int index = 0; index < shaderdesc_to_scene_linear->getNumUniforms(); index++) { + if (!addGPUUniform(textures, shaderdesc_to_scene_linear, index)) { + return false; + } + } + for (int index = 0; index < shaderdesc_to_scene_linear->getNumTextures(); index++) { + if (!addGPULut2D(textures, shaderdesc_to_scene_linear, index)) { + return false; + } + } + for (int index = 0; index < shaderdesc_to_scene_linear->getNum3DTextures(); index++) { + if (!addGPULut3D(textures, shaderdesc_to_scene_linear, index)) { + return false; + } + } + for (int index = 0; index < shaderdesc_to_display->getNumUniforms(); index++) { + if (!addGPUUniform(textures, shaderdesc_to_display, index)) { + return false; + } + } + for (int index = 0; index < shaderdesc_to_display->getNumTextures(); index++) { + if (!addGPULut2D(textures, shaderdesc_to_display, index)) { + return false; + } + } + for (int index = 0; index < shaderdesc_to_display->getNum3DTextures(); index++) { + if (!addGPULut3D(textures, shaderdesc_to_display, index)) { + return false; + } + } - OBJECT_GUARDED_DELETE(lut3d, OCIO_GLSLLut3d); + return true; } /** \} */ @@ -317,72 +424,47 @@ static void freeGLSLLut3d(OCIO_GLSLLut3d *lut3d) /* -------------------------------------------------------------------- */ /** \name Curve Mapping * \{ */ -static void allocateCurveMappingTexture(OCIO_GLSLCurveMappping *curvemap, - OCIO_CurveMappingSettings *curve_mapping_settings) -{ - int lut_size = curve_mapping_settings ? curve_mapping_settings->lut_size : 1; - /* Do not initialize. Only if used. */ - curvemap->texture = GPU_texture_create_1d("OCIOCurveMap", lut_size, 1, GPU_RGBA16F, NULL); - GPU_texture_filter_mode(curvemap->texture, false); - GPU_texture_wrap_mode(curvemap->texture, false, true); -} -/* curve_mapping_settings can be null. In this case we alloc a dummy curvemap. */ -static void ensureGLSLCurveMapping(OCIO_GLSLCurveMappping **curvemap_ptr, - OCIO_CurveMappingSettings *curve_mapping_settings) +static bool createGPUCurveMapping(OCIO_GPUCurveMappping &curvemap, + OCIO_CurveMappingSettings *curve_mapping_settings) { - if (*curvemap_ptr != NULL) { - return; - } - - OCIO_GLSLCurveMappping *curvemap = OBJECT_GUARDED_NEW(OCIO_GLSLCurveMappping); + if (curve_mapping_settings) { + int lut_size = curve_mapping_settings->lut_size; - /* Texture. */ - allocateCurveMappingTexture(curvemap, curve_mapping_settings); + curvemap.texture = GPU_texture_create_1d("OCIOCurveMap", lut_size, 1, GPU_RGBA16F, nullptr); + GPU_texture_filter_mode(curvemap.texture, false); + GPU_texture_wrap_mode(curvemap.texture, false, true); - /* Uniform buffer object. */ - curvemap->buffer = GPU_uniformbuf_create(sizeof(OCIO_GLSLCurveMappingParameters)); + curvemap.buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUCurveMappingParameters)); - curvemap->valid = (curvemap->texture != 0); - curvemap->cacheId = 0; - - *curvemap_ptr = curvemap; -} - -static void freeGLSLCurveMapping(OCIO_GLSLCurveMappping *curvemap) -{ - GPU_texture_free(curvemap->texture); - GPU_uniformbuf_free(curvemap->buffer); + if (curvemap.texture == nullptr || curvemap.buffer == nullptr) { + return false; + } + } - OBJECT_GUARDED_DELETE(curvemap, OCIO_GLSLCurveMappping); + return true; } -static void updateGLSLCurveMapping(OCIO_GLSLCurveMappping *curvemap, - OCIO_CurveMappingSettings *curve_mapping_settings, - size_t cacheId) +static void updateGPUCurveMapping(OCIO_GPUCurveMappping &curvemap, + OCIO_CurveMappingSettings *curve_mapping_settings) { - /* No need to continue if curvemapping is not used. Just use whatever is in this cache. */ - if (curve_mapping_settings == NULL) + /* Test if we need to update. The caller ensures the curve_mapping_settings + * changes when its contents changes. */ + if (curve_mapping_settings == nullptr || curvemap.cache_id == curve_mapping_settings->cache_id) { return; - - if (curvemap->cacheId == cacheId) - return; - - if (curvemap->cacheId == 0) { - /* This cache was previously used as dummy. Recreate the texture. */ - GPU_texture_free(curvemap->texture); - allocateCurveMappingTexture(curvemap, curve_mapping_settings); } + curvemap.cache_id = curve_mapping_settings->cache_id; + /* Update texture. */ int offset[3] = {0, 0, 0}; int extent[3] = {curve_mapping_settings->lut_size, 0, 0}; const float *pixels = curve_mapping_settings->lut; GPU_texture_update_sub( - curvemap->texture, GPU_DATA_FLOAT, pixels, UNPACK3(offset), UNPACK3(extent)); + curvemap.texture, GPU_DATA_FLOAT, pixels, UNPACK3(offset), UNPACK3(extent)); /* Update uniforms. */ - OCIO_GLSLCurveMappingParameters data; + OCIO_GPUCurveMappingParameters data; for (int i = 0; i < 4; i++) { data.curve_mapping_range[i] = curve_mapping_settings->range[i]; data.curve_mapping_mintable[i] = curve_mapping_settings->mintable[i]; @@ -402,198 +484,193 @@ static void updateGLSLCurveMapping(OCIO_GLSLCurveMappping *curvemap, data.curve_mapping_lut_size = curve_mapping_settings->lut_size; data.curve_mapping_use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate; - GPU_uniformbuf_update(curvemap->buffer, &data); - - curvemap->cacheId = cacheId; + GPU_uniformbuf_update(curvemap.buffer, &data); } /** \} */ /* -------------------------------------------------------------------- */ -/** \name LRU cache +/** \name OCIO GPU Shader Implementation * \{ */ -static size_t hash_string(const char *str) +bool OCIOImpl::supportGPUShader() { - size_t i = 0, c; - while ((c = *str++)) { - i = i * 37 + c; - } - return i; + /* Minimum supported version 3.3 does meet all requirements. */ + return true; } -static OCIO_GLSLCacheHandle *cacheSearch(OCIO_GLSLCacheHandle cache[SHADER_CACHE_SIZE], - size_t cache_id) +static OCIO_GPUDisplayShader &getGPUDisplayShader( + OCIO_ConstConfigRcPtr *config, + const char *input, + const char *view, + const char *display, + const char *look, + OCIO_CurveMappingSettings *curve_mapping_settings) { - OCIO_GLSLCacheHandle *cached_item = &cache[0]; - for (int i = 0; i < SHADER_CACHE_SIZE; i++, cached_item++) { - if (cached_item->data == NULL) { - continue; - } - else if (cached_item->cache_id == cache_id) { - /* LRU cache, so move to front. */ - OCIO_GLSLCacheHandle found_item = *cached_item; - for (int j = i; j > 0; j--) { - cache[j] = cache[j - 1]; + /* Find existing shader in cache. */ + const bool use_curve_mapping = (curve_mapping_settings != nullptr); + for (std::list<OCIO_GPUDisplayShader>::iterator it = SHADER_CACHE.begin(); + it != SHADER_CACHE.end(); + it++) { + if (it->input == input && it->view == view && it->display == display && it->look == look && + it->use_curve_mapping == use_curve_mapping) { + /* Move to front of the cache to mark as most recently used. */ + if (it != SHADER_CACHE.begin()) { + SHADER_CACHE.splice(SHADER_CACHE.begin(), SHADER_CACHE, it); } - cache[0] = found_item; - return &cache[0]; + + return *it; } } - /* LRU cache, shift other items back so we can insert at the front. */ - OCIO_GLSLCacheHandle last_item = cache[SHADER_CACHE_SIZE - 1]; - for (int j = SHADER_CACHE_SIZE - 1; j > 0; j--) { - cache[j] = cache[j - 1]; + + /* Remove least recently used element from cache. */ + if (SHADER_CACHE.size() >= SHADER_CACHE_MAX_SIZE) { + SHADER_CACHE.pop_back(); } - /* Copy last to front and let the caller initialize it. */ - cache[0] = last_item; - return &cache[0]; -} -/** \} */ + /* Create GPU shader. */ + SHADER_CACHE.emplace_front(); + OCIO_GPUDisplayShader &display_shader = SHADER_CACHE.front(); + + display_shader.input = input; + display_shader.view = view; + display_shader.display = display; + display_shader.look = look; + display_shader.use_curve_mapping = use_curve_mapping; + display_shader.valid = false; + + /* Create Processors. + * + * Scale and exponent are handled outside of OCIO shader so we can handle them + * as uniforms at the binding stage. OCIO would otherwise bake them into the + * shader code, requiring slow recompiles when interactively adjusting them. + * + * Note that OCIO does have the concept of dynamic properties, however there + * is no dynamic gamma and exposure is part of more expensive operations only. + * + * Since exposure must happen in scene linear, we use two processors. The input + * is usually scene linear already and so that conversion is often a no-op. + */ + OCIO_ConstProcessorRcPtr *processor_to_scene_linear = OCIO_configGetProcessorWithNames( + config, input, ROLE_SCENE_LINEAR); + OCIO_ConstProcessorRcPtr *processor_to_display = OCIO_createDisplayProcessor( + config, ROLE_SCENE_LINEAR, view, display, look, 1.0f, 1.0f); + + /* Create shader descriptions. */ + if (processor_to_scene_linear && processor_to_display) { + GpuShaderDescRcPtr shaderdesc_to_scene_linear = GpuShaderDesc::CreateShaderDesc(); + shaderdesc_to_scene_linear->setLanguage(GPU_LANGUAGE_GLSL_1_3); + shaderdesc_to_scene_linear->setFunctionName("OCIO_to_scene_linear"); + (*(ConstProcessorRcPtr *)processor_to_scene_linear) + ->getDefaultGPUProcessor() + ->extractGpuShaderInfo(shaderdesc_to_scene_linear); + shaderdesc_to_scene_linear->finalize(); + + GpuShaderDescRcPtr shaderdesc_to_display = GpuShaderDesc::CreateShaderDesc(); + shaderdesc_to_display->setLanguage(GPU_LANGUAGE_GLSL_1_3); + shaderdesc_to_display->setFunctionName("OCIO_to_display"); + (*(ConstProcessorRcPtr *)processor_to_display) + ->getDefaultGPUProcessor() + ->extractGpuShaderInfo(shaderdesc_to_display); + shaderdesc_to_display->finalize(); + + /* Create GPU shader and textures. */ + if (createGPUTextures( + display_shader.textures, shaderdesc_to_scene_linear, shaderdesc_to_display) && + createGPUCurveMapping(display_shader.curvemap, curve_mapping_settings) && + createGPUShader(display_shader.shader, + display_shader.textures, + shaderdesc_to_scene_linear, + shaderdesc_to_display, + use_curve_mapping)) { + display_shader.valid = true; + } + } -/* -------------------------------------------------------------------- */ -/** \name OCIO GLSL Implementation - * \{ */ + /* Free processors. */ + if (processor_to_scene_linear) { + OCIO_processorRelease(processor_to_scene_linear); + } + if (processor_to_display) { + OCIO_processorRelease(processor_to_display); + } -/* Detect if we can support GLSL drawing */ -bool OCIOImpl::supportGLSLDraw() -{ - /* Minimum supported version 3.3 does meet all requirements. */ - return true; + return display_shader; } /** - * Setup OpenGL contexts for a transform defined by processor using GLSL + * Setup GPU contexts for a transform defined by processor using GLSL. * All LUT allocating baking and shader compilation happens here. * * Once this function is called, callee could start drawing images * using regular 2D texture. * - * When all drawing is finished, finishGLSLDraw shall be called to - * restore OpenGL context to its pre-GLSL draw state. + * When all drawing is finished, gpuDisplayShaderUnbind must be called to + * restore GPU context to its previous state. */ -bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, - OCIO_ConstProcessorRcPtr *ocio_processor_scene_to_ui, - OCIO_ConstProcessorRcPtr *ocio_processor_ui_to_display, - OCIO_CurveMappingSettings *curve_mapping_settings, - float dither, - bool use_predivide, - bool use_overlay) +bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, + const char *input, + const char *view, + const char *display, + const char *look, + OCIO_CurveMappingSettings *curve_mapping_settings, + const float scale, + const float exponent, + const float dither, + const bool use_predivide, + const bool use_overlay) { - ConstProcessorRcPtr processor_scene_to_ui = *(ConstProcessorRcPtr *)ocio_processor_scene_to_ui; - ConstProcessorRcPtr processpr_ui_to_display = *( - ConstProcessorRcPtr *)ocio_processor_ui_to_display; - bool use_curve_mapping = curve_mapping_settings != NULL; - - if (!processor_scene_to_ui || !processor_scene_to_ui) { + /* Get GPU shader from cache or create new one. */ + OCIO_GPUDisplayShader &display_shader = getGPUDisplayShader( + config, input, view, display, look, curve_mapping_settings); + if (!display_shader.valid) { return false; } - /* Create state if needed. */ - OCIO_GLSLDrawState *state; - if (!*state_r) - *state_r = allocateOpenGLState(); - state = *state_r; - - /* Compute cache IDs. */ - GpuShaderDesc shaderDesc; - shaderDesc.setLanguage(GPU_LANGUAGE_GLSL_1_3); - shaderDesc.setFunctionName("OCIODisplay"); - shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE); - - const char *shader_cache_str = processor_scene_to_ui->getGpuShaderTextCacheID(shaderDesc); - const char *lut3d_cache_str = processor_scene_to_ui->getGpuLut3DCacheID(shaderDesc); - /* Used for comparison. */ - std::string shaderCacheID = shader_cache_str; - std::string lut3dCacheID = lut3d_cache_str; - - size_t shader_cache_id = hash_string(shader_cache_str); - size_t lut3d_cache_id = hash_string(lut3d_cache_str); - size_t curvemap_cache_id = curve_mapping_settings ? curve_mapping_settings->cache_id : 0; - - OCIO_GLSLCacheHandle *shader_handle = cacheSearch(state->shader_cache, shader_cache_id); - OCIO_GLSLCacheHandle *lut3d_handle = cacheSearch(state->lut3d_cache, lut3d_cache_id); - /* We cannot keep more than one cache for curvemap because their cache id is a pointer. - * The pointer cannot be the same for one update but can be the same after a second update. */ - OCIO_GLSLCacheHandle *curvemap_handle = &state->curvemap_cache[0]; - - OCIO_GLSLShader **shader_ptr = (OCIO_GLSLShader **)&shader_handle->data; - OCIO_GLSLLut3d **lut3d_ptr = (OCIO_GLSLLut3d **)&lut3d_handle->data; - OCIO_GLSLCurveMappping **curvemap_ptr = (OCIO_GLSLCurveMappping **)&curvemap_handle->data; - - ensureGLSLShader( - shader_ptr, &processor_scene_to_ui, &processpr_ui_to_display, &shaderDesc, shaderCacheID); - ensureGLSLLut3d( - lut3d_ptr, &processor_scene_to_ui, &processpr_ui_to_display, &shaderDesc, shaderCacheID); - ensureGLSLCurveMapping(curvemap_ptr, curve_mapping_settings); - - OCIO_GLSLShader *shader = (OCIO_GLSLShader *)shader_handle->data; - OCIO_GLSLLut3d *shader_lut = (OCIO_GLSLLut3d *)lut3d_handle->data; - OCIO_GLSLCurveMappping *shader_curvemap = (OCIO_GLSLCurveMappping *)curvemap_handle->data; - - updateGLSLShader( - shader, &processor_scene_to_ui, &processpr_ui_to_display, &shaderDesc, shaderCacheID); - updateGLSLLut3d( - shader_lut, &processor_scene_to_ui, &processpr_ui_to_display, &shaderDesc, lut3dCacheID); - updateGLSLCurveMapping(shader_curvemap, curve_mapping_settings, curvemap_cache_id); - - /* Update handles cache keys. */ - shader_handle->cache_id = shader_cache_id; - lut3d_handle->cache_id = lut3d_cache_id; - curvemap_handle->cache_id = curvemap_cache_id; - - if (shader->valid && shader_lut->valid && shader_curvemap->valid) { - /* Bind textures to sampler units. Texture 0 is set by caller. - * Uniforms have already been set for texture bind points.*/ - - if (!use_overlay) { - /* Avoid missing binds. */ - GPU_texture_bind(shader_lut->texture_dummy, 1); - } - GPU_texture_bind(shader_lut->texture, 2); - GPU_texture_bind(shader_lut->texture_display, 3); - GPU_texture_bind(shader_curvemap->texture, 4); + /* Verify the shader is valid. */ + OCIO_GPUTextures &textures = display_shader.textures; + OCIO_GPUShader &shader = display_shader.shader; + OCIO_GPUCurveMappping &curvemap = display_shader.curvemap; - /* Bind UBO. */ - GPU_uniformbuf_bind(shader_curvemap->buffer, shader->ubo_bind); + /* Update and bind curve mapping data. */ + if (curve_mapping_settings) { + updateGPUCurveMapping(curvemap, curve_mapping_settings); + GPU_uniformbuf_bind(curvemap.buffer, shader.ubo_bind); + GPU_texture_bind(curvemap.texture, TEXTURE_SLOT_CURVE_MAPPING); + } - /* TODO(fclem): remove remains of IMM. */ - immBindShader(shader->shader); + /* Bind textures to sampler units. Texture 0 is set by caller. + * Uniforms have already been set for texture bind points.*/ + if (!use_overlay) { + /* Avoid missing binds. */ + GPU_texture_bind(textures.dummy, TEXTURE_SLOT_OVERLAY); + } + for (int i = 0; i < textures.luts.size(); i++) { + GPU_texture_bind(textures.luts[i].texture, TEXTURE_SLOT_LUTS_OFFSET + i); + } - /* Bind Shader and set uniforms. */ - // GPU_shader_bind(shader->shader); - GPU_shader_uniform_float(shader->shader, shader->dither_loc, dither); - GPU_shader_uniform_int(shader->shader, shader->overlay_loc, use_overlay); - GPU_shader_uniform_int(shader->shader, shader->predivide_loc, use_predivide); - GPU_shader_uniform_int(shader->shader, shader->curve_mapping_loc, use_curve_mapping); + /* TODO(fclem): remove remains of IMM. */ + immBindShader(shader.shader); - return true; - } + /* Bind Shader and set uniforms. */ + // GPU_shader_bind(shader.shader); + GPU_shader_uniform_float(shader.shader, shader.scale_loc, scale); + GPU_shader_uniform_float(shader.shader, shader.exponent_loc, exponent); + GPU_shader_uniform_float(shader.shader, shader.dither_loc, dither); + GPU_shader_uniform_int(shader.shader, shader.overlay_loc, use_overlay); + GPU_shader_uniform_int(shader.shader, shader.predivide_loc, use_predivide); - return false; + return true; } -void OCIOImpl::finishGLSLDraw(OCIO_GLSLDrawState * /*state*/) +void OCIOImpl::gpuDisplayShaderUnbind() { immUnbindProgram(); } -void OCIOImpl::freeGLState(OCIO_GLSLDrawState *state) +void OCIOImpl::gpuCacheFree() { - for (int i = 0; i < SHADER_CACHE_SIZE; i++) { - if (state->shader_cache[i].data) { - freeGLSLShader((OCIO_GLSLShader *)state->shader_cache[i].data); - } - if (state->lut3d_cache[i].data) { - freeGLSLLut3d((OCIO_GLSLLut3d *)state->lut3d_cache[i].data); - } - if (state->curvemap_cache[i].data) { - freeGLSLCurveMapping((OCIO_GLSLCurveMappping *)state->curvemap_cache[i].data); - } - } - - MEM_freeN(state); + SHADER_CACHE.clear(); } /** \} */ |