Attempt to fix thanos effect on D3D11.

This commit is contained in:
John Preston
2026-05-26 21:54:13 +04:00
parent 6eccdae3a1
commit eb31bda16b
6 changed files with 144 additions and 70 deletions
@@ -20,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
namespace {
constexpr auto kParticleStride = int(24);
constexpr auto kQuadVertexCount = int(6);
constexpr auto kQuadVertexStride = int(2 * sizeof(float));
constexpr auto kComputeWorkgroupSize = int(64);
@@ -157,12 +156,6 @@ void ThanosEffectRenderer::initialize(
sizeof(RenderUniforms));
_renderUniformBuffer->create();
_placeholderParticleBuffer = rhi->newBuffer(
QRhiBuffer::Immutable,
QRhiBuffer::VertexBuffer | QRhiBuffer::StorageBuffer,
kParticleStride);
_placeholderParticleBuffer->create();
_placeholderTexture = rhi->newTexture(
QRhiTexture::RGBA8,
QSize(1, 1));
@@ -176,6 +169,21 @@ void ThanosEffectRenderer::initialize(
QRhiSampler::ClampToEdge);
_placeholderSampler->create();
_placeholderStateTexture = rhi->newTexture(
QRhiTexture::RGBA32F,
QSize(1, 1),
1,
QRhiTexture::UsedWithLoadStore);
_placeholderStateTexture->create();
_placeholderStateSampler = rhi->newSampler(
QRhiSampler::Nearest,
QRhiSampler::Nearest,
QRhiSampler::None,
QRhiSampler::ClampToEdge,
QRhiSampler::ClampToEdge);
_placeholderStateSampler->create();
if (!createPipelines(rt)) {
LOG(("ThanosEffect: pipeline creation failed, disabling effect"));
_creationFailed = true;
@@ -203,14 +211,20 @@ bool ThanosEffectRenderer::createPipelines(QRhiRenderTarget *rt) {
_computeInitSrbLayout = _rhi->newShaderResourceBindings();
_computeInitSrbLayout->setBindings({
QRhiShaderResourceBinding::bufferLoadStore(
QRhiShaderResourceBinding::imageLoadStore(
0,
QRhiShaderResourceBinding::ComputeStage,
_placeholderParticleBuffer),
_placeholderStateTexture,
0),
QRhiShaderResourceBinding::uniformBuffer(
1,
QRhiShaderResourceBinding::ComputeStage,
_computeInitUniformBuffer),
QRhiShaderResourceBinding::imageLoadStore(
2,
QRhiShaderResourceBinding::ComputeStage,
_placeholderStateTexture,
0),
});
_computeInitSrbLayout->create();
@@ -224,14 +238,20 @@ bool ThanosEffectRenderer::createPipelines(QRhiRenderTarget *rt) {
_computeUpdateSrbLayout = _rhi->newShaderResourceBindings();
_computeUpdateSrbLayout->setBindings({
QRhiShaderResourceBinding::bufferLoadStore(
QRhiShaderResourceBinding::imageLoadStore(
0,
QRhiShaderResourceBinding::ComputeStage,
_placeholderParticleBuffer),
_placeholderStateTexture,
0),
QRhiShaderResourceBinding::uniformBuffer(
1,
QRhiShaderResourceBinding::ComputeStage,
_computeUpdateUniformBuffer),
QRhiShaderResourceBinding::imageLoadStore(
2,
QRhiShaderResourceBinding::ComputeStage,
_placeholderStateTexture,
0),
});
_computeUpdateSrbLayout->create();
@@ -255,6 +275,11 @@ bool ThanosEffectRenderer::createPipelines(QRhiRenderTarget *rt) {
QRhiShaderResourceBinding::FragmentStage,
_placeholderTexture,
_placeholderSampler),
QRhiShaderResourceBinding::sampledTexture(
2,
QRhiShaderResourceBinding::VertexStage,
_placeholderStateTexture,
_placeholderStateSampler),
});
_renderSrbLayout->create();
@@ -267,13 +292,9 @@ bool ThanosEffectRenderer::createPipelines(QRhiRenderTarget *rt) {
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ quint32(kQuadVertexStride) },
{ quint32(kParticleStride),
QRhiVertexInputBinding::PerInstance },
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 1, 1, QRhiVertexInputAttribute::Float2, 0 },
{ 1, 2, QRhiVertexInputAttribute::Float, 16 },
});
_renderPipeline->setVertexInputLayout(inputLayout);
@@ -450,9 +471,8 @@ void ThanosEffectRenderer::render(
const QRhiCommandBuffer::VertexInput vbufs[] = {
{ _quadVertexBuffer, 0 },
{ item.particleBuffer, 0 },
};
cb->setVertexInput(0, 2, vbufs);
cb->setVertexInput(0, 1, vbufs);
const auto instanceCount =
item.particleCountX * item.particleCountY;
@@ -544,8 +564,6 @@ ThanosEffectRenderer::AnimatingItem ThanosEffectRenderer::createAnimatingItem(
uint32_t(1),
uint32_t(maxParticles / float64(result.particleCountY)));
}
const auto particleCount =
result.particleCountX * result.particleCountY;
auto *tex = _rhi->newTexture(
QRhiTexture::RGBA8,
@@ -572,16 +590,42 @@ ThanosEffectRenderer::AnimatingItem ThanosEffectRenderer::createAnimatingItem(
}
result.sampler = sampler;
auto *particleBuf = _rhi->newBuffer(
QRhiBuffer::Static,
QRhiBuffer::VertexBuffer | QRhiBuffer::StorageBuffer,
particleCount * kParticleStride);
if (!particleBuf->create()) {
delete particleBuf;
auto *stateTex = _rhi->newTexture(
QRhiTexture::RGBA32F,
QSize(int(result.particleCountX), int(result.particleCountY)),
1,
QRhiTexture::UsedWithLoadStore);
if (!stateTex->create()) {
delete stateTex;
destroyAnimatingItem(result);
return result;
}
result.particleBuffer = particleBuf;
result.particleStateTexture = stateTex;
auto *velocityTex = _rhi->newTexture(
QRhiTexture::RGBA32F,
QSize(int(result.particleCountX), int(result.particleCountY)),
1,
QRhiTexture::UsedWithLoadStore);
if (!velocityTex->create()) {
delete velocityTex;
destroyAnimatingItem(result);
return result;
}
result.particleVelocityTexture = velocityTex;
auto *stateSampler = _rhi->newSampler(
QRhiSampler::Nearest,
QRhiSampler::Nearest,
QRhiSampler::None,
QRhiSampler::ClampToEdge,
QRhiSampler::ClampToEdge);
if (!stateSampler->create()) {
delete stateSampler;
destroyAnimatingItem(result);
return result;
}
result.particleStateSampler = stateSampler;
auto *initUbo = _rhi->newBuffer(
QRhiBuffer::Dynamic,
@@ -607,14 +651,20 @@ ThanosEffectRenderer::AnimatingItem ThanosEffectRenderer::createAnimatingItem(
result.computeInitSrb = _rhi->newShaderResourceBindings();
result.computeInitSrb->setBindings({
QRhiShaderResourceBinding::bufferLoadStore(
QRhiShaderResourceBinding::imageLoadStore(
0,
QRhiShaderResourceBinding::ComputeStage,
particleBuf),
stateTex,
0),
QRhiShaderResourceBinding::uniformBuffer(
1,
QRhiShaderResourceBinding::ComputeStage,
initUbo),
QRhiShaderResourceBinding::imageLoadStore(
2,
QRhiShaderResourceBinding::ComputeStage,
velocityTex,
0),
});
if (!result.computeInitSrb->create()) {
destroyAnimatingItem(result);
@@ -623,14 +673,20 @@ ThanosEffectRenderer::AnimatingItem ThanosEffectRenderer::createAnimatingItem(
result.computeUpdateSrb = _rhi->newShaderResourceBindings();
result.computeUpdateSrb->setBindings({
QRhiShaderResourceBinding::bufferLoadStore(
QRhiShaderResourceBinding::imageLoadStore(
0,
QRhiShaderResourceBinding::ComputeStage,
particleBuf),
stateTex,
0),
QRhiShaderResourceBinding::uniformBuffer(
1,
QRhiShaderResourceBinding::ComputeStage,
updateUbo),
QRhiShaderResourceBinding::imageLoadStore(
2,
QRhiShaderResourceBinding::ComputeStage,
velocityTex,
0),
});
if (!result.computeUpdateSrb->create()) {
destroyAnimatingItem(result);
@@ -659,6 +715,11 @@ ThanosEffectRenderer::AnimatingItem ThanosEffectRenderer::createAnimatingItem(
QRhiShaderResourceBinding::FragmentStage,
tex,
sampler),
QRhiShaderResourceBinding::sampledTexture(
2,
QRhiShaderResourceBinding::VertexStage,
stateTex,
stateSampler),
});
if (!result.renderSrb->create()) {
destroyAnimatingItem(result);
@@ -681,7 +742,9 @@ void ThanosEffectRenderer::destroyAnimatingItem(AnimatingItem &item) {
deferDelete(item.renderUniformBuffer);
deferDelete(item.computeUpdateUniformBuffer);
deferDelete(item.computeInitUniformBuffer);
deferDelete(item.particleBuffer);
deferDelete(item.particleStateSampler);
deferDelete(item.particleVelocityTexture);
deferDelete(item.particleStateTexture);
deferDelete(item.sampler);
deferDelete(item.texture);
item = {};
@@ -710,12 +773,14 @@ void ThanosEffectRenderer::releaseResources() {
delete _computeInitSrbLayout;
_computeInitSrbLayout = nullptr;
delete _placeholderStateSampler;
_placeholderStateSampler = nullptr;
delete _placeholderStateTexture;
_placeholderStateTexture = nullptr;
delete _placeholderSampler;
_placeholderSampler = nullptr;
delete _placeholderTexture;
_placeholderTexture = nullptr;
delete _placeholderParticleBuffer;
_placeholderParticleBuffer = nullptr;
delete _renderUniformBuffer;
_renderUniformBuffer = nullptr;
@@ -66,7 +66,9 @@ private:
struct AnimatingItem {
QRhiTexture *texture = nullptr;
QRhiSampler *sampler = nullptr;
QRhiBuffer *particleBuffer = nullptr;
QRhiTexture *particleStateTexture = nullptr;
QRhiTexture *particleVelocityTexture = nullptr;
QRhiSampler *particleStateSampler = nullptr;
QRhiBuffer *computeInitUniformBuffer = nullptr;
QRhiBuffer *computeUpdateUniformBuffer = nullptr;
QRhiBuffer *renderUniformBuffer = nullptr;
@@ -95,9 +97,10 @@ private:
QRhiBuffer *_computeUpdateUniformBuffer = nullptr;
QRhiBuffer *_renderUniformBuffer = nullptr;
QRhiBuffer *_placeholderParticleBuffer = nullptr;
QRhiTexture *_placeholderTexture = nullptr;
QRhiSampler *_placeholderSampler = nullptr;
QRhiTexture *_placeholderStateTexture = nullptr;
QRhiSampler *_placeholderStateSampler = nullptr;
QRhiShaderResourceBindings *_computeInitSrbLayout = nullptr;
QRhiShaderResourceBindings *_computeUpdateSrbLayout = nullptr;
+7 -1
View File
@@ -44,7 +44,13 @@ foreach(_src ${_shader_sources})
if("${_ext}" STREQUAL ".comp")
set(_glsl_ver "310es,430")
else()
set(_glsl_ver "100es,120,150")
file(READ ${_src} _src_contents)
string(FIND "${_src_contents}" "texelFetch" _has_texelfetch)
if(NOT _has_texelfetch EQUAL -1)
set(_glsl_ver "300es,150")
else()
set(_glsl_ver "100es,120,150")
endif()
endif()
add_custom_command(
+9 -3
View File
@@ -2,12 +2,11 @@
layout(location = 0) in vec2 inQuadPos;
layout(location = 1) in vec2 inOffset;
layout(location = 2) in float inLifetime;
layout(location = 0) out vec2 v_texcoord;
layout(location = 1) out float v_alpha;
layout(binding = 2) uniform sampler2D particleStateTex;
layout(std140, binding = 0) uniform Params {
vec4 rect;
vec2 size;
@@ -20,6 +19,13 @@ void main() {
uint pX = particleId % particleResolution.x;
uint pY = particleId / particleResolution.x;
vec4 state = texelFetch(
particleStateTex,
ivec2(int(pX), int(pY)),
0);
vec2 inOffset = state.xy;
float inLifetime = state.z;
vec2 particleSize = size / vec2(particleResolution);
vec2 topLeft = vec2(float(pX) * particleSize.x, float(pY) * particleSize.y);
+9 -16
View File
@@ -2,16 +2,8 @@
layout(local_size_x = 64) in;
struct Particle {
vec2 offset;
vec2 velocity;
float lifetime;
float _pad;
};
layout(std430, binding = 0) buffer Particles {
Particle particles[];
};
layout(rgba32f, binding = 0) uniform image2D particleStateImage;
layout(rgba32f, binding = 2) uniform image2D particleVelocityImage;
layout(std140, binding = 1) uniform Params {
uint particleCountX;
@@ -45,11 +37,12 @@ void main() {
float direction = hashFloat(s) * (3.14159265 * 2.0);
float speed = (0.1 + hashFloat(s + 1u) * 0.1) * 320.0;
Particle p;
p.offset = vec2(0.0, 0.0);
p.velocity = vec2(cos(direction) * speed, sin(direction) * speed);
p.lifetime = 1.5 + hashFloat(s + 2u) * 1.5;
p._pad = 0.0;
vec2 velocity = vec2(cos(direction) * speed, sin(direction) * speed);
float lifetime = 1.5 + hashFloat(s + 2u) * 1.5;
particles[gid] = p;
ivec2 coord = ivec2(
int(gid % particleCountX),
int(gid / particleCountX));
imageStore(particleStateImage, coord, vec4(0.0, 0.0, lifetime, 0.0));
imageStore(particleVelocityImage, coord, vec4(velocity, 0.0, 0.0));
}
+16 -15
View File
@@ -2,16 +2,8 @@
layout(local_size_x = 64) in;
struct Particle {
vec2 offset;
vec2 velocity;
float lifetime;
float _pad;
};
layout(std430, binding = 0) buffer Particles {
Particle particles[];
};
layout(rgba32f, binding = 0) uniform image2D particleStateImage;
layout(rgba32f, binding = 2) uniform image2D particleVelocityImage;
layout(std140, binding = 1) uniform Params {
uint particleCountX;
@@ -43,11 +35,20 @@ void main() {
float particleXFraction = float(particleX) / float(particleCountX);
float particleFraction = easeInWindow(effectFraction, particleXFraction);
Particle p = particles[gid];
ivec2 coord = ivec2(
int(particleX),
int(gid / particleCountX));
p.offset += p.velocity * timeStep * particleFraction;
p.velocity.y += 80.0 * timeStep * particleFraction;
p.lifetime = max(0.0, p.lifetime - 0.6 * timeStep * particleFraction);
vec4 state = imageLoad(particleStateImage, coord);
vec2 offset = state.xy;
float lifetime = state.z;
particles[gid] = p;
vec2 velocity = imageLoad(particleVelocityImage, coord).xy;
offset += velocity * timeStep * particleFraction;
velocity.y += 80.0 * timeStep * particleFraction;
lifetime = max(0.0, lifetime - 0.6 * timeStep * particleFraction);
imageStore(particleStateImage, coord, vec4(offset, lifetime, 0.0));
imageStore(particleVelocityImage, coord, vec4(velocity, 0.0, 0.0));
}