From 62c3ded1f868c5e68b9fc04c8f4f4a0c8c088226 Mon Sep 17 00:00:00 2001 From: Aiden Date: Tue, 5 May 2026 23:47:08 +1000 Subject: [PATCH] Font working --- README.md | 3 + .../OpenGLComposite.cpp | 154 +++++++++++++++--- .../OpenGLComposite.h | 1 + .../RuntimeHost.cpp | 56 ++++++- .../ShaderCompiler.cpp | 11 +- image.png | Bin 0 -> 48830 bytes shaders/text-overlay/shader.slang | 9 +- ui/src/components/ParameterField.jsx | 1 + ui/src/hooks/useThrottledParameterValue.js | 21 +-- 9 files changed, 220 insertions(+), 36 deletions(-) create mode 100644 image.png diff --git a/README.md b/README.md index 8ca92cf..809f01d 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,10 @@ Fonts genlock Logs anamorphic desqueeze +solid color layer refactor, cleanup of source files display URL (Maybe clicakable) for control in the windows app (Not on the output) Sound shader as seperate .slang in shader package? runtime date time +Add a value control to the color wheels +![alt text](image.png) \ No newline at end of file diff --git a/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.cpp index cd8f1fa..cfa3a2c 100644 --- a/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,31 @@ const char* kDecodeFragmentShaderSource = " fragColor = rec709YCbCr2rgba(ySample, macroPixel.b, macroPixel.r, 1.0);\n" "}\n"; +class GdiplusSession +{ +public: + GdiplusSession() + { + Gdiplus::GdiplusStartupInput startupInput; + mStarted = Gdiplus::GdiplusStartup(&mToken, &startupInput, NULL) == Gdiplus::Ok; + } + + ~GdiplusSession() + { + if (mStarted) + Gdiplus::GdiplusShutdown(mToken); + } + + GdiplusSession(const GdiplusSession&) = delete; + GdiplusSession& operator=(const GdiplusSession&) = delete; + + bool started() const { return mStarted; } + +private: + ULONG_PTR mToken = 0; + bool mStarted = false; +}; + void CopyErrorMessage(const std::string& message, int errorMessageSize, char* errorMessage) { if (!errorMessage || errorMessageSize <= 0) @@ -191,11 +217,87 @@ std::vector BuildLocalSdf(const std::vector& alpha return sdf; } +std::vector FlipTextTextureForShaderUv(const std::vector& pixels, unsigned width, unsigned height) +{ + std::vector flipped(pixels.size(), 0); + const std::size_t stride = static_cast(width) * 4; + for (unsigned y = 0; y < height; ++y) + { + const std::size_t srcOffset = static_cast(y) * stride; + const std::size_t dstOffset = static_cast(height - 1 - y) * stride; + std::memcpy(flipped.data() + dstOffset, pixels.data() + srcOffset, stride); + } + return flipped; +} + +void WriteTextMaskDebugDump(const std::string& text, const std::vector& alpha, const std::vector& sdf, unsigned width, unsigned height) +{ + try + { + std::filesystem::path debugDir = std::filesystem::current_path() / "runtime"; + std::filesystem::create_directories(debugDir); + + auto writePgm = [width, height](const std::filesystem::path& path, const std::vector& gray, std::size_t stride) + { + std::ofstream out(path, std::ios::binary); + if (!out) + return; + out << "P5\n" << width << " " << height << "\n255\n"; + for (unsigned y = 0; y < height; ++y) + { + for (unsigned x = 0; x < width; ++x) + out.put(static_cast(gray[(static_cast(y) * width + x) * stride])); + } + }; + + writePgm(debugDir / "text-mask-alpha-debug.pgm", alpha, 1); + writePgm(debugDir / "text-mask-sdf-debug.pgm", sdf, 4); + + unsigned alphaMin = 255; + unsigned alphaMax = 0; + unsigned sdfMin = 255; + unsigned sdfMax = 0; + std::size_t alphaLit = 0; + std::size_t sdfLit = 0; + for (unsigned char value : alpha) + { + alphaMin = std::min(alphaMin, value); + alphaMax = std::max(alphaMax, value); + if (value > 0) + ++alphaLit; + } + for (std::size_t index = 0; index < sdf.size(); index += 4) + { + const unsigned char value = sdf[index]; + sdfMin = std::min(sdfMin, value); + sdfMax = std::max(sdfMax, value); + if (value > 127) + ++sdfLit; + } + + std::ostringstream message; + message << "Text mask debug for '" << text << "': alpha min/max/lit=" << alphaMin << "/" << alphaMax << "/" << alphaLit + << ", sdf min/max/gt127=" << sdfMin << "/" << sdfMax << "/" << sdfLit << "\n"; + OutputDebugStringA(message.str().c_str()); + } + catch (...) + { + OutputDebugStringA("Failed to write text mask debug dump.\n"); + } +} + +GLint FindSamplerUniformLocation(GLuint program, const std::string& samplerName) +{ + GLint location = glGetUniformLocation(program, samplerName.c_str()); + if (location >= 0) + return location; + return glGetUniformLocation(program, (samplerName + "_0").c_str()); +} + bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& fontPath, std::vector& sdf, std::string& error) { - ULONG_PTR gdiplusToken = 0; - Gdiplus::GdiplusStartupInput startupInput; - if (Gdiplus::GdiplusStartup(&gdiplusToken, &startupInput, NULL) != Gdiplus::Ok) + GdiplusSession gdiplus; + if (!gdiplus.started()) { error = "Could not start GDI+ for text rendering."; return false; @@ -210,7 +312,6 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font { if (fontCollection.AddFontFile(wideFontPath.c_str()) != Gdiplus::Ok) { - Gdiplus::GdiplusShutdown(gdiplusToken); error = "Could not load packaged font file for text rendering: " + fontPath.string(); return false; } @@ -218,7 +319,6 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font const INT familyCount = fontCollection.GetFamilyCount(); if (familyCount <= 0) { - Gdiplus::GdiplusShutdown(gdiplusToken); error = "Packaged font did not contain a usable font family: " + fontPath.string(); return false; } @@ -227,7 +327,6 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font INT found = 0; if (fontCollection.GetFamilies(familyCount, families.get(), &found) != Gdiplus::Ok || found <= 0) { - Gdiplus::GdiplusShutdown(gdiplusToken); error = "Could not read the packaged font family: " + fontPath.string(); return false; } @@ -236,7 +335,9 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font Gdiplus::Bitmap bitmap(kTextTextureWidth, kTextTextureHeight, PixelFormat32bppARGB); Gdiplus::Graphics graphics(&bitmap); - graphics.Clear(Gdiplus::Color(0, 0, 0, 0)); + graphics.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + graphics.Clear(Gdiplus::Color(255, 0, 0, 0)); + graphics.SetCompositingMode(Gdiplus::CompositingModeSourceOver); graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAliasGridFit); graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality); Gdiplus::Font font(fontFamily, 72.0f, Gdiplus::FontStyleRegular, Gdiplus::UnitPixel); @@ -256,11 +357,17 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font { Gdiplus::Color pixel; bitmap.GetPixel(x, y, &pixel); - alpha[static_cast(y) * kTextTextureWidth + x] = pixel.GetAlpha(); + BYTE luminance = pixel.GetRed(); + if (pixel.GetGreen() > luminance) + luminance = pixel.GetGreen(); + if (pixel.GetBlue() > luminance) + luminance = pixel.GetBlue(); + alpha[static_cast(y) * kTextTextureWidth + x] = static_cast(luminance); } } sdf = BuildLocalSdf(alpha, kTextTextureWidth, kTextTextureHeight); - Gdiplus::GdiplusShutdown(gdiplusToken); + sdf = FlipTextTextureForShaderUv(sdf, kTextTextureWidth, kTextTextureHeight); + WriteTextMaskDebugDump(text, alpha, sdf, kTextTextureWidth, kTextTextureHeight); return true; } @@ -1670,7 +1777,7 @@ bool OpenGLComposite::compileSingleLayerProgram(const RuntimeRenderState& state, glUniformBlockBinding(newProgram.get(), globalParamsIndex, kGlobalParamsBindingPoint); const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0; - const GLuint shaderTextureBase = kSourceHistoryTextureUnitBase + historyCap + historyCap; + const GLuint shaderTextureBase = state.isTemporal ? kSourceHistoryTextureUnitBase + historyCap + historyCap : kSourceHistoryTextureUnitBase; glUseProgram(newProgram.get()); const GLint videoInputLocation = glGetUniformLocation(newProgram.get(), "gVideoInput"); if (videoInputLocation >= 0) @@ -1689,14 +1796,14 @@ bool OpenGLComposite::compileSingleLayerProgram(const RuntimeRenderState& state, } for (std::size_t index = 0; index < textureBindings.size(); ++index) { - const GLint textureSamplerLocation = glGetUniformLocation(newProgram.get(), textureBindings[index].samplerName.c_str()); + const GLint textureSamplerLocation = FindSamplerUniformLocation(newProgram.get(), textureBindings[index].samplerName); if (textureSamplerLocation >= 0) glUniform1i(textureSamplerLocation, static_cast(shaderTextureBase + static_cast(index))); } const GLuint textTextureBase = shaderTextureBase + static_cast(textureBindings.size()); for (std::size_t index = 0; index < textBindings.size(); ++index) { - const GLint textSamplerLocation = glGetUniformLocation(newProgram.get(), textBindings[index].samplerName.c_str()); + const GLint textSamplerLocation = FindSamplerUniformLocation(newProgram.get(), textBindings[index].samplerName); if (textSamplerLocation >= 0) glUniform1i(textSamplerLocation, static_cast(textTextureBase + static_cast(index))); } @@ -1704,6 +1811,7 @@ bool OpenGLComposite::compileSingleLayerProgram(const RuntimeRenderState& state, layerProgram.layerId = state.layerId; layerProgram.shaderId = state.shaderId; + layerProgram.shaderTextureBase = shaderTextureBase; layerProgram.program = newProgram.release(); layerProgram.vertexShader = newVertexShader.release(); layerProgram.fragmentShader = newFragmentShader.release(); @@ -1972,9 +2080,18 @@ bool OpenGLComposite::renderTextBindingTexture(const RuntimeRenderState& state, if (!RasterizeTextSdf(text, fontPath, sdf, error)) return false; + GLint previousActiveTexture = 0; + GLint previousUnpackBuffer = 0; + glGetIntegerv(GL_ACTIVE_TEXTURE, &previousActiveTexture); + glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &previousUnpackBuffer); + glActiveTexture(GL_TEXTURE0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, textBinding.texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTextTextureWidth, kTextTextureHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, sdf.data()); glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, static_cast(previousUnpackBuffer)); + glActiveTexture(static_cast(previousActiveTexture)); + textBinding.renderedText = text; textBinding.renderedWidth = kTextTextureWidth; textBinding.renderedHeight = kTextTextureHeight; @@ -1983,8 +2100,7 @@ bool OpenGLComposite::renderTextBindingTexture(const RuntimeRenderState& state, void OpenGLComposite::bindLayerTextureAssets(const LayerProgram& layerProgram) { - const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0; - const GLuint shaderTextureBase = kSourceHistoryTextureUnitBase + historyCap + historyCap; + const GLuint shaderTextureBase = layerProgram.shaderTextureBase != 0 ? layerProgram.shaderTextureBase : kSourceHistoryTextureUnitBase; for (std::size_t index = 0; index < layerProgram.textureBindings.size(); ++index) { glActiveTexture(GL_TEXTURE0 + shaderTextureBase + static_cast(index)); @@ -2023,7 +2139,7 @@ void OpenGLComposite::destroyDecodeShaderProgram() bool OpenGLComposite::validateTemporalTextureUnitBudget(const std::vector& layerStates, std::string& error) const { const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0; - unsigned maxAssetTextures = 0; + unsigned requiredUnits = kSourceHistoryTextureUnitBase; for (const RuntimeRenderState& state : layerStates) { unsigned textTextureCount = 0; @@ -2033,12 +2149,12 @@ bool OpenGLComposite::validateTemporalTextureUnitBudget(const std::vector(state.textureAssets.size()) + textTextureCount; - if (totalShaderTextures > maxAssetTextures) - maxAssetTextures = totalShaderTextures; + const unsigned layerRequiredUnits = kSourceHistoryTextureUnitBase + (state.isTemporal ? historyCap + historyCap : 0u) + totalShaderTextures; + if (layerRequiredUnits > requiredUnits) + requiredUnits = layerRequiredUnits; } GLint maxTextureUnits = 0; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits); - const unsigned requiredUnits = kSourceHistoryTextureUnitBase + historyCap + historyCap + maxAssetTextures; const unsigned availableUnits = maxTextureUnits > 0 ? static_cast(maxTextureUnits) : 0u; if (requiredUnits > availableUnits) { @@ -2327,7 +2443,7 @@ void OpenGLComposite::renderShaderProgram(GLuint sourceTexture, GLuint destinati glActiveTexture(GL_TEXTURE0 + kSourceHistoryTextureUnitBase + historyCap + index); glBindTexture(GL_TEXTURE_2D, 0); } - const GLuint shaderTextureBase = kSourceHistoryTextureUnitBase + historyCap + historyCap; + const GLuint shaderTextureBase = layerProgram.shaderTextureBase != 0 ? layerProgram.shaderTextureBase : kSourceHistoryTextureUnitBase; for (std::size_t index = 0; index < layerProgram.textureBindings.size() + layerProgram.textBindings.size(); ++index) { glActiveTexture(GL_TEXTURE0 + shaderTextureBase + static_cast(index)); diff --git a/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.h b/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.h index 2936f62..3be8815 100644 --- a/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.h +++ b/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.h @@ -180,6 +180,7 @@ private: std::string layerId; std::string shaderId; + GLuint shaderTextureBase = 0; GLuint program = 0; GLuint vertexShader = 0; GLuint fragmentShader = 0; diff --git a/apps/LoopThroughWithOpenGLCompositing/RuntimeHost.cpp b/apps/LoopThroughWithOpenGLCompositing/RuntimeHost.cpp index 8272b21..fdcc916 100644 --- a/apps/LoopThroughWithOpenGLCompositing/RuntimeHost.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/RuntimeHost.cpp @@ -1571,8 +1571,62 @@ void RuntimeHost::EnsureLayerDefaultsLocked(LayerPersistentState& layerState, co { for (const ShaderParameterDefinition& definition : shaderPackage.parameters) { - if (layerState.parameterValues.find(definition.id) == layerState.parameterValues.end()) + auto valueIt = layerState.parameterValues.find(definition.id); + if (valueIt == layerState.parameterValues.end()) + { layerState.parameterValues[definition.id] = DefaultValueForDefinition(definition); + continue; + } + + JsonValue valueJson; + bool shouldNormalize = true; + switch (definition.type) + { + case ShaderParameterType::Float: + if (valueIt->second.numberValues.empty()) + shouldNormalize = false; + else + valueJson = JsonValue(valueIt->second.numberValues.front()); + break; + case ShaderParameterType::Vec2: + case ShaderParameterType::Color: + valueJson = JsonValue::MakeArray(); + for (double number : valueIt->second.numberValues) + valueJson.pushBack(JsonValue(number)); + break; + case ShaderParameterType::Boolean: + valueJson = JsonValue(valueIt->second.booleanValue); + break; + case ShaderParameterType::Enum: + valueJson = JsonValue(valueIt->second.enumValue); + break; + case ShaderParameterType::Text: + { + const std::string textValue = !valueIt->second.textValue.empty() + ? valueIt->second.textValue + : valueIt->second.enumValue; + if (textValue.empty()) + { + valueIt->second = DefaultValueForDefinition(definition); + shouldNormalize = false; + } + else + { + valueJson = JsonValue(textValue); + } + break; + } + } + + if (!shouldNormalize) + continue; + + ShaderParameterValue normalizedValue; + std::string normalizeError; + if (NormalizeAndValidateValue(definition, valueJson, normalizedValue, normalizeError)) + valueIt->second = normalizedValue; + else + valueIt->second = DefaultValueForDefinition(definition); } } diff --git a/apps/LoopThroughWithOpenGLCompositing/ShaderCompiler.cpp b/apps/LoopThroughWithOpenGLCompositing/ShaderCompiler.cpp index a015ccc..36cf4ac 100644 --- a/apps/LoopThroughWithOpenGLCompositing/ShaderCompiler.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/ShaderCompiler.cpp @@ -100,6 +100,8 @@ std::string BuildTextHelpers(const std::vector& param source << "float sample" << suffix << "(float2 uv)\n" << "{\n" + << "\tif (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)\n" + << "\t\treturn 0.0;\n" << "\treturn " << definition.id << "Texture.Sample(uv).r;\n" << "}\n\n" << "float4 draw" << suffix << "(float2 uv, float4 fillColor)\n" @@ -166,13 +168,14 @@ bool ShaderCompiler::BuildWrapperSlangSource(const ShaderPackage& shaderPackage, return false; wrapperSource = ReplaceAll(wrapperSource, "{{PARAMETER_UNIFORMS}}", BuildParameterUniforms(shaderPackage.parameters)); - wrapperSource = ReplaceAll(wrapperSource, "{{SOURCE_HISTORY_SAMPLERS}}", BuildHistorySamplerDeclarations("gSourceHistory", mMaxTemporalHistoryFrames)); - wrapperSource = ReplaceAll(wrapperSource, "{{TEMPORAL_HISTORY_SAMPLERS}}", BuildHistorySamplerDeclarations("gTemporalHistory", mMaxTemporalHistoryFrames)); + const unsigned historySamplerCount = shaderPackage.temporal.enabled ? mMaxTemporalHistoryFrames : 0; + wrapperSource = ReplaceAll(wrapperSource, "{{SOURCE_HISTORY_SAMPLERS}}", BuildHistorySamplerDeclarations("gSourceHistory", historySamplerCount)); + wrapperSource = ReplaceAll(wrapperSource, "{{TEMPORAL_HISTORY_SAMPLERS}}", BuildHistorySamplerDeclarations("gTemporalHistory", historySamplerCount)); wrapperSource = ReplaceAll(wrapperSource, "{{TEXTURE_SAMPLERS}}", BuildTextureSamplerDeclarations(shaderPackage.textureAssets)); wrapperSource = ReplaceAll(wrapperSource, "{{TEXT_SAMPLERS}}", BuildTextSamplerDeclarations(shaderPackage.parameters)); wrapperSource = ReplaceAll(wrapperSource, "{{TEXT_HELPERS}}", BuildTextHelpers(shaderPackage.parameters)); - wrapperSource = ReplaceAll(wrapperSource, "{{SOURCE_HISTORY_SWITCH_CASES}}", BuildHistorySwitchCases("gSourceHistory", mMaxTemporalHistoryFrames)); - wrapperSource = ReplaceAll(wrapperSource, "{{TEMPORAL_HISTORY_SWITCH_CASES}}", BuildHistorySwitchCases("gTemporalHistory", mMaxTemporalHistoryFrames)); + wrapperSource = ReplaceAll(wrapperSource, "{{SOURCE_HISTORY_SWITCH_CASES}}", BuildHistorySwitchCases("gSourceHistory", historySamplerCount)); + wrapperSource = ReplaceAll(wrapperSource, "{{TEMPORAL_HISTORY_SWITCH_CASES}}", BuildHistorySwitchCases("gTemporalHistory", historySamplerCount)); wrapperSource = ReplaceAll(wrapperSource, "{{USER_SHADER_INCLUDE}}", shaderPackage.shaderPath.generic_string()); wrapperSource = ReplaceAll(wrapperSource, "{{ENTRY_POINT_CALL}}", shaderPackage.entryPoint + "(context)"); return true; diff --git a/image.png b/image.png new file mode 100644 index 0000000000000000000000000000000000000000..f8ceba4c09059daa70025f65e845d7eaaa4be5a3 GIT binary patch literal 48830 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 literal 0 HcmV?d00001 diff --git a/shaders/text-overlay/shader.slang b/shaders/text-overlay/shader.slang index abfdebb..118d47e 100644 --- a/shaders/text-overlay/shader.slang +++ b/shaders/text-overlay/shader.slang @@ -15,11 +15,16 @@ float4 shadeVideo(ShaderContext context) float2 resolution = max(context.outputResolution, float2(1.0, 1.0)); float aspect = resolution.x / resolution.y; float2 textSize = float2(0.72 * scale, 0.09 * scale * aspect); - float2 textUv = (context.uv - position) / max(textSize, float2(0.0001, 0.0001)); + float2 safeTextSize = max(textSize, float2(0.0001, 0.0001)); + float2 textUv = (context.uv - position) / safeTextSize; + bool insideTextRect = textUv.x >= 0.0 && textUv.x <= 1.0 && textUv.y >= 0.0 && textUv.y <= 1.0; - float mask = sampleTitleText(textUv); + float mask = insideTextRect ? sampleTitleText(textUv) : 0.0; float fill = smoothstep(0.48, 0.54, mask); float outline = smoothstep(0.48 - outlineWidth, 0.54 - outlineWidth, mask); + float textAlpha = max(fill * fillColor.a, outline * outlineColor.a); + if (textAlpha <= 0.0001) + return context.sourceColor; float4 base = context.sourceColor; float4 outlineLayer = float4(outlineColor.rgb * outlineColor.a, outline * outlineColor.a); diff --git a/ui/src/components/ParameterField.jsx b/ui/src/components/ParameterField.jsx index 946815a..348941a 100644 --- a/ui/src/components/ParameterField.jsx +++ b/ui/src/components/ParameterField.jsx @@ -280,6 +280,7 @@ export function ParameterField({ layer, parameter, onParameterChange }) { sendValue(event.target.value)} diff --git a/ui/src/hooks/useThrottledParameterValue.js b/ui/src/hooks/useThrottledParameterValue.js index c8e8121..583ecb5 100644 --- a/ui/src/hooks/useThrottledParameterValue.js +++ b/ui/src/hooks/useThrottledParameterValue.js @@ -5,33 +5,34 @@ function valuesMatch(left, right) { } export function useThrottledParameterValue(parameter, onParameterChange) { - const [draftValue, setDraftValue] = useState(parameter.value); - const [appliedValue, setAppliedValue] = useState(parameter.value); + const currentValue = parameter.value === undefined ? parameter.defaultValue : parameter.value; + const [draftValue, setDraftValue] = useState(currentValue); + const [appliedValue, setAppliedValue] = useState(currentValue); const pendingTimeoutRef = useRef(null); - const latestDraftRef = useRef(parameter.value); + const latestDraftRef = useRef(currentValue); const lastSentAtRef = useRef(0); const isInteractingRef = useRef(false); const isDirtyRef = useRef(false); useEffect(() => { - setDraftValue(parameter.value); - setAppliedValue(parameter.value); - latestDraftRef.current = parameter.value; + setDraftValue(currentValue); + setAppliedValue(currentValue); + latestDraftRef.current = currentValue; lastSentAtRef.current = 0; isInteractingRef.current = false; isDirtyRef.current = false; }, [parameter.id]); useEffect(() => { - setAppliedValue(parameter.value); + setAppliedValue(currentValue); latestDraftRef.current = draftValue; - if (isDirtyRef.current && valuesMatch(parameter.value, latestDraftRef.current)) { + if (isDirtyRef.current && valuesMatch(currentValue, latestDraftRef.current)) { isDirtyRef.current = false; } if (!isInteractingRef.current && !isDirtyRef.current) { - setDraftValue(parameter.value); + setDraftValue(currentValue); } - }, [draftValue, parameter.value]); + }, [draftValue, currentValue]); useEffect(() => { return () => {