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