Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brecht@blender.org>2021-01-31 21:35:00 +0300
committerBrecht Van Lommel <brecht@blender.org>2021-02-12 21:06:35 +0300
commit1b4961b318f14064bc3c915da7206a74146af95d (patch)
treefd8168e60333efad3dd0433b5229e2f7f9d97131 /intern/opencolorio/ocio_impl_glsl.cc
parent6b40ee608c5a1dbe67870fab4b83cce4cc4b54f5 (diff)
OpenColorIO: upgrade to version 2.0.0
Ref T84819 Build System ============ This is an API breaking new version, and the updated code only builds with OpenColorIO 2.0 and later. Adding backwards compatibility was too complicated. * Tinyxml was replaced with Expat, adding a new dependency. * Yaml-cpp is now built as a dependency on Unix, as was already done on Windows. * Removed currently unused LCMS code. * Pystring remains built as part of OCIO itself, since it has no good build system. * Linux and macOS check for the OpenColorIO verison, and disable it if too old. Ref D10270 Processors and Transforms ========================= CPU processors now need to be created to do CPU processing. These are cached internally, but the cache lookup is not fast enough to execute per pixel or texture sample, so for performance these are now also exposed in the C API. The C API for transforms will no longer be needed afer all changes, so remove it to simplify the API and fallback implementation. Ref D10271 Display Transforms ================== Needs a bit more manual work constructing the transform. LegacyViewingPipeline could also have been used, but isn't really any simpler and since it's legacy we better not rely on it. We moved more logic into the opencolorio module, to simplify the API. There is no need to wrap a dozen functions just to be able to do this in C rather than C++. It's also tightly coupled to the GPU shader logic, and so should be in the same module. Ref D10271 GPU Display Shader ================== To avoid baking exposure and gamma into the GLSL shader and requiring slow recompiles when tweaking, we manually apply them in the shader. This leads to some logic duplicaton between the CPU and GPU display processor, but it seems unavoidable. Caching was also changed. Previously this was done both on the imbuf and opencolorio module levels. Now it's all done in the opencolorio module by simply matching color space names. We no longer use cacheIDs from OpenColorIO since computing them is expensive, and they are unlikely to match now that more is baked into the shader code. Shaders can now use multiple 2D textures, 3D textures and uniforms, rather than a single 3D texture. So allocating and binding those adds some code. Color space conversions for blending with overlays is now hardcoded in the shader. This was using harcoded numbers anyway, if this every becomes a general OpenColorIO transform it can be changed, but for now there is no point to add code complexity. Ref D10273 CIE XYZ ======= We need standard CIE XYZ values for rendering effects like blackbody emission. The relation to the scene linear role is based on OpenColorIO configuration. In OpenColorIO 2.0 configs roles can no longer have the same name as color spaces, which means our XYZ role and colorspace in the configuration give an error. Instead use the new standard aces_interchange role, which relates scene linear to a known scene referred color space. Compatibility with the old XYZ role is preserved, if the configuration file has no conflicting names. Also includes a non-functional change to the configuraton file to use an XYZ-to-ACES matrix instead of REC709-to-ACES, makes debugging a little easier since the matrix is the same one we have in the code now and that is also found easily in the ACES specs. Ref D10274
Diffstat (limited to 'intern/opencolorio/ocio_impl_glsl.cc')
-rw-r--r--intern/opencolorio/ocio_impl_glsl.cc787
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();
}
/** \} */