KiCad PCB EDA Suite
Loading...
Searching...
No Matches
rhi_cull_runs.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef RHI_CULL_RUNS_H
21#define RHI_CULL_RUNS_H
22
23#include <rhi/frame_packet.h>
24#include <rhi/rhi_instance.h>
25#include <rhi/rhi_vertex.h>
26
27#include <algorithm>
28#include <array>
29#include <cmath>
30#include <cstdint>
31#include <limits>
32#include <vector>
33
34#include <glm/glm.hpp>
35
42
43namespace KIGFX
44{
45
46constexpr uint32_t RHI_CULL_RUN_LENGTH = 256;
47
51constexpr float RHI_CULL_PIXEL_SLACK = 4.0f;
52
54{
55 float minX = std::numeric_limits<float>::max();
56 float minY = std::numeric_limits<float>::max();
57 float maxX = std::numeric_limits<float>::lowest();
58 float maxY = std::numeric_limits<float>::lowest();
60
65 float lod = std::numeric_limits<float>::max();
66
67 bool Empty() const { return minX > maxX; }
68
69 void Add( float aX, float aY, float aWorldReach, float aPixelReach, float aLod = 0.0f )
70 {
71 lod = std::min( lod, aLod );
72 minX = std::min( minX, aX - aWorldReach );
73 minY = std::min( minY, aY - aWorldReach );
74 maxX = std::max( maxX, aX + aWorldReach );
75 maxY = std::max( maxY, aY + aWorldReach );
76 pixels = std::max( pixels, RHI_CULL_PIXEL_SLACK + aPixelReach );
77 }
78};
79
80
86{
90 std::array<std::vector<RHI_RUN_BOUNDS>, RHI_SHAPES> runs;
91 std::array<std::vector<RHI_RUN_BOUNDS>, RHI_SHAPES> negRuns;
92
93 const std::vector<RHI_RUN_BOUNDS>& Of( RHI_SHAPE aShape, bool aNegatives ) const
94 {
95 return aNegatives ? negRuns[aShape] : runs[aShape];
96 }
97};
98
99
103inline void RhiInstanceReach( const RHI_INSTANCE& aInstance, RHI_SHAPE aShape, float& aWorld, float& aPixels )
104{
105 const float scale = std::max( std::abs( glm::unpackHalf1x16( aInstance.scaleX ) ),
106 std::abs( glm::unpackHalf1x16( aInstance.scaleY ) ) );
107
108 float extent = 0.0f;
109
110 if( aShape == RHI_SHAPE_PAD )
111 extent = std::sqrt( 2.0f ) * std::max( std::abs( aInstance.params[0] ), std::abs( aInstance.params[1] ) );
112 else
113 extent = std::abs( aInstance.params[0] ) + std::abs( aInstance.params[1] );
114
115 aPixels = static_cast<float>( aInstance.pixelPad ) / 32.0f;
116
117 if( aShape == RHI_SHAPE_TRACK && aInstance.HasFlag( RHI_INSTANCE_LENGTH_PAD ) )
118 aPixels += aInstance.params[2];
119
123 if( aShape == RHI_SHAPE_TRACK && aInstance.HasFlag( RHI_INSTANCE_SCREEN_SPACE ) )
124 {
125 aWorld = 0.0f;
126 aPixels += extent * scale;
127 }
128 else
129 {
130 aWorld = extent * scale;
131 }
132}
133
134
135inline RHI_RUN_BOUNDS RhiInstanceRun( const RHI_INSTANCE* aInstances, size_t aCount, RHI_SHAPE aShape )
136{
137 RHI_RUN_BOUNDS run;
138
139 for( size_t ii = 0; ii < aCount; ++ii )
140 {
144 if( aInstances[ii].scaleX == 0 && aInstances[ii].scaleY == 0 )
145 continue;
146
147 float world = 0.0f;
148 float pixels = 0.0f;
149
150 RhiInstanceReach( aInstances[ii], aShape, world, pixels );
151
152 const bool levelled = aShape == RHI_SHAPE_TRACK && aInstances[ii].HasFlag( RHI_INSTANCE_SCALE_LOD );
153 const float lod = levelled ? aInstances[ii].params[3] : 0.0f;
154
155 run.Add( aInstances[ii].x, aInstances[ii].y, world, pixels, lod );
156 }
157
158 return run;
159}
160
161
162inline RHI_RUN_BOUNDS RhiVertexRun( const RHI_VERTEX* aVertices, size_t aCount )
163{
164 RHI_RUN_BOUNDS run;
165
166 for( size_t ii = 0; ii < aCount; ++ii )
167 run.Add( aVertices[ii].x, aVertices[ii].y, 0.0f, 0.0f );
168
169 return run;
170}
171
172
177
178
179constexpr uint32_t RhiCullRunLength( int aShape )
180{
182}
183
184
189{
190 float minX = 0.0f;
191 float minY = 0.0f;
192 float maxX = 0.0f;
193 float maxY = 0.0f;
194 float worldPerPixel = 0.0f;
195 float viewScale = 0.0f;
196 bool valid = false;
197
198 bool Reaches( const RHI_RUN_BOUNDS& aRun ) const
199 {
200 if( !valid )
201 return true;
202
206 if( aRun.Empty() || ( aRun.lod > 0.0f && !( aRun.lod < viewScale ) ) )
207 return false;
208
209 const float grow = aRun.pixels * worldPerPixel;
210
211 return aRun.maxX + grow >= minX && aRun.minX - grow <= maxX && aRun.maxY + grow >= minY
212 && aRun.minY - grow <= maxY;
213 }
214};
215
216
221{
222 RHI_CULL_VIEW view;
223
227 const glm::dmat2 m( aFrame.projection[0], aFrame.projection[1], aFrame.projection[4], aFrame.projection[5] );
228 const glm::dvec2 t( aFrame.projection[12], aFrame.projection[13] );
229
230 if( glm::determinant( m ) == 0.0 || aFrame.viewport[0] <= 0.0f || aFrame.viewport[1] <= 0.0f )
231 return view;
232
233 const glm::dmat2 inverse = glm::inverse( m );
234
235 double minX = std::numeric_limits<double>::max();
236 double minY = std::numeric_limits<double>::max();
237 double maxX = std::numeric_limits<double>::lowest();
238 double maxY = std::numeric_limits<double>::lowest();
239
240 for( double nx : { -1.0, 1.0 } )
241 {
242 for( double ny : { -1.0, 1.0 } )
243 {
244 const glm::dvec2 world = inverse * ( glm::dvec2( nx, ny ) - t );
245
246 minX = std::min( minX, world.x );
247 minY = std::min( minY, world.y );
248 maxX = std::max( maxX, world.x );
249 maxY = std::max( maxY, world.y );
250 }
251 }
252
256 const double pixelsPerUnit = 0.5 * std::hypot( m[0][0] * aFrame.viewport[0], m[0][1] * aFrame.viewport[1] );
257
258 if( pixelsPerUnit <= 0.0 )
259 return view;
260
261 view.minX = static_cast<float>( minX );
262 view.minY = static_cast<float>( minY );
263 view.maxX = static_cast<float>( maxX );
264 view.maxY = static_cast<float>( maxY );
265 view.worldPerPixel = static_cast<float>( 1.0 / pixelsPerUnit );
266 view.viewScale = aFrame.viewScale;
267 view.valid = true;
268 return view;
269}
270
271
276{
277 uint64_t runs = 0;
278 uint64_t drawn = 0;
279 uint64_t draws = 0;
280};
281
282
287template <typename DRAW>
288void RhiForVisibleStretches( const std::vector<RHI_RUN_BOUNDS>* aRuns, uint32_t aRunLength, uint32_t aStart,
289 uint32_t aCount, const RHI_CULL_VIEW& aView, DRAW&& aDraw,
290 RHI_CULL_COUNT* aTally = nullptr )
291{
292 if( aCount == 0 )
293 return;
294
295 const uint32_t end = aStart + aCount;
296 const uint32_t spanned = ( end - 1 ) / aRunLength - aStart / aRunLength + 1;
297
298 if( aTally )
299 aTally->runs += spanned;
300
301 if( !aRuns || !aView.valid || static_cast<size_t>( ( end - 1 ) / aRunLength ) >= aRuns->size() )
302 {
303 if( aTally )
304 {
305 aTally->drawn += spanned;
306 aTally->draws++;
307 }
308
309 aDraw( aStart, aCount );
310 return;
311 }
312
313 uint32_t stretchStart = aStart;
314 bool open = false;
315
316 for( uint32_t run = aStart / aRunLength; run * aRunLength < end; ++run )
317 {
318 const uint32_t runStart = std::max( aStart, run * aRunLength );
319 const uint32_t runEnd = std::min( end, ( run + 1 ) * aRunLength );
320
321 if( aView.Reaches( ( *aRuns )[run] ) )
322 {
323 if( aTally )
324 aTally->drawn++;
325
326 if( !open )
327 stretchStart = runStart;
328
329 open = true;
330 }
331 else if( open )
332 {
333 aDraw( stretchStart, runStart - stretchStart );
334 open = false;
335
336 if( aTally )
337 aTally->draws++;
338 }
339
340 if( runEnd == end && open )
341 {
342 aDraw( stretchStart, end - stretchStart );
343
344 if( aTally )
345 aTally->draws++;
346 }
347 }
348}
349
353inline bool RhiRangeReaches( const LAYER_RANGE& aRange, const RHI_CULL_VIEW& aView )
354{
355 if( !aRange.cullRuns || !aView.valid || aRange.bitmapCount > 0 )
356 return true;
357
358 const RHI_RUN_TABLE& runs = *aRange.cullRuns;
359 bool reaches = false;
360
361 auto found = [&]( uint32_t, uint32_t )
362 {
363 reaches = true;
364 };
365
366 for( int shape = 0; shape < RHI_SHAPES && !reaches; ++shape )
367 {
368 RhiForVisibleStretches( &runs.runs[shape], RhiCullRunLength( shape ), aRange.start[shape],
369 aRange.count[shape], aView, found );
370 RhiForVisibleStretches( &runs.negRuns[shape], RhiCullRunLength( shape ), aRange.negStart[shape],
371 aRange.negCount[shape], aView, found );
372 }
373
374 return reaches;
375}
376
377} // namespace KIGFX
378
379#endif // RHI_CULL_RUNS_H
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:30
RHI_CULL_VIEW RhiCullView(const FRAME_UBO_DATA &aFrame)
Invert the frame's 2D projection over the normalized device square.
RHI_RUN_BOUNDS RhiInstanceRun(const RHI_INSTANCE *aInstances, size_t aCount, RHI_SHAPE aShape)
void RhiInstanceReach(const RHI_INSTANCE &aInstance, RHI_SHAPE aShape, float &aWorld, float &aPixels)
How far an instance reaches from its position, in world units and in pixels.
constexpr uint32_t RhiCullRunLength(int aShape)
void RhiForVisibleStretches(const std::vector< RHI_RUN_BOUNDS > *aRuns, uint32_t aRunLength, uint32_t aStart, uint32_t aCount, const RHI_CULL_VIEW &aView, DRAW &&aDraw, RHI_CULL_COUNT *aTally=nullptr)
Call aDraw( first, count ) for each stretch of [aStart, aStart + aCount) whose runs can reach aView.
constexpr uint32_t RHI_CULL_RUN_LENGTH
bool RhiRangeReaches(const LAYER_RANGE &aRange, const RHI_CULL_VIEW &aView)
A range without runs or with bitmaps always reaches.
constexpr float RHI_CULL_PIXEL_SLACK
Reach of every element beyond its own extent for antialiasing padding, pixel snapping and the edge.
RHI_SHAPE
Shapes a frame draws, each with its own array and pipelines.
@ RHI_SHAPE_POLYGON
@ RHI_SHAPE_TRACK
@ RHI_SHAPE_PAD
RHI_RUN_BOUNDS RhiVertexRun(const RHI_VERTEX *aVertices, size_t aCount)
constexpr int RHI_SHAPES
constexpr uint32_t RHI_CULL_VERTEX_RUN_LENGTH
Polygon vertices per run, three per triangle.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ RHI_INSTANCE_LENGTH_PAD
a track longer at each end by param 2 pixels
@ RHI_INSTANCE_SCREEN_SPACE
@ RHI_INSTANCE_SCALE_LOD
a track shown only while the view scale is above param 3
const int scale
Per-frame uniform block matching FrameUBO in frame_ubo.glsl (std140).
float projection[16]
column-major for GLSL
float viewScale
the view's scale, which levels of detail are compared with
One layer's instance ranges within the flat GPU buffers, normal instances before negatives.
RHI_SHAPE_COUNTS negStart
RHI_SHAPE_COUNTS count
std::shared_ptr< const RHI_RUN_TABLE > cullRuns
Bounds of the range's buffers so off-screen runs are skipped.
RHI_SHAPE_COUNTS negCount
RHI_SHAPE_COUNTS start
Each shape's normal elements, then for an instanced shape its negatives.
Runs a pass looked at and how many it drew, to tell how well culling works.
uint64_t draws
stretches drawn, each one draw call
The world rectangle a frame shows and the world size of one of its pixels.
float viewScale
the view's scale, which the elements' levels of detail are compared with
bool Reaches(const RHI_RUN_BOUNDS &aRun) const
float pixels
reach beyond the world bounds, in pixels
void Add(float aX, float aY, float aWorldReach, float aPixelReach, float aLod=0.0f)
float lod
Lowest level of detail among the run's elements, 0 when one is always shown.
Runs of each array a layer range draws from, indexed by element / RHI_CULL_RUN_LENGTH.
std::array< std::vector< RHI_RUN_BOUNDS >, RHI_SHAPES > negRuns
const std::vector< RHI_RUN_BOUNDS > & Of(RHI_SHAPE aShape, bool aNegatives) const
std::array< std::vector< RHI_RUN_BOUNDS >, RHI_SHAPES > runs
By RHI_SHAPE.
Per-instance data for instanced 2D rendering, packed into 40 bytes.
bool HasFlag(RHI_INSTANCE_FLAG aFlag) const
uint16_t scaleX
uint16_t pixelPad
extra stroke width in sixteenths of a pixel
float params[4]
uint16_t scaleY
Position is in world coordinates, color is premultiplied alpha and u, v hold half floats.
Definition rhi_vertex.h:37
VECTOR2I end