2af3befc8a935fe9d988e9946fc374416a96c87e
[blender-staging.git] / intern / opencolorio / ocio_impl_glsl.cc
1 /*
2  * Adapted from OpenColorIO with this license:
3  *
4  * Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
5  * All Rights Reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  * * Redistributions of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  * * Neither the name of Sony Pictures Imageworks nor the names of its
16  *   contributors may be used to endorse or promote products derived from
17  *   this software without specific prior written permission.
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * Modifications Copyright 2013, Blender Foundation.
31  *
32  * Contributor(s): Sergey Sharybin
33  *
34  */
35
36 #include <sstream>
37 #include <string.h>
38
39 #include <GL/glew.h>
40
41 #include <OpenColorIO/OpenColorIO.h>
42
43 using namespace OCIO_NAMESPACE;
44
45 #include "MEM_guardedalloc.h"
46
47 #include "ocio_impl.h"
48
49 static const int LUT3D_EDGE_SIZE = 64;
50
51 extern "C" char datatoc_gpu_shader_display_transform_glsl[];
52
53 /* **** OpenGL drawing routines using GLSL for color space transform ***** */
54
55 typedef struct OCIO_GLSLDrawState {
56         bool lut3d_texture_allocated;  /* boolean flag indicating whether
57                                         * lut texture is allocated
58                                         */
59         bool lut3d_texture_valid;
60
61         GLuint lut3d_texture;  /* OGL texture ID for 3D LUT */
62
63         float *lut3d;  /* 3D LUT table */
64
65         bool curve_mapping_used;
66         bool curve_mapping_texture_allocated;
67         bool curve_mapping_texture_valid;
68         GLuint curve_mapping_texture;
69         size_t curve_mapping_cache_id;
70
71         /* Cache */
72         std::string lut3dcacheid;
73         std::string shadercacheid;
74
75         /* GLSL stuff */
76         GLuint ocio_shader;
77         GLuint program;
78
79         /* Previous OpenGL state. */
80         GLint last_texture, last_texture_unit;
81 } OCIO_GLSLDrawState;
82
83 static GLuint compileShaderText(GLenum shaderType, const char *text)
84 {
85         GLuint shader;
86         GLint stat;
87
88         shader = glCreateShader(shaderType);
89         glShaderSource(shader, 1, (const GLchar **) &text, NULL);
90         glCompileShader(shader);
91         glGetShaderiv(shader, GL_COMPILE_STATUS, &stat);
92
93         if (!stat) {
94                 GLchar log[1000];
95                 GLsizei len;
96                 glGetShaderInfoLog(shader, 1000, &len, log);
97                 fprintf(stderr, "Shader compile error:\n%s\n", log);
98                 return 0;
99         }
100
101         return shader;
102 }
103
104 static GLuint linkShaders(GLuint ocio_shader)
105 {
106         if (!ocio_shader)
107                 return 0;
108
109         GLuint program = glCreateProgram();
110
111         glAttachShader(program, ocio_shader);
112
113         glLinkProgram(program);
114
115         /* check link */
116         {
117                 GLint stat;
118                 glGetProgramiv(program, GL_LINK_STATUS, &stat);
119                 if (!stat) {
120                         GLchar log[1000];
121                         GLsizei len;
122                         glGetProgramInfoLog(program, 1000, &len, log);
123                         fprintf(stderr, "Shader link error:\n%s\n", log);
124                         return 0;
125                 }
126         }
127
128         return program;
129 }
130
131 static OCIO_GLSLDrawState *allocateOpenGLState(void)
132 {
133         OCIO_GLSLDrawState *state;
134
135         /* Allocate memory for state. */
136         state = (OCIO_GLSLDrawState *) MEM_callocN(sizeof(OCIO_GLSLDrawState),
137                                                    "OCIO OpenGL State struct");
138
139         /* Call constructors on new memory. */
140         new (&state->lut3dcacheid) std::string("");
141         new (&state->shadercacheid) std::string("");
142
143         return state;
144 }
145
146 /* Ensure LUT texture and array are allocated */
147 static bool ensureLUT3DAllocated(OCIO_GLSLDrawState *state)
148 {
149         int num_3d_entries = 3 * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE;
150
151         if (state->lut3d_texture_allocated)
152                 return state->lut3d_texture_valid;
153
154         glGenTextures(1, &state->lut3d_texture);
155
156         state->lut3d = (float *) MEM_callocN(sizeof(float) * num_3d_entries, "OCIO GPU 3D LUT");
157
158         glActiveTexture(GL_TEXTURE1);
159         glBindTexture(GL_TEXTURE_3D, state->lut3d_texture);
160         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
161         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
162         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
163         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
164         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
165
166         /* clean glError buffer */
167         while (glGetError() != GL_NO_ERROR) {}
168
169         glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB,
170                      LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE,
171                      0, GL_RGB, GL_FLOAT, state->lut3d);
172
173         state->lut3d_texture_allocated = true;
174
175         /* GL_RGB16F_ARB could be not supported at some drivers
176          * in this case we could not use GLSL display
177          */
178         state->lut3d_texture_valid = glGetError() == GL_NO_ERROR;
179
180         return state->lut3d_texture_valid;
181 }
182
183 static bool ensureCurveMappingAllocated(OCIO_GLSLDrawState *state, OCIO_CurveMappingSettings *curve_mapping_settings)
184 {
185         if (state->curve_mapping_texture_allocated)
186                 return state->curve_mapping_texture_valid;
187
188         glGenTextures(1, &state->curve_mapping_texture);
189
190         glActiveTexture(GL_TEXTURE2);
191         glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture);
192         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
193         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
194         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
195         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
196         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
197
198         /* clean glError buffer */
199         while (glGetError() != GL_NO_ERROR) {}
200
201         glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16F_ARB, curve_mapping_settings->lut_size,
202                      0, GL_RGBA, GL_FLOAT, curve_mapping_settings->lut);
203
204         state->curve_mapping_texture_allocated = true;
205
206         /* GL_RGB16F_ARB could be not supported at some drivers
207          * in this case we could not use GLSL display
208          */
209         state->curve_mapping_texture_valid = glGetError() == GL_NO_ERROR;
210
211         return state->curve_mapping_texture_valid;
212 }
213
214 /* Detect if we can support GLSL drawing */
215 bool OCIOImpl::supportGLSLDraw()
216 {
217         /* GLSL and GL_RGB16F_ARB */
218         return GLEW_VERSION_2_0 && (GLEW_VERSION_3_0 || GLEW_ARB_texture_float);
219 }
220
221 /**
222  * Setup OpenGL contexts for a transform defined by processor using GLSL
223  * All LUT allocating baking and shader compilation happens here.
224  *
225  * Once this function is called, callee could start drawing images
226  * using regular 2D texture.
227  *
228  * When all drawing is finished, finishGLSLDraw shall be called to
229  * restore OpenGL context to it's pre-GLSL draw state.
230  */
231 bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor,
232                              OCIO_CurveMappingSettings *curve_mapping_settings, bool predivide)
233 {
234         ConstProcessorRcPtr ocio_processor = *(ConstProcessorRcPtr *) processor;
235         bool use_curve_mapping = curve_mapping_settings != NULL;
236
237         /* Create state if needed. */
238         OCIO_GLSLDrawState *state;
239         if (!*state_r)
240                 *state_r = allocateOpenGLState();
241         state = *state_r;
242
243         glGetIntegerv(GL_TEXTURE_2D, &state->last_texture);
244         glGetIntegerv(GL_ACTIVE_TEXTURE, &state->last_texture_unit);
245
246         if (!ensureLUT3DAllocated(state)) {
247                 glActiveTexture(state->last_texture_unit);
248                 glBindTexture(GL_TEXTURE_2D, state->last_texture);
249
250                 return false;
251         }
252
253         if (use_curve_mapping) {
254                 if (!ensureCurveMappingAllocated(state, curve_mapping_settings)) {
255                         glActiveTexture(state->last_texture_unit);
256                         glBindTexture(GL_TEXTURE_2D, state->last_texture);
257
258                         return false;
259                 }
260         }
261         else {
262                 if (state->curve_mapping_texture_allocated) {
263                         glDeleteTextures(1, &state->curve_mapping_texture);
264                         state->curve_mapping_texture_allocated = false;
265                 }
266         }
267
268         /* Step 1: Create a GPU Shader Description */
269         GpuShaderDesc shaderDesc;
270         shaderDesc.setLanguage(GPU_LANGUAGE_GLSL_1_0);
271         shaderDesc.setFunctionName("OCIODisplay");
272         shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE);
273
274         if (use_curve_mapping) {
275                 if (state->curve_mapping_cache_id != curve_mapping_settings->cache_id) {
276                         glActiveTexture(GL_TEXTURE2);
277                         glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture);
278                         glTexSubImage1D(GL_TEXTURE_1D, 0, 0, curve_mapping_settings->lut_size,
279                                         GL_RGBA, GL_FLOAT, curve_mapping_settings->lut);
280                 }
281         }
282
283         /* Step 2: Compute the 3D LUT */
284         std::string lut3dCacheID = ocio_processor->getGpuLut3DCacheID(shaderDesc);
285         if (lut3dCacheID != state->lut3dcacheid) {
286                 state->lut3dcacheid = lut3dCacheID;
287                 ocio_processor->getGpuLut3D(state->lut3d, shaderDesc);
288
289                 glActiveTexture(GL_TEXTURE1);
290                 glBindTexture(GL_TEXTURE_3D, state->lut3d_texture);
291                 glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0,
292                                 LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE,
293                                 GL_RGB, GL_FLOAT, state->lut3d);
294         }
295
296         /* Step 3: Compute the Shader */
297         std::string shaderCacheID = ocio_processor->getGpuShaderTextCacheID(shaderDesc);
298         if (state->program == 0 ||
299             shaderCacheID != state->shadercacheid ||
300             use_curve_mapping != state->curve_mapping_used)
301         {
302                 state->shadercacheid = shaderCacheID;
303
304                 if (state->program) {
305                         glDeleteProgram(state->program);
306                 }
307
308                 if (state->ocio_shader) {
309                         glDeleteShader(state->ocio_shader);
310                 }
311
312                 std::ostringstream os;
313
314                 if (use_curve_mapping) {
315                         os << "#define USE_CURVE_MAPPING\n";
316                 }
317
318                 os << ocio_processor->getGpuShaderText(shaderDesc) << "\n";
319                 os << datatoc_gpu_shader_display_transform_glsl;
320
321                 state->ocio_shader = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str());
322
323                 if (state->ocio_shader) {
324                         state->program = linkShaders(state->ocio_shader);
325                 }
326
327                 state->curve_mapping_used = use_curve_mapping;
328         }
329
330         if (state->program) {
331                 glActiveTexture(GL_TEXTURE1);
332                 glBindTexture(GL_TEXTURE_3D, state->lut3d_texture);
333
334                 if (use_curve_mapping) {
335                         glActiveTexture(GL_TEXTURE2);
336                         glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture);
337                 }
338
339                 glActiveTexture(GL_TEXTURE0);
340
341                 glUseProgram(state->program);
342
343                 glUniform1i(glGetUniformLocation(state->program, "image_texture"), 0);
344                 glUniform1i(glGetUniformLocation(state->program, "lut3d_texture"), 1);
345                 glUniform1i(glGetUniformLocation(state->program, "predivide"), predivide);
346
347                 if (use_curve_mapping) {
348                         glUniform1i(glGetUniformLocation(state->program, "curve_mapping_texture"), 2);
349                         glUniform1i(glGetUniformLocation(state->program, "curve_mapping_lut_size"), curve_mapping_settings->lut_size);
350                         glUniform4iv(glGetUniformLocation(state->program, "use_curve_mapping_extend_extrapolate"), 1, curve_mapping_settings->use_extend_extrapolate);
351                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_mintable"), 1, curve_mapping_settings->mintable);
352                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_range"), 1, curve_mapping_settings->range);
353                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_in_x"), 1, curve_mapping_settings->ext_in_x);
354                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_in_y"), 1, curve_mapping_settings->ext_in_y);
355                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_out_x"), 1, curve_mapping_settings->ext_out_x);
356                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_out_y"), 1, curve_mapping_settings->ext_out_y);
357                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_first_x"), 1, curve_mapping_settings->first_x);
358                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_first_y"), 1, curve_mapping_settings->first_y);
359                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_last_x"), 1, curve_mapping_settings->last_x);
360                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_last_y"), 1, curve_mapping_settings->last_y);
361                         glUniform3fv(glGetUniformLocation(state->program, "curve_mapping_black"), 1, curve_mapping_settings->black);
362                         glUniform3fv(glGetUniformLocation(state->program, "curve_mapping_bwmul"), 1, curve_mapping_settings->bwmul);
363                 }
364
365                 return true;
366         }
367         else {
368                 glActiveTexture(state->last_texture_unit);
369                 glBindTexture(GL_TEXTURE_2D, state->last_texture);
370
371                 return false;
372         }
373 }
374
375 void OCIOImpl::finishGLSLDraw(OCIO_GLSLDrawState *state)
376 {
377         glActiveTexture(state->last_texture_unit);
378         glBindTexture(GL_TEXTURE_2D, state->last_texture);
379         glUseProgram(0);
380 }
381
382 void OCIOImpl::freeGLState(struct OCIO_GLSLDrawState *state)
383 {
384         using std::string;
385
386         if (state->lut3d_texture_allocated)
387                 glDeleteTextures(1, &state->lut3d_texture);
388
389         if (state->lut3d)
390                 MEM_freeN(state->lut3d);
391
392         if (state->program)
393                 glDeleteProgram(state->program);
394
395         if (state->ocio_shader)
396                 glDeleteShader(state->ocio_shader);
397
398         state->lut3dcacheid.~string();
399         state->shadercacheid.~string();
400
401         MEM_freeN(state);
402 }