scene: Add pixel grid snapping debug pass
The debug shader is targeted to help with debugging blurriness when using fractional scaling. The shader works as follows: - if the vertex coordinate has fractional part, the item will be highlighted with blue color - if the texture coordinate (in device pixels) has fractional part, the item will be highlighted with red color The shader can be toggled by setting the KWIN_SCENE_VISUALIZE=fractional environment variable.
This commit is contained in:
parent
6f5aa9e4f3
commit
07f6713a18
8 changed files with 249 additions and 4 deletions
|
@ -120,6 +120,7 @@ target_sources(kwin PRIVATE
|
|||
pointer_input.cpp
|
||||
popup_input_filter.cpp
|
||||
rootinfo_filter.cpp
|
||||
resources.qrc
|
||||
rulebooksettings.cpp
|
||||
rules.cpp
|
||||
scene/cursoritem.cpp
|
||||
|
|
8
src/resources.qrc
Normal file
8
src/resources.qrc
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>scene/shaders/debug_fractional.frag</file>
|
||||
<file>scene/shaders/debug_fractional_core.frag</file>
|
||||
<file>scene/shaders/debug_fractional.vert</file>
|
||||
<file>scene/shaders/debug_fractional_core.vert</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -19,6 +19,11 @@ namespace KWin
|
|||
|
||||
ItemRendererOpenGL::ItemRendererOpenGL()
|
||||
{
|
||||
const QString visualizeOptionsString = qEnvironmentVariable("KWIN_SCENE_VISUALIZE");
|
||||
if (!visualizeOptionsString.isEmpty()) {
|
||||
const QStringList visualtizeOptions = visualizeOptionsString.split(';');
|
||||
m_debug.fractionalEnabled = visualtizeOptions.contains(QLatin1StringView("fractional"));
|
||||
}
|
||||
}
|
||||
|
||||
ImageItem *ItemRendererOpenGL::createImageItem(Scene *scene, Item *parent)
|
||||
|
@ -255,6 +260,7 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
|
|||
}
|
||||
|
||||
RenderContext renderContext{
|
||||
.projectionMatrix = data.projectionMatrix(),
|
||||
.clip = region,
|
||||
.hardwareClipping = region != infiniteRegion() && ((mask & Scene::PAINT_WINDOW_TRANSFORMED) || (mask & Scene::PAINT_SCREEN_TRANSFORMED)),
|
||||
.renderTargetScale = viewport.scale(),
|
||||
|
@ -332,7 +338,6 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
|
|||
scissorRegion = viewport.mapToRenderTarget(region);
|
||||
}
|
||||
|
||||
const QMatrix4x4 projectionMatrix = data.projectionMatrix();
|
||||
for (int i = 0; i < renderContext.renderNodes.count(); i++) {
|
||||
const RenderNode &renderNode = renderContext.renderNodes[i];
|
||||
if (renderNode.vertexCount == 0) {
|
||||
|
@ -341,7 +346,7 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
|
|||
|
||||
setBlendEnabled(renderNode.hasAlpha || renderNode.opacity < 1.0);
|
||||
|
||||
shader->setUniform(GLShader::ModelViewProjectionMatrix, projectionMatrix * renderNode.transformMatrix);
|
||||
shader->setUniform(GLShader::ModelViewProjectionMatrix, renderContext.projectionMatrix * renderNode.transformMatrix);
|
||||
if (opacity != renderNode.opacity) {
|
||||
shader->setUniform(GLShader::ModulationConstant,
|
||||
modulate(renderNode.opacity, data.brightness()));
|
||||
|
@ -356,15 +361,56 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
|
|||
renderNode.vertexCount, renderContext.hardwareClipping);
|
||||
}
|
||||
|
||||
ShaderManager::instance()->popShader();
|
||||
|
||||
if (m_debug.fractionalEnabled) {
|
||||
visualizeFractional(viewport, scissorRegion, renderContext);
|
||||
}
|
||||
|
||||
vbo->unbindArrays();
|
||||
|
||||
setBlendEnabled(false);
|
||||
|
||||
ShaderManager::instance()->popShader();
|
||||
|
||||
if (renderContext.hardwareClipping) {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
void ItemRendererOpenGL::visualizeFractional(const RenderViewport &viewport, const QRegion ®ion, const RenderContext &renderContext)
|
||||
{
|
||||
if (!m_debug.fractionalShader) {
|
||||
m_debug.fractionalShader = ShaderManager::instance()->generateShaderFromFile(
|
||||
ShaderTrait::MapTexture,
|
||||
QStringLiteral(":/scene/shaders/debug_fractional.vert"),
|
||||
QStringLiteral(":/scene/shaders/debug_fractional.frag"));
|
||||
}
|
||||
|
||||
if (!m_debug.fractionalShader) {
|
||||
return;
|
||||
}
|
||||
|
||||
ShaderBinder debugShaderBinder(m_debug.fractionalShader.get());
|
||||
m_debug.fractionalShader->setUniform("fractionalPrecision", 0.01f);
|
||||
|
||||
auto screenSize = viewport.renderRect().size() * viewport.scale();
|
||||
m_debug.fractionalShader->setUniform("screenSize", QVector2D(float(screenSize.width()), float(screenSize.height())));
|
||||
|
||||
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
|
||||
|
||||
for (int i = 0; i < renderContext.renderNodes.count(); i++) {
|
||||
const RenderNode &renderNode = renderContext.renderNodes[i];
|
||||
if (renderNode.vertexCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setBlendEnabled(true);
|
||||
|
||||
m_debug.fractionalShader->setUniform("geometrySize", QVector2D(renderNode.texture->width(), renderNode.texture->height()));
|
||||
m_debug.fractionalShader->setUniform(GLShader::ModelViewProjectionMatrix, renderContext.projectionMatrix * renderNode.transformMatrix);
|
||||
|
||||
vbo->draw(region, GL_TRIANGLES, renderNode.firstVertex,
|
||||
renderNode.vertexCount, renderContext.hardwareClipping);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
QVector<RenderNode> renderNodes;
|
||||
QStack<QMatrix4x4> transformStack;
|
||||
QStack<qreal> opacityStack;
|
||||
const QMatrix4x4 projectionMatrix;
|
||||
const QRegion clip;
|
||||
const bool hardwareClipping;
|
||||
const qreal renderTargetScale;
|
||||
|
@ -53,8 +54,15 @@ private:
|
|||
QVector4D modulate(float opacity, float brightness) const;
|
||||
void setBlendEnabled(bool enabled);
|
||||
void createRenderNode(Item *item, RenderContext *context);
|
||||
void visualizeFractional(const RenderViewport &viewport, const QRegion ®ion, const RenderContext &renderContext);
|
||||
|
||||
bool m_blendingEnabled = false;
|
||||
|
||||
struct
|
||||
{
|
||||
bool fractionalEnabled = false;
|
||||
std::unique_ptr<GLShader> fractionalShader;
|
||||
} m_debug;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
|
|
46
src/scene/shaders/debug_fractional.frag
Normal file
46
src/scene/shaders/debug_fractional.frag
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#version 130 compatibility
|
||||
|
||||
uniform float fractionalPrecision;
|
||||
uniform vec2 geometrySize;
|
||||
|
||||
varying vec2 texcoord0;
|
||||
varying float vertexFractional;
|
||||
|
||||
// paint every time we query textures at non-integer alignments
|
||||
// it implies we're being upscaled in ways that will cause blurryness
|
||||
// 2x scaling will go through fine
|
||||
void main()
|
||||
{
|
||||
const float strength = 0.4;
|
||||
|
||||
// Calculate an error correction value based on the minimum precision we
|
||||
// want to measure.
|
||||
float errorCorrection = 1.0 / fractionalPrecision;
|
||||
|
||||
// Determine which exact pixel we are reading from the source texture.
|
||||
// Texture sampling happens in the middle of a pixel so we need to add 0.5.
|
||||
vec2 sourcePixel = texcoord0 * geometrySize + 0.5;
|
||||
// Cancel out any precision artifacts below what we actually want to measure.
|
||||
sourcePixel = round(sourcePixel * errorCorrection) / errorCorrection;
|
||||
|
||||
// The total error is the sum of the fractional parts of the source pixel.
|
||||
float error = dot(fract(sourcePixel), vec2(1.0));
|
||||
|
||||
vec4 fragColor = vec4(0.0);
|
||||
|
||||
if (vertexFractional > 0.5) {
|
||||
fragColor = mix(fragColor, vec4(0.0, 0.0, 1.0, 1.0), strength);
|
||||
}
|
||||
|
||||
if (error > fractionalPrecision) {
|
||||
fragColor = mix(fragColor, vec4(1.0, 0.0, 0.0, 1.0), strength);
|
||||
}
|
||||
|
||||
gl_FragColor = fragColor;
|
||||
}
|
43
src/scene/shaders/debug_fractional.vert
Normal file
43
src/scene/shaders/debug_fractional.vert
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
uniform mat4 modelViewProjectionMatrix;
|
||||
uniform float fractionalPrecision;
|
||||
uniform vec2 screenSize;
|
||||
uniform vec2 geometrySize;
|
||||
|
||||
attribute vec4 vertex;
|
||||
attribute vec4 texcoord;
|
||||
|
||||
varying vec2 texcoord0;
|
||||
varying float vertexFractional;
|
||||
|
||||
// This shader calculates the fractional component of the vertex position and
|
||||
// passes 1 to the fragment shader if it is larger than the precision we want to
|
||||
// measure, or 0 if it is not. The fragment shader can then use that information
|
||||
// to color the pixel based on that value. 0 or 1 is used instead of something
|
||||
// like vertex coloring because of vertex interpolation and the fragment shader
|
||||
// having control over the final appearance.
|
||||
void main(void)
|
||||
{
|
||||
float errorCorrection = 1.0 / fractionalPrecision;
|
||||
|
||||
gl_Position = modelViewProjectionMatrix * vertex;
|
||||
|
||||
vec2 screenPosition = ((gl_Position.xy / gl_Position.w + vec2(1.0)) / vec2(2.0)) * screenSize;
|
||||
// Cancel out any floating point errors below what we want to measure.
|
||||
screenPosition = round(screenPosition * errorCorrection) / errorCorrection;
|
||||
|
||||
// Dermine how far off the pixel grid this vertex is.
|
||||
vec2 error = fract(screenPosition);
|
||||
|
||||
vertexFractional = dot(error, vec2(1.0)) > fractionalPrecision ? 1.0 : 0.0;
|
||||
|
||||
// Correct texture sampling for floating-point error on the vertices.
|
||||
// This currently assumes UV coordinates are always from 0 to 1 over an
|
||||
// entire triangle.
|
||||
texcoord0 = texcoord.xy + (error / geometrySize);
|
||||
}
|
48
src/scene/shaders/debug_fractional_core.frag
Normal file
48
src/scene/shaders/debug_fractional_core.frag
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#version 140
|
||||
|
||||
uniform float fractionalPrecision;
|
||||
uniform vec2 geometrySize;
|
||||
|
||||
in vec2 texcoord0;
|
||||
in float vertexFractional;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// paint every time we query textures at non-integer alignments
|
||||
// it implies we're being upscaled in ways that will cause blurryness
|
||||
// 2x scaling will go through fine
|
||||
void main()
|
||||
{
|
||||
const float strength = 0.4;
|
||||
|
||||
// Calculate an error correction value based on the minimum precision we
|
||||
// want to measure.
|
||||
float errorCorrection = 1.0 / fractionalPrecision;
|
||||
|
||||
// Determine which exact pixel we are reading from the source texture.
|
||||
// Texture sampling happens in the middle of a pixel so we need to add 0.5.
|
||||
vec2 sourcePixel = texcoord0 * geometrySize + 0.5;
|
||||
// Cancel out any precision artifacts below what we actually want to measure.
|
||||
sourcePixel = round(sourcePixel * errorCorrection) / errorCorrection;
|
||||
|
||||
// The total error is the sum of the fractional parts of the source pixel.
|
||||
float error = dot(fract(sourcePixel), vec2(1.0));
|
||||
|
||||
fragColor = vec4(0.0);
|
||||
|
||||
if (vertexFractional > 0.5) {
|
||||
fragColor = mix(fragColor, vec4(0.0, 0.0, 1.0, 1.0), strength);
|
||||
}
|
||||
|
||||
if (error > fractionalPrecision) {
|
||||
fragColor = mix(fragColor, vec4(1.0, 0.0, 0.0, 1.0), strength);
|
||||
}
|
||||
}
|
||||
|
45
src/scene/shaders/debug_fractional_core.vert
Normal file
45
src/scene/shaders/debug_fractional_core.vert
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#version 140
|
||||
|
||||
uniform mat4 modelViewProjectionMatrix;
|
||||
uniform float fractionalPrecision;
|
||||
uniform vec2 screenSize;
|
||||
uniform vec2 geometrySize;
|
||||
|
||||
in vec4 vertex;
|
||||
in vec4 texcoord;
|
||||
|
||||
out vec2 texcoord0;
|
||||
out float vertexFractional;
|
||||
|
||||
// This shader calculates the fractional component of the vertex position and
|
||||
// passes 1 to the fragment shader if it is larger than the precision we want to
|
||||
// measure, or 0 if it is not. The fragment shader can then use that information
|
||||
// to color the pixel based on that value. 0 or 1 is used instead of something
|
||||
// like vertex coloring because of vertex interpolation and the fragment shader
|
||||
// having control over the final appearance.
|
||||
void main(void)
|
||||
{
|
||||
float errorCorrection = 1.0 / fractionalPrecision;
|
||||
|
||||
gl_Position = modelViewProjectionMatrix * vertex;
|
||||
|
||||
vec2 screenPosition = ((gl_Position.xy / gl_Position.w + vec2(1.0)) / vec2(2.0)) * screenSize;
|
||||
// Cancel out any floating point errors below what we want to measure.
|
||||
screenPosition = round(screenPosition * errorCorrection) / errorCorrection;
|
||||
|
||||
// Dermine how far off the pixel grid this vertex is.
|
||||
vec2 error = fract(screenPosition);
|
||||
|
||||
vertexFractional = dot(error, vec2(1.0)) > fractionalPrecision ? 1.0 : 0.0;
|
||||
|
||||
// Correct texture sampling for floating-point error on the vertices.
|
||||
// This currently assumes UV coordinates are always from 0 to 1 over an
|
||||
// entire triangle.
|
||||
texcoord0 = texcoord.xy + (error / geometrySize);
|
||||
}
|
Loading…
Reference in a new issue