Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AtlasEngine: Fix custom shader time imprecision #17104

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src/renderer/atlas/BackendD3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ using namespace Microsoft::Console::Render::Atlas;
static constexpr D2D1_MATRIX_3X2_F identityTransform{ .m11 = 1, .m22 = 1 };
static constexpr D2D1_COLOR_F whiteColor{ 1, 1, 1, 1 };

static u64 queryPerfFreq() noexcept
{
LARGE_INTEGER li;
QueryPerformanceFrequency(&li);
return std::bit_cast<u64>(li.QuadPart);
}

static u64 queryPerfCount() noexcept
{
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return std::bit_cast<u64>(li.QuadPart);
}

BackendD3D::BackendD3D(const RenderingPayload& p)
{
THROW_IF_FAILED(p.device->CreateVertexShader(&shader_vs[0], sizeof(shader_vs), nullptr, _vertexShader.addressof()));
Expand Down Expand Up @@ -485,7 +499,14 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p)
THROW_IF_FAILED(p.device->CreateSamplerState(&desc, _customShaderSamplerState.put()));
}

_customShaderStartTime = std::chrono::steady_clock::now();
// Since floats are imprecise we need to constrain the time value into a range that can be accurately represented.
// Assuming a monitor refresh rate of 1000 Hz, we can still easily represent 1000 seconds accurately (roughly 16 minutes).
// 10000 seconds would already result in a 50% error. So to avoid this, we use queryPerfCount() modulo _customShaderPerfTickMod.
// The use of a power of 10 is intentional, because shaders are often periodic and this makes any decimal multiplier up to 3 fractional
// digits not break the periodicity. For instance, with a wraparound of 1000 seconds sin(1.234*x) is still perfectly periodic.
const auto freq = queryPerfFreq();
_customShaderPerfTickMod = freq * 1000;
_customShaderSecsPerPerfTick = 1.0f / freq;
}
}

Expand Down Expand Up @@ -2111,8 +2132,12 @@ void BackendD3D::_debugDumpRenderTarget(const RenderingPayload& p)
void BackendD3D::_executeCustomShader(RenderingPayload& p)
{
{
// See the comment in _recreateCustomShader() which initializes the two members below and explains what they do.
const auto now = queryPerfCount();
const auto time = static_cast<int>(now % _customShaderPerfTickMod) * _customShaderSecsPerPerfTick;

const CustomConstBuffer data{
.time = std::chrono::duration<f32>(std::chrono::steady_clock::now() - _customShaderStartTime).count(),
.time = time,
.scale = static_cast<f32>(p.s->font->dpi) / static_cast<f32>(USER_DEFAULT_SCREEN_DPI),
.resolution = {
static_cast<f32>(_viewportCellCount.x * p.s->font->cellSize.x),
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/atlas/BackendD3D.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ namespace Microsoft::Console::Render::Atlas
wil::com_ptr<ID3D11SamplerState> _customShaderSamplerState;
wil::com_ptr<ID3D11Texture2D> _customShaderTexture;
wil::com_ptr<ID3D11ShaderResourceView> _customShaderTextureView;
std::chrono::steady_clock::time_point _customShaderStartTime;
u64 _customShaderPerfTickMod = 0;
f32 _customShaderSecsPerPerfTick = 0;

wil::com_ptr<ID3D11Texture2D> _backgroundBitmap;
wil::com_ptr<ID3D11ShaderResourceView> _backgroundBitmapView;
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/atlas/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ namespace Microsoft::Console::Render::Atlas
using i32x4 = vec4<i32>;
using i32r = rect<i32>;

using u64 = uint64_t;

using f32 = float;
using f32x2 = vec2<f32>;
using f32x4 = vec4<f32>;
Expand Down
Loading