Skip to content

Commit 8547e8b

Browse files
zhanimnutt
authored andcommitted
[TextureMapper] Polygon clipping performance is extremely slow
https://bugs.webkit.org/show_bug.cgi?id=284027 Reviewed by Fujii Hironori. Rendering complex preserve-3D scenes might involve a lot of layer splitting. These splits are handled using polygon clipping, but the current implementation is inefficient. Each clip operation clears the full stencil buffer and masks the clipped part using separate vertex buffer objects that are allocated on demand. This places significant stress on the GPU and on hardware like rpi3 it completely chokes the rendering pipeline. To address this issue, polygon clip vertices for preserve-3d scenes should be loaded to the GPU only once, leveraging cached VBOs. Additionally, stencil buffer clearing should be optimized using scissors to restrict stencil clears to relevant areas, avoiding full-buffer operations. * Source/WebCore/platform/TextureMapper.cmake: * Source/WebCore/platform/graphics/texmap/ClipPath.cpp: Added. (WebCore::ClipPath::ClipPath): (WebCore::ClipPath::data const): * Source/WebCore/platform/graphics/texmap/ClipPath.h: Added. * Source/WebCore/platform/graphics/texmap/TextureMapper.cpp: (WebCore::nextPowerOf2): (WebCore::TextureMapperGLData::initializeStencil): (WebCore::TextureMapperGLData::getBufferFromPool): (WebCore::TextureMapper::beginClip): (WebCore::TextureMapper::updateProjectionMatrix): (WebCore::TextureMapper::acquireBufferFromPool): * Source/WebCore/platform/graphics/texmap/TextureMapper.h: * Source/WebCore/platform/graphics/texmap/TextureMapperGPUBuffer.cpp: Added. (WebCore::TextureMapperGPUBuffer::TextureMapperGPUBuffer): (WebCore::TextureMapperGPUBuffer::~TextureMapperGPUBuffer): (WebCore::TextureMapperGPUBuffer::updateData): * Source/WebCore/platform/graphics/texmap/TextureMapperGPUBuffer.h: Added. * Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp: (WebCore::TextureMapperLayer::paintWith3DRenderingContext): * Source/WebCore/platform/graphics/texmap/TextureMapperLayer3DRenderingContext.cpp: (WebCore::TextureMapperLayer3DRenderingContext::paint): (WebCore::TextureMapperLayer3DRenderingContext::traverseTree): (WebCore::TextureMapperLayer3DRenderingContext::traverseTreeAndPaint): Deleted. * Source/WebCore/platform/graphics/texmap/TextureMapperLayer3DRenderingContext.h: Canonical link: https://commits.webkit.org/287519@main
1 parent a17bc29 commit 8547e8b

File tree

10 files changed

+399
-55
lines changed

10 files changed

+399
-55
lines changed

