Color management: Implement shader to convert to scene linear
Previous conversion to scene linear was done in the display transform shader. Having a separate shader to convert texture to scene linear allows drawing input textures with different color spaces into a viewport and apply display transform on all of them. Currently unused, but is required for !138094. It could also be used in the future to avoid host-side linearization in the image engine. Internally it uses a lot of the same logic for shader caching and binding, but the code is refactored a bit to make it easier to have a stronger separation in the future if needed. Ref #138094 Pull Request: https://projects.blender.org/blender/blender/pulls/138308
This commit is contained in:
committed by
Sergey Sharybin
parent
a26ed85adf
commit
5b29ba488f
@@ -333,9 +333,16 @@ bool OCIO_gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
use_white_balance);
|
||||
}
|
||||
|
||||
void OCIO_gpuDisplayShaderUnbind()
|
||||
bool OCIO_gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
const char *from_colorspace_name,
|
||||
const bool use_predivide)
|
||||
{
|
||||
impl->gpuDisplayShaderUnbind();
|
||||
return impl->gpuToSceneLinearShaderBind(config, from_colorspace_name, use_predivide);
|
||||
}
|
||||
|
||||
void OCIO_gpuShaderUnbind()
|
||||
{
|
||||
impl->gpuShaderUnbind();
|
||||
}
|
||||
|
||||
void OCIO_gpuCacheFree()
|
||||
|
||||
@@ -211,7 +211,10 @@ bool OCIO_gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
const bool use_overlay,
|
||||
const bool use_hdr,
|
||||
const bool use_white_balance);
|
||||
void OCIO_gpuDisplayShaderUnbind(void);
|
||||
bool OCIO_gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
const char *from_colorspace_name,
|
||||
bool use_predivide);
|
||||
void OCIO_gpuShaderUnbind(void);
|
||||
void OCIO_gpuCacheFree(void);
|
||||
|
||||
const char *OCIO_getVersionString(void);
|
||||
|
||||
@@ -128,7 +128,14 @@ class IOCIOImpl {
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual void gpuDisplayShaderUnbind() {}
|
||||
virtual bool gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr * /*config*/,
|
||||
const char * /*from_colorspace_name*/,
|
||||
const bool /*use_predivide*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void gpuShaderUnbind() {}
|
||||
virtual void gpuCacheFree() {}
|
||||
|
||||
virtual const char *getVersionString() = 0;
|
||||
@@ -339,6 +346,7 @@ class OCIOImpl : public IOCIOImpl {
|
||||
void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) override;
|
||||
|
||||
bool supportGPUShader() override;
|
||||
|
||||
/**
|
||||
* Setup GPU contexts for a transform defined by processor using GLSL.
|
||||
* All LUT allocating baking and shader compilation happens here.
|
||||
@@ -346,7 +354,7 @@ class OCIOImpl : public IOCIOImpl {
|
||||
* Once this function is called, callee could start drawing images
|
||||
* using regular 2D texture.
|
||||
*
|
||||
* When all drawing is finished, gpuDisplayShaderUnbind must be called to
|
||||
* When all drawing is finished, gpuShaderUnbind must be called to
|
||||
* restore GPU context to its previous state.
|
||||
*/
|
||||
bool gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
@@ -364,7 +372,21 @@ class OCIOImpl : public IOCIOImpl {
|
||||
const bool use_overlay,
|
||||
const bool use_hdr,
|
||||
const bool use_white_balance) override;
|
||||
void gpuDisplayShaderUnbind() override;
|
||||
|
||||
/**
|
||||
* Setup GPU contexts for a GPU-side transform form the given space to scene linear.
|
||||
*
|
||||
* Once this function is called, callee could start drawing images using regular 2D texture
|
||||
* (in the same way as GPU_SHADER_3D_IMAGE_COLOR immediate mode shader).
|
||||
*
|
||||
* When all drawing is finished, gpuShaderUnbind must be called to restore GPU context to its
|
||||
* previous state.
|
||||
*/
|
||||
bool gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
const char *from_colorspace_name,
|
||||
bool use_predivide) override;
|
||||
|
||||
void gpuShaderUnbind() override;
|
||||
void gpuCacheFree() override;
|
||||
|
||||
const char *getVersionString() override;
|
||||
|
||||
@@ -139,9 +139,19 @@ struct OCIO_GPUDisplayShader {
|
||||
|
||||
/** Error checking. */
|
||||
bool valid = false;
|
||||
|
||||
bool equals(const char *input,
|
||||
const char *view,
|
||||
const char *display,
|
||||
const char *look,
|
||||
const bool use_curve_mapping) const
|
||||
{
|
||||
return (this->input == input && this->view == view && this->display == display &&
|
||||
this->look == look && this->use_curve_mapping == use_curve_mapping);
|
||||
}
|
||||
};
|
||||
|
||||
static const int SHADER_CACHE_MAX_SIZE = 4;
|
||||
static const int SHADER_CACHE_MAX_SIZE = 8;
|
||||
std::list<OCIO_GPUDisplayShader> SHADER_CACHE;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -605,32 +615,33 @@ bool OCIOImpl::supportGPUShader()
|
||||
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)
|
||||
static OCIO_GPUDisplayShader *getGPUDisplayShaderFromCache(const char *input,
|
||||
const char *view,
|
||||
const char *display,
|
||||
const char *look,
|
||||
const bool use_curve_mapping)
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
if (it->equals(input, view, display, look, 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;
|
||||
return &(*it);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default-initialized OCIO_GPUDisplayShader and put it to cache.
|
||||
* The function ensures the cache has up to SHADER_CACHE_MAX_SIZE entries.
|
||||
*/
|
||||
static OCIO_GPUDisplayShader &gpuDisplayShaderCreateAndCache()
|
||||
{
|
||||
/* Remove least recently used element from cache. */
|
||||
if (SHADER_CACHE.size() >= SHADER_CACHE_MAX_SIZE) {
|
||||
SHADER_CACHE.pop_back();
|
||||
@@ -640,6 +651,60 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader(
|
||||
SHADER_CACHE.emplace_front();
|
||||
OCIO_GPUDisplayShader &display_shader = SHADER_CACHE.front();
|
||||
|
||||
return display_shader;
|
||||
}
|
||||
|
||||
static void createGPUShaderDescriptors(OCIO_GPUDisplayShader &display_shader,
|
||||
ConstProcessorRcPtr processor_to_scene_linear,
|
||||
ConstProcessorRcPtr processor_to_display,
|
||||
const bool use_curve_mapping)
|
||||
{
|
||||
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");
|
||||
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");
|
||||
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) &&
|
||||
createGPUShader(display_shader.shader,
|
||||
display_shader.textures,
|
||||
shaderdesc_to_scene_linear,
|
||||
shaderdesc_to_display,
|
||||
use_curve_mapping))
|
||||
{
|
||||
display_shader.valid = 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)
|
||||
{
|
||||
const bool use_curve_mapping = (curve_mapping_settings != nullptr);
|
||||
|
||||
/* Find existing shader in cache. */
|
||||
OCIO_GPUDisplayShader *cached_shader = getGPUDisplayShaderFromCache(
|
||||
input, view, display, look, use_curve_mapping);
|
||||
if (cached_shader) {
|
||||
return *cached_shader;
|
||||
}
|
||||
|
||||
OCIO_GPUDisplayShader &display_shader = gpuDisplayShaderCreateAndCache();
|
||||
display_shader.input = input;
|
||||
display_shader.view = view;
|
||||
display_shader.display = display;
|
||||
@@ -668,35 +733,13 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader(
|
||||
|
||||
/* 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;
|
||||
createGPUShaderDescriptors(display_shader,
|
||||
*(ConstProcessorRcPtr *)processor_to_scene_linear,
|
||||
*(ConstProcessorRcPtr *)processor_to_display,
|
||||
use_curve_mapping);
|
||||
if (display_shader.valid) {
|
||||
display_shader.valid &= createGPUCurveMapping(display_shader.curvemap,
|
||||
curve_mapping_settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,25 +754,62 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader(
|
||||
return display_shader;
|
||||
}
|
||||
|
||||
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 float temperature,
|
||||
const float tint,
|
||||
const bool use_predivide,
|
||||
const bool use_overlay,
|
||||
const bool use_hdr,
|
||||
const bool use_white_balance)
|
||||
static OCIO_GPUDisplayShader &getGPUToLinearDisplayShader(OCIO_ConstConfigRcPtr *config,
|
||||
const char *input)
|
||||
{
|
||||
/* Find existing shader in cache.
|
||||
* Assume that empty names for display, view, and look are not valid for OCIO configuration, and
|
||||
* so they can be used to indicate that the processor is used to convert from the given space to
|
||||
* the linear. */
|
||||
/* TODO(sergey): Using separate storage for to-linear processor caches might be better. */
|
||||
OCIO_GPUDisplayShader *cached_shader = getGPUDisplayShaderFromCache(input, "", "", "", false);
|
||||
if (cached_shader) {
|
||||
return *cached_shader;
|
||||
}
|
||||
|
||||
OCIO_GPUDisplayShader &display_shader = gpuDisplayShaderCreateAndCache();
|
||||
display_shader.input = input;
|
||||
display_shader.use_curve_mapping = false;
|
||||
display_shader.valid = false;
|
||||
|
||||
OCIO_ConstProcessorRcPtr *processor_to_scene_linear = OCIO_configGetProcessorWithNames(
|
||||
config, input, ROLE_SCENE_LINEAR);
|
||||
OCIO_ConstProcessorRcPtr *processor_to_display = OCIO_configGetProcessorWithNames(
|
||||
config, input, input);
|
||||
|
||||
/* Create shader descriptions. */
|
||||
if (processor_to_scene_linear && processor_to_display) {
|
||||
createGPUShaderDescriptors(display_shader,
|
||||
*(ConstProcessorRcPtr *)processor_to_scene_linear,
|
||||
*(ConstProcessorRcPtr *)processor_to_display,
|
||||
false);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Bind the shader and update parameters and uniforms. */
|
||||
static bool gpuShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
OCIO_GPUDisplayShader &display_shader,
|
||||
OCIO_CurveMappingSettings *curve_mapping_settings,
|
||||
const float scale,
|
||||
const float exponent,
|
||||
const float dither,
|
||||
const float temperature,
|
||||
const float tint,
|
||||
const bool use_predivide,
|
||||
const bool use_overlay,
|
||||
const bool use_hdr,
|
||||
const bool use_white_balance)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
@@ -764,7 +844,7 @@ bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
if (use_white_balance) {
|
||||
/* Compute white point of the scene space in XYZ.*/
|
||||
float3x3 xyz_to_scene;
|
||||
configGetXYZtoSceneLinear(config, xyz_to_scene.ptr());
|
||||
OCIO_configGetXYZtoSceneLinear(config, xyz_to_scene.ptr());
|
||||
float3x3 scene_to_xyz = blender::math::invert(xyz_to_scene);
|
||||
float3 target = scene_to_xyz * float3(1.0f);
|
||||
|
||||
@@ -786,7 +866,63 @@ bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
return true;
|
||||
}
|
||||
|
||||
void OCIOImpl::gpuDisplayShaderUnbind()
|
||||
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 float temperature,
|
||||
const float tint,
|
||||
const bool use_predivide,
|
||||
const bool use_overlay,
|
||||
const bool use_hdr,
|
||||
const bool use_white_balance)
|
||||
{
|
||||
/* Get GPU shader from cache or create new one. */
|
||||
OCIO_GPUDisplayShader &display_shader = getGPUDisplayShader(
|
||||
config, input, view, display, look, curve_mapping_settings);
|
||||
|
||||
return gpuShaderBind(config,
|
||||
display_shader,
|
||||
curve_mapping_settings,
|
||||
scale,
|
||||
exponent,
|
||||
dither,
|
||||
temperature,
|
||||
tint,
|
||||
use_predivide,
|
||||
use_overlay,
|
||||
use_hdr,
|
||||
use_white_balance);
|
||||
}
|
||||
|
||||
bool OCIOImpl::gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr *config,
|
||||
const char *from_colorspace_name,
|
||||
const bool use_predivide)
|
||||
{
|
||||
/* Get GPU shader from cache or create new one. */
|
||||
OCIO_GPUDisplayShader &display_shader = getGPUToLinearDisplayShader(config,
|
||||
from_colorspace_name);
|
||||
|
||||
return gpuShaderBind(config,
|
||||
display_shader,
|
||||
nullptr, /* curve_mapping_settings */
|
||||
1.0f, /* scale */
|
||||
1.0f, /* exponent */
|
||||
0.0f, /* dither */
|
||||
6500.0f, /* temperature */
|
||||
10.0f, /* tint */
|
||||
use_predivide,
|
||||
false /* use_overlay */,
|
||||
true, /* use_hdr */
|
||||
false /* use_white_balance */);
|
||||
}
|
||||
|
||||
void OCIOImpl::gpuShaderUnbind()
|
||||
{
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
@@ -486,6 +486,16 @@ bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C,
|
||||
ColorSpace *from_colorspace,
|
||||
float dither,
|
||||
bool predivide);
|
||||
|
||||
/**
|
||||
* Configures GPU shader for conversion from the given space to scene linear.
|
||||
* Drawing happens in the same immediate mode as when GPU_SHADER_3D_IMAGE_COLOR shader is used.
|
||||
*
|
||||
* Returns true if the GPU shader was successfully bound.
|
||||
*/
|
||||
bool IMB_colormanagement_setup_glsl_draw_to_scene_linear(const char *from_colorspace_name,
|
||||
bool predivide);
|
||||
|
||||
/**
|
||||
* Finish GLSL-based display space conversion.
|
||||
*/
|
||||
|
||||
@@ -4285,10 +4285,23 @@ bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bo
|
||||
return IMB_colormanagement_setup_glsl_draw_from_space_ctx(C, nullptr, dither, predivide);
|
||||
}
|
||||
|
||||
bool IMB_colormanagement_setup_glsl_draw_to_scene_linear(const char *from_colorspace_name,
|
||||
const bool predivide)
|
||||
{
|
||||
OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
|
||||
|
||||
global_gpu_state.gpu_shader_bound = OCIO_gpuToSceneLinearShaderBind(
|
||||
config, from_colorspace_name, predivide);
|
||||
|
||||
OCIO_configRelease(config);
|
||||
|
||||
return global_gpu_state.gpu_shader_bound;
|
||||
}
|
||||
|
||||
void IMB_colormanagement_finish_glsl_draw()
|
||||
{
|
||||
if (global_gpu_state.gpu_shader_bound) {
|
||||
OCIO_gpuDisplayShaderUnbind();
|
||||
OCIO_gpuShaderUnbind();
|
||||
global_gpu_state.gpu_shader_bound = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user