Warning, file /graphics/krita/plugins/dockers/lut/ocio_display_filter_vfx2021.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Boudewijn Rempt <boud@valdyas.org> 0003 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "ocio_display_filter_vfx2021.h" 0009 0010 #include <QMessageBox> 0011 #include <QOpenGLContext> 0012 #include <QOpenGLExtraFunctions> 0013 #include <QOpenGLFunctions_2_0> 0014 #include <QOpenGLFunctions_3_0> 0015 #include <QOpenGLFunctions_3_2_Core> 0016 0017 #include <cmath> 0018 #include <cstring> 0019 0020 #include <kis_config.h> 0021 #include <kis_debug.h> 0022 #include <opengl/kis_opengl.h> 0023 0024 #if defined(QT_OPENGL_ES_2) 0025 #define GL_RGBA32F_ARB GL_RGBA32F_EXT 0026 #define GL_RGB32F_ARB GL_RGB32F_EXT 0027 #endif 0028 0029 #if defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_ES_3) 0030 #define GL_R32F GL_R32F_EXT 0031 #define GL_RED GL_RED_EXT 0032 #define GL_TEXTURE_WRAP_R GL_TEXTURE_WRAP_R_OES 0033 #endif 0034 0035 #include "kis_context_thread_locale.h" 0036 0037 OcioDisplayFilter::OcioDisplayFilter(KisExposureGammaCorrectionInterface *interface, QObject *parent) 0038 : KisDisplayFilter(parent) 0039 , inputColorSpaceName(0) 0040 , displayDevice(0) 0041 , view(0) 0042 , look(0) 0043 , swizzle(RGBA) 0044 , m_interface(interface) 0045 , m_lut3dTexIDs() 0046 , m_lut3dUniforms() 0047 , m_shaderDirty(true) 0048 { 0049 } 0050 0051 OcioDisplayFilter::~OcioDisplayFilter() 0052 { 0053 } 0054 0055 KisExposureGammaCorrectionInterface *OcioDisplayFilter::correctionInterface() const 0056 { 0057 return m_interface; 0058 } 0059 0060 void OcioDisplayFilter::filter(quint8 *pixels, quint32 numPixels) 0061 { 0062 // processes that data _in_ place 0063 if (m_processor) { 0064 if (numPixels > 16) { 0065 // creation of PackedImageDesc is really slow on Windows due to malloc/free 0066 OCIO::PackedImageDesc img(reinterpret_cast<float *>(pixels), numPixels, 1, 4); 0067 m_processorCPU->apply(img); 0068 } else { 0069 for (quint32 i = 0; i < numPixels; i++) { 0070 m_processorCPU->applyRGBA(reinterpret_cast<float*>(pixels)); 0071 pixels+=4; 0072 } 0073 } 0074 } 0075 } 0076 0077 void OcioDisplayFilter::approximateInverseTransformation(quint8 *pixels, quint32 numPixels) 0078 { 0079 // processes that data _in_ place 0080 if (m_reverseApproximationProcessor) { 0081 if (numPixels > 16) { 0082 // creation of PackedImageDesc is really slow on Windows due to malloc/free 0083 OCIO::PackedImageDesc img(reinterpret_cast<float *>(pixels), numPixels, 1, 4); 0084 m_reverseApproximationProcessorCPU->apply(img); 0085 } else { 0086 for (quint32 i = 0; i < numPixels; i++) { 0087 m_reverseApproximationProcessorCPU->applyRGBA(reinterpret_cast<float*>(pixels)); 0088 pixels+=4; 0089 } 0090 } 0091 } 0092 } 0093 0094 void OcioDisplayFilter::approximateForwardTransformation(quint8 *pixels, quint32 numPixels) 0095 { 0096 // processes that data _in_ place 0097 if (m_forwardApproximationProcessor) { 0098 if (numPixels > 16) { 0099 // creation of PackedImageDesc is really slow on Windows due to malloc/free 0100 OCIO::PackedImageDesc img(reinterpret_cast<float *>(pixels), numPixels, 1, 4); 0101 m_forwardApproximationProcessorCPU->apply(img); 0102 } else { 0103 for (quint32 i = 0; i < numPixels; i++) { 0104 m_forwardApproximationProcessorCPU->applyRGBA(reinterpret_cast<float*>(pixels)); 0105 pixels+=4; 0106 } 0107 } 0108 } 0109 } 0110 0111 bool OcioDisplayFilter::useInternalColorManagement() const 0112 { 0113 return forceInternalColorManagement; 0114 } 0115 0116 bool OcioDisplayFilter::lockCurrentColorVisualRepresentation() const 0117 { 0118 return m_lockCurrentColorVisualRepresentation; 0119 } 0120 0121 void OcioDisplayFilter::setLockCurrentColorVisualRepresentation(bool value) 0122 { 0123 m_lockCurrentColorVisualRepresentation = value; 0124 } 0125 0126 QString OcioDisplayFilter::program() const 0127 { 0128 return m_program; 0129 } 0130 0131 void OcioDisplayFilter::updateProcessor() 0132 { 0133 if (!config) { 0134 return; 0135 } 0136 0137 if (!displayDevice) { 0138 displayDevice = config->getDefaultDisplay(); 0139 } 0140 0141 if (!view) { 0142 view = config->getDefaultView(displayDevice); 0143 } 0144 0145 if (!inputColorSpaceName) { 0146 inputColorSpaceName = config->getColorSpaceNameByIndex(0); 0147 } 0148 if (!look) { 0149 look = config->getLookNameByIndex(0); 0150 } 0151 0152 if (!displayDevice || !view || !inputColorSpaceName) { 0153 return; 0154 } 0155 0156 OCIO::DisplayViewTransformRcPtr transform = OCIO::DisplayViewTransform::Create(); 0157 transform->setSrc(inputColorSpaceName); 0158 transform->setDisplay(displayDevice); 0159 transform->setView(view); 0160 0161 OCIO::LegacyViewingPipelineRcPtr vpt = OCIO::LegacyViewingPipeline::Create(); 0162 0163 vpt->setDisplayViewTransform(transform); 0164 0165 /** 0166 * Look support: 0167 * As the OCIO docs will tell you, looks are a aesthetic transform that is 0168 * added onto the mix. 0169 * A view+display can have it's own assigned Look, or list of looks, and these 0170 * can be overridden optionally. 0171 * What the OCIO docs won't tell you is that a display transform won't use the 0172 * looks attached to it unless "skipColorSpaceConversions" is false... 0173 * I have no idea what "skipColorSpaceConversions" is beyond what it says on the 0174 * tin. It is not mentioned in the documentation anywhere. Or on the website. 0175 * Or how to set it. Or unset it. Why it is apparently set true to begin with. 0176 * Only that, apparently, this was done with non-color data in mind... 0177 * 0178 * Until there's clear documentation on how to use this feature, I am afraid the 0179 * override is all we can offer. 0180 */ 0181 if (config->getLook(look)) { 0182 vpt->setLooksOverride(look); 0183 vpt->setLooksOverrideEnabled(true); 0184 } 0185 0186 OCIO::GroupTransformRcPtr approximateTransform = OCIO::GroupTransform::Create(); 0187 0188 // fstop exposure control -- not sure how that translates to our exposure 0189 { 0190 const double exposureGain = pow(2.0, exposure); 0191 0192 const double minRange = 0.001; 0193 if (qAbs(blackPoint - whitePoint) < minRange) { 0194 whitePoint = blackPoint + minRange; 0195 } 0196 0197 const double oldMin[] = {blackPoint, blackPoint, blackPoint, 0.0}; 0198 const double oldMax[] = {whitePoint, whitePoint, whitePoint, 1.0}; 0199 0200 const double newMin[] = {0.0, 0.0, 0.0, 0.0}; 0201 const double newMax[] = {exposureGain, exposureGain, exposureGain, 1.0}; 0202 0203 double m44[16]; 0204 double offset4[4]; 0205 OCIO::MatrixTransform::Fit(m44, offset4, oldMin, oldMax, newMin, newMax); 0206 OCIO::MatrixTransformRcPtr mtx = OCIO::MatrixTransform::Create(); 0207 mtx->setMatrix(m44); 0208 mtx->setOffset(offset4); 0209 vpt->setLinearCC(mtx); 0210 0211 // approximation (no color correction); 0212 approximateTransform->appendTransform(mtx); 0213 } 0214 0215 // channel swizzle 0216 { 0217 int channelHot[4]; 0218 switch (swizzle) { 0219 case LUMINANCE: 0220 channelHot[0] = 1; 0221 channelHot[1] = 1; 0222 channelHot[2] = 1; 0223 channelHot[3] = 0; 0224 break; 0225 case RGBA: 0226 channelHot[0] = 1; 0227 channelHot[1] = 1; 0228 channelHot[2] = 1; 0229 channelHot[3] = 1; 0230 break; 0231 case R: 0232 channelHot[0] = 1; 0233 channelHot[1] = 0; 0234 channelHot[2] = 0; 0235 channelHot[3] = 0; 0236 break; 0237 case G: 0238 channelHot[0] = 0; 0239 channelHot[1] = 1; 0240 channelHot[2] = 0; 0241 channelHot[3] = 0; 0242 break; 0243 case B: 0244 channelHot[0] = 0; 0245 channelHot[1] = 0; 0246 channelHot[2] = 1; 0247 channelHot[3] = 0; 0248 break; 0249 case A: 0250 channelHot[0] = 0; 0251 channelHot[1] = 0; 0252 channelHot[2] = 0; 0253 channelHot[3] = 1; 0254 default:; 0255 } 0256 double lumacoef[3]; 0257 config->getDefaultLumaCoefs(lumacoef); 0258 double m44[16]; 0259 double offset[4]; 0260 OCIO::MatrixTransform::View(m44, offset, channelHot, lumacoef); 0261 OCIO::MatrixTransformRcPtr swizzleTransform = OCIO::MatrixTransform::Create(); 0262 swizzleTransform->setMatrix(m44); 0263 swizzleTransform->setOffset(offset); 0264 vpt->setChannelView(swizzleTransform); 0265 } 0266 0267 // Post-display transform gamma 0268 { 0269 double exponent = 1.0 / std::max(1e-6, gamma); 0270 const double exponent4f[] = {exponent, exponent, exponent, exponent}; 0271 OCIO::ExponentTransformRcPtr expTransform = OCIO::ExponentTransform::Create(); 0272 expTransform->setValue(exponent4f); 0273 vpt->setDisplayCC(expTransform); 0274 0275 // approximation (no color correction); 0276 approximateTransform->appendTransform(expTransform); 0277 } 0278 0279 try { 0280 AutoSetAndRestoreThreadLocale l; 0281 m_processor = vpt->getProcessor(config, config->getCurrentContext()); 0282 m_processorCPU = m_processor->getDefaultCPUProcessor(); 0283 } catch (OCIO::Exception &e) { 0284 // XXX: How to not break the OCIO shader now? 0285 errKrita << "OCIO exception while parsing the current context:" << e.what(); 0286 m_shaderDirty = false; 0287 return; 0288 } 0289 0290 m_forwardApproximationProcessor = config->getProcessor(approximateTransform, OCIO::TRANSFORM_DIR_FORWARD); 0291 m_forwardApproximationProcessorCPU = m_forwardApproximationProcessor->getDefaultCPUProcessor(); 0292 0293 try { 0294 m_reverseApproximationProcessor = config->getProcessor(approximateTransform, OCIO::TRANSFORM_DIR_INVERSE); 0295 m_reverseApproximationProcessorCPU = m_reverseApproximationProcessor->getDefaultCPUProcessor(); 0296 } catch (...) { 0297 warnKrita << "OCIO inverted matrix does not exist!"; 0298 // m_reverseApproximationProcessor; 0299 } 0300 0301 m_shaderDirty = true; 0302 } 0303 0304 bool OcioDisplayFilter::updateShader() 0305 { 0306 if (KisOpenGL::hasOpenGLES()) { 0307 QOpenGLContext *ctx = QOpenGLContext::currentContext(); 0308 0309 KIS_ASSERT_RECOVER_RETURN_VALUE(ctx, false); 0310 0311 if (ctx->format().majorVersion() >= 3) { 0312 QOpenGLExtraFunctions *f = ctx->extraFunctions(); 0313 if (f) { 0314 return updateShaderImpl(f); 0315 } 0316 } else if (ctx->hasExtension("GL_OES_texture_float") 0317 && (ctx->hasExtension("GL_EXT_texture_storage") || ctx->hasExtension("EXT_color_buffer_float")) 0318 && ctx->hasExtension("GL_OES_texture_float_linear")) { 0319 QOpenGLExtraFunctions *f = ctx->extraFunctions(); 0320 if (f) { 0321 return updateShaderImpl(f); 0322 } 0323 } else { 0324 dbgKrita << "OcioDisplayFilter::updateShader" 0325 << "OpenGL ES v2+ support detected but no OES_texture_float," 0326 "GL_EXT_color_buffer_float or GL_EXT_texture_storage, or GL_OES_texture_float_linear were found"; 0327 return false; 0328 } 0329 #if defined(QT_OPENGL_3) 0330 } else if (KisOpenGL::hasOpenGL3()) { 0331 QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>(); 0332 if (f) { 0333 return updateShaderImpl(f); 0334 } 0335 #endif 0336 } 0337 0338 // XXX This option can be removed once we move to Qt 5.7+ 0339 if (KisOpenGL::supportsLoD()) { 0340 #if defined(QT_OPENGL_3) 0341 #if defined(Q_OS_MAC) && defined(QT_OPENGL_3_2) 0342 QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>(); 0343 #else 0344 QOpenGLFunctions_3_0 *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_0>(); 0345 #endif 0346 if (f) { 0347 return updateShaderImpl(f); 0348 } 0349 #endif 0350 } 0351 #if !defined(QT_OPENGL_ES_2) 0352 QOpenGLFunctions_2_0 *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_0>(); 0353 if (f) { 0354 return updateShaderImpl(f); 0355 } 0356 #endif 0357 0358 return false; 0359 } 0360 0361 template<class F> 0362 bool OcioDisplayFilter::updateShaderImpl(F *f) 0363 { 0364 // check whether we are allowed to use shaders -- though that should 0365 // work for everyone these days 0366 KisConfig cfg(true); 0367 if (!cfg.useOpenGL()) 0368 return false; 0369 0370 if (!m_shaderDirty) 0371 return false; 0372 0373 if (!f) { 0374 qWarning() << "Failed to get valid OpenGL functions for OcioDisplayFilter!"; 0375 return false; 0376 } 0377 0378 f->initializeOpenGLFunctions(); 0379 0380 bool shouldRecompileShader = false; 0381 0382 // Step 1: Create a GPU Shader Description 0383 OCIO::GpuShaderDescRcPtr shaderDesc = OCIO::GpuShaderDesc::CreateShaderDesc(); 0384 0385 #if OCIO_VERSION_HEX >= 0x2010100 || OCIO_VERSION_HEX >= 0x2020000 0386 if (KisOpenGL::supportsLoD()) { 0387 shaderDesc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_3_0); 0388 } else { 0389 shaderDesc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_1_0); 0390 } 0391 #else 0392 if (KisOpenGL::supportsLoD()) { 0393 shaderDesc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_3); 0394 } else { 0395 shaderDesc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_2); 0396 } 0397 #endif 0398 0399 shaderDesc->setFunctionName("OCIODisplay"); 0400 shaderDesc->setResourcePrefix("ocio_"); 0401 0402 // Step 2: Compute the 3D LUT 0403 #if OCIO_VERSION_HEX >= 0x2010100 || OCIO_VERSION_HEX >= 0x2020000 0404 // ensure the new GPU pipeline is used with our GLES3 patch 0405 // this way users won't run into errors when using Angle along with OCIO 0406 const auto gpu = m_processor->getOptimizedGPUProcessor(OCIO::OptimizationFlags::OPTIMIZATION_DEFAULT); 0407 #else 0408 const int lut3DEdgeSize = cfg.ocioLutEdgeSize(); 0409 const auto gpu = 0410 m_processor->getOptimizedLegacyGPUProcessor(OCIO::OptimizationFlags::OPTIMIZATION_DEFAULT, lut3DEdgeSize); 0411 #endif 0412 0413 gpu->extractGpuShaderInfo(shaderDesc); 0414 0415 // OCIO v2 assumes you'll use the OglApp helpers 0416 // these are unusable from a Qt backend, because they rely on GLUT/GLFW 0417 // ociodisplay original pipeline: 0418 // https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/508b3f4a0618435aeed2f45058208bdfa99e0887/src/apps/ociodisplay/main.cpp 0419 // ociodisplay new pipeline is a single call: 0420 // https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/ffddc3341f5775c7866fe2c93275e1d5e0b0540f/src/apps/ociodisplay/main.cpp#L427 0421 // we need to replicate this loop: 0422 // https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/dd59baf555656e09f52c3838e85ccf154497ec1d/src/libutils/oglapphelpers/oglapp.cpp#L191-L223 0423 // calls functions from here: 0424 // https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/dd59baf555656e09f52c3838e85ccf154497ec1d/src/libutils/oglapphelpers/glsl.cpp 0425 0426 for (const auto &tex : m_lut3dTexIDs) { 0427 f->glDeleteTextures(1, &tex.m_uid); 0428 } 0429 0430 m_lut3dTexIDs.clear(); 0431 0432 // This is the first available index for the textures. 0433 unsigned currIndex = 1; 0434 0435 // Process the 3D LUT first. 0436 0437 const unsigned maxTexture3D = shaderDesc->getNum3DTextures(); 0438 for (unsigned idx = 0; idx < maxTexture3D; ++idx) { 0439 // 1. Get the information of the 3D LUT. 0440 0441 const char *textureName = nullptr; 0442 const char *samplerName = nullptr; 0443 unsigned edgelen = 0; 0444 OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR; 0445 shaderDesc->get3DTexture(idx, textureName, samplerName, edgelen, interpolation); 0446 0447 if (!textureName || !*textureName || !samplerName || !*samplerName || edgelen == 0) { 0448 errOpenGL << "The texture data is corrupted"; 0449 return false; 0450 } 0451 0452 const float *values = nullptr; 0453 shaderDesc->get3DTextureValues(idx, values); 0454 if (!values) { 0455 errOpenGL << "The texture values are missing"; 0456 return false; 0457 } 0458 0459 // 2. Allocate the 3D LUT. 0460 0461 unsigned texId = 0; 0462 { 0463 if (values == nullptr) { 0464 errOpenGL << "3D LUT" << idx << "Missing texture data"; 0465 return false; 0466 } 0467 0468 f->glGenTextures(1, &texId); 0469 0470 f->glActiveTexture(GL_TEXTURE0 + currIndex); 0471 0472 f->glBindTexture(GL_TEXTURE_3D, texId); 0473 0474 { 0475 if (interpolation == OCIO::INTERP_NEAREST) { 0476 f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 0477 f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 0478 } else { 0479 f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 0480 f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 0481 } 0482 0483 f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 0484 f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 0485 f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 0486 } 0487 0488 f->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB32F_ARB, edgelen, edgelen, edgelen, 0, GL_RGB, GL_FLOAT, values); 0489 } 0490 0491 // 3. Keep the texture id & name for the later enabling. 0492 0493 m_lut3dTexIDs.push_back({texId, textureName, samplerName, GL_TEXTURE_3D}); 0494 0495 currIndex++; 0496 } 0497 0498 // Process the 1D LUTs. 0499 0500 const unsigned maxTexture2D = shaderDesc->getNumTextures(); 0501 for (unsigned idx = 0; idx < maxTexture2D; ++idx) { 0502 // 1. Get the information of the 1D LUT. 0503 0504 const char *textureName = nullptr; 0505 const char *samplerName = nullptr; 0506 unsigned width = 0; 0507 unsigned height = 0; 0508 OCIO::GpuShaderDesc::TextureType channel = OCIO::GpuShaderDesc::TEXTURE_RGB_CHANNEL; 0509 OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR; 0510 0511 #if OCIO_VERSION_HEX >= 0x2030000 0512 OCIO::GpuShaderCreator::TextureDimensions dimensions; 0513 shaderDesc->getTexture(idx, textureName, samplerName, width, height, channel, dimensions, interpolation); 0514 #else 0515 shaderDesc->getTexture(idx, textureName, samplerName, width, height, channel, interpolation); 0516 #endif 0517 0518 if (!textureName || !*textureName || !samplerName || !*samplerName || width == 0) { 0519 errOpenGL << "The texture data is corrupted"; 0520 return false; 0521 } 0522 0523 const float *values = nullptr; 0524 shaderDesc->getTextureValues(idx, values); 0525 if (!values) { 0526 errOpenGL << "The texture values are missing"; 0527 return false; 0528 } 0529 0530 // 2. Allocate the 1D LUT (a 2D texture is needed to hold large LUTs). 0531 0532 unsigned texId = 0; 0533 { 0534 if (values == nullptr) { 0535 errOpenGL << "1D LUT" << idx << "Missing texture data."; 0536 return false; 0537 } 0538 0539 unsigned internalformat = GL_RGB32F_ARB; 0540 unsigned format = GL_RGB; 0541 0542 if (channel == OCIO::GpuShaderCreator::TEXTURE_RED_CHANNEL) { 0543 internalformat = GL_R32F; 0544 format = GL_RED; 0545 } 0546 0547 f->glGenTextures(1, &texId); 0548 0549 f->glActiveTexture(GL_TEXTURE0 + currIndex); 0550 0551 #if OCIO_VERSION_HEX >= 0x2010100 || OCIO_VERSION_HEX >= 0x2020000 0552 #else 0553 // 1D Textures are unsupported by OpenGL ES. 0554 // https://github.com/AcademySoftwareFoundation/OpenColorIO/issues/1486 0555 if (height > 1) { 0556 #endif 0557 f->glBindTexture(GL_TEXTURE_2D, texId); 0558 0559 { 0560 if (interpolation == OCIO::INTERP_NEAREST) { 0561 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 0562 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 0563 } else { 0564 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 0565 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 0566 } 0567 0568 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 0569 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 0570 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 0571 } 0572 0573 f->glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, GL_FLOAT, values); 0574 #if OCIO_VERSION_HEX >= 0x2010100 || OCIO_VERSION_HEX >= 0x2020000 0575 #else 0576 } else { 0577 errOpenGL << "1D texture detected @" << idx << ", not supported by OpenGLES"; 0578 return false; 0579 } 0580 #endif 0581 } 0582 0583 // 3. Keep the texture id & name for the later enabling. 0584 0585 unsigned type = GL_TEXTURE_2D; 0586 m_lut3dTexIDs.push_back({texId, textureName, samplerName, type}); 0587 currIndex++; 0588 } 0589 0590 // Step 3: Generate the shader text 0591 QString shaderCacheID = QString::fromLatin1(shaderDesc->getCacheID()); 0592 if (m_program.isEmpty() || shaderCacheID != m_shadercacheid) { 0593 // dbgKrita << "Computing Shader " << m_shadercacheid; 0594 0595 m_shadercacheid = shaderCacheID; 0596 0597 m_program = QString::fromLatin1("%1\n").arg(shaderDesc->getShaderText()); 0598 shouldRecompileShader = true; 0599 } 0600 0601 // Step 4: mirror and bind uniforms 0602 m_lut3dUniforms.clear(); 0603 0604 const unsigned maxUniforms = shaderDesc->getNumUniforms(); 0605 for (unsigned idx = 0; idx < maxUniforms; ++idx) { 0606 OCIO::GpuShaderDesc::UniformData data; 0607 const char *name = shaderDesc->getUniform(idx, data); 0608 if (data.m_type == OCIO::UNIFORM_UNKNOWN) { 0609 errOpenGL << "Uniform" << idx << "has an unknown type"; 0610 return false; 0611 } 0612 // Transfer uniform. 0613 m_lut3dUniforms.push_back({name, data}); 0614 } 0615 0616 m_shaderDirty = false; 0617 return shouldRecompileShader; 0618 } 0619 0620 void OcioDisplayFilter::setupTextures(GLFunctions *f, QOpenGLShaderProgram *program) const 0621 { 0622 for (unsigned int idx = 0; idx < m_lut3dTexIDs.size(); ++idx) { 0623 const auto &data = m_lut3dTexIDs[idx]; 0624 f->glActiveTexture(GL_TEXTURE0 + 1 + idx); 0625 f->glBindTexture(data.m_type, data.m_uid); 0626 program->setUniformValue(program->uniformLocation(data.m_samplerName), GLint(1 + idx)); 0627 } 0628 0629 for (const KisTextureUniform &uniform : m_lut3dUniforms) { 0630 const int m_handle = program->uniformLocation(uniform.m_name); 0631 0632 const OCIO::GpuShaderDesc::UniformData &m_data = uniform.m_data; 0633 0634 // Update value. 0635 if (m_data.m_getDouble) { 0636 program->setUniformValue(m_handle, static_cast<GLfloat>(m_data.m_getDouble())); 0637 } else if (m_data.m_getBool) { 0638 program->setUniformValue(m_handle, static_cast<GLfloat>(m_data.m_getBool() ? 1.0f : 0.0f)); 0639 } else if (m_data.m_getFloat3) { 0640 program->setUniformValue(m_handle, 0641 m_data.m_getFloat3()[0], 0642 m_data.m_getFloat3()[1], 0643 m_data.m_getFloat3()[2]); 0644 } else if (m_data.m_vectorFloat.m_getSize && m_data.m_vectorFloat.m_getVector) { 0645 program->setUniformValueArray(m_handle, 0646 m_data.m_vectorFloat.m_getVector(), 0647 m_data.m_vectorFloat.m_getSize(), 0648 1); 0649 } else if (m_data.m_vectorInt.m_getSize && m_data.m_vectorInt.m_getVector) { 0650 program->setUniformValueArray(m_handle, m_data.m_vectorInt.m_getVector(), m_data.m_vectorInt.m_getSize()); 0651 } else { 0652 errOpenGL << "Uniform" << uniform.m_name << "is not linked to any value"; 0653 continue; 0654 } 0655 } 0656 }