Source/WebCore/platform/TextureMapper.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ list(APPEND WebCore_PRIVATE_INCLUDE_DIRECTORIES
55
list(APPEND WebCore_SOURCES
66
platform/graphics/texmap/BitmapTexture.cpp
77
platform/graphics/texmap/BitmapTexturePool.cpp
8+
platform/graphics/texmap/ClipPath.cpp
89
platform/graphics/texmap/ClipStack.cpp
910
platform/graphics/texmap/FloatPlane3D.cpp
1011
platform/graphics/texmap/FloatPolygon3D.cpp
@@ -14,6 +15,7 @@ list(APPEND WebCore_SOURCES
1415
platform/graphics/texmap/TextureMapperBackingStore.cpp
1516
platform/graphics/texmap/TextureMapperFPSCounter.cpp
1617
platform/graphics/texmap/TextureMapperGCGLPlatformLayer.cpp
18+
platform/graphics/texmap/TextureMapperGPUBuffer.cpp
1719
platform/graphics/texmap/TextureMapperLayer.cpp
1820
platform/graphics/texmap/TextureMapperLayer3DRenderingContext.cpp
1921
platform/graphics/texmap/TextureMapperPlatformLayer.cpp
@@ -23,6 +25,7 @@ list(APPEND WebCore_SOURCES
2325
list(APPEND WebCore_PRIVATE_FRAMEWORK_HEADERS
2426
platform/graphics/texmap/BitmapTexture.h
2527
platform/graphics/texmap/BitmapTexturePool.h
28+
platform/graphics/texmap/ClipPath.h
2629
platform/graphics/texmap/ClipStack.h
2730
platform/graphics/texmap/FloatPlane3D.h
2831
platform/graphics/texmap/FloatPolygon3D.h
@@ -35,6 +38,7 @@ list(APPEND WebCore_PRIVATE_FRAMEWORK_HEADERS
3538
platform/graphics/texmap/TextureMapperFlags.h
3639
platform/graphics/texmap/TextureMapperFPSCounter.h
3740
platform/graphics/texmap/TextureMapperGLHeaders.h
41+
platform/graphics/texmap/TextureMapperGPUBuffer.h
3842
platform/graphics/texmap/TextureMapperLayer.h
3943
platform/graphics/texmap/TextureMapperLayer3DRenderingContext.h
4044
platform/graphics/texmap/TextureMapperPlatformLayer.h
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (C) 2024 Jani Hautakangas <[email protected]>
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* 2. Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following
12+
* disclaimer in the documentation and/or other materials provided
13+
* with the distribution.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
*/
27+
28+
#include "config.h"
29+
#include "ClipPath.h"
30+
31+
#include <wtf/TZoneMallocInlines.h>
32+
33+
namespace WebCore {
34+
35+
WTF_MAKE_TZONE_ALLOCATED_IMPL(ClipPath);
36+
37+
ClipPath::ClipPath(Vector<FloatPoint>&& vertices, unsigned bufferID, unsigned bufferOffsetInBytes)
38+
: m_vertices(WTFMove(vertices))
39+
, m_bufferID(bufferID)
40+
, m_bufferOffsetInBytes(bufferOffsetInBytes)
41+
{
42+
unsigned vertexCount = numberOfVertices();
43+
if (vertexCount) {
44+
m_bounds.setLocation(m_vertices.at(0));
45+
for (size_t i = 1; i < vertexCount; i++)
46+
m_bounds.extend(m_vertices.at(i));
47+
}
48+
}
49+
50+
const void* ClipPath::bufferDataOffsetAsPtr() const
51+
{
52+
if (m_bufferID)
53+
return reinterpret_cast<const void*>(m_bufferOffsetInBytes);
54+
55+
return nullptr;
56+
}
57+
58+
} // namespace WebCore
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (C) 2024 Jani Hautakangas <[email protected]>
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* 2. Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following
12+
* disclaimer in the documentation and/or other materials provided
13+
* with the distribution.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
*/
27+
28+
#pragma once
29+
30+
#include "FloatPoint.h"
31+
#include "FloatRect.h"
32+
#include <wtf/TZoneMalloc.h>
33+
#include <wtf/Vector.h>
34+
35+
namespace WebCore {
36+
37+
class ClipPath final {
38+
WTF_MAKE_TZONE_ALLOCATED(ClipPath);
39+
public:
40+
ClipPath(Vector<FloatPoint>&& vertices, unsigned bufferID, unsigned bufferOffsetInBytes);
41+
42+
bool isEmpty() const { return m_vertices.isEmpty(); }
43+
unsigned bufferID() const { return m_bufferID; }
44+
const void* bufferDataOffsetAsPtr() const;
45+
unsigned numberOfVertices() const { return m_vertices.size(); }
46+
47+
FloatRect bounds() const { return m_bounds; }
48+
49+
private:
50+
51+
const Vector<FloatPoint> m_vertices;
52+
unsigned m_bufferID;
53+
unsigned m_bufferOffsetInBytes;
54+
55+
FloatRect m_bounds;
56+
};
57+
58+
} // namespace WebCore

Source/WebCore/platform/graphics/texmap/TextureMapper.cpp

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#if USE(TEXTURE_MAPPER)
2626

2727
#include "BitmapTexture.h"
28+
#include "ClipPath.h"
2829
#include "FilterOperations.h"
2930
#include "FloatPolygon.h"
3031
#include "FloatQuad.h"
@@ -54,6 +55,15 @@ namespace WebCore {
5455

5556
WTF_MAKE_TZONE_ALLOCATED_IMPL(TextureMapper);
5657

58+
static size_t nextPowerOf2(size_t n)
59+
{
60+
if (!n)
61+
return 1;
62+
63+
const int totalBits = static_cast<int>(sizeof(size_t) * CHAR_BIT);
64+
return static_cast<size_t>(1) << (totalBits - std::countl_zero(n - 1));
65+
}
66+
5767
class TextureMapperGLData {
5868
WTF_MAKE_TZONE_ALLOCATED_INLINE(TextureMapperGLData);
5969
public:
@@ -63,6 +73,7 @@ class TextureMapperGLData {
6373
void initializeStencil();
6474
GLuint getStaticVBO(GLenum target, GLsizeiptr, const void* data);
6575
Ref<TextureMapperShaderProgram> getShaderProgram(TextureMapperShaderProgram::Options);
76+
Ref<TextureMapperGPUBuffer> getBufferFromPool(size_t, TextureMapperGPUBuffer::Type);
6677

6778
TransformationMatrix projectionMatrix;
6879
TextureMapper::FlipY flipY { TextureMapper::FlipY::No };
@@ -119,6 +130,7 @@ class TextureMapperGLData {
119130

120131
Ref<SharedGLData> m_sharedGLData;
121132
UncheckedKeyHashMap<const void*, GLuint> m_vbos;
133+
UncheckedKeyHashMap<uint64_t, Vector<Ref<TextureMapperGPUBuffer>>> m_buffers;
122134
};
123135

124136
TextureMapperGLData::TextureMapperGLData(void* platformContext)
@@ -130,6 +142,9 @@ TextureMapperGLData::~TextureMapperGLData()
130142
{
131143
for (auto& entry : m_vbos)
132144
glDeleteBuffers(1, &entry.value);
145+
146+
for (auto& entry : m_buffers)
147+
entry.value.clear();
133148
}
134149

135150
void TextureMapperGLData::initializeStencil()
@@ -141,7 +156,6 @@ void TextureMapperGLData::initializeStencil()
141156

142157
if (didModifyStencil)
143158
return;
144-
145159
glClearStencil(0);
146160
glClear(GL_STENCIL_BUFFER_BIT);
147161
didModifyStencil = true;
@@ -169,6 +183,29 @@ Ref<TextureMapperShaderProgram> TextureMapperGLData::getShaderProgram(TextureMap
169183
return *addResult.iterator->value;
170184
}
171185

186+
Ref<TextureMapperGPUBuffer> TextureMapperGLData::getBufferFromPool(size_t size, TextureMapperGPUBuffer::Type type)
187+
{
188+
if (!size) {
189+
// Use static zero buffer
190+
static auto zeroBuffer = TextureMapperGPUBuffer::create(size, type, TextureMapperGPUBuffer::Usage::Dynamic);
191+
return zeroBuffer;
192+
}
193+
194+
RELEASE_ASSERT(size < std::numeric_limits<uint32_t>::max());
195+
uint64_t key = (static_cast<uint64_t>(type) << 32) | static_cast<uint32_t>(size);
196+
auto& buffers = m_buffers.ensure(key, [] {
197+
return Vector<Ref<TextureMapperGPUBuffer>> { };
198+
}).iterator->value;
199+
200+
for (auto& buffer : buffers) {
201+
if (buffer->refCount() == 1)
202+
return buffer;
203+
}
204+
205+
buffers.append(TextureMapperGPUBuffer::create(size, type, TextureMapperGPUBuffer::Usage::Dynamic));
206+
return buffers.last();
207+
}
208+
172209
std::unique_ptr<TextureMapper> TextureMapper::create()
173210
{
174211
return makeUnique<TextureMapper>();
@@ -1319,7 +1356,7 @@ void TextureMapper::beginClip(const TransformationMatrix& modelViewMatrix, const
13191356
clipStack().applyIfNeeded();
13201357
}
13211358

1322-
void TextureMapper::beginClip(const TransformationMatrix& modelViewMatrix, const FloatPolygon& polygon)
1359+
void TextureMapper::beginClip(const TransformationMatrix& modelViewMatrix, const ClipPath& clipPath)
13231360
{
13241361
clipStack().push();
13251362
data().initializeStencil();
@@ -1329,14 +1366,14 @@ void TextureMapper::beginClip(const TransformationMatrix& modelViewMatrix, const
13291366
glUseProgram(program->programID());
13301367
glEnableVertexAttribArray(program->vertexLocation());
13311368

1332-
unsigned numberOfVertices = polygon.numberOfVertices();
1333-
Vector<GLfloat> polygonVertices;
1334-
polygonVertices.reserveCapacity(numberOfVertices * 2);
1335-
for (unsigned i = 0; i < numberOfVertices; i++) {
1336-
auto v = polygon.vertexAt(i);
1337-
polygonVertices.append(v.x());
1338-
polygonVertices.append(v.y());
1339-
}
1369+
// Compute the scissor rectangle from the clip path bounding box.
1370+
IntRect scissorRect = modelViewMatrix.mapQuad(clipPath.bounds()).enclosingBoundingBox();
1371+
IntRect viewport(data().viewport[0], data().viewport[1], data().viewport[2], data().viewport[3]);
1372+
scissorRect.intersect(viewport);
1373+
1374+
// Set up the scissor rectangle to limit stencil operations to the clip bounds.
1375+
glScissor(scissorRect.x(), (data().flipY == FlipY::Yes) ? scissorRect.y() : viewport.height() - scissorRect.maxY(),
1376+
scissorRect.width(), scissorRect.height());
13401377

13411378
int stencilIndex = clipStack().getStencilIndex();
13421379

@@ -1348,9 +1385,9 @@ void TextureMapper::beginClip(const TransformationMatrix& modelViewMatrix, const
13481385
// Operate only on the stencilIndex and above.
13491386
glStencilMask(0xff & ~(stencilIndex - 1));
13501387

1351-
// First clear the entire buffer at the current index.
1388+
// Clear the stencil buffer at the current index.
13521389
static const TransformationMatrix fullProjectionMatrix = TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), FloatRect(-1, -1, 2, 2));
1353-
const GLfloat unitRect[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
1390+
static const GLfloat unitRect[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
13541391
GLuint vbo = data().getStaticVBO(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, unitRect);
13551392
glBindBuffer(GL_ARRAY_BUFFER, vbo);
13561393
glVertexAttribPointer(program->vertexLocation(), 2, GL_FLOAT, false, 0, 0);
@@ -1360,22 +1397,21 @@ void TextureMapper::beginClip(const TransformationMatrix& modelViewMatrix, const
13601397
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
13611398

13621399
// Now apply the current index to the new polygon.
1363-
GLuint polygonVBO;
1364-
glGenBuffers(1, &polygonVBO);
1365-
glBindBuffer(GL_ARRAY_BUFFER, polygonVBO);
1366-
glBufferData(GL_ARRAY_BUFFER, polygonVertices.size() * sizeof(GLfloat), polygonVertices.data(), GL_STATIC_DRAW);
1367-
glVertexAttribPointer(program->vertexLocation(), 2, GL_FLOAT, false, 0, 0);
1368-
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
1400+
glBindBuffer(GL_ARRAY_BUFFER, clipPath.bufferID());
1401+
glVertexAttribPointer(program->vertexLocation(), 2, GL_FLOAT, false, 0, clipPath.bufferDataOffsetAsPtr());
13691402
program->setMatrix(program->projectionMatrixLocation(), data().projectionMatrix);
13701403
program->setMatrix(program->modelViewMatrixLocation(), modelViewMatrix);
1371-
glDrawArrays(GL_TRIANGLE_FAN, 0, polygonVertices.size() / 2);
1372-
glDeleteBuffers(1, &polygonVBO);
1404+
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
1405+
glDrawArrays(GL_TRIANGLE_FAN, 0, clipPath.numberOfVertices());
13731406

13741407
// Clear the state.
13751408
glBindBuffer(GL_ARRAY_BUFFER, 0);
13761409
glDisableVertexAttribArray(program->vertexLocation());
13771410
glStencilMask(0);
13781411

1412+
// Store the scissor box in the clip stack to prevent it from being reset.
1413+
clipStack().intersect(scissorRect);
1414+
13791415
// Increase stencilIndex and apply stencil testing.
13801416
clipStack().setStencilIndex(stencilIndex * 2);
13811417
clipStack().applyIfNeeded();
@@ -1417,6 +1453,7 @@ void TextureMapper::updateProjectionMatrix()
14171453
WTF_ALLOW_UNSAFE_BUFFER_USAGE_END
14181454
flipY = data().flipY == FlipY::Yes;
14191455
}
1456+
14201457
data().projectionMatrix = createProjectionMatrix(size, flipY, data().zNear, data().zFar);
14211458
}
14221459

@@ -1427,6 +1464,16 @@ void TextureMapper::drawTextureExternalOES(GLuint texture, OptionSet<TextureMapp
14271464
drawTexturedQuadWithProgram(program.get(), { { texture, program->externalOESTextureLocation() } }, flags, targetRect, modelViewMatrix, opacity);
14281465
}
14291466

1467+
Ref<TextureMapperGPUBuffer> TextureMapper::acquireBufferFromPool(size_t size, TextureMapperGPUBuffer::Type type)
1468+
{
1469+
size_t ceil = nextPowerOf2(size);
1470+
size_t floor = ceil >> 1; // half of ceil
1471+
size_t mid = floor + (floor >> 1); // (1.5 times floor)
1472+
size_t requestSize = (size <= mid) ? mid : ceil;
1473+
1474+
return data().getBufferFromPool(requestSize, type);
1475+
}
1476+
14301477
} // namespace WebCore
14311478

14321479
#endif // USE(TEXTURE_MAPPER)

Source/WebCore/platform/graphics/texmap/TextureMapper.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "IntRect.h"
2929
#include "IntSize.h"
3030
#include "TextureMapperGLHeaders.h"
31+
#include "TextureMapperGPUBuffer.h"
3132
#include "TransformationMatrix.h"
3233
#include <array>
3334
#include <wtf/OptionSet.h>
@@ -39,10 +40,11 @@
3940

4041
namespace WebCore {
4142

43+
class ClipPath;
4244
class TextureMapperGLData;
45+
class TextureMapperGPUBuffer;
4346
class TextureMapperShaderProgram;
4447
class FilterOperations;
45-
class FloatPolygon;
4648
class FloatRoundedRect;
4749
enum class TextureMapperFlags : uint16_t;
4850

@@ -78,7 +80,7 @@ class TextureMapper {
7880
void bindSurface(BitmapTexture* surface);
7981
BitmapTexture* currentSurface();
8082
void beginClip(const TransformationMatrix&, const FloatRoundedRect&);
81-
void beginClip(const TransformationMatrix&, const FloatPolygon&);
83+
void beginClip(const TransformationMatrix&, const ClipPath&);
8284
WEBCORE_EXPORT void beginPainting(FlipY = FlipY::No, BitmapTexture* = nullptr);
8385
WEBCORE_EXPORT void endPainting();
8486
void endClip();
@@ -98,6 +100,8 @@ class TextureMapper {
98100
WEBCORE_EXPORT void releaseUnusedTexturesNow();
99101
#endif
100102

103+
Ref<TextureMapperGPUBuffer> acquireBufferFromPool(size_t, TextureMapperGPUBuffer::Type);
104+
101105
private:
102106
bool isInMaskMode() const { return m_isMaskMode; }
103107
const TransformationMatrix& patternTransform() const { return m_patternTransform; }

0 commit comments

Comments
 (0)