Commit 3f647b1b by Jamie Madill Committed by Commit Bot

Vulkan: Improve Bresenham line emulation.

Clamps the vertex position to the subpixel grid before interpolation. This will give more correct results on systems that have less than 8 bits of subpixel accuracy. Also uses a more accurate formulation for the emulation filter in the fragment shader using dfdx and dfdy. Fixes line raster CTS tests on SwiftShader. Still does not produce spec conformant lines. Updates the public docs to indicate this. Bug: angleproject:2830 Change-Id: Ib9a268df3e7d986bd2b1348be664389fe8fc0ef2 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1826598 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent b9b1bae4
......@@ -504,6 +504,9 @@ struct ShBuiltInResources
int MaxGeometryShaderStorageBlocks;
int MaxGeometryShaderInvocations;
int MaxGeometryImageUniforms;
// Subpixel bits used in rasterization.
int SubPixelBits;
};
//
......
......@@ -253,6 +253,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxGeometryShaderStorageBlocks = 0;
resources->MaxGeometryShaderInvocations = 32;
resources->MaxGeometryImageUniforms = 0;
resources->SubPixelBits = 8;
}
//
......
......@@ -73,6 +73,23 @@ TIntermTyped *CreateBuiltInFunctionCallNode(const char *name,
const TSymbolTable &symbolTable,
int shaderVersion);
inline void GetSwizzleIndex(TVector<int> *indexOut) {}
template <typename T, typename... ArgsT>
void GetSwizzleIndex(TVector<int> *indexOut, T arg, ArgsT... args)
{
indexOut->push_back(arg);
GetSwizzleIndex(indexOut, args...);
}
template <typename... ArgsT>
TIntermSwizzle *CreateSwizzle(TIntermTyped *reference, ArgsT... args)
{
TVector<int> swizzleIndex;
GetSwizzleIndex(&swizzleIndex, args...);
return new TIntermSwizzle(reference, swizzleIndex);
}
} // namespace sh
#endif // COMPILER_TRANSLATOR_INTERMNODEUTIL_H_
......@@ -187,6 +187,9 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state)
mResources.MaxGeometryShaderStorageBlocks = caps.maxShaderStorageBlocks[ShaderType::Geometry];
mResources.MaxGeometryShaderInvocations = caps.maxGeometryShaderInvocations;
mResources.MaxGeometryImageUniforms = caps.maxShaderImageUniforms[ShaderType::Geometry];
// Subpixel bits.
mResources.SubPixelBits = static_cast<int>(caps.subPixelBits);
}
Compiler::~Compiler()
......
......@@ -23,22 +23,29 @@ more info. See the below diagram for an illustration of the diamond rule:
![OpenGL Diamond Rule Example][DiamondRule]
We can implement the OpenGL test by checking the intersection of the line and the medial axes of the
pixel `p`. If the length of the line segment between intersections `p` and the point center is
greater than a half-pixel for all possible `p` then the pixel is not on the segment. To solve for
`p` we use the pixel center `a` given by `gl_FragCoord` and the projection of `a` onto the line
segment `b` given by the interpolated `gl_Position`. Since `gl_Position` is not available in the
fragment shader we must add an internal position varying when drawing lines.
The full code derivation is omitted for brevity. It reduces to the following shader snippet:
```vec2 position = PositionVarying.xy / PositionVarying.w;
vec2 b = ((position * 0.5) + 0.5) * gl_Viewport.zw + gl_Viewport.xy;
vec2 ba = abs(b - gl_FragCoord.xy);
vec2 ba2 = 2.0 * (ba * ba);
vec2 bp = ba2 + ba2.yx - ba;
if (bp.x > epsilon && bp.y > epsilon)
discard;
The diamond rule can be implemented in the fragment shader by computing the
intersection between the line segment and the grid that crosses the pixel
center. If the distance between an intersection and the pixel center is less
than half a pixel then the line enters and exits the diamond. `f` is the pixel
center in the diagram. The green circle indicates a diamond exit and the red
circles indicate intersections that do not exit the diamond. We detect
non-Bresenham fragments when both intersections are outside the diamond.
The full code derivation is omitted for brevity. It produces the following
fragment shader patch implementation:
```
vec2 p = (((((ANGLEPosition.xy) * 0.5) + 0.5) * viewport.zw) + viewport.xy);
vec2 d = dFdx(p) + dFdy(p);
vec2 f = gl_FragCoord.xy;
vec2 p_ = p.yx;
vec2 d_ = d.yx;
vec2 f_ = f.yx;
vec2 i = abs(p - f + (d/d_) * (f_ - p_));
if (i.x > 0.500001 && i.y > 0.500001)
discard;
```
Note that we must also pass the viewport size as an internal uniform. We use a small epsilon value
......@@ -46,8 +53,22 @@ to correct for cases when the line segment is perfectly parallel or perpendicula
code please see [TranslatorVulkan.cpp][TranslatorVulkan.cpp] under
`AddLineSegmentRasterizationEmulation`.
## Limitations
Although this emulation passes all current GLES CTS tests it is not guaranteed
to produce conformant lines. In particular lines that very nearly intersect
the junction of four pixels render with holes. For example:
![Holes in the emulated Bresenham line][Holes]
Therefore for a complete implementation we require the Bresenham line
rasterization feature from
[VK_EXT_line_rasterization][VK_EXT_line_rasterization].
[Bresenham]: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
[DiamondRule]: img/LineRasterPixelExample.png
[Holes]: img/LineRasterHoles.jpg
[TranslatorVulkan.cpp]: https://chromium.googlesource.com/angle/angle/+/refs/heads/master/src/compiler/translator/TranslatorVulkan.cpp
[VK_EXT_line_rasterization]: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_EXT_line_rasterization.html
[VulkanLineRaster]: https://www.khronos.org/registry/vulkan/specs/1.1/html/chap24.html#primsrast-lines-basic
[VulkanVsGLLineRaster]: img/LineRasterComparison.gif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment