Timing and saftey pass
This commit is contained in:
@@ -243,7 +243,6 @@ D:\SDKs\slang-2026.8-windows-x86_64
|
|||||||
|
|
||||||
If neither variable is set, the workflow falls back to the repo-local defaults under `3rdParty/`.
|
If neither variable is set, the workflow falls back to the repo-local defaults under `3rdParty/`.
|
||||||
|
|
||||||
|
|
||||||
## Still Todo
|
## Still Todo
|
||||||
|
|
||||||
- Audio.
|
- Audio.
|
||||||
|
|||||||
@@ -294,13 +294,20 @@ void OpenGLComposite::renderEffect()
|
|||||||
if (mUseCommittedLayerStates)
|
if (mUseCommittedLayerStates)
|
||||||
{
|
{
|
||||||
layerStates = mShaderPrograms->CommittedLayerStates();
|
layerStates = mShaderPrograms->CommittedLayerStates();
|
||||||
|
if (mRuntimeHost)
|
||||||
|
mRuntimeHost->RefreshDynamicRenderStateFields(layerStates);
|
||||||
}
|
}
|
||||||
else if (mRuntimeHost)
|
else if (mRuntimeHost)
|
||||||
{
|
{
|
||||||
if (mRuntimeHost->TryGetLayerRenderStates(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), layerStates))
|
if (mRuntimeHost->TryGetLayerRenderStates(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), layerStates))
|
||||||
|
{
|
||||||
mCachedLayerRenderStates = layerStates;
|
mCachedLayerRenderStates = layerStates;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
layerStates = mCachedLayerRenderStates;
|
layerStates = mCachedLayerRenderStates;
|
||||||
|
mRuntimeHost->RefreshDynamicRenderStateFields(layerStates);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
|
const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
|
||||||
mRenderPass->Render(
|
mRenderPass->Render(
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ void OpenGLDeckLinkBridge::VideoFrameArrived(IDeckLinkVideoInputFrame* inputFram
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Use a straightforward texture buffer
|
// Use a straightforward texture buffer
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.UnpinnedTextureBuffer());
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.UnpinnedTextureBuffer());
|
||||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, videoPixels, GL_DYNAMIC_DRAW);
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, videoPixels, GL_DYNAMIC_DRAW);
|
||||||
glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
|
glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
|
||||||
@@ -174,13 +176,14 @@ void OpenGLDeckLinkBridge::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedF
|
|||||||
if (!mDeckLink.TransferPlayoutFrame(pFrame, mRenderer.OutputTexture()))
|
if (!mDeckLink.TransferPlayoutFrame(pFrame, mRenderer.OutputTexture()))
|
||||||
OutputDebugStringA("Playback: transferFrame() failed\n");
|
OutputDebugStringA("Playback: transferFrame() failed\n");
|
||||||
|
|
||||||
mPaint();
|
|
||||||
|
|
||||||
// Wait for transfer to system memory to complete
|
// Wait for transfer to system memory to complete
|
||||||
mDeckLink.WaitForPlayoutTransferComplete(pFrame);
|
mDeckLink.WaitForPlayoutTransferComplete(pFrame);
|
||||||
|
mPaint();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||||
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer());
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer());
|
||||||
glReadPixels(0, 0, mDeckLink.OutputFrameWidth(), mDeckLink.OutputFrameHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame);
|
glReadPixels(0, 0, mDeckLink.OutputFrameWidth(), mDeckLink.OutputFrameHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame);
|
||||||
mPaint();
|
mPaint();
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ void OpenGLRenderPass::Render(
|
|||||||
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
if (hasInputSource)
|
if (hasInputSource)
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ void OpenGLRenderer::PresentToWindow(HDC hdc, unsigned outputFrameWidth, unsigne
|
|||||||
|
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, mOutputFrameBuf);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mOutputFrameBuf);
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
glViewport(0, 0, mViewWidth, mViewHeight);
|
glViewport(0, 0, mViewWidth, mViewHeight);
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|||||||
@@ -1274,16 +1274,11 @@ void RuntimeHost::SetFramePacingStatsLocked(double completionIntervalMillisecond
|
|||||||
|
|
||||||
void RuntimeHost::AdvanceFrame()
|
void RuntimeHost::AdvanceFrame()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
|
||||||
++mFrameCounter;
|
++mFrameCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RuntimeHost::TryAdvanceFrame()
|
bool RuntimeHost::TryAdvanceFrame()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
|
|
||||||
if (!lock.owns_lock())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
++mFrameCounter;
|
++mFrameCounter;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1345,9 +1340,23 @@ bool RuntimeHost::TryGetLayerRenderStates(unsigned outputWidth, unsigned outputH
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
void RuntimeHost::RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const
|
||||||
{
|
{
|
||||||
const RuntimeClockSnapshot clock = GetRuntimeClockSnapshot();
|
const RuntimeClockSnapshot clock = GetRuntimeClockSnapshot();
|
||||||
|
const double timeSeconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mStartTime).count();
|
||||||
|
const double frameCount = static_cast<double>(mFrameCounter.load(std::memory_order_relaxed));
|
||||||
|
|
||||||
|
for (RuntimeRenderState& state : states)
|
||||||
|
{
|
||||||
|
state.timeSeconds = timeSeconds;
|
||||||
|
state.utcTimeSeconds = clock.utcTimeSeconds;
|
||||||
|
state.utcOffsetSeconds = clock.utcOffsetSeconds;
|
||||||
|
state.frameCount = frameCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
|
||||||
|
{
|
||||||
for (const LayerPersistentState& layer : mPersistentState.layers)
|
for (const LayerPersistentState& layer : mPersistentState.layers)
|
||||||
{
|
{
|
||||||
auto shaderIt = mPackagesById.find(layer.shaderId);
|
auto shaderIt = mPackagesById.find(layer.shaderId);
|
||||||
@@ -1357,10 +1366,6 @@ void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned ou
|
|||||||
RuntimeRenderState state;
|
RuntimeRenderState state;
|
||||||
state.layerId = layer.id;
|
state.layerId = layer.id;
|
||||||
state.shaderId = layer.shaderId;
|
state.shaderId = layer.shaderId;
|
||||||
state.timeSeconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mStartTime).count();
|
|
||||||
state.utcTimeSeconds = clock.utcTimeSeconds;
|
|
||||||
state.utcOffsetSeconds = clock.utcOffsetSeconds;
|
|
||||||
state.frameCount = static_cast<double>(mFrameCounter);
|
|
||||||
state.mixAmount = 1.0;
|
state.mixAmount = 1.0;
|
||||||
state.bypass = layer.bypass ? 1.0 : 0.0;
|
state.bypass = layer.bypass ? 1.0 : 0.0;
|
||||||
state.inputWidth = mSignalWidth;
|
state.inputWidth = mSignalWidth;
|
||||||
@@ -1386,6 +1391,8 @@ void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned ou
|
|||||||
|
|
||||||
states.push_back(state);
|
states.push_back(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefreshDynamicRenderStateFields(states);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RuntimeHost::BuildStateJson() const
|
std::string RuntimeHost::BuildStateJson() const
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "RuntimeJson.h"
|
#include "RuntimeJson.h"
|
||||||
#include "ShaderTypes.h"
|
#include "ShaderTypes.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -50,6 +51,7 @@ public:
|
|||||||
bool BuildLayerFragmentShaderSource(const std::string& layerId, std::string& fragmentShaderSource, std::string& error);
|
bool BuildLayerFragmentShaderSource(const std::string& layerId, std::string& fragmentShaderSource, std::string& error);
|
||||||
std::vector<RuntimeRenderState> GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const;
|
std::vector<RuntimeRenderState> GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const;
|
||||||
bool TryGetLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const;
|
bool TryGetLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const;
|
||||||
|
void RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const;
|
||||||
std::string BuildStateJson() const;
|
std::string BuildStateJson() const;
|
||||||
|
|
||||||
const std::filesystem::path& GetRepoRoot() const { return mRepoRoot; }
|
const std::filesystem::path& GetRepoRoot() const { return mRepoRoot; }
|
||||||
@@ -173,6 +175,6 @@ private:
|
|||||||
bool mAutoReloadEnabled;
|
bool mAutoReloadEnabled;
|
||||||
std::chrono::steady_clock::time_point mStartTime;
|
std::chrono::steady_clock::time_point mStartTime;
|
||||||
std::chrono::steady_clock::time_point mLastScanTime;
|
std::chrono::steady_clock::time_point mLastScanTime;
|
||||||
uint64_t mFrameCounter;
|
std::atomic<uint64_t> mFrameCounter;
|
||||||
uint64_t mNextLayerId;
|
uint64_t mNextLayerId;
|
||||||
};
|
};
|
||||||
|
|||||||
54
shaders/happy-accident/shader.json
Normal file
54
shaders/happy-accident/shader.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"id": "happy-accident",
|
||||||
|
"name": "Happy Accident",
|
||||||
|
"description": "Raymarched generative line field. CC0 original 'Clearly a bug' adapted from https://www.shadertoy.com/view/33cGDj.",
|
||||||
|
"category": "Generative",
|
||||||
|
"entryPoint": "shadeVideo",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"id": "speed",
|
||||||
|
"label": "Speed",
|
||||||
|
"type": "float",
|
||||||
|
"default": 1.0,
|
||||||
|
"min": 0.0,
|
||||||
|
"max": 4.0,
|
||||||
|
"step": 0.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scale",
|
||||||
|
"label": "Scale",
|
||||||
|
"type": "float",
|
||||||
|
"default": 1.0,
|
||||||
|
"min": 0.25,
|
||||||
|
"max": 3.0,
|
||||||
|
"step": 0.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "raySteps",
|
||||||
|
"label": "Ray Steps",
|
||||||
|
"type": "float",
|
||||||
|
"default": 77.0,
|
||||||
|
"min": 8.0,
|
||||||
|
"max": 77.0,
|
||||||
|
"step": 1.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "intensity",
|
||||||
|
"label": "Intensity",
|
||||||
|
"type": "float",
|
||||||
|
"default": 1.0,
|
||||||
|
"min": 0.1,
|
||||||
|
"max": 4.0,
|
||||||
|
"step": 0.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sourceMix",
|
||||||
|
"label": "Source Mix",
|
||||||
|
"type": "float",
|
||||||
|
"default": 0.0,
|
||||||
|
"min": 0.0,
|
||||||
|
"max": 1.0,
|
||||||
|
"step": 0.01
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
61
shaders/happy-accident/shader.slang
Normal file
61
shaders/happy-accident/shader.slang
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
float happyNoise(float2 p)
|
||||||
|
{
|
||||||
|
return frac(dot(p, sin(p))) - 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
float2x2 rotateAroundZ(float angle)
|
||||||
|
{
|
||||||
|
float c = cos(angle);
|
||||||
|
float s = sin(angle);
|
||||||
|
return float2x2(c, s, -s, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
float2x2 happyAccidentMatrix(float3 originalPosition, float timeCos)
|
||||||
|
{
|
||||||
|
return float2x2(
|
||||||
|
cos(originalPosition.x),
|
||||||
|
sin(originalPosition.y),
|
||||||
|
-sin(originalPosition.z),
|
||||||
|
timeCos);
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 shadeVideo(ShaderContext context)
|
||||||
|
{
|
||||||
|
float2 resolution = max(context.outputResolution, float2(1.0, 1.0));
|
||||||
|
float2 fragCoord = context.uv * resolution;
|
||||||
|
float2 normalizedCoord = (fragCoord - 0.5 * resolution) / resolution.y / max(scale, 0.001);
|
||||||
|
float time = context.time * speed;
|
||||||
|
float timeCos = cos(0.1 * time);
|
||||||
|
float3 direction = normalize(float3(normalizedCoord, 1.0));
|
||||||
|
float3 origin = float3(0.0, 0.0, time);
|
||||||
|
|
||||||
|
float z = happyNoise(fragCoord);
|
||||||
|
float distanceToSurface = 0.0;
|
||||||
|
float4 accumulated = float4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
float clampedSteps = clamp(raySteps, 1.0, 77.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 77; ++i)
|
||||||
|
{
|
||||||
|
if (float(i) >= clampedSteps)
|
||||||
|
break;
|
||||||
|
|
||||||
|
z += 0.6 * distanceToSurface;
|
||||||
|
|
||||||
|
float3 position = origin + z * direction;
|
||||||
|
float3 originalPosition = position;
|
||||||
|
|
||||||
|
position.xy = mul(rotateAroundZ(2.0 + originalPosition.z), position.xy);
|
||||||
|
position.xy = mul(happyAccidentMatrix(originalPosition, timeCos), position.xy);
|
||||||
|
|
||||||
|
float colorSeed = 0.5 * originalPosition.z + length(position - originalPosition);
|
||||||
|
float4 palette = 1.0 + sin(colorSeed + float4(0.0, 4.0, 3.0, 6.0));
|
||||||
|
palette /= 0.5 + 2.0 * dot(originalPosition.xy, originalPosition.xy);
|
||||||
|
|
||||||
|
position = abs(frac(position) - 0.5);
|
||||||
|
distanceToSurface = abs(min(length(position.xy) - 0.125, min(position.x, position.y) + 0.001)) + 0.001;
|
||||||
|
accumulated += palette.w * palette / distanceToSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 color = float4(tanh((accumulated.rgb * intensity) / 20000.0), 1.0);
|
||||||
|
return saturate(lerp(color, context.sourceColor, sourceMix));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user