From 3e89f13feec8a993db78d2d6fa3afe763ea06611 Mon Sep 17 00:00:00 2001 From: Hannes Vernooij Date: Fri, 17 Nov 2023 10:17:32 +0100 Subject: [PATCH] fixed off-by-one error in the pixel height calculation It should be max index / max size because 4096 / 4096 + 1 = 2, which means that two rows are allocated when a LUT1D with a size of 4096 fits in a 1D texture of 4096 pixels. Signed-off-by: Hannes Vernooij --- src/OpenColorIO/ops/lut1d/Lut1DOpGPU.cpp | 35 ++++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/OpenColorIO/ops/lut1d/Lut1DOpGPU.cpp b/src/OpenColorIO/ops/lut1d/Lut1DOpGPU.cpp index 59091817da..7d1eeaa546 100644 --- a/src/OpenColorIO/ops/lut1d/Lut1DOpGPU.cpp +++ b/src/OpenColorIO/ops/lut1d/Lut1DOpGPU.cpp @@ -48,6 +48,11 @@ void CreatePaddedLutChannels(unsigned long width, leftover -= step; } + // Skip the duplicate entry if the last value is the last pixel on a row + if ((currWidth + (height -1)) % width == 0) { + leftover -= 1; + } + // If there are still texels to fill, add them to the texture data. if (leftover > 0) { @@ -111,6 +116,11 @@ void CreatePaddedRedChannel(unsigned long width, leftover -= step; } + // Skip the duplicate entry if the last value is the last pixel on a row + if ((currWidth + (height -1)) % width == 0) { + leftover -= 1; + } + // If there are still texels to fill, add them to the texture data. if (leftover > 0) { @@ -154,13 +164,27 @@ void GetLut1DGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, const unsigned long length = lutData->getArray().getLength(); const unsigned long width = std::min(length, defaultMaxWidth); - const unsigned long height = (length / defaultMaxWidth) + 1; + const unsigned long heightWithDuplicates = (length - 1) / (defaultMaxWidth - 1) + 1; + + // The height is calculated based on the amount of rows without duplicate data. + // ceil(length / defaultMaxWidth) + const unsigned long heightWithoutDuplicates = (std::max(2UL, length) - 1) / defaultMaxWidth + 1; + + // The result of this determines how many duplicate values are really needed. + // When the amount of rows is one, the amount of duplicate entries will be zero. + // ceil(length / defaultMaxWidth) -1) + const unsigned long numDuplicateEntries = heightWithoutDuplicates - 1; + + // Once we know the amount of duplicate entries, we can calculate if the last + // value ends on the same scanline, if that is the case we won't need an extra line. + // ((ceil(length / defaultMaxWidth) -1 + length) % defaultMaxWidth == 0) + const unsigned long height = heightWithDuplicates - ((numDuplicateEntries + length) % defaultMaxWidth == 0); const unsigned long numChannels = lutData->getArray().getNumColorComponents(); - // Note: The 1D LUT needs a GPU texture for the Look-up table implementation. + // Note: The 1D LUT needs a GPU texture for the Look-up table implementation. // However, the texture type & content may vary based on the number of channels // i.e. when all channels are identical a F32 Red GPU texture is enough. - + const bool singleChannel = (numChannels == 1); // Adjust LUT texture to allow for correct 2d linear interpolation, if needed. @@ -335,13 +359,13 @@ void GetLut1DGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, { const std::string str = name + "_computePos(" + shaderCreator->getPixelName(); - ss.newLine() << shaderCreator->getPixelName() << ".r = " + ss.newLine() << shaderCreator->getPixelName() << ".r = " << ss.sampleTex2D(name, str + ".r)") << ".r;"; ss.newLine() << shaderCreator->getPixelName() << ".g = " << ss.sampleTex2D(name, str + ".g)") << (singleChannel ? ".r;" : ".g;"); - ss.newLine() << shaderCreator->getPixelName() << ".b = " + ss.newLine() << shaderCreator->getPixelName() << ".b = " << ss.sampleTex2D(name, str + ".b)") << (singleChannel ? ".r;" : ".b;"); } else @@ -387,4 +411,3 @@ void GetLut1DGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, } } // namespace OCIO_NAMESPACE -