-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce uniform bindings #6018
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great!
The main big-picture question I'm pondering is whether we can/should push the typing further.
I.e.:
- For each program type (
fill
,circle
, etc.), declare a base object mapping uniform names to their types. - Parameterize
Uniforms
,UniformValues
,UniformLocations
, andProgram
itself on that base object. (We'd need to figure out how to deal with the dynamically-determined paint property uniforms that come out ofProgramConfiguration#getUniforms()
.)
src/render/uniform_binding.js
Outdated
export interface UniformInterface<T> { | ||
context: Context; | ||
set(location: WebGLUniformLocation, value: T): void; | ||
_set(location: WebGLUniformLocation, value: T): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the purpose of _set
to allow each concrete uniform subclass to call the correct gl.uniform*()
method? If so, rather than having it in this public interface, it can just be declared as a property on the Uniform<T>
class _set: (location: WebGLUniformLocation, value: T) => void;
src/render/uniform_binding.js
Outdated
let diff = false; | ||
if (!this.current && v) { | ||
diff = true; | ||
} else if (Array.isArray(this.current) && Array.isArray(v) && this.current !== v) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.current !== v
means we're assuming array values are never mutated in place. That's probably a valid assumption, flagging just in case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 it feels like a valid assumption, is currently true, and would obviously be more performant to proceed as such, although I suppose there's nothing preventing someone from doing this…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember how for performance reasons we ended up inlining the this.current
tests for Value
implementations? Let's do the same for uniforms. In addition to the likely performance advantage, we can reduce the required assumptions by tailoring conditionals to the value type, e.g. for Uniform2f
: if (this.current !== v || this.current[0] !== v[0] || this.current[1] !== v[1]) {
.
src/render/draw_fill.js
Outdated
} | ||
} | ||
|
||
function drawFillTiles(painter, sourceCache, layer, coords, drawFn) { | ||
if (pattern.isPatternMissing(layer.paint.get('fill-pattern'), painter)) return; | ||
function drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, fillTiles) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
small clarification: maybe change fillTiles
to something like phase: 'fill' | 'outline'
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to change to isOutline
to maintain a simple boolean while being a bit more descriptive.
tile: {tileID: OverscaledTileID, tileSize: number} | ||
): UniformValues { | ||
return util.extend(pattern.prepare(image, painter), | ||
pattern.setTile(tile, painter), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since pattern.prepare()
and pattern.setTile()
are state-mutating methods, I'm wary of them being called from within backgroundUniformValues()
, etc., which seem intended to be "pure" functions that just return computed uniform values.
df5fbd9
to
c9049a9
Compare
Updated OP with current benchmarks; the Paint benchmark is reliably performing about 2 ms slower than master at the moment :( |
src/render/uniform_binding.js
Outdated
|
||
import type Context from '../gl/context'; | ||
|
||
export interface UniformInterface<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UniformInterface
isn't imported anywhere; I think it can be removed (along with the implements
below).
src/render/uniform_binding.js
Outdated
|
||
export type UniformValue = number | Array<number> | Float32Array; | ||
export type UniformValues<Us: Object> | ||
= $Exact<$ObjMap<Us, <V>(u: Uniform<V>) => UniformValue>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally this would be:
= $Exact<$ObjMap<Us, <V>(u: Uniform<V>) => V>>;
That would permit the type checker to verify that Uniforms<Us>#set
is receiving the right types of values in the second parameter.
There are a number of things that need to happen to make this possible. A starting point is changing:
class Uniform2fv extends Uniform<Array<number>> {
_set(location: WebGLUniformLocation, v: Array<number>): void {
this.context.gl.uniform2fv(location, v);
}
}
to:
class Uniform2f extends Uniform<[number, number]> {
_set(location: WebGLUniformLocation, v: [number, number]): void {
this.context.gl.uniform2f(location, v[0], v[1]);
}
}
(And so on for the other Uniform*
classes.)
src/render/uniform_binding.js
Outdated
let diff = false; | ||
if (!this.current && v) { | ||
diff = true; | ||
} else if (Array.isArray(this.current) && Array.isArray(v) && this.current !== v) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember how for performance reasons we ended up inlining the this.current
tests for Value
implementations? Let's do the same for uniforms. In addition to the likely performance advantage, we can reduce the required assumptions by tailoring conditionals to the value type, e.g. for Uniform2f
: if (this.current !== v || this.current[0] !== v[0] || this.current[1] !== v[1]) {
.
7988f3d
to
637a096
Compare
For performance reasons, in 637a096 I reverted the |
src/render/program.js
Outdated
@@ -90,7 +90,6 @@ class Program<Us: UniformBindings> { | |||
} | |||
} | |||
|
|||
this.binderUniforms = configuration.getUniformBindings(context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should still live on Program
, so that set
s are deduped across different ProgramConfiguration
s.
src/render/draw_line.js
Outdated
firstTile = false; | ||
// once refactored so that bound texture state is managed, we'll also be able to remove this firstTile/programChanged logic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the specific issue here is that painter.lineAtlas.bind(context)
must currently be called after lineSDFUniformValues(...)
, because the latter produces side effects in the lineAtlas
when it calls lineAtlas.getDash(...)
.
Before 8230e38: https://bl.ocks.org/anonymous/raw/7f4b0baa728fc39995ab1c13a267cdef/ |
6d03fa5
to
e5b91bd
Compare
e5b91bd improves upon layer performance by switching |
fa68809
to
e019157
Compare
Benchmarks for current rebased version: http://bl.ocks.org/lbud/raw/9920556dde95d6b156e838559b36596d/ There are a few layer-specific benchmarks that are still slower. The paint benchmark is consistently performing as well if not slightly better than 0.45 + master.
|
2121a68
to
d710415
Compare
Re-rebased. |
c34e74d
to
3042bd2
Compare
* Introduce uniform bining classes * Set uniforms and color/depth/stencil modes within Program#draw * Use drawElements exclusively (eliminate all uses of drawArrays)
… initialize bindings in program constructor
3042bd2
to
4c843a7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
This PR
render/program/*_program.js
files (open to renaming — these may be better for now as*_program_uniforms.js
or something) which create uniform bindings for per-layer uniforms (henceforthfixedUniforms
) and perform those uniform calculations (moved fromdraw_*
functions)Program#draw
functions tofixedUniforms
and paint property-based uniforms, henceforthbinderUniforms
; the state bindings for these are initialized upon program construction)drawElements
(nodrawArrays
) so that all drawing is done through the sameProgram#draw
pathSegmentVector#simpleSegment
in order to create necessary segments to manage these simple draw calls; we create these in lieu of VAOs in painter setupCurrent benchmark results (updated 3/19): https://bl.ocks.org/anonymous/raw/cc118c1ebafcbb06a7d8fdd8c2b50c8b/
Launch Checklist