-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Conversation
@jfirebaugh, thanks for your PR! By analyzing this pull request, we identified @ansis to be potential reviewers. |
@@ -76,7 +76,7 @@ struct DepthMask { | |||
} | |||
}; | |||
|
|||
struct ColorMask { | |||
struct ColorMaskValue { |
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.
Renames here to avoid conflicts with new type names. I envision that all the structs in gl_values.hpp will eventually go away.
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.
The goal of these values, and their surrounding Value
objects is to prevent duplicate state from being set. It seems like this is no longer the case with this code? Will it come back as work progresses?
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.
Yep, I was just waiting for #6470 to land.
@@ -76,7 +76,7 @@ struct DepthMask { | |||
} | |||
}; | |||
|
|||
struct ColorMask { | |||
struct ColorMaskValue { |
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.
The goal of these values, and their surrounding Value
objects is to prevent duplicate state from being set. It seems like this is no longer the case with this code? Will it come back as work progresses?
gl::Uniforms { | ||
gl::UniformMatrix<4> { program.u_matrix, matrix } | ||
}, | ||
tileStencilBuffer); |
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.
How does this draw call know that it should draw triangles?
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 was thinking that vertex buffer types would be tagged with a primitive type, but in some cases we want to reuse vertexes in multiple primitive types. So I think this API will probably need to be revised to accept vertex sources and index source in separate parameters, with a marker value for the latter to indicate unindexed drawing (e.g. Unindexed<Triangles>()
). This is what the glium API does.
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.
Yeah, in some cases we're using the same buffer for different draw operations.
gl::Stencil::Replace | ||
}, | ||
gl::Blend::disabled(), | ||
gl::ColorMask::disabled() |
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.
Do all of these have to be specified? Can we use some defaults here?
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.
Yes, we could allow defaults. However, given that part of the motivation for moving to this API is to eliminate errors caused by forgetting to specify the desired state, I suggest that we start by making all parameters required. We can define additional helpers in the vein of gl::*::disabled()
to package up common values. For example I anticipate we'll want gl::Blend::alphaTransparency()
and functions for the most common depth and stencil settings.
gl::AttributeSources {}, | ||
gl::Uniforms { | ||
gl::UniformMatrix<4> { program.u_matrix, matrix } | ||
}, |
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 there a way to ensure that all uniforms in a program can be set? In the past, we've seen issues with not all uniforms for a draw call being set so we didn't have predictable results.
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.
Yes, I think we can accomplish this with a higher-level API that layers on top of this one.
template <> struct AttributeType<int32_t> : std::integral_constant<GLenum, GL_INT> {}; | ||
template <> struct AttributeType<uint32_t> : std::integral_constant<GLenum, GL_UNSIGNED_INT> {}; | ||
template <> struct AttributeType<float> : std::integral_constant<GLenum, GL_FLOAT> {}; | ||
template <> struct AttributeType<double> : std::integral_constant<GLenum, GL_DOUBLE> {}; |
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.
At some point, we should hide the GL_*
, and gl*
symbols so users of this library don't need to include gl.h
.
|
||
using AttributeSources = std::vector<AttributeSource>; | ||
|
||
namespace detail { |
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, we'd move all of the detail
code that calls actual GL functions to an implementation file
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 think this is doable. It means that all polymorphic input types have to be wrapped in a variant
at some point -- no full monomorphization via template methods. I think that's fine, it was already mostly that way.
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.
Or did you just mean move them to an _impl.hpp
file? I assumed you meant move them to a .cpp
file.
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.
Yeah, .cpp
file.
bool r; | ||
bool g; | ||
bool b; | ||
bool a; |
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.
For types like this, we should either default them, or write explicit initializers that require all parameters to prevent accidental misuse, which results in uninitialized values. Valgrind may catch them, but it's always better to have a compiler error slapped in the face.
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.
clang gives a warning if you omit parameters in brace initialization:
warning: missing field 'b' initializer [-Wmissing-field-initializers]
return ColorMask { false, false };
^
This will be an error once we fix #6487.
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.
For consecutive bool
values we could save some memory space by using bit field e.g.:
bool r : 1;
bool g : 1;
bool b : 1;
bool a : 1;
This is excellent work @jfirebaugh! I am excited to iterate on GL Native and GL JS rendering architecture together as we work on mapbox/mapbox-gl-js#145. |
ecc3ae0
to
3bf08b4
Compare
3bf08b4
to
12112c6
Compare
2bc6035
to
c0b3c9f
Compare
e2bd2e6
to
9d229b7
Compare
3ef3084
to
49a01e8
Compare
7dba523
to
ce34d4e
Compare
ce34d4e
to
8ac0eec
Compare
MBGL_CHECK_ERROR(glDrawArrays(GL_LINE_STRIP, 0, static_cast<GLsizei>(tileLineStripVertexBuffer.vertexCount))); | ||
} | ||
|
||
#ifndef NDEBUG |
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 removed Painter::renderClipMasks
and Painter::renderDepthBuffer
in an early step to avoid dealing with them at the time, and need to go back and convert them now.
@@ -27,10 +25,8 @@ class AttributeBinding { | |||
type(DataTypeOf<T>::value), | |||
count(N), | |||
offset(O) { | |||
static_assert(std::is_standard_layout<Vertex>::value, "vertex type must use standard layout"); |
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.
Why are these removed?
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 one moved to the Drawable
constructor. I just dropped "vertex type is too big", because it was kind of silly.
blend = true; | ||
blendColor = color.blendColor; | ||
apply_visitor([&] (const auto& blendFunction) { | ||
// TODO: blendEquation = blendFunction.equation; |
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.
TODO
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.
Previous code didn't call glBlendEquation
anywhere and we always want the default of GL_FUNC_ADD
. But we should set it explicitly right? In case a custom layer changes it.
|
||
drawable.bindUniforms(); | ||
|
||
for (const auto& segment : drawable.segments) { |
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 it possible to have zero drawable segments? In that case, we shouldn't set any of the state prior to this whatsoever.
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 think it'll be short circuited prior to getting here, but I'll add an internal guard as well.
}; | ||
|
||
if (needAttributeBindings()) { | ||
vertexBuffer.setDirty(); |
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.
why setDirty
here?
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's for the case where a VAO is being initialized. VAOs don't inherit the existing buffer bindings; we need to ensure that glBindBuffer
actually gets called. But it can be moved up into the needAttributeBindings
lambda and commented. I'll do so.
TriangleStrip>; | ||
|
||
} // namespace gl | ||
} // namespace mbgl |
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.
Can we use a different name? Mode
feels way too generic for this. I know that the parameter is called mode in OpenGL as well, but maybe we can use a name like PrimitiveType
instead?
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.
PrimitiveMode
?
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.
Or:
Mode
⇢DrawMode
Depth
⇢DepthMode
Stencil
⇢StencilMode
Color
⇢ColorMode
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.
Either work for me. Maybe adding the Mode
to all avoids ambiguity?
Mode mode; | ||
Depth depth; | ||
Stencil stencil; | ||
Color color; |
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.
Rename to blendColor
?
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 chose Color
for parallelism with Depth
and Stencil
-- each holds the configuration that affects the respective GL buffer attachment of that type. For Color
, that's both blend settings and the color mask.
u_extrude_scale, | ||
u_devicepixelratio> {}; | ||
|
||
} // namespace mbgl |
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.
Can we merge all of the _uniforms.hpp
files into the shader headers? It seems like they are very short, and are always used together, so having them in the same file makes the code more readable. Maybe even make them a child class of the shader class?
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.
There's not a one-to-one relationship -- e.g. FillShader
, FillPatternShader
, FillOutlineShader
, FillOutlinePatternShader
. I think there's some consolidation to be done -- perhaps replacing explicit shader subclasses with a single Shader
template -- but I'd like to defer it to a future PR.
|
||
namespace mbgl { | ||
|
||
MBGL_DEFINE_UNIFORM_SCALAR(float, u_radius); |
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.
These are going to define global symbols, and I assume it's just a coincidence that we don't have any name clashes for different symbols. Instead we should define them inside the Shader class.
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.
Where multiple shaders use the same uniform name, I've hoisted the definition to a shared file.
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.
Yeah, I've seen that; I'm referring to the situation when two shaders use the same name for different uniform types.
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.
Ok -- there aren't any cases of that currently, and I think we should avoid introducing any. Besides link errors, it would just be confusing to use the same uniform name for different types.
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.
Okay; but it still feels weird to have them in the main mbgl
namespace so we have symbols like mbgl::u_radius
. Can we at least move them to another namespace?
VertexArrayID id = 0; | ||
MBGL_CHECK_ERROR(gl::GenVertexArrays(1, &id)); | ||
vertexArrayObject = id; | ||
vaos.emplace(vaoKey, UniqueVertexArray(std::move(id), { 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.
I'm not sure I like this design. Can we come up with a version that does the VAO management statically instead of during runtime?
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.
Well, the static design is the previously existing one, where each buffer Segment
holds a corresponding VAO, and binding is done explicitly. That approach seems to be error-prone.
cd43b2c
to
713e8b7
Compare
Can you add the |
c1dd5ad
to
39d5cb3
Compare
Squashed one, prefixed the other. I think it's prudent for this not to go into iOS v3.4.0 or Android v4.2.0, as it's an invasive change. iOS is on a release branch already and not planning any more merges from |
@jfirebaugh feel free to merge to master, we have a release branch called |
🎉 |
This PR introduces mid-level bindings for OpenGL, specifically a procedural API that wraps a single primitive draw operation, with all state affecting the result provided via method parameters rather than the OpenGL default of implicit state and bindings. The primary design reference for this API was glium, with secondary inspiration from regl.
This parameters for a draw operation are the following:
Depth
,Stencil
, andBlend
.glVertexAttribPointer
), or a constant scalar or vector value (glVertexAttrib*
). (It remains to be seen if we use the latter for DDS or go the route of GL JS and generate shader source code that uses attributes or uniforms as appropriate.) A buffer or buffer slice comes with a specification of its layout.glDrawArrays
) and indexed (glDrawElements
) drawing. Input can be array buffer or element buffer objects or slices thereof. Buffer types are templated on their geometry, e.g.ElementBuffer<Triangles, uint16_t>
.Type safety is generally provided at the level of individual parameters, and notably not at the level of matching types between shader source and uniform or attribute types, or between attribute types and C++ object layout. Some additional safety can be obtained by writing higher-level bindings atop this API, e.g. along the lines of the API for uniforms proposed by @kkaefer here.
Likewise, we can layer a declarative, object-based API more like regl atop this one, as suggested in this gl-js issue. Doing so may simplify the communication pathway between workers and the renderer, and provide a clearer path to use VAOs transparently than with a purely procedural approach.
TODOs
The remaining items are numerous; I'll edit to flesh out this section.
gl::ObjectStore
andgl::Config
(Merge gl::ObjectStore and gl::Config #6470). Theprepare
anddraw
functions I've sketched here should be methods on this object, and refer to its internal state to avoid no-op OpenGL operations asgl::Config
currently does.cc @kkaefer @lucaswoj