tornavis/intern/opencolorio/ocio_impl_glsl.cc

780 lines
25 KiB
C++

/* SPDX-FileCopyrightText: 2003-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
* (BSD-3-Clause).
* SPDX-FileCopyrightText: 2013 Blender Authors (GPL-2.0-or-later).
*
* SPDX-License-Identifier: GPL-2.0-or-later AND BSD-3-Clause */
#include <limits>
#include <list>
#include <sstream>
#include <string.h>
#include <vector>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4251 4275)
#endif
#include <OpenColorIO/OpenColorIO.h>
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#include "GPU_immediate.hh"
#include "GPU_shader.hh"
#include "GPU_uniform_buffer.hh"
#include "gpu_shader_create_info.hh"
using namespace OCIO_NAMESPACE;
#include "MEM_guardedalloc.h"
#include "ocio_impl.h"
#include "ocio_shader_shared.hh"
/* **** 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,
};
enum OCIO_GPUUniformBufSlots {
UNIFORMBUF_SLOT_DISPLAY = 0,
UNIFORMBUF_SLOT_CURVEMAP = 1,
UNIFORMBUF_SLOT_LUTS = 2,
};
struct OCIO_GPUShader {
/* GPU shader. */
struct GPUShader *shader = nullptr;
/** Uniform parameters. */
OCIO_GPUParameters parameters = {};
GPUUniformBuf *parameters_buffer = nullptr;
/* Destructor. */
~OCIO_GPUShader()
{
if (shader) {
GPU_shader_free(shader);
}
if (parameters_buffer) {
GPU_uniformbuf_free(parameters_buffer);
}
}
};
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;
GPUUniformBuf *uniforms_buffer = nullptr;
/* Destructor. */
~OCIO_GPUTextures()
{
for (OCIO_GPULutTexture &lut : luts) {
GPU_texture_free(lut.texture);
}
if (dummy) {
GPU_texture_free(dummy);
}
if (uniforms_buffer) {
GPU_uniformbuf_free(uniforms_buffer);
}
}
};
struct OCIO_GPUCurveMappping {
/** GPU Uniform Buffer handle. 0 if not allocated. */
GPUUniformBuf *buffer = nullptr;
/** OpenGL Texture handles. 0 if not allocated. */
GPUTexture *texture = nullptr;
/* To detect when to update the uniforms and textures. */
size_t cache_id = 0;
/* Destructor. */
~OCIO_GPUCurveMappping()
{
if (texture) {
GPU_texture_free(texture);
}
if (buffer) {
GPU_uniformbuf_free(buffer);
}
}
};
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 const int SHADER_CACHE_MAX_SIZE = 4;
std::list<OCIO_GPUDisplayShader> SHADER_CACHE;
/* -------------------------------------------------------------------- */
/** \name Shader
* \{ */
static void string_replace_all(std::string &haystack,
const std::string &needle,
const std::string &other)
{
size_t i = 0, index;
while ((index = haystack.find(needle, i)) != std::string::npos) {
haystack.replace(index, needle.size(), other);
i = index + other.size();
}
}
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)
{
using namespace blender::gpu::shader;
std::string source;
source += shaderdesc_to_scene_linear->getShaderText();
source += "\n";
source += shaderdesc_to_display->getShaderText();
source += "\n";
{
/* Replace all uniform declarations by a comment.
* This avoids double declarations from the backend. */
size_t index = 0;
while (true) {
index = source.find("uniform ", index);
if (index == -1) {
break;
}
source.replace(index, 2, "//");
index += 2;
}
}
/* Comparison operator in Metal returns per-element comparison and returns a vector of booleans.
* Need a special syntax to see if two vec3 are matched.
*
* NOTE: The replacement is optimized for transforming code generated by
* GradingPrimaryTransform. A more general approach is possible, but for now prefer processing
* speed.
*
* NOTE: The syntax works for all backends Blender supports. */
string_replace_all(
source, "if ( gamma != vec3(1., 1., 1.) )", "if (! all(equal(gamma, vec3(1., 1., 1.))) )");
StageInterfaceInfo iface("OCIO_Interface", "");
iface.smooth(Type::VEC2, "texCoord_interp");
ShaderCreateInfo info("OCIO_Display");
/* Work around OpenColorIO not supporting latest GLSL yet. */
info.define("texture1D", "texture");
info.define("texture2D", "texture");
info.define("texture3D", "texture");
info.typedef_source("ocio_shader_shared.hh");
info.sampler(TEXTURE_SLOT_IMAGE, ImageType::FLOAT_2D, "image_texture");
info.sampler(TEXTURE_SLOT_OVERLAY, ImageType::FLOAT_2D, "overlay_texture");
info.uniform_buf(UNIFORMBUF_SLOT_DISPLAY, "OCIO_GPUParameters", "parameters");
info.push_constant(Type::MAT4, "ModelViewProjectionMatrix");
info.vertex_in(0, Type::VEC2, "pos");
info.vertex_in(1, Type::VEC2, "texCoord");
info.vertex_out(iface);
info.fragment_out(0, Type::VEC4, "fragColor");
info.vertex_source("gpu_shader_display_transform_vert.glsl");
info.fragment_source("gpu_shader_display_transform_frag.glsl");
info.fragment_source_generated = source;
/* #96502: Work around for incorrect OCIO GLSL code generation when using
* GradingPrimaryTransform. Should be reevaluated when changing to a next version of OCIO.
* (currently v2.1.1). */
info.define("inf 1e32");
if (use_curve_mapping) {
info.define("USE_CURVE_MAPPING");
info.uniform_buf(UNIFORMBUF_SLOT_CURVEMAP, "OCIO_GPUCurveMappingParameters", "curve_mapping");
info.sampler(TEXTURE_SLOT_CURVE_MAPPING, ImageType::FLOAT_1D, "curve_mapping_texture");
}
/* Set LUT textures. */
int slot = TEXTURE_SLOT_LUTS_OFFSET;
for (OCIO_GPULutTexture &texture : textures.luts) {
const int dimensions = GPU_texture_dimensions(texture.texture);
ImageType type = (dimensions == 1) ? ImageType::FLOAT_1D :
(dimensions == 2) ? ImageType::FLOAT_2D :
ImageType::FLOAT_3D;
info.sampler(slot++, type, texture.sampler_name.c_str());
}
/* Set LUT uniforms. */
if (!textures.uniforms.empty()) {
/* NOTE: For simplicity, we pad everything to size of vec4 avoiding sorting and alignment
* issues. It is unlikely that this becomes a real issue. */
size_t ubo_size = textures.uniforms.size() * sizeof(float) * 4;
void *ubo_data_buf = malloc(ubo_size);
uint32_t *ubo_data = reinterpret_cast<uint32_t *>(ubo_data_buf);
std::stringstream ss;
ss << "struct OCIO_GPULutParameters {\n";
int index = 0;
for (OCIO_GPUUniform &uniform : textures.uniforms) {
index += 1;
const GpuShaderDesc::UniformData &data = uniform.data;
const char *name = uniform.name.c_str();
char prefix = ' ';
int vec_len;
switch (data.m_type) {
case UNIFORM_DOUBLE: {
vec_len = 1;
float value = float(data.m_getDouble());
memcpy(ubo_data, &value, sizeof(float));
break;
}
case UNIFORM_BOOL: {
prefix = 'b';
vec_len = 1;
int value = int(data.m_getBool());
memcpy(ubo_data, &value, sizeof(int));
break;
}
case UNIFORM_FLOAT3:
vec_len = 3;
memcpy(ubo_data, data.m_getFloat3().data(), sizeof(float) * 3);
break;
case UNIFORM_VECTOR_FLOAT:
vec_len = data.m_vectorFloat.m_getSize();
memcpy(ubo_data, data.m_vectorFloat.m_getVector(), sizeof(float) * vec_len);
break;
case UNIFORM_VECTOR_INT:
prefix = 'i';
vec_len = data.m_vectorInt.m_getSize();
memcpy(ubo_data, data.m_vectorInt.m_getVector(), sizeof(int) * vec_len);
break;
default:
continue;
}
/* Align every member to 16bytes. */
ubo_data += 4;
/* Use a generic variable name because some GLSL compilers can interpret the preprocessor
* define as recursive. */
ss << " " << prefix << "vec4 var" << index << ";\n";
/* Use a define to keep the generated code working. */
blender::StringRef suffix = blender::StringRefNull("xyzw").substr(0, vec_len);
ss << "#define " << name << " lut_parameters.var" << index << "." << suffix << "\n";
}
ss << "};\n";
info.typedef_source_generated = ss.str();
info.uniform_buf(UNIFORMBUF_SLOT_LUTS, "OCIO_GPULutParameters", "lut_parameters");
textures.uniforms_buffer = GPU_uniformbuf_create_ex(
ubo_size, ubo_data_buf, "OCIO_LutParameters");
free(ubo_data_buf);
}
shader.shader = GPU_shader_create_from_info(reinterpret_cast<GPUShaderCreateInfo *>(&info));
return (shader.shader != nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Textures
* \{ */
static bool addGPUUniform(OCIO_GPUTextures &textures,
const GpuShaderDescRcPtr &shader_desc,
int index)
{
OCIO_GPUUniform uniform;
uniform.name = shader_desc->getUniform(index, uniform.data);
if (uniform.data.m_type == UNIFORM_UNKNOWN) {
return false;
}
textures.uniforms.push_back(uniform);
return true;
}
static bool addGPULut1D2D(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;
#if OCIO_VERSION_HEX >= 0x02030000
/* Always use 2D textures in OpenColorIO 2.3, simpler and same performance. */
GpuShaderDesc::TextureDimensions dimensions = GpuShaderDesc::TEXTURE_2D;
shader_desc->getTexture(
index, texture_name, sampler_name, width, height, channel, dimensions, interpolation);
#else
shader_desc->getTexture(
index, texture_name, sampler_name, width, height, channel, interpolation);
#endif
const float *values;
shader_desc->getTextureValues(index, values);
if (texture_name == nullptr || sampler_name == nullptr || width == 0 || height == 0 ||
values == nullptr)
{
return false;
}
eGPUTextureFormat format = (channel == GpuShaderCreator::TEXTURE_RGB_CHANNEL) ? GPU_RGB16F :
GPU_R16F;
OCIO_GPULutTexture lut;
#if OCIO_VERSION_HEX < 0x02030000
/* There does not appear to be an explicit way to check if a texture is 1D or 2D.
* It depends on more than height. So check instead by looking at the source. */
std::string sampler1D_name = std::string("sampler1D ") + sampler_name;
if (strstr(shader_desc->getShaderText(), sampler1D_name.c_str()) != nullptr) {
lut.texture = GPU_texture_create_1d(
texture_name, width, 1, format, GPU_TEXTURE_USAGE_SHADER_READ, values);
}
else
#endif
{
lut.texture = GPU_texture_create_2d(
texture_name, width, height, 1, format, GPU_TEXTURE_USAGE_SHADER_READ, values);
}
if (lut.texture == nullptr) {
return false;
}
GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST);
GPU_texture_extend_mode(lut.texture, GPU_SAMPLER_EXTEND_MODE_EXTEND);
lut.sampler_name = sampler_name;
textures.luts.push_back(lut);
return true;
}
static bool addGPULut3D(OCIO_GPUTextures &textures,
const GpuShaderDescRcPtr &shader_desc,
int index)
{
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_GPULutTexture lut;
lut.texture = GPU_texture_create_3d(texture_name,
edgelen,
edgelen,
edgelen,
1,
GPU_RGB16F,
GPU_TEXTURE_USAGE_SHADER_READ,
values);
if (lut.texture == nullptr) {
return false;
}
GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST);
GPU_texture_extend_mode(lut.texture, GPU_SAMPLER_EXTEND_MODE_EXTEND);
lut.sampler_name = sampler_name;
textures.luts.push_back(lut);
return true;
}
static bool createGPUTextures(OCIO_GPUTextures &textures,
const GpuShaderDescRcPtr &shaderdesc_to_scene_linear,
const GpuShaderDescRcPtr &shaderdesc_to_display)
{
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 (!addGPULut1D2D(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 (!addGPULut1D2D(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;
}
}
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Curve Mapping
* \{ */
static bool createGPUCurveMapping(OCIO_GPUCurveMappping &curvemap,
OCIO_CurveMappingSettings *curve_mapping_settings)
{
if (curve_mapping_settings) {
int lut_size = curve_mapping_settings->lut_size;
curvemap.texture = GPU_texture_create_1d(
"OCIOCurveMap", lut_size, 1, GPU_RGBA16F, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
GPU_texture_filter_mode(curvemap.texture, false);
GPU_texture_extend_mode(curvemap.texture, GPU_SAMPLER_EXTEND_MODE_EXTEND);
curvemap.buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUCurveMappingParameters));
if (curvemap.texture == nullptr || curvemap.buffer == nullptr) {
return false;
}
}
return true;
}
static void updateGPUCurveMapping(OCIO_GPUCurveMappping &curvemap,
OCIO_CurveMappingSettings *curve_mapping_settings)
{
/* 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;
}
curvemap.cache_id = curve_mapping_settings->cache_id;
/* Update texture. */
const int offset[3] = {0, 0, 0};
const 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));
/* Update uniforms. */
OCIO_GPUCurveMappingParameters data;
for (int i = 0; i < 4; i++) {
data.range[i] = curve_mapping_settings->range[i];
data.mintable[i] = curve_mapping_settings->mintable[i];
data.ext_in_x[i] = curve_mapping_settings->ext_in_x[i];
data.ext_in_y[i] = curve_mapping_settings->ext_in_y[i];
data.ext_out_x[i] = curve_mapping_settings->ext_out_x[i];
data.ext_out_y[i] = curve_mapping_settings->ext_out_y[i];
data.first_x[i] = curve_mapping_settings->first_x[i];
data.first_y[i] = curve_mapping_settings->first_y[i];
data.last_x[i] = curve_mapping_settings->last_x[i];
data.last_y[i] = curve_mapping_settings->last_y[i];
}
for (int i = 0; i < 3; i++) {
data.black[i] = curve_mapping_settings->black[i];
data.bwmul[i] = curve_mapping_settings->bwmul[i];
}
data.lut_size = curve_mapping_settings->lut_size;
data.use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate;
GPU_uniformbuf_update(curvemap.buffer, &data);
}
static void updateGPUDisplayParameters(OCIO_GPUShader &shader,
float scale,
float exponent,
float dither,
bool use_predivide,
bool use_overlay,
bool use_hdr)
{
bool do_update = false;
if (shader.parameters_buffer == nullptr) {
shader.parameters_buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUParameters));
do_update = true;
}
OCIO_GPUParameters &data = shader.parameters;
if (data.scale != scale) {
data.scale = scale;
do_update = true;
}
if (data.exponent != exponent) {
data.exponent = exponent;
do_update = true;
}
if (data.dither != dither) {
data.dither = dither;
do_update = true;
}
if (bool(data.use_predivide) != use_predivide) {
data.use_predivide = use_predivide;
do_update = true;
}
if (bool(data.use_overlay) != use_overlay) {
data.use_overlay = use_overlay;
do_update = true;
}
if (bool(data.use_hdr) != use_hdr) {
data.use_hdr = use_hdr;
do_update = true;
}
if (do_update) {
GPU_uniformbuf_update(shader.parameters_buffer, &data);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name OCIO GPU Shader Implementation
* \{ */
bool OCIOImpl::supportGPUShader()
{
/* Minimum supported version 3.3 does meet all requirements. */
return true;
}
static OCIO_GPUDisplayShader &getGPUDisplayShader(
OCIO_ConstConfigRcPtr *config,
const char *input,
const char *view,
const char *display,
const char *look,
OCIO_CurveMappingSettings *curve_mapping_settings)
{
/* 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);
}
return *it;
}
}
/* Remove least recently used element from cache. */
if (SHADER_CACHE.size() >= SHADER_CACHE_MAX_SIZE) {
SHADER_CACHE.pop_back();
}
/* 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, false);
/* 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");
shaderdesc_to_scene_linear->setResourcePrefix("to_scene");
(*(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");
shaderdesc_to_display->setResourcePrefix("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;
}
}
/* Free processors. */
if (processor_to_scene_linear) {
OCIO_processorRelease(processor_to_scene_linear);
}
if (processor_to_display) {
OCIO_processorRelease(processor_to_display);
}
return display_shader;
}
/**
* 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, gpuDisplayShaderUnbind must be called to
* restore GPU context to its previous state.
*/
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,
const bool use_hdr)
{
/* 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;
}
/* Verify the shader is valid. */
OCIO_GPUTextures &textures = display_shader.textures;
OCIO_GPUShader &shader = display_shader.shader;
OCIO_GPUCurveMappping &curvemap = display_shader.curvemap;
/* Update and bind curve mapping data. */
if (curve_mapping_settings) {
updateGPUCurveMapping(curvemap, curve_mapping_settings);
GPU_uniformbuf_bind(curvemap.buffer, UNIFORMBUF_SLOT_CURVEMAP);
GPU_texture_bind(curvemap.texture, TEXTURE_SLOT_CURVE_MAPPING);
}
/* 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);
}
if (textures.uniforms_buffer) {
GPU_uniformbuf_bind(textures.uniforms_buffer, UNIFORMBUF_SLOT_LUTS);
}
updateGPUDisplayParameters(shader, scale, exponent, dither, use_predivide, use_overlay, use_hdr);
GPU_uniformbuf_bind(shader.parameters_buffer, UNIFORMBUF_SLOT_DISPLAY);
/* TODO(fclem): remove remains of IMM. */
immBindShader(shader.shader);
return true;
}
void OCIOImpl::gpuDisplayShaderUnbind()
{
immUnbindProgram();
}
void OCIOImpl::gpuCacheFree()
{
SHADER_CACHE.clear();
}
/** \} */