Skip to content

Commit 281cd43

Browse files
committed
switching tabs pauses the clock
- internal refactoring to use the Clock concept and cleanup accordingly
1 parent 7b51cc2 commit 281cd43

File tree

7 files changed

+131
-61
lines changed

7 files changed

+131
-61
lines changed

CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ cmake_minimum_required(VERSION 3.28)
33
# CLion configuration. Note that Ninja does NOT work with c++ 20 for some unknown reason...
44
# -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=/usr/local/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Debug
55

6-
set(wgpu-shader-toy_RELEASE_YEAR "2024")
7-
set(wgpu-shader-toy_RELEASE_MONTH "09" )
8-
set(wgpu-shader-toy_RELEASE_DAY "08" )
6+
set(wgpu-shader-toy_RELEASE_YEAR "2025")
7+
set(wgpu-shader-toy_RELEASE_MONTH "02" )
8+
set(wgpu-shader-toy_RELEASE_DAY "26" )
99

1010
set(wgpu-shader-toy_VERSION "${wgpu-shader-toy_RELEASE_YEAR}.${wgpu-shader-toy_RELEASE_MONTH}.${wgpu-shader-toy_RELEASE_DAY}")
1111

@@ -51,6 +51,7 @@ set(wgpu_shader_toy_sources
5151
src/cpp/gpu/Window.h
5252
src/cpp/gpu/Window.cpp
5353

54+
src/cpp/utils/Clock.h
5455
src/cpp/utils/DataManager.h
5556
src/cpp/utils/DataManager.cpp
5657
src/cpp/utils/JSStorage.cpp

src/cpp/FragmentShader.cpp

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,12 @@ TextEditor &FragmentShader::edit()
6868
//------------------------------------------------------------------------
6969
// FragmentShader::toggleRunning
7070
//------------------------------------------------------------------------
71-
void FragmentShader::toggleRunning(double iCurrentTime)
71+
void FragmentShader::toggleRunning()
7272
{
73-
if(fRunning)
74-
fInputs.time = static_cast<gpu::f32>(iCurrentTime - fStartTime);
73+
if(fClock.isRunning())
74+
fClock.pause();
7575
else
76-
fStartTime = iCurrentTime - fInputs.time;
77-
fRunning = ! fRunning;
76+
fClock.resume();
7877
}
7978

8079
//------------------------------------------------------------------------
@@ -99,41 +98,39 @@ char const *FragmentShader::getStatus() const
9998
}
10099

101100
//------------------------------------------------------------------------
102-
// FragmentShader::nextFrame
101+
// FragmentShader::updateInputsFromClock
103102
//------------------------------------------------------------------------
104-
void FragmentShader::nextFrame(double iCurrentTime, int frameCount)
103+
void FragmentShader::updateInputsFromClock()
105104
{
106-
fManualTime = true;
107-
108-
fStartTime = iCurrentTime - fInputs.time;
109-
fInputs.frame += frameCount;
110-
fInputs.time = static_cast<gpu::f32>(iCurrentTime - fStartTime) + static_cast<float>(frameCount) / 60.0f;
105+
fInputs.time = static_cast<gpu::f32>(fClock.getTime());
106+
fInputs.frame = fClock.getFrame();
111107
}
112108

113109
//------------------------------------------------------------------------
114-
// FragmentShader::previousFrame
110+
// FragmentShader::resetTime
115111
//------------------------------------------------------------------------
116-
void FragmentShader::previousFrame(double iCurrentTime, int frameCount)
112+
void FragmentShader::resetTime()
117113
{
118-
fManualTime = true;
114+
fClock.reset();
115+
updateInputsFromClock();
116+
}
119117

120-
fStartTime = iCurrentTime - fInputs.time;
121-
fInputs.frame = std::max(0, fInputs.frame - frameCount);
122-
fInputs.time =
123-
std::max(0.0f, static_cast<gpu::f32>(iCurrentTime - fStartTime) - static_cast<float>(frameCount) / 60.0f);
118+
//------------------------------------------------------------------------
119+
// FragmentShader::tickTime
120+
//------------------------------------------------------------------------
121+
void FragmentShader::tickTime(double iTimeDelta)
122+
{
123+
fClock.tickTime(iTimeDelta);
124+
updateInputsFromClock();
124125
}
125126

126127
//------------------------------------------------------------------------
127-
// FragmentShader::stopManualTime
128+
// FragmentShader::tickFrame
128129
//------------------------------------------------------------------------
129-
void FragmentShader::stopManualTime(double iCurrentTime)
130+
void FragmentShader::tickFrame(int iFrameCount)
130131
{
131-
if(fManualTime)
132-
{
133-
if(fRunning)
134-
fStartTime = iCurrentTime - fInputs.time;
135-
fManualTime = false;
136-
}
132+
fClock.tickFrame(iFrameCount);
133+
updateInputsFromClock();
137134
}
138135

139136
//------------------------------------------------------------------------

src/cpp/FragmentShader.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <webgpu/webgpu_cpp.h>
2727
#include "TextEditor.h"
2828
#include "State.h"
29+
#include "utils/Clock.h"
2930

3031
namespace pongasoft::gpu {
3132
using vec2f = ImVec2;
@@ -96,22 +97,20 @@ struct ShaderToyInputs {
9697
inline int getCompilationErrorLine() const { return std::get<FragmentShader::State::CompiledInError>(fState).fErrorLine; }
9798
constexpr bool isCompiled() const { return std::holds_alternative<FragmentShader::State::Compiled>(fState); }
9899

99-
void setStartTime(double iTime) { fStartTime = iTime; fInputs.time = 0; fInputs.frame = 0; }
100-
void toggleRunning(double iCurrentTime);
101-
constexpr bool isRunning() const { return fRunning; }
100+
void toggleRunning();
101+
constexpr bool isRunning() const { return fClock.isRunning(); }
102+
void resetTime();
103+
void startManualClock() { fClock.setManual(true); }
104+
void endManualClock() { fClock.setManual(false); }
102105

103106
constexpr bool isEnabled() const { return fEnabled; }
104107
constexpr void toggleEnabled() { fEnabled = !fEnabled; }
105108

106109
char const* getStatus() const;
107110

108-
void nextFrame(double iCurrentTime, int frameCount = 1);
111+
void nextFrame(int iFrameCount = 1) { tickFrame(iFrameCount); }
109112

110-
void previousFrame(double iCurrentTime, int frameCount = 1);
111-
112-
void stopManualTime(double iCurrentTime);
113-
114-
constexpr bool isTimeEnabled() const { return fRunning && !fManualTime; }
113+
void previousFrame(int iFrameCount = 1) { tickFrame(-iFrameCount); }
115114

116115
TextEditor &edit();
117116

@@ -126,6 +125,9 @@ struct ShaderToyInputs {
126125
constexpr bool isCompiling() const { return std::holds_alternative<FragmentShader::State::Compiling>(fState); }
127126
constexpr bool isNotCompiled() const { return std::holds_alternative<FragmentShader::State::NotCompiled>(fState); }
128127
inline wgpu::RenderPipeline getRenderPipeline() const { return std::get<FragmentShader::State::Compiled>(fState).fRenderPipeline; }
128+
void tickTime(double iTimeDelta);
129+
void tickFrame(int iFrameCount);
130+
void updateInputsFromClock();
129131

130132
private:
131133
std::string fName;
@@ -135,12 +137,10 @@ struct ShaderToyInputs {
135137
ShaderToyInputs fInputs{};
136138

137139
state_t fState{State::NotCompiled{}};
138-
double fStartTime{};
139140

140141
std::optional<TextEditor> fTextEditor{};
141142

142-
bool fRunning{true};
143-
bool fManualTime{false};
143+
utils::Clock fClock{};
144144
bool fEnabled{true};
145145
};
146146

src/cpp/FragmentShaderWindow.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,7 @@ void FragmentShaderWindow::setCurrentFragmentShader(std::shared_ptr<FragmentShad
357357
void FragmentShaderWindow::initFragmentShader(std::shared_ptr<FragmentShader> const &iFragmentShader) const
358358
{
359359
if(iFragmentShader)
360-
{
361-
iFragmentShader->fStartTime = getCurrentTime();
362-
iFragmentShader->fInputs.frame = 0;
363-
}
360+
iFragmentShader->resetTime();
364361
}
365362

366363
//------------------------------------------------------------------------
@@ -370,6 +367,10 @@ void FragmentShaderWindow::beforeFrame()
370367
{
371368
Window::beforeFrame();
372369

370+
auto currentTime = getCurrentTime();
371+
auto deltaTime = currentTime - fLastFrameCurrentTime;
372+
fLastFrameCurrentTime = currentTime;
373+
373374
if(glfwGetMouseButton(fWindow, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)
374375
{
375376
if(fMouseClick.x == -1)
@@ -388,11 +389,8 @@ void FragmentShaderWindow::beforeFrame()
388389
{
389390
glfwGetWindowContentScale(fWindow, &fContentScale.x, &fContentScale.y);
390391

391-
if(fCurrentFragmentShader->isTimeEnabled())
392-
{
393-
fCurrentFragmentShader->fInputs.frame++;
394-
fCurrentFragmentShader->fInputs.time = static_cast<gpu::f32>(getCurrentTime() - fCurrentFragmentShader->fStartTime);
395-
}
392+
fCurrentFragmentShader->tickTime(deltaTime);
393+
396394
fCurrentFragmentShader->fInputs.size = {
397395
static_cast<float>(fFrameBufferSize.width), static_cast<float>(fFrameBufferSize.height),
398396
fContentScale.x, fContentScale.y

src/cpp/FragmentShaderWindow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class FragmentShaderWindow : public gpu::Window, public std::enable_shared_from_
8080

8181
ImVec2 fContentScale{1.0, 1.0};
8282
ImVec2 fMouseClick{-1, -1};
83+
double fLastFrameCurrentTime{};
8384
};
8485

8586
}

src/cpp/MainWindow.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ void MainWindow::renderTimeControls()
444444
{
445445
// Reset time
446446
if(ImGui::Button(fa::kClockRotateLeft, fIconButtonSize))
447-
fCurrentFragmentShader->setStartTime(getCurrentTime());
447+
fCurrentFragmentShader->resetTime();
448448

449449
ImGui::SameLine();
450450

@@ -454,14 +454,17 @@ void MainWindow::renderTimeControls()
454454
// Previous frame
455455
ImGui::BeginDisabled(fCurrentFragmentShader->getInputs().frame == 0);
456456
{
457+
static bool kClockState{};
457458
ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
458-
auto button = ImGui::Button(isKeyAlt ? fa::kBackward : fa::kBackwardFast, fIconButtonSize);
459+
auto button = ImGui::Button(isKeyAlt ? ICON_FA_Backward "###Previous" : ICON_FA_BackwardFast "###Previous", fIconButtonSize);
459460
if(ImGui::IsItemDeactivated())
460-
fCurrentFragmentShader->stopManualTime(getCurrentTime());
461+
fCurrentFragmentShader->endManualClock();
461462
else
462463
{
463-
if(button || ImGui::IsItemActivated())
464-
fCurrentFragmentShader->previousFrame(getCurrentTime(), frameCount);
464+
if(ImGui::IsItemActivated())
465+
fCurrentFragmentShader->startManualClock();
466+
if(button)
467+
fCurrentFragmentShader->previousFrame(frameCount);
465468
}
466469
ImGui::PopItemFlag();
467470
}
@@ -471,20 +474,22 @@ void MainWindow::renderTimeControls()
471474

472475
// Play / Pause
473476
if(ImGui::Button(fCurrentFragmentShader->isRunning() ? fa::kCirclePause : fa::kCirclePlay, fIconButtonSize))
474-
fCurrentFragmentShader->toggleRunning(getCurrentTime());
477+
fCurrentFragmentShader->toggleRunning();
475478

476479
ImGui::SameLine();
477480

478481
// Next frame
479482
ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
480483
{
481-
auto button = ImGui::Button(isKeyAlt ? fa::kForward : fa::kForwardFast, fIconButtonSize);
484+
auto button = ImGui::Button(isKeyAlt ? ICON_FA_Forward "###Next" : ICON_FA_ForwardFast "###Next", fIconButtonSize);
482485
if(ImGui::IsItemDeactivated())
483-
fCurrentFragmentShader->stopManualTime(getCurrentTime());
486+
fCurrentFragmentShader->endManualClock();
484487
else
485488
{
486-
if(button || ImGui::IsItemActivated())
487-
fCurrentFragmentShader->nextFrame(getCurrentTime(), frameCount);
489+
if(ImGui::IsItemActivated())
490+
fCurrentFragmentShader->startManualClock();
491+
if(button)
492+
fCurrentFragmentShader->nextFrame(frameCount);
488493
}
489494
}
490495
ImGui::PopItemFlag();

src/cpp/utils/Clock.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) 2025 pongasoft
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*
16+
* @author Yan Pujante
17+
*/
18+
19+
#ifndef WGPU_SHADER_TOY_CLOCK_H
20+
#define WGPU_SHADER_TOY_CLOCK_H
21+
22+
namespace pongasoft::utils {
23+
24+
class Clock
25+
{
26+
public:
27+
void tickTime(double iTimeDelta)
28+
{
29+
if(!fPaused && !fManual)
30+
{
31+
fTime += iTimeDelta;
32+
fFrame++;
33+
}
34+
}
35+
36+
void tickFrame(int iFrameCount, double iTimeDelta = 1.0/60.0)
37+
{
38+
fTime += iTimeDelta * iFrameCount;
39+
fFrame += iFrameCount;
40+
if(fTime < 0 || fFrame < 0)
41+
reset();
42+
}
43+
44+
constexpr double getTime() const { return fTime; }
45+
constexpr int getFrame() const { return fFrame; }
46+
constexpr bool isRunning() const { return !fPaused; }
47+
48+
void pause() { fPaused = true; }
49+
void resume() { fPaused = false; }
50+
51+
void setManual(bool iManual) { fManual = iManual; }
52+
53+
void reset()
54+
{
55+
fTime = 0;
56+
fFrame = 0;
57+
}
58+
59+
private:
60+
double fTime{};
61+
int fFrame{};
62+
bool fPaused{};
63+
bool fManual{};
64+
};
65+
66+
}
67+
68+
#endif //WGPU_SHADER_TOY_CLOCK_H

0 commit comments

Comments
 (0)