Blender V4.5
libocio_gpu_shader_binder.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#if defined(WITH_OPENCOLORIO)
8
9# include "GPU_texture.hh"
10
11# include "error_handling.hh"
12# include "libocio_config.hh"
14# include "libocio_processor.hh"
15
17
18namespace blender::ocio {
19
20namespace {
21
22using namespace OCIO_NAMESPACE;
23
24static ConstProcessorRcPtr create_to_scene_linear_processor(
25 const ConstConfigRcPtr &ocio_config, const internal::GPUDisplayShader &display_shader)
26{
27 return create_ocio_processor(
28 ocio_config, display_shader.from_colorspace.c_str(), ROLE_SCENE_LINEAR);
29}
30
31static ConstProcessorRcPtr create_to_display_processor(
32 const LibOCIOConfig &config, const internal::GPUDisplayShader &display_shader)
33{
34 DisplayParameters display_parameters;
35 display_parameters.from_colorspace = ROLE_SCENE_LINEAR;
36 display_parameters.view = display_shader.view;
37 display_parameters.display = display_shader.display;
38 display_parameters.look = display_shader.look;
39 return create_ocio_display_processor(config, display_parameters);
40}
41
42static bool add_gpu_uniform(internal::GPUTextures &textures,
43 const GpuShaderDescRcPtr &shader_desc,
44 const int index)
45{
47 uniform.name = shader_desc->getUniform(index, uniform.data);
48 if (uniform.data.m_type == UNIFORM_UNKNOWN) {
49 return false;
50 }
51
52 textures.uniforms.append(uniform);
53 return true;
54}
55
56static bool add_gpu_lut_1D2D(internal::GPUTextures &textures,
57 const GpuShaderDescRcPtr &shader_desc,
58 const int index)
59{
60 const char *texture_name = nullptr;
61 const char *sampler_name = nullptr;
62 uint width = 0;
63 uint height = 0;
64
65 GpuShaderCreator::TextureType channel = GpuShaderCreator::TEXTURE_RGB_CHANNEL;
66 Interpolation interpolation = INTERP_LINEAR;
67
68 /* Always use 2D textures in OpenColorIO 2.3, simpler and same performance. */
69 static_assert(OCIO_VERSION_HEX >= 0x02030000);
70 GpuShaderDesc::TextureDimensions dimensions = GpuShaderDesc::TEXTURE_2D;
71 shader_desc->getTexture(
72 index, texture_name, sampler_name, width, height, channel, dimensions, interpolation);
73
74 const float *values;
75 shader_desc->getTextureValues(index, values);
76 if (texture_name == nullptr || sampler_name == nullptr || width == 0 || height == 0 ||
77 values == nullptr)
78 {
79 return false;
80 }
81
82 eGPUTextureFormat format = (channel == GpuShaderCreator::TEXTURE_RGB_CHANNEL) ? GPU_RGB16F :
84
86 /* There does not appear to be an explicit way to check if a texture is 1D or 2D.
87 * It depends on more than height. So check instead by looking at the source.
88 * The Blender default config does not use 1D textures, but for example
89 * studio-config-v3.0.0_aces-v2.0_ocio-v2.4.ocio needs this code. */
90 std::string sampler1D_name = std::string("sampler1D ") + sampler_name;
91 if (strstr(shader_desc->getShaderText(), sampler1D_name.c_str()) != nullptr) {
92 lut.texture = GPU_texture_create_1d(
93 texture_name, width, 1, format, GPU_TEXTURE_USAGE_SHADER_READ, values);
94 }
95 else {
96 lut.texture = GPU_texture_create_2d(
97 texture_name, width, height, 1, format, GPU_TEXTURE_USAGE_SHADER_READ, values);
98 }
99 if (lut.texture == nullptr) {
100 return false;
101 }
102
103 GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST);
105
106 lut.sampler_name = sampler_name;
107
108 textures.luts.append(lut);
109
110 return true;
111}
112
113static bool add_gpu_lut_3D(internal::GPUTextures &textures,
114 const GpuShaderDescRcPtr &shader_desc,
115 const int index)
116{
117 const char *texture_name = nullptr;
118 const char *sampler_name = nullptr;
119 uint edgelen = 0;
120 Interpolation interpolation = INTERP_LINEAR;
121 shader_desc->get3DTexture(index, texture_name, sampler_name, edgelen, interpolation);
122
123 const float *values;
124 shader_desc->get3DTextureValues(index, values);
125 if (texture_name == nullptr || sampler_name == nullptr || edgelen == 0 || values == nullptr) {
126 return false;
127 }
128
130 lut.texture = GPU_texture_create_3d(texture_name,
131 edgelen,
132 edgelen,
133 edgelen,
134 1,
137 values);
138 if (lut.texture == nullptr) {
139 return false;
140 }
141
142 GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST);
144
145 lut.sampler_name = sampler_name;
146
147 textures.luts.append(lut);
148 return true;
149}
150
151static bool create_gpu_textures(internal::GPUTextures &textures,
152 const GpuShaderDescRcPtr &shader_desc)
153{
154 for (int index = 0; index < shader_desc->getNumUniforms(); index++) {
155 if (!add_gpu_uniform(textures, shader_desc, index)) {
156 return false;
157 }
158 }
159 for (int index = 0; index < shader_desc->getNumTextures(); index++) {
160 if (!add_gpu_lut_1D2D(textures, shader_desc, index)) {
161 return false;
162 }
163 }
164 for (int index = 0; index < shader_desc->getNum3DTextures(); index++) {
165 if (!add_gpu_lut_3D(textures, shader_desc, index)) {
166 return false;
167 }
168 }
169
170 return true;
171}
172
173} // namespace
174
175void LibOCIOGPUShaderBinder::construct_shader_for_processors(
176 internal::GPUDisplayShader &display_shader,
177 const ConstProcessorRcPtr &processor_to_scene_linear,
178 const ConstProcessorRcPtr &processor_to_display,
179 const Span<std::array<StringRefNull, 2>> additional_defines) const
180{
181 std::string fragment_source;
182
183 GpuShaderDescRcPtr shaderdesc_to_scene_linear;
184 if (processor_to_scene_linear) {
185 shaderdesc_to_scene_linear = GpuShaderDesc::CreateShaderDesc();
186 shaderdesc_to_scene_linear->setLanguage(GPU_LANGUAGE_GLSL_1_3);
187 shaderdesc_to_scene_linear->setFunctionName("OCIO_to_scene_linear");
188 shaderdesc_to_scene_linear->setResourcePrefix("to_scene");
189 processor_to_scene_linear->getDefaultGPUProcessor()->extractGpuShaderInfo(
190 shaderdesc_to_scene_linear);
191 shaderdesc_to_scene_linear->finalize();
192
193 if (!create_gpu_textures(display_shader.textures, shaderdesc_to_scene_linear)) {
194 display_shader.is_valid = false;
195 return;
196 }
197
198 fragment_source += shaderdesc_to_scene_linear->getShaderText();
199 fragment_source += "\n";
200 }
201
202 GpuShaderDescRcPtr shaderdesc_to_display;
203 if (processor_to_display) {
204 shaderdesc_to_display = GpuShaderDesc::CreateShaderDesc();
205 shaderdesc_to_display->setLanguage(GPU_LANGUAGE_GLSL_1_3);
206 shaderdesc_to_display->setFunctionName("OCIO_to_display");
207 shaderdesc_to_display->setResourcePrefix("to_display");
208 processor_to_display->getDefaultGPUProcessor()->extractGpuShaderInfo(shaderdesc_to_display);
209 shaderdesc_to_display->finalize();
210
211 if (!create_gpu_textures(display_shader.textures, shaderdesc_to_display)) {
212 display_shader.is_valid = false;
213 return;
214 }
215
216 fragment_source += shaderdesc_to_display->getShaderText();
217 fragment_source += "\n";
218 }
219
220 if (!create_gpu_shader(display_shader, fragment_source, additional_defines)) {
221 display_shader.is_valid = false;
222 return;
223 }
224
225 display_shader.is_valid = true;
226}
227
228void LibOCIOGPUShaderBinder::construct_display_shader(
229 internal::GPUDisplayShader &display_shader) const
230{
231 const LibOCIOConfig &config = static_cast<const LibOCIOConfig &>(config_);
232 const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config = config.get_ocio_config();
233
234 ConstProcessorRcPtr processor_to_scene_linear = create_to_scene_linear_processor(ocio_config,
235 display_shader);
236 ConstProcessorRcPtr processor_to_display = create_to_display_processor(config, display_shader);
237
238 if (!processor_to_scene_linear || !processor_to_display) {
239 display_shader.is_valid = false;
240 return;
241 }
242
243 construct_shader_for_processors(
244 display_shader, processor_to_scene_linear, processor_to_display, {});
245}
246
247void LibOCIOGPUShaderBinder::construct_scene_linear_shader(
248 internal::GPUDisplayShader &display_shader) const
249{
250 const LibOCIOConfig &config = static_cast<const LibOCIOConfig &>(config_);
251 const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config = config.get_ocio_config();
252
253 ConstProcessorRcPtr processor_to_scene_linear = create_to_scene_linear_processor(ocio_config,
254 display_shader);
255 if (!processor_to_scene_linear) {
256 display_shader.is_valid = false;
257 return;
258 }
259
260 construct_shader_for_processors(
261 display_shader, processor_to_scene_linear, nullptr, {{"USE_TO_SCENE_LINEAR_ONLY", ""}});
262}
263
264} // namespace blender::ocio
265
266#endif
unsigned int uint
GPUTexture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
GPUTexture * GPU_texture_create_1d(const char *name, int width, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_extend_mode(GPUTexture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_SAMPLER_EXTEND_MODE_EXTEND
GPUTexture * GPU_texture_create_3d(const char *name, int width, int height, int depth, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const void *data)
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
eGPUTextureFormat
@ GPU_R16F
@ GPU_RGB16F
format