Implement GPU-side dither
[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 <limits>
37 #include <sstream>
38 #include <string.h>
39
40 #include <GL/glew.h>
41
42 #include <OpenColorIO/OpenColorIO.h>
43
44 using namespace OCIO_NAMESPACE;
45
46 #include "MEM_guardedalloc.h"
47
48 #include "ocio_impl.h"
49
50 static const int LUT3D_EDGE_SIZE = 64;
51
52 extern "C" char datatoc_gpu_shader_display_transform_glsl[];
53
54 /* **** OpenGL drawing routines using GLSL for color space transform ***** */
55
56 typedef struct OCIO_GLSLDrawState {
57         bool lut3d_texture_allocated;  /* boolean flag indicating whether
58                                         * lut texture is allocated
59                                         */
60         bool lut3d_texture_valid;
61
62         GLuint lut3d_texture;  /* OGL texture ID for 3D LUT */
63
64         float *lut3d;  /* 3D LUT table */
65
66         bool dither_used;
67
68         bool curve_mapping_used;
69         bool curve_mapping_texture_allocated;
70         bool curve_mapping_texture_valid;
71         GLuint curve_mapping_texture;
72         size_t curve_mapping_cache_id;
73
74         /* Cache */
75         std::string lut3dcacheid;
76         std::string shadercacheid;
77
78         /* GLSL stuff */
79         GLuint ocio_shader;
80         GLuint program;
81
82         /* Previous OpenGL state. */
83         GLint last_texture, last_texture_unit;
84 } OCIO_GLSLDrawState;
85
86 static GLuint compileShaderText(GLenum shaderType, const char *text)
87 {
88         GLuint shader;
89         GLint stat;
90
91         shader = glCreateShader(shaderType);
92         glShaderSource(shader, 1, (const GLchar **) &text, NULL);
93         glCompileShader(shader);
94         glGetShaderiv(shader, GL_COMPILE_STATUS, &stat);
95
96         if (!stat) {
97                 GLchar log[1000];
98                 GLsizei len;
99                 glGetShaderInfoLog(shader, 1000, &len, log);
100                 fprintf(stderr, "Shader compile error:\n%s\n", log);
101                 return 0;
102         }
103
104         return shader;
105 }
106
107 static GLuint linkShaders(GLuint ocio_shader)
108 {
109         if (!ocio_shader)
110                 return 0;
111
112         GLuint program = glCreateProgram();
113
114         glAttachShader(program, ocio_shader);
115
116         glLinkProgram(program);
117
118         /* check link */
119         {
120                 GLint stat;
121                 glGetProgramiv(program, GL_LINK_STATUS, &stat);
122                 if (!stat) {
123                         GLchar log[1000];
124                         GLsizei len;
125                         glGetProgramInfoLog(program, 1000, &len, log);
126                         fprintf(stderr, "Shader link error:\n%s\n", log);
127                         return 0;
128                 }
129         }
130
131         return program;
132 }
133
134 static OCIO_GLSLDrawState *allocateOpenGLState(void)
135 {
136         OCIO_GLSLDrawState *state;
137
138         /* Allocate memory for state. */
139         state = (OCIO_GLSLDrawState *) MEM_callocN(sizeof(OCIO_GLSLDrawState),
140                                                    "OCIO OpenGL State struct");
141
142         /* Call constructors on new memory. */
143         new (&state->lut3dcacheid) std::string("");
144         new (&state->shadercacheid) std::string("");
145
146         return state;
147 }
148
149 /* Ensure LUT texture and array are allocated */
150 static bool ensureLUT3DAllocated(OCIO_GLSLDrawState *state)
151 {
152         int num_3d_entries = 3 * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE;
153
154         if (state->lut3d_texture_allocated)
155                 return state->lut3d_texture_valid;
156
157         glGenTextures(1, &state->lut3d_texture);
158
159         state->lut3d = (float *) MEM_callocN(sizeof(float) * num_3d_entries, "OCIO GPU 3D LUT");
160
161         glActiveTexture(GL_TEXTURE1);
162         glBindTexture(GL_TEXTURE_3D, state->lut3d_texture);
163         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
164         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
165         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
166         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
167         glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
168
169         /* clean glError buffer */
170         while (glGetError() != GL_NO_ERROR) {}
171
172         glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB,
173                      LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE,
174                      0, GL_RGB, GL_FLOAT, state->lut3d);
175
176         state->lut3d_texture_allocated = true;
177
178         /* GL_RGB16F_ARB could be not supported at some drivers
179          * in this case we could not use GLSL display
180          */
181         state->lut3d_texture_valid = glGetError() == GL_NO_ERROR;
182
183         return state->lut3d_texture_valid;
184 }
185
186 static bool ensureCurveMappingAllocated(OCIO_GLSLDrawState *state, OCIO_CurveMappingSettings *curve_mapping_settings)
187 {
188         if (state->curve_mapping_texture_allocated)
189                 return state->curve_mapping_texture_valid;
190
191         glGenTextures(1, &state->curve_mapping_texture);
192
193         glActiveTexture(GL_TEXTURE2);
194         glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture);
195         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
196         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
197         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
198         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
199         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
200
201         /* clean glError buffer */
202         while (glGetError() != GL_NO_ERROR) {}
203
204         glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16F_ARB, curve_mapping_settings->lut_size,
205                      0, GL_RGBA, GL_FLOAT, curve_mapping_settings->lut);
206
207         state->curve_mapping_texture_allocated = true;
208
209         /* GL_RGB16F_ARB could be not supported at some drivers
210          * in this case we could not use GLSL display
211          */
212         state->curve_mapping_texture_valid = glGetError() == GL_NO_ERROR;
213
214         return state->curve_mapping_texture_valid;
215 }
216
217 /* Detect if we can support GLSL drawing */
218 bool OCIOImpl::supportGLSLDraw()
219 {
220         /* GLSL and GL_RGB16F_ARB */
221         return GLEW_VERSION_2_0 && (GLEW_VERSION_3_0 || GLEW_ARB_texture_float);
222 }
223
224 /**
225  * Setup OpenGL contexts for a transform defined by processor using GLSL
226  * All LUT allocating baking and shader compilation happens here.
227  *
228  * Once this function is called, callee could start drawing images
229  * using regular 2D texture.
230  *
231  * When all drawing is finished, finishGLSLDraw shall be called to
232  * restore OpenGL context to it's pre-GLSL draw state.
233  */
234 bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor,
235                              OCIO_CurveMappingSettings *curve_mapping_settings,
236                              float dither, bool predivide)
237 {
238         ConstProcessorRcPtr ocio_processor = *(ConstProcessorRcPtr *) processor;
239         bool use_curve_mapping = curve_mapping_settings != NULL;
240         bool use_dither = dither > std::numeric_limits<float>::epsilon();
241
242         /* Create state if needed. */
243         OCIO_GLSLDrawState *state;
244         if (!*state_r)
245                 *state_r = allocateOpenGLState();
246         state = *state_r;
247
248         glGetIntegerv(GL_TEXTURE_2D, &state->last_texture);
249         glGetIntegerv(GL_ACTIVE_TEXTURE, &state->last_texture_unit);
250
251         if (!ensureLUT3DAllocated(state)) {
252                 glActiveTexture(state->last_texture_unit);
253                 glBindTexture(GL_TEXTURE_2D, state->last_texture);
254
255                 return false;
256         }
257
258         if (use_curve_mapping) {
259                 if (!ensureCurveMappingAllocated(state, curve_mapping_settings)) {
260                         glActiveTexture(state->last_texture_unit);
261                         glBindTexture(GL_TEXTURE_2D, state->last_texture);
262
263                         return false;
264                 }
265         }
266         else {
267                 if (state->curve_mapping_texture_allocated) {
268                         glDeleteTextures(1, &state->curve_mapping_texture);
269                         state->curve_mapping_texture_allocated = false;
270                 }
271         }
272
273         /* Step 1: Create a GPU Shader Description */
274         GpuShaderDesc shaderDesc;
275         shaderDesc.setLanguage(GPU_LANGUAGE_GLSL_1_3);
276         shaderDesc.setFunctionName("OCIODisplay");
277         shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE);
278
279         if (use_curve_mapping) {
280                 if (state->curve_mapping_cache_id != curve_mapping_settings->cache_id) {
281                         glActiveTexture(GL_TEXTURE2);
282                         glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture);
283                         glTexSubImage1D(GL_TEXTURE_1D, 0, 0, curve_mapping_settings->lut_size,
284                                         GL_RGBA, GL_FLOAT, curve_mapping_settings->lut);
285                 }
286         }
287
288         /* Step 2: Compute the 3D LUT */
289         std::string lut3dCacheID = ocio_processor->getGpuLut3DCacheID(shaderDesc);
290         if (lut3dCacheID != state->lut3dcacheid) {
291                 state->lut3dcacheid = lut3dCacheID;
292                 ocio_processor->getGpuLut3D(state->lut3d, shaderDesc);
293
294                 glActiveTexture(GL_TEXTURE1);
295                 glBindTexture(GL_TEXTURE_3D, state->lut3d_texture);
296                 glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0,
297                                 LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE,
298                                 GL_RGB, GL_FLOAT, state->lut3d);
299         }
300
301         /* Step 3: Compute the Shader */
302         std::string shaderCacheID = ocio_processor->getGpuShaderTextCacheID(shaderDesc);
303         if (state->program == 0 ||
304             shaderCacheID != state->shadercacheid ||
305             use_curve_mapping != state->curve_mapping_used ||
306             use_dither != state->dither_used)
307         {
308                 state->shadercacheid = shaderCacheID;
309
310                 if (state->program) {
311                         glDeleteProgram(state->program);
312                 }
313
314                 if (state->ocio_shader) {
315                         glDeleteShader(state->ocio_shader);
316                 }
317
318                 std::ostringstream os;
319
320                 os << "#version 130\n";
321
322                 if (use_dither) {
323                         os << "#define USE_DITHER\n";
324                 }
325
326                 if (use_curve_mapping) {
327                         os << "#define USE_CURVE_MAPPING\n";
328                 }
329
330                 os << ocio_processor->getGpuShaderText(shaderDesc) << "\n";
331                 os << datatoc_gpu_shader_display_transform_glsl;
332
333                 state->ocio_shader = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str());
334
335                 if (state->ocio_shader) {
336                         state->program = linkShaders(state->ocio_shader);
337                 }
338
339                 state->curve_mapping_used = use_curve_mapping;
340                 state->dither_used = use_dither;
341         }
342
343         if (state->program) {
344                 glActiveTexture(GL_TEXTURE1);
345                 glBindTexture(GL_TEXTURE_3D, state->lut3d_texture);
346
347                 if (use_curve_mapping) {
348                         glActiveTexture(GL_TEXTURE2);
349                         glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture);
350                 }
351
352                 glActiveTexture(GL_TEXTURE0);
353
354                 glUseProgram(state->program);
355
356                 glUniform1i(glGetUniformLocation(state->program, "image_texture"), 0);
357                 glUniform1i(glGetUniformLocation(state->program, "lut3d_texture"), 1);
358                 glUniform1i(glGetUniformLocation(state->program, "predivide"), predivide);
359
360                 if (use_dither) {
361                         glUniform1f(glGetUniformLocation(state->program, "dither"), dither);
362                 }
363
364                 if (use_curve_mapping) {
365                         glUniform1i(glGetUniformLocation(state->program, "curve_mapping_texture"), 2);
366                         glUniform1i(glGetUniformLocation(state->program, "curve_mapping_lut_size"), curve_mapping_settings->lut_size);
367                         glUniform4iv(glGetUniformLocation(state->program, "use_curve_mapping_extend_extrapolate"), 1, curve_mapping_settings->use_extend_extrapolate);
368                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_mintable"), 1, curve_mapping_settings->mintable);
369                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_range"), 1, curve_mapping_settings->range);
370                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_in_x"), 1, curve_mapping_settings->ext_in_x);
371                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_in_y"), 1, curve_mapping_settings->ext_in_y);
372                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_out_x"), 1, curve_mapping_settings->ext_out_x);
373                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_out_y"), 1, curve_mapping_settings->ext_out_y);
374                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_first_x"), 1, curve_mapping_settings->first_x);
375                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_first_y"), 1, curve_mapping_settings->first_y);
376                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_last_x"), 1, curve_mapping_settings->last_x);
377                         glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_last_y"), 1, curve_mapping_settings->last_y);
378                         glUniform3fv(glGetUniformLocation(state->program, "curve_mapping_black"), 1, curve_mapping_settings->black);
379                         glUniform3fv(glGetUniformLocation(state->program, "curve_mapping_bwmul"), 1, curve_mapping_settings->bwmul);
380                 }
381
382                 return true;
383         }
384         else {
385                 glActiveTexture(state->last_texture_unit);
386                 glBindTexture(GL_TEXTURE_2D, state->last_texture);
387
388                 return false;
389         }
390 }
391
392 void OCIOImpl::finishGLSLDraw(OCIO_GLSLDrawState *state)
393 {
394         glActiveTexture(state->last_texture_unit);
395         glBindTexture(GL_TEXTURE_2D, state->last_texture);
396         glUseProgram(0);
397 }
398
399 void OCIOImpl::freeGLState(struct OCIO_GLSLDrawState *state)
400 {
401         using std::string;
402
403         if (state->lut3d_texture_allocated)
404                 glDeleteTextures(1, &state->lut3d_texture);
405
406         if (state->lut3d)
407                 MEM_freeN(state->lut3d);
408
409         if (state->program)
410                 glDeleteProgram(state->program);
411
412         if (state->ocio_shader)
413                 glDeleteShader(state->ocio_shader);
414
415         state->lut3dcacheid.~string();
416         state->shadercacheid.~string();
417
418         MEM_freeN(state);
419 }