diff --git a/include/mbgl/gfx/shader_group.hpp b/include/mbgl/gfx/shader_group.hpp index dcb9f79ee..4ecb65f33 100644 --- a/include/mbgl/gfx/shader_group.hpp +++ b/include/mbgl/gfx/shader_group.hpp @@ -3,9 +3,11 @@ #include #include +#include #include #include #include +#include #include namespace mbgl { @@ -151,6 +153,11 @@ public: return {}; } +protected: + std::string getShaderName(const std::string_view& name, const std::size_t key) { + return (std::ostringstream() << name << '#' << std::hex << key).str(); + } + private: mbgl::unordered_map> programs; mutable std::shared_mutex programLock; diff --git a/include/mbgl/gfx/vertex_attribute.hpp b/include/mbgl/gfx/vertex_attribute.hpp index 14e19b7d7..0ecec50cc 100644 --- a/include/mbgl/gfx/vertex_attribute.hpp +++ b/include/mbgl/gfx/vertex_attribute.hpp @@ -354,15 +354,6 @@ public: for (auto& kv : attrs) { delegate(kv.first, *kv.second, overrides.get(kv.first)); } - // For OpenGL, the shader attributes are established with reflection, and we have extra - // entries when we share attributes between, e.g., fill and fill-outline drawables. -#if !defined(NDEBUG) && MLN_RENDERER_BACKEND_METAL - // Every override should match a defined attribute. - for (const auto& kv : overrides.attrs) { - const auto hit = attrs.find(kv.first); - assert(hit != attrs.end()); - } -#endif } VertexAttributeArray& operator=(VertexAttributeArray&&); diff --git a/include/mbgl/mtl/command_encoder.hpp b/include/mbgl/mtl/command_encoder.hpp index d098e1249..efc841616 100644 --- a/include/mbgl/mtl/command_encoder.hpp +++ b/include/mbgl/mtl/command_encoder.hpp @@ -25,6 +25,9 @@ public: explicit CommandEncoder(Context& context_); ~CommandEncoder() override; + mtl::Context& getContext() { return context; } + const mtl::Context& getContext() const { return context; } + std::unique_ptr createUploadPass(const char* name, gfx::Renderable&) override; std::unique_ptr createRenderPass(const char* name, const gfx::RenderPassDescriptor&) override; diff --git a/include/mbgl/mtl/context.hpp b/include/mbgl/mtl/context.hpp index 97e1e4f5a..24d3b8e16 100644 --- a/include/mbgl/mtl/context.hpp +++ b/include/mbgl/mtl/context.hpp @@ -125,6 +125,9 @@ public: std::size_t size, bool persistent); + /// Get an empty buffer to act as a placeholder + const BufferResource& getEmptyBuffer(); + /// Get a reusable buffer containing the standard fixed tile vertices (+/- `util::EXTENT`) const BufferResource& getTileVertexBuffer(); @@ -142,6 +145,7 @@ private: RendererBackend& backend; bool cleanupOnDestruction = true; + std::optional emptyBuffer; std::optional tileVertexBuffer; std::optional tileIndexBuffer; diff --git a/include/mbgl/mtl/render_pass.hpp b/include/mbgl/mtl/render_pass.hpp index c781560fc..f87691132 100644 --- a/include/mbgl/mtl/render_pass.hpp +++ b/include/mbgl/mtl/render_pass.hpp @@ -21,6 +21,9 @@ public: RenderPass(CommandEncoder&, const char* name, const gfx::RenderPassDescriptor&); ~RenderPass() override; + mtl::CommandEncoder& getCommandEncoder() { return commandEncoder; } + const mtl::CommandEncoder& getCommandEncoder() const { return commandEncoder; } + const MTLRenderCommandEncoderPtr& getMetalEncoder() const { return encoder; } const gfx::RenderPassDescriptor& getDescriptor() const { return descriptor; } diff --git a/include/mbgl/shaders/gl/shader_group_gl.hpp b/include/mbgl/shaders/gl/shader_group_gl.hpp index e6af81320..e7489fb7f 100644 --- a/include/mbgl/shaders/gl/shader_group_gl.hpp +++ b/include/mbgl/shaders/gl/shader_group_gl.hpp @@ -7,8 +7,6 @@ #include #include -#include - namespace mbgl { namespace gl { @@ -16,13 +14,14 @@ template class ShaderGroupGL final : public gfx::ShaderGroup { public: ShaderGroupGL(const ProgramParameters& programParameters_) - : ShaderGroup(), + : gfx::ShaderGroup(), programParameters(programParameters_) {} ~ShaderGroupGL() noexcept override = default; gfx::ShaderPtr getOrCreateShader(gfx::Context& context, const mbgl::unordered_set& propertiesAsUniforms, std::string_view firstAttribName) override { + constexpr auto& name = shaders::ShaderSource::name; constexpr auto& vert = shaders::ShaderSource::vertex; constexpr auto& frag = shaders::ShaderSource::fragment; @@ -31,7 +30,7 @@ public: // We could cache these by key here to avoid creating a string key each time, but we // would need another mutex. We could also push string IDs down into `ShaderGroup`. - const std::string shaderName = getShaderName(key); + const std::string shaderName = getShaderName(name, key); auto shader = get(shaderName); if (shader) { return shader; @@ -62,16 +61,6 @@ public: return shader; } -protected: - std::string getShaderName(std::size_t key) { - constexpr auto& name = shaders::ShaderSource::name; - - // This could be more efficient. - std::ostringstream stream; - stream << name << '#' << key; - return stream.str(); - } - private: ProgramParameters programParameters; }; diff --git a/include/mbgl/shaders/mtl/background.hpp b/include/mbgl/shaders/mtl/background.hpp index e324b3c50..192a3990f 100644 --- a/include/mbgl/shaders/mtl/background.hpp +++ b/include/mbgl/shaders/mtl/background.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "BackgroundShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/background_pattern.hpp b/include/mbgl/shaders/mtl/background_pattern.hpp index 9161f3886..2b46b11c3 100644 --- a/include/mbgl/shaders/mtl/background_pattern.hpp +++ b/include/mbgl/shaders/mtl/background_pattern.hpp @@ -10,6 +10,7 @@ struct ShaderSource static constexpr auto name = "BackgroundPatternShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/circle.hpp b/include/mbgl/shaders/mtl/circle.hpp index 2fbcf303b..738edb547 100644 --- a/include/mbgl/shaders/mtl/circle.hpp +++ b/include/mbgl/shaders/mtl/circle.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "CircleShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; @@ -22,27 +23,56 @@ struct ShaderSource { struct VertexStage { short2 position [[attribute(0)]]; + +#if !defined(HAS_UNIFORM_u_color) float4 color [[attribute(1)]]; +#endif +#if !defined(HAS_UNIFORM_u_radius) float2 radius [[attribute(2)]]; +#endif +#if !defined(HAS_UNIFORM_u_blur) float2 blur [[attribute(3)]]; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float2 opacity [[attribute(4)]]; +#endif +#if !defined(HAS_UNIFORM_u_stroke_color) float4 stroke_color [[attribute(5)]]; +#endif +#if !defined(HAS_UNIFORM_u_stroke_width) float2 stroke_width [[attribute(6)]]; +#endif +#if !defined(HAS_UNIFORM_u_stroke_opacity) float2 stroke_opacity [[attribute(7)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; - float4 color; - float radius; - half blur; - half opacity; - float4 stroke_color; - float stroke_width; - half stroke_opacity; - float2 extrude; half antialiasblur; + +#if !defined(HAS_UNIFORM_u_color) + half4 color; +#endif +#if !defined(HAS_UNIFORM_u_radius) + float radius; +#endif +#if !defined(HAS_UNIFORM_u_blur) + half blur; +#endif +#if !defined(HAS_UNIFORM_u_opacity) + half opacity; +#endif +#if !defined(HAS_UNIFORM_u_stroke_color) + half4 stroke_color; +#endif +#if !defined(HAS_UNIFORM_u_stroke_width) + half stroke_width; +#endif +#if !defined(HAS_UNIFORM_u_stroke_opacity) + half stroke_opacity; +#endif }; struct alignas(16) CircleDrawableUBO { @@ -98,13 +128,17 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const CirclePermutationUBO& permutation [[buffer(12)]], device const ExpressionInputsUBO& expr [[buffer(13)]]) { - const auto color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr); - const auto radius = valueFor(permutation.radius, props.radius, vertx.radius, interp.radius_t, expr); - const auto blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr); - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); - const auto stroke_color = colorFor(permutation.stroke_color, props.stroke_color, vertx.stroke_color, interp.stroke_color_t, expr); - const auto stroke_width = valueFor(permutation.stroke_width, props.stroke_width, vertx.stroke_width, interp.stroke_width_t, expr); - const auto stroke_opacity = valueFor(permutation.stroke_opacity, props.stroke_opacity, vertx.stroke_opacity, interp.stroke_opacity_t, expr); +#if defined(HAS_UNIFORM_u_radius) + const auto radius = props.radius; +#else + const auto radius = valueFor(permutation.radius, props.radius, vertx.radius, interp.radius_t, expr); +#endif + +#if defined(HAS_UNIFORM_u_stroke_width) + const auto stroke_width = props.stroke_width; +#else + const auto stroke_width = valueFor(permutation.stroke_width, props.stroke_width, vertx.stroke_width, interp.stroke_width_t, expr); +#endif // unencode the extrusion vector that we snuck into the a_pos vector const float2 extrude = glMod(float2(vertx.position), 2.0) * 2.0 - 1.0; @@ -142,15 +176,30 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], return { .position = position, - .color = color, - .radius = radius, - .blur = half(blur), - .opacity = half(opacity), - .stroke_color = stroke_color, - .stroke_width = stroke_width, - .stroke_opacity = half(stroke_opacity), .extrude = extrude, .antialiasblur = antialiasblur, + +#if !defined(HAS_UNIFORM_u_color) + .color = half4(colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_radius) + .radius = half(radius), +#endif +#if !defined(HAS_UNIFORM_u_blur) + .blur = half(valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = half(valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_stroke_color) + .stroke_color = half4(colorFor(permutation.stroke_color, props.stroke_color, vertx.stroke_color, interp.stroke_color_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_stroke_width) + .stroke_width = half(stroke_width), +#endif +#if !defined(HAS_UNIFORM_u_stroke_opacity) + .stroke_opacity = half(valueFor(permutation.stroke_opacity, props.stroke_opacity, vertx.stroke_opacity, interp.stroke_opacity_t, expr)), +#endif }; } @@ -162,13 +211,49 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } - const float extrude_length = length(in.extrude); - const float antialiased_blur = -max(in.blur, in.antialiasblur); - const float opacity_t = smoothstep(0.0, antialiased_blur, extrude_length - 1.0); - const float color_t = (in.stroke_width < 0.01) ? 0.0 : - smoothstep(antialiased_blur, 0.0, extrude_length - in.radius / (in.radius + in.stroke_width)); +#if defined(HAS_UNIFORM_u_color) + const half4 color = half4(props.color); +#else + const half4 color = in.color; +#endif +#if defined(HAS_UNIFORM_u_radius) + const float radius = props.radius; +#else + const float radius = in.radius; +#endif +#if defined(HAS_UNIFORM_u_blur) + const half blur = props.blur; +#else + const half blur = in.blur; +#endif +#if defined(HAS_UNIFORM_u_opacity) + const half opacity = props.opacity; +#else + const half opacity = in.opacity; +#endif +#if defined(HAS_UNIFORM_u_stroke_color) + const half4 stroke_color = half4(props.stroke_color); +#else + const half4 stroke_color = in.stroke_color; +#endif +#if defined(HAS_UNIFORM_u_stroke_width) + const half stroke_width = props.stroke_width; +#else + const half stroke_width = in.stroke_width; +#endif +#if defined(HAS_UNIFORM_u_stroke_opacity) + const half stroke_opacity = props.stroke_opacity; +#else + const half stroke_opacity = in.stroke_opacity; +#endif - return half4(opacity_t * mix(in.color * in.opacity, in.stroke_color * in.stroke_opacity, color_t)); + const float extrude_length = length(in.extrude); + const float antialiased_blur = -max(blur, in.antialiasblur); + const float opacity_t = smoothstep(0.0, antialiased_blur, extrude_length - 1.0); + const float color_t = (stroke_width < 0.01) ? 0.0 : + smoothstep(antialiased_blur, 0.0, extrude_length - radius / (radius + stroke_width)); + + return half4(opacity_t * mix(color * opacity, stroke_color * stroke_opacity, color_t)); } )"; }; diff --git a/include/mbgl/shaders/mtl/clipping_mask.hpp b/include/mbgl/shaders/mtl/clipping_mask.hpp index 197f5c4f4..596ea618e 100644 --- a/include/mbgl/shaders/mtl/clipping_mask.hpp +++ b/include/mbgl/shaders/mtl/clipping_mask.hpp @@ -21,6 +21,7 @@ struct ShaderSource { static constexpr auto name = "ClippingMaskProgram"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/collision_box.hpp b/include/mbgl/shaders/mtl/collision_box.hpp index 6f87dfce9..aa55feacd 100644 --- a/include/mbgl/shaders/mtl/collision_box.hpp +++ b/include/mbgl/shaders/mtl/collision_box.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "CollisionBoxShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/collision_circle.hpp b/include/mbgl/shaders/mtl/collision_circle.hpp index 52a9b2e24..7b6b3d120 100644 --- a/include/mbgl/shaders/mtl/collision_circle.hpp +++ b/include/mbgl/shaders/mtl/collision_circle.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "CollisionCircleShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/debug.hpp b/include/mbgl/shaders/mtl/debug.hpp index b59dc284f..72ae1b399 100644 --- a/include/mbgl/shaders/mtl/debug.hpp +++ b/include/mbgl/shaders/mtl/debug.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "DebugShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/fill.hpp b/include/mbgl/shaders/mtl/fill.hpp index fe43c3489..2f5051e8a 100644 --- a/include/mbgl/shaders/mtl/fill.hpp +++ b/include/mbgl/shaders/mtl/fill.hpp @@ -13,8 +13,9 @@ struct ShaderSource { static constexpr auto name = "FillShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; - static const std::array attributes; + static const std::array attributes; static const std::array uniforms; static const std::array textures; @@ -22,14 +23,24 @@ struct ShaderSource { struct VertexStage { short2 position [[attribute(0)]]; + +#if !defined(HAS_UNIFORM_u_color) float4 color [[attribute(1)]]; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float2 opacity [[attribute(2)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; - float4 color; + +#if !defined(HAS_UNIFORM_u_color) + half4 color; +#endif +#if !defined(HAS_UNIFORM_u_opacity) half opacity; +#endif }; struct alignas(16) FillDrawableUBO { @@ -58,16 +69,14 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillInterpolateUBO& interp [[buffer(5)]], device const FillPermutationUBO& permutation [[buffer(6)]], device const ExpressionInputsUBO& expr [[buffer(7)]]) { - - const auto color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr); - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); - - float4 position = drawable.matrix * float4(float2(vertx.position), 0.0f, 1.0f); - return { - .position = position, - .color = color, - .opacity = half(opacity), + .position = drawable.matrix * float4(float2(vertx.position), 0.0f, 1.0f), +#if !defined(HAS_UNIFORM_u_color) + .color = half4(colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = half(valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr)), +#endif }; } @@ -78,7 +87,19 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } - return half4(in.color * in.opacity); +#if defined(HAS_UNIFORM_u_color) + const half4 color = half4(props.color); +#else + const half4 color = in.color; +#endif + +#if defined(HAS_UNIFORM_u_opacity) + const half opacity = props.opacity; +#else + const half opacity = in.opacity; +#endif + + return half4(color * opacity); } )"; }; @@ -88,13 +109,13 @@ struct ShaderSource { static constexpr auto name = "FillOutlineShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; - static const std::array attributes; + static const std::array attributes; static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( - struct VertexStage { short2 position [[attribute(0)]]; float4 outline_color [[attribute(1)]]; @@ -103,9 +124,13 @@ struct VertexStage { struct FragmentStage { float4 position [[position, invariant]]; - float4 outline_color; float2 pos; +#if !defined(HAS_UNIFORM_u_outline_color) + half4 outline_color; +#endif +#if !defined(HAS_UNIFORM_u_opacity) half opacity; +#endif }; struct alignas(16) FillOutlineDrawableUBO { @@ -136,18 +161,16 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillOutlineInterpolateUBO& interp [[buffer(5)]], device const FillOutlinePermutationUBO& permutation [[buffer(6)]], device const ExpressionInputsUBO& expr [[buffer(7)]]) { - - const auto outline_color = colorFor(permutation.outline_color, props.outline_color, vertx.outline_color, interp.outline_color_t, expr); - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); - - float4 position = drawable.matrix * float4(float2(vertx.position), 0.0f, 1.0f); - float2 v_pos = (position.xy / position.w + 1.0) / 2.0 * drawable.world; - + const float4 position = drawable.matrix * float4(float2(vertx.position), 0.0f, 1.0f); return { .position = position, - .outline_color = outline_color, - .pos = v_pos, - .opacity = half(opacity), + .pos = (position.xy / position.w + 1.0) / 2.0 * drawable.world, +#if !defined(HAS_UNIFORM_u_outline_color) + .outline_color = half4(colorFor(permutation.outline_color, props.outline_color, vertx.outline_color, interp.outline_color_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = half( valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr)), +#endif }; } @@ -164,7 +187,19 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], // float dist = length(in.pos - in.position.xy); // float alpha = 1.0 - smoothstep(0.0, 1.0, dist); - return half4(in.outline_color * in.opacity); +#if defined(HAS_UNIFORM_u_outline_color) + const half4 color = half4(props.outline_color); +#else + const half4 color = in.outline_color; +#endif + +#if defined(HAS_UNIFORM_u_opacity) + const half opacity = props.opacity; +#else + const half opacity = in.opacity; +#endif + + return half4(color * opacity); } )"; }; @@ -174,27 +209,41 @@ struct ShaderSource { static constexpr auto name = "FillPatternShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( - struct VertexStage { short2 position [[attribute(0)]]; + +#if !defined(HAS_UNIFORM_u_pattern_from) ushort4 pattern_from [[attribute(1)]]; +#endif +#if !defined(HAS_UNIFORM_u_pattern_to) ushort4 pattern_to [[attribute(2)]]; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float2 opacity [[attribute(3)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; - float4 pattern_from; - float4 pattern_to; float2 v_pos_a; float2 v_pos_b; + +#if !defined(HAS_UNIFORM_u_pattern_from) + float4 pattern_from; +#endif +#if !defined(HAS_UNIFORM_u_pattern_to) + float4 pattern_to; +#endif +#if !defined(HAS_UNIFORM_u_opacity) half opacity; +#endif }; struct alignas(16) FillPatternDrawableUBO { @@ -235,37 +284,51 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const FillPatternInterpolateUBO& interp [[buffer(7)]], device const FillPatternPermutationUBO& permutation [[buffer(8)]], device const ExpressionInputsUBO& expr [[buffer(9)]]) { +#if defined(HAS_UNIFORM_u_pattern_from) + const auto pattern_from = tileProps.pattern_from; +#else + const auto pattern_from = patternFor(permutation.pattern_from, tileProps.pattern_from, vertx.pattern_from, interp.pattern_from_t, expr); +#endif - const auto pattern_from = patternFor(permutation.pattern_from, tileProps.pattern_from, vertx.pattern_from, interp.pattern_from_t, expr); - const auto pattern_to = patternFor(permutation.pattern_to, tileProps.pattern_to, vertx.pattern_to, interp.pattern_to_t, expr); - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); +#if defined(HAS_UNIFORM_u_pattern_to) + const auto pattern_to = tileProps.pattern_to; +#else + const auto pattern_to = patternFor(permutation.pattern_to, tileProps.pattern_to, vertx.pattern_to, interp.pattern_to_t, expr); +#endif - float2 pattern_tl_a = pattern_from.xy; - float2 pattern_br_a = pattern_from.zw; - float2 pattern_tl_b = pattern_to.xy; - float2 pattern_br_b = pattern_to.zw; + const float2 pattern_tl_a = pattern_from.xy; + const float2 pattern_br_a = pattern_from.zw; + const float2 pattern_tl_b = pattern_to.xy; + const float2 pattern_br_b = pattern_to.zw; - float pixelRatio = drawable.scale.x; - float tileZoomRatio = drawable.scale.y; - float fromScale = drawable.scale.z; - float toScale = drawable.scale.w; + const float pixelRatio = drawable.scale.x; + const float tileZoomRatio = drawable.scale.y; + const float fromScale = drawable.scale.z; + const float toScale = drawable.scale.w; - float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); - float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); - float2 postion = float2(vertx.position); + const float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); + const float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); + const float2 postion = float2(vertx.position); return { .position = drawable.matrix * float4(postion, 0, 1), - .pattern_from = pattern_from, - .pattern_to = pattern_to, .v_pos_a = get_pattern_pos(drawable.pixel_coord_upper, drawable.pixel_coord_lower, fromScale * display_size_a, tileZoomRatio, postion), .v_pos_b = get_pattern_pos(drawable.pixel_coord_upper, drawable.pixel_coord_lower, toScale * display_size_b, tileZoomRatio, postion), - .opacity = half(opacity), +#if !defined(HAS_UNIFORM_u_pattern_from) + .pattern_from = pattern_from, +#endif +#if !defined(HAS_UNIFORM_u_pattern_to) + .pattern_to = pattern_to, +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = half(valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr)), +#endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const FillPatternDrawableUBO& drawable [[buffer(4)]], + device const FillPatternTilePropsUBO& tileProps [[buffer(5)]], device const FillPatternEvaluatedPropsUBO& props [[buffer(6)]], device const FillPatternPermutationUBO& permutation [[buffer(8)]], texture2d image0 [[texture(0)]], @@ -274,10 +337,28 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } - const float2 pattern_tl_a = in.pattern_from.xy; - const float2 pattern_br_a = in.pattern_from.zw; - const float2 pattern_tl_b = in.pattern_to.xy; - const float2 pattern_br_b = in.pattern_to.zw; +#if defined(HAS_UNIFORM_u_pattern_from) + const auto pattern_from = tileProps.pattern_from; +#else + const auto pattern_from = in.pattern_from; +#endif + +#if defined(HAS_UNIFORM_u_pattern_to) + const auto pattern_to = tileProps.pattern_to; +#else + const auto pattern_to = in.pattern_to; +#endif + +#if defined(HAS_UNIFORM_u_opacity) + const auto opacity = props.opacity; +#else + const auto opacity = in.opacity; +#endif + + const float2 pattern_tl_a = pattern_from.xy; + const float2 pattern_br_a = pattern_from.zw; + const float2 pattern_tl_b = pattern_to.xy; + const float2 pattern_br_b = pattern_to.zw; const float2 imagecoord = glMod(in.v_pos_a, 1.0); const float2 pos = mix(pattern_tl_a / drawable.texsize, pattern_br_a / drawable.texsize, imagecoord); @@ -287,7 +368,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], const float2 pos2 = mix(pattern_tl_b / drawable.texsize, pattern_br_b / drawable.texsize, imagecoord_b); const float4 color2 = image0.sample(image0_sampler, pos2); - return half4(mix(color1, color2, props.fade) * in.opacity); + return half4(mix(color1, color2, props.fade) * opacity); } )"; }; @@ -297,6 +378,7 @@ struct ShaderSource attributes; static const std::array uniforms; @@ -306,19 +388,33 @@ struct ShaderSource image0 [[texture(0)]], @@ -401,10 +516,28 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } - const float2 pattern_tl_a = in.pattern_from.xy; - const float2 pattern_br_a = in.pattern_from.zw; - const float2 pattern_tl_b = in.pattern_to.xy; - const float2 pattern_br_b = in.pattern_to.zw; +#if defined(HAS_UNIFORM_u_pattern_from) + const auto pattern_from = tileProps.pattern_from; +#else + const auto pattern_from = in.pattern_from; +#endif + +#if defined(HAS_UNIFORM_u_pattern_to) + const auto pattern_to = tileProps.pattern_to; +#else + const auto pattern_to = in.pattern_to; +#endif + +#if defined(HAS_UNIFORM_u_opacity) + const auto opacity = props.opacity; +#else + const auto opacity = in.opacity; +#endif + + const float2 pattern_tl_a = pattern_from.xy; + const float2 pattern_br_a = pattern_from.zw; + const float2 pattern_tl_b = pattern_to.xy; + const float2 pattern_br_b = pattern_to.zw; const float2 imagecoord = glMod(in.v_pos_a, 1.0); const float2 pos = mix(pattern_tl_a / drawable.texsize, pattern_br_a / drawable.texsize, imagecoord); @@ -418,7 +551,7 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], //float dist = length(in.v_pos - in.position.xy); //float alpha = 1.0 - smoothstep(0.0, 1.0, dist); - return half4(mix(color1, color2, props.fade) * in.opacity); + return half4(mix(color1, color2, props.fade) * opacity); } )"; }; diff --git a/include/mbgl/shaders/mtl/fill_extrusion.hpp b/include/mbgl/shaders/mtl/fill_extrusion.hpp index d0c78eab3..b20274087 100644 --- a/include/mbgl/shaders/mtl/fill_extrusion.hpp +++ b/include/mbgl/shaders/mtl/fill_extrusion.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "FillExtrusionShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp b/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp index 67724b000..0ef4f28b4 100644 --- a/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp +++ b/include/mbgl/shaders/mtl/fill_extrusion_pattern.hpp @@ -13,6 +13,7 @@ struct ShaderSource attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/heatmap.hpp b/include/mbgl/shaders/mtl/heatmap.hpp index 8ea16d229..c0aef6c0c 100644 --- a/include/mbgl/shaders/mtl/heatmap.hpp +++ b/include/mbgl/shaders/mtl/heatmap.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "HeatmapShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/heatmap_texture.hpp b/include/mbgl/shaders/mtl/heatmap_texture.hpp index d9a621489..793bef27e 100644 --- a/include/mbgl/shaders/mtl/heatmap_texture.hpp +++ b/include/mbgl/shaders/mtl/heatmap_texture.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "HeatmapTextureShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/hillshade.hpp b/include/mbgl/shaders/mtl/hillshade.hpp index 4248ce134..852af4f4a 100644 --- a/include/mbgl/shaders/mtl/hillshade.hpp +++ b/include/mbgl/shaders/mtl/hillshade.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "HillshadeShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/hillshade_prepare.hpp b/include/mbgl/shaders/mtl/hillshade_prepare.hpp index c9a99daec..c4c9617f9 100644 --- a/include/mbgl/shaders/mtl/hillshade_prepare.hpp +++ b/include/mbgl/shaders/mtl/hillshade_prepare.hpp @@ -13,6 +13,7 @@ struct ShaderSource static constexpr auto name = "HillshadePrepareShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/line.hpp b/include/mbgl/shaders/mtl/line.hpp index 19955ba95..6e057fabe 100644 --- a/include/mbgl/shaders/mtl/line.hpp +++ b/include/mbgl/shaders/mtl/line.hpp @@ -13,32 +13,52 @@ struct ShaderSource { static constexpr auto name = "LineShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( - struct VertexStage { short2 pos_normal [[attribute(0)]]; uchar4 data [[attribute(1)]]; + +#if !defined(HAS_UNIFORM_u_color) float4 color [[attribute(2)]]; +#endif +#if !defined(HAS_UNIFORM_u_blur) float2 blur [[attribute(3)]]; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float2 opacity [[attribute(4)]]; +#endif +#if !defined(HAS_UNIFORM_u_gapwidth) float2 gapwidth [[attribute(5)]]; +#endif +#if !defined(HAS_UNIFORM_u_offset) float2 offset [[attribute(6)]]; +#endif +#if !defined(HAS_UNIFORM_u_width) float2 width [[attribute(7)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; float2 width2; float2 normal; - float gamma_scale; + half gamma_scale; + +#if !defined(HAS_UNIFORM_u_color) float4 color; +#endif +#if !defined(HAS_UNIFORM_u_blur) float blur; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float opacity; +#endif }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], @@ -48,12 +68,21 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const LinePermutationUBO& permutation [[buffer(11)]], device const ExpressionInputsUBO& expr [[buffer(12)]]) { - const auto color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr); - const auto blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr); - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); +#if defined(HAS_UNIFORM_u_gapwidth) + const auto gapwidth = props.gapwidth / 2; +#else const auto gapwidth = valueFor(permutation.gapwidth, props.gapwidth, vertx.gapwidth, interp.gapwidth_t, expr) / 2; +#endif +#if defined(HAS_UNIFORM_u_offset) + const auto offset = props.offset * -1; +#else const auto offset = valueFor(permutation.offset, props.offset, vertx.offset, interp.offset_t, expr) * -1; +#endif +#if defined(HAS_UNIFORM_u_width) + const auto width = props.width; +#else const auto width = valueFor(permutation.width, props.width, vertx.width, interp.width_t, expr); +#endif // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. @@ -95,29 +124,53 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], .position = position, .width2 = float2(outset, inset), .normal = v_normal, - .gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective, - .color = color, - .blur = blur, - .opacity = opacity, + .gamma_scale = half(extrude_length_without_perspective / extrude_length_with_perspective), + +#if !defined(HAS_UNIFORM_u_color) + .color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr), +#endif +#if !defined(HAS_UNIFORM_u_blur) + .blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr), +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr), +#endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const LineUBO& line [[buffer(8)]], + device const LinePropertiesUBO& props [[buffer(9)]], device const LinePermutationUBO& permutation [[buffer(11)]]) { if (permutation.overdrawInspector) { return half4(1.0); } +#if defined(HAS_UNIFORM_u_color) + const float4 color = props.color; +#else + const float4 color = in.color; +#endif +#if defined(HAS_UNIFORM_u_blur) + const float blur = props.blur; +#else + const float blur = in.blur; +#endif +#if defined(HAS_UNIFORM_u_opacity) + const float opacity = props.opacity; +#else + const float opacity = in.opacity; +#endif + // Calculate the distance of the pixel from the line in pixels. const float dist = length(in.normal) * in.width2.x; // Calculate the antialiasing fade factor. This is either when fading in the // line in case of an offset line (v_width2.y) or when fading out (v_width2.x) - const float blur2 = (in.blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; + const float blur2 = (blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; const float alpha = clamp(min(dist - (in.width2.y - blur2), in.width2.x - dist) / blur2, 0.0, 1.0); - return half4(in.color * (alpha * in.opacity)); + return half4(color * (alpha * opacity)); } )"; }; @@ -127,35 +180,59 @@ struct ShaderSource { static constexpr auto name = "LinePatternShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( - struct VertexStage { short2 pos_normal [[attribute(0)]]; uchar4 data [[attribute(1)]]; + +#if !defined(HAS_UNIFORM_u_blur) float2 blur [[attribute(2)]]; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float2 opacity [[attribute(3)]]; +#endif +#if !defined(HAS_UNIFORM_u_gapwidth) float2 gapwidth [[attribute(4)]]; +#endif +#if !defined(HAS_UNIFORM_u_offset) float2 offset [[attribute(5)]]; +#endif +#if !defined(HAS_UNIFORM_u_width) float2 width [[attribute(6)]]; +#endif +#if !defined(HAS_UNIFORM_u_pattern_from) ushort4 pattern_from [[attribute(7)]]; +#endif +#if !defined(HAS_UNIFORM_u_pattern_to) ushort4 pattern_to [[attribute(8)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; float2 width2; float2 normal; - float gamma_scale; - float blur; - float opacity; - float4 pattern_from; - float4 pattern_to; + half gamma_scale; float linesofar; + +#if !defined(HAS_UNIFORM_u_blur) + float blur; +#endif +#if !defined(HAS_UNIFORM_u_opacity) + float opacity; +#endif +#if !defined(HAS_UNIFORM_u_pattern_from) + float4 pattern_from; +#endif +#if !defined(HAS_UNIFORM_u_pattern_to) + float4 pattern_to; +#endif }; struct alignas(16) LinePatternUBO { @@ -202,14 +279,21 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const LinePermutationUBO& permutation [[buffer(13)]], device const ExpressionInputsUBO& expr [[buffer(14)]]) { - const auto blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr); - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); +#if defined(HAS_UNIFORM_u_gapwidth) + const auto gapwidth = props.gapwidth / 2; +#else const auto gapwidth = valueFor(permutation.gapwidth, props.gapwidth, vertx.gapwidth, interp.gapwidth_t, expr) / 2; +#endif +#if defined(HAS_UNIFORM_u_offset) + const auto offset = props.offset * -1; +#else const auto offset = valueFor(permutation.offset, props.offset, vertx.offset, interp.offset_t, expr) * -1; +#endif +#if defined(HAS_UNIFORM_u_width) + const auto width = props.width; +#else const auto width = valueFor(permutation.width, props.width, vertx.width, interp.width_t, expr); - - const auto pattern_from = patternFor(permutation.pattern_from, tileProps.pattern_from, vertx.pattern_from, interp.pattern_from_t, expr); - const auto pattern_to = patternFor(permutation.pattern_to, tileProps.pattern_to, vertx.pattern_to, interp.pattern_to_t, expr); +#endif // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. @@ -218,7 +302,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const float2 a_extrude = float2(vertx.data.xy) - 128.0; const float a_direction = fmod(float(vertx.data.z), 4.0) - 1.0; - float linesofar = (floor(vertx.data.z / 4.0) + vertx.data.w * 64.0) * LINE_DISTANCE_SCALE; + const float linesofar = (floor(vertx.data.z / 4.0) + vertx.data.w * 64.0) * LINE_DISTANCE_SCALE; const float2 pos = floor(float2(vertx.pos_normal) * 0.5); // x is 1 if it's a round cap, 0 otherwise @@ -253,17 +337,28 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], .position = position, .width2 = float2(outset, inset), .normal = v_normal, - .gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective, - .blur = blur, - .opacity = opacity, - .pattern_from = pattern_from, - .pattern_to = pattern_to, + .gamma_scale = half(extrude_length_without_perspective / extrude_length_with_perspective), .linesofar = linesofar, + +#if !defined(HAS_UNIFORM_u_blur) + .blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr), +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr), +#endif +#if !defined(HAS_UNIFORM_u_pattern_from) + .pattern_from = patternFor(permutation.pattern_from, tileProps.pattern_from, vertx.pattern_from, interp.pattern_from_t, expr), +#endif +#if !defined(HAS_UNIFORM_u_pattern_to) + .pattern_to = patternFor(permutation.pattern_to, tileProps.pattern_to, vertx.pattern_to, interp.pattern_to_t, expr), +#endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const LinePatternUBO& line [[buffer(9)]], + device const LinePatternPropertiesUBO& props [[buffer(10)]], + device const LinePatternTilePropertiesUBO& tileProps [[buffer(12)]], device const LinePermutationUBO& permutation [[buffer(13)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { @@ -271,47 +366,68 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } - float2 pattern_tl_a = in.pattern_from.xy; - float2 pattern_br_a = in.pattern_from.zw; - float2 pattern_tl_b = in.pattern_to.xy; - float2 pattern_br_b = in.pattern_to.zw; - - float pixelRatio = line.scale.x; - float tileZoomRatio = line.scale.y; - float fromScale = line.scale.z; - float toScale = line.scale.w; - - float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); - float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); - - float2 pattern_size_a = float2(display_size_a.x * fromScale / tileZoomRatio, display_size_a.y); - float2 pattern_size_b = float2(display_size_b.x * toScale / tileZoomRatio, display_size_b.y); - +#if defined(HAS_UNIFORM_u_blur) + const auto blur = props.blur; +#else + const auto blur = in.blur; +#endif +#if defined(HAS_UNIFORM_u_opacity) + const auto opacity = props.opacity; +#else + const auto opacity = in.opacity; +#endif +#if defined(HAS_UNIFORM_u_pattern_from) + const auto pattern_from = tileProps.pattern_from; +#else + const auto pattern_from = in.pattern_from; +#endif +#if defined(HAS_UNIFORM_u_pattern_to) + const auto pattern_to = tileProps.pattern_to; +#else + const auto pattern_to = in.pattern_to; +#endif + + const float2 pattern_tl_a = pattern_from.xy; + const float2 pattern_br_a = pattern_from.zw; + const float2 pattern_tl_b = pattern_to.xy; + const float2 pattern_br_b = pattern_to.zw; + + const float pixelRatio = line.scale.x; + const float tileZoomRatio = line.scale.y; + const float fromScale = line.scale.z; + const float toScale = line.scale.w; + + const float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); + const float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); + + const float2 pattern_size_a = float2(display_size_a.x * fromScale / tileZoomRatio, display_size_a.y); + const float2 pattern_size_b = float2(display_size_b.x * toScale / tileZoomRatio, display_size_b.y); + // Calculate the distance of the pixel from the line in pixels. - float dist = length(in.normal) * in.width2.x; - + const float dist = length(in.normal) * in.width2.x; + // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (in.width2.y) or when fading out // (in.width2.x) - float blur2 = (in.blur + 1.0 / DEVICE_PIXEL_RATIO) * in.gamma_scale; - float alpha = clamp(min(dist - (in.width2.y - blur2), in.width2.x - dist) / blur2, 0.0, 1.0); - - float x_a = glMod(in.linesofar / pattern_size_a.x, 1.0); - float x_b = glMod(in.linesofar / pattern_size_b.x, 1.0); - + const float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * in.gamma_scale; + const float alpha = clamp(min(dist - (in.width2.y - blur2), in.width2.x - dist) / blur2, 0.0, 1.0); + + const float x_a = glMod(in.linesofar / pattern_size_a.x, 1.0); + const float x_b = glMod(in.linesofar / pattern_size_b.x, 1.0); + // in.normal.y is 0 at the midpoint of the line, -1 at the lower edge, 1 at the upper edge // we clamp the line width outset to be between 0 and half the pattern height plus padding (2.0) // to ensure we don't sample outside the designated symbol on the sprite sheet. // 0.5 is added to shift the component to be bounded between 0 and 1 for interpolation of // the texture coordinate - float y_a = 0.5 + (in.normal.y * clamp(in.width2.x, 0.0, (pattern_size_a.y + 2.0) / 2.0) / pattern_size_a.y); - float y_b = 0.5 + (in.normal.y * clamp(in.width2.x, 0.0, (pattern_size_b.y + 2.0) / 2.0) / pattern_size_b.y); - float2 pos_a = mix(pattern_tl_a / line.texsize, pattern_br_a / line.texsize, float2(x_a, y_a)); - float2 pos_b = mix(pattern_tl_b / line.texsize, pattern_br_b / line.texsize, float2(x_b, y_b)); + const float y_a = 0.5 + (in.normal.y * clamp(in.width2.x, 0.0, (pattern_size_a.y + 2.0) / 2.0) / pattern_size_a.y); + const float y_b = 0.5 + (in.normal.y * clamp(in.width2.x, 0.0, (pattern_size_b.y + 2.0) / 2.0) / pattern_size_b.y); + const float2 pos_a = mix(pattern_tl_a / line.texsize, pattern_br_a / line.texsize, float2(x_a, y_a)); + const float2 pos_b = mix(pattern_tl_b / line.texsize, pattern_br_b / line.texsize, float2(x_b, y_b)); - float4 color = mix(image0.sample(image0_sampler, pos_a), image0.sample(image0_sampler, pos_b), line.fade); - - return half4(color * alpha * in.opacity); + const float4 color = mix(image0.sample(image0_sampler, pos_a), image0.sample(image0_sampler, pos_b), line.fade); + + return half4(color * alpha * opacity); } )"; }; @@ -321,36 +437,60 @@ struct ShaderSource { static constexpr auto name = "LineSDFShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( - struct VertexStage { short2 pos_normal [[attribute(0)]]; uchar4 data [[attribute(1)]]; + +#if !defined(HAS_UNIFORM_u_color) float4 color [[attribute(2)]]; +#endif +#if !defined(HAS_UNIFORM_u_blur) float2 blur [[attribute(3)]]; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float2 opacity [[attribute(4)]]; +#endif +#if !defined(HAS_UNIFORM_u_gapwidth) float2 gapwidth [[attribute(5)]]; +#endif +#if !defined(HAS_UNIFORM_u_offset) float2 offset [[attribute(6)]]; +#endif +#if !defined(HAS_UNIFORM_u_width) float2 width [[attribute(7)]]; +#endif +#if !defined(HAS_UNIFORM_u_floorwidth) float2 floorwidth [[attribute(8)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; - float4 color; float2 width2; float2 normal; - float gamma_scale; - float blur; - float opacity; float2 tex_a; float2 tex_b; + half gamma_scale; + +#if !defined(HAS_UNIFORM_u_color) + float4 color; +#endif +#if !defined(HAS_UNIFORM_u_blur) + float blur; +#endif +#if !defined(HAS_UNIFORM_u_opacity) + float opacity; +#endif +#if !defined(HAS_UNIFORM_u_floorwidth) float floorwidth; +#endif }; struct alignas(16) LineSDFUBO { @@ -395,13 +535,26 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const LinePermutationUBO& permutation [[buffer(12)]], device const ExpressionInputsUBO& expr [[buffer(13)]]) { - const auto color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr); - const auto blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr); - const auto opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr); - const auto gapwidth = valueFor(permutation.gapwidth, props.gapwidth, vertx.gapwidth, interp.gapwidth_t, expr) / 2; - const auto offset = valueFor(permutation.offset, props.offset, vertx.offset, interp.offset_t, expr) * -1; - const auto width = valueFor(permutation.width, props.width, vertx.width, interp.width_t, expr); - const auto floorwidth = valueFor(permutation.floorwidth, props.floorwidth, vertx.floorwidth, interp.floorwidth_t, expr); +#if defined(HAS_UNIFORM_u_gapwidth) + const auto gapwidth = props.gapwidth / 2; +#else + const auto gapwidth = valueFor(permutation.gapwidth, props.gapwidth, vertx.gapwidth, interp.gapwidth_t, expr) / 2; +#endif +#if defined(HAS_UNIFORM_u_offset) + const auto offset = props.offset * -1; +#else + const auto offset = valueFor(permutation.offset, props.offset, vertx.offset, interp.offset_t, expr) * -1; +#endif +#if defined(HAS_UNIFORM_u_width) + const auto width = props.width; +#else + const auto width = valueFor(permutation.width, props.width, vertx.width, interp.width_t, expr); +#endif +#if defined(HAS_UNIFORM_u_floorwidth) + const auto floorwidth = props.floorwidth; +#else + const auto floorwidth = valueFor(permutation.floorwidth, props.floorwidth, vertx.floorwidth, interp.floorwidth_t, expr); +#endif // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. @@ -443,20 +596,30 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], return { .position = position, - .color = color, .width2 = float2(outset, inset), .normal = v_normal, - .gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective, - .blur = blur, - .opacity = opacity, + .gamma_scale = half(extrude_length_without_perspective / extrude_length_with_perspective), .tex_a = float2(linesofar * line.patternscale_a.x / floorwidth, (normal.y * line.patternscale_a.y + line.tex_y_a) * 2.0), .tex_b = float2(linesofar * line.patternscale_b.x / floorwidth, (normal.y * line.patternscale_b.y + line.tex_y_b) * 2.0), + +#if !defined(HAS_UNIFORM_u_color) + .color = colorFor(permutation.color, props.color, vertx.color, interp.color_t, expr), +#endif +#if !defined(HAS_UNIFORM_u_blur) + .blur = valueFor(permutation.blur, props.blur, vertx.blur, interp.blur_t, expr), +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = valueFor(permutation.opacity, props.opacity, vertx.opacity, interp.opacity_t, expr), +#endif +#if !defined(HAS_UNIFORM_u_floorwidth) .floorwidth = floorwidth, +#endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const LineSDFUBO& line [[buffer(9)]], + device const LineSDFPropertiesUBO& props [[buffer(10)]], device const LinePermutationUBO& permutation [[buffer(12)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { @@ -464,18 +627,39 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } - const float dist = length(in.normal) * in.width2.x; +#if defined(HAS_UNIFORM_u_color) + const float4 color = props.color; +#else + const float4 color = in.color; +#endif +#if defined(HAS_UNIFORM_u_blur) + const float blur = props.blur; +#else + const float blur = in.blur; +#endif +#if defined(HAS_UNIFORM_u_opacity) + const float opacity = props.opacity; +#else + const float opacity = in.opacity; +#endif +#if defined(HAS_UNIFORM_u_floorwidth) + const float floorwidth = props.floorwidth; +#else + const float floorwidth = in.floorwidth; +#endif + // Calculate the antialiasing fade factor. This is either when fading in the - // line in case of an offset line (v_width2.y) or when fading out (v_width2.x) - const float blur2 = (in.blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; - float alpha = clamp(min(dist - (in.width2.y - blur2), in.width2.x - dist) / blur2, 0.0, 1.0); + // line in case of an offset line (`v_width2.y`) or when fading out (`v_width2.x`) + const float blur2 = (blur + 1.0 / line.device_pixel_ratio) * in.gamma_scale; - float sdfdist_a = image0.sample(image0_sampler, in.tex_a).a; - float sdfdist_b = image0.sample(image0_sampler, in.tex_b).a; - float sdfdist = mix(sdfdist_a, sdfdist_b, line.mix); - alpha *= smoothstep(0.5 - line.sdfgamma / in.floorwidth, 0.5 + line.sdfgamma / in.floorwidth, sdfdist); + const float sdfdist_a = image0.sample(image0_sampler, in.tex_a).a; + const float sdfdist_b = image0.sample(image0_sampler, in.tex_b).a; + const float sdfdist = mix(sdfdist_a, sdfdist_b, line.mix); + const float dist = length(in.normal) * in.width2.x; + const float alpha = clamp(min(dist - (in.width2.y - blur2), in.width2.x - dist) / blur2, 0.0, 1.0) * + smoothstep(0.5 - line.sdfgamma / floorwidth, 0.5 + line.sdfgamma / floorwidth, sdfdist); - return half4(in.color * (alpha * in.opacity)); + return half4(color * (alpha * opacity)); } )"; }; @@ -485,13 +669,13 @@ struct ShaderSource { static constexpr auto name = "LineBasicShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; static const std::array textures; static constexpr auto source = R"( - struct VertexStage { short2 pos_normal [[attribute(0)]]; uchar4 data [[attribute(1)]]; @@ -501,9 +685,7 @@ struct FragmentStage { float4 position [[position, invariant]]; float width2; float2 normal; - float gamma_scale; - float4 color; - float opacity; + half gamma_scale; }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], @@ -540,24 +722,23 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], .position = position, .width2 = outset, .normal = v_normal, - .gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective, - .color = props.color, - .opacity = props.opacity, + .gamma_scale = half(extrude_length_without_perspective / extrude_length_with_perspective), }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], - device const LineBasicUBO& line [[buffer(2)]]) { + device const LineBasicUBO& line [[buffer(2)]], + device const LineBasicPropertiesUBO& props [[buffer(3)]]) { // Calculate the distance of the pixel from the line in pixels. const float dist = length(in.normal) * in.width2; // Calculate the antialiasing fade factor. This is either when fading in the - // line in case of an offset line (v_width2.y) or when fading out (v_width2.x) + // line in case of an offset line (`v_width2.y`) or when fading out (`v_width2.x`) const float blur2 = (1.0 / line.device_pixel_ratio) * in.gamma_scale; const float alpha = clamp(min(dist + blur2, in.width2 - dist) / blur2, 0.0, 1.0); - return half4(in.color * (alpha * in.opacity)); + return half4(props.color * (alpha * props.opacity)); } )"; }; diff --git a/include/mbgl/shaders/mtl/line_gradient.hpp b/include/mbgl/shaders/mtl/line_gradient.hpp index e3f23e85f..7a0d1e09e 100644 --- a/include/mbgl/shaders/mtl/line_gradient.hpp +++ b/include/mbgl/shaders/mtl/line_gradient.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "LineGradientShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/raster.hpp b/include/mbgl/shaders/mtl/raster.hpp index 4b9022991..630d4c2d3 100644 --- a/include/mbgl/shaders/mtl/raster.hpp +++ b/include/mbgl/shaders/mtl/raster.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "RasterShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = false; static const std::array attributes; static const std::array uniforms; diff --git a/include/mbgl/shaders/mtl/shader_group.hpp b/include/mbgl/shaders/mtl/shader_group.hpp index 6b0c3888a..6726bc71d 100644 --- a/include/mbgl/shaders/mtl/shader_group.hpp +++ b/include/mbgl/shaders/mtl/shader_group.hpp @@ -5,47 +5,82 @@ #include #include #include +#include #include #include #include -#include +#include namespace mbgl { namespace mtl { +class ShaderGroupBase : public gfx::ShaderGroup { +public: + using StringIDSet = mbgl::unordered_set; + +protected: + using DefinesMap = mbgl::unordered_map; + void addAdditionalDefines(const StringIDSet& propertiesAsUniforms, DefinesMap& additionalDefines) { + const std::string uniformPrefix = "HAS_UNIFORM_u_"; + additionalDefines.reserve(propertiesAsUniforms.size() + 1); + additionalDefines.insert(std::make_pair("HAS_PERMUTATIONS", std::string())); + for (const auto nameID : propertiesAsUniforms) { + // We expect the names to be prefixed by "a_", but we need just the base here. + const auto name = stringIndexer().get(nameID); + const auto* base = (name[0] == 'a' && name[1] == '_') ? &name[2] : name.data(); + additionalDefines.insert(std::make_pair(uniformPrefix + base, std::string())); + } + } +}; + template -class ShaderGroup final : public gfx::ShaderGroup { +class ShaderGroup final : public ShaderGroupBase { public: ShaderGroup(const ProgramParameters& programParameters_) - : gfx::ShaderGroup(), + : ShaderGroupBase(), programParameters(programParameters_) {} ~ShaderGroup() noexcept override = default; gfx::ShaderPtr getOrCreateShader(gfx::Context& gfxContext, const mbgl::unordered_set& propertiesAsUniforms, std::string_view /*firstAttribName*/) override { - constexpr auto& name = shaders::ShaderSource::name; - constexpr auto& source = shaders::ShaderSource::source; - constexpr auto& vertMain = shaders::ShaderSource::vertexMainFunction; - constexpr auto& fragMain = shaders::ShaderSource::fragmentMainFunction; + using ShaderSource = shaders::ShaderSource; + constexpr auto& name = ShaderSource::name; + constexpr auto& source = ShaderSource::source; + constexpr auto& vertMain = ShaderSource::vertexMainFunction; + constexpr auto& fragMain = ShaderSource::fragmentMainFunction; + constexpr auto permutations = shaders::ShaderSource::hasPermutations; - const std::string shaderName = std::string(name); + const size_t key = permutations + ? util::order_independent_hash(propertiesAsUniforms.begin(), propertiesAsUniforms.end()) + : 0; + const std::string shaderName = permutations ? getShaderName(name, key) : name; auto shader = get(shaderName); if (!shader) { + DefinesMap additionalDefines; + if (permutations) { + addAdditionalDefines(propertiesAsUniforms, additionalDefines); + } + auto& context = static_cast(gfxContext); const auto shaderSource = std::string(shaders::prelude) + source; - shader = context.createProgram(shaderName, shaderSource, vertMain, fragMain, programParameters, {}); + shader = context.createProgram( + shaderName, shaderSource, vertMain, fragMain, programParameters, additionalDefines); assert(shader); if (!shader || !registerShader(shader, shaderName)) { assert(false); throw std::runtime_error("Failed to register " + shaderName + " with shader group!"); } + shader->setBindMissingAttributes(!permutations); + using ShaderClass = shaders::ShaderSource; for (const auto& attrib : ShaderClass::attributes) { - shader->initAttribute(attrib); + if (!permutations || !propertiesAsUniforms.count(attrib.nameID)) { + shader->initAttribute(attrib); + } } for (const auto& uniform : ShaderClass::uniforms) { shader->initUniformBlock(uniform); diff --git a/include/mbgl/shaders/mtl/shader_program.hpp b/include/mbgl/shaders/mtl/shader_program.hpp index 6e38a4451..390000586 100644 --- a/include/mbgl/shaders/mtl/shader_program.hpp +++ b/include/mbgl/shaders/mtl/shader_program.hpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -50,7 +49,7 @@ public: RendererBackend& backend, MTLFunctionPtr vertexFunction, MTLFunctionPtr fragmentFunction); - ~ShaderProgram() noexcept override = default; + ~ShaderProgram() noexcept override; static constexpr std::string_view Name{"GenericMTLShader"}; const std::string_view typeName() const noexcept override { return Name; } @@ -70,6 +69,9 @@ public: void initUniformBlock(const shaders::UniformBlockInfo&); void initTexture(const shaders::TextureInfo&); + bool getBindMissingAttributes() const { return bindMissingAttributes; } + void setBindMissingAttributes(bool value) { bindMissingAttributes = value; } + protected: std::string shaderName; RendererBackend& backend; @@ -78,6 +80,7 @@ protected: UniformBlockArray uniformBlocks; VertexAttributeArray vertexAttributes; std::unordered_map textureBindings; + bool bindMissingAttributes = true; }; } // namespace mtl diff --git a/include/mbgl/shaders/mtl/symbol_icon.hpp b/include/mbgl/shaders/mtl/symbol_icon.hpp index 3de7d6d39..49d463890 100644 --- a/include/mbgl/shaders/mtl/symbol_icon.hpp +++ b/include/mbgl/shaders/mtl/symbol_icon.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "SymbolIconShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; @@ -25,14 +26,20 @@ struct VertexStage { float4 pixeloffset [[attribute(2)]]; float3 projected_pos [[attribute(3)]]; float fade_opacity [[attribute(4)]]; + +#if !defined(HAS_UNIFORM_u_opacity) float opacity [[attribute(5)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; float2 tex; - float fade_opacity; - float opacity; + half fade_opacity; + +#if !defined(HAS_UNIFORM_u_opacity) + half opacity; +#endif }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], @@ -44,8 +51,6 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const SymbolPermutationUBO& permutation [[buffer(13)]], device const ExpressionInputsUBO& expr [[buffer(14)]]) { - const auto opacity = valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr); - const float2 a_pos = vertx.pos_offset.xy; const float2 a_offset = vertx.pos_offset.zw; @@ -107,13 +112,16 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], return { .position = position, .tex = a_tex / drawable.texsize, - .fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change)), - .opacity = opacity, + .fade_opacity = half(max(0.0, min(1.0, fade_opacity[0] + fade_change))), +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = half(valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr)), +#endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const SymbolDrawableUBO& drawable [[buffer(8)]], + device const SymbolDrawablePaintUBO& paint [[buffer(10)]], device const SymbolPermutationUBO& permutation [[buffer(13)]], texture2d image [[texture(0)]], sampler image_sampler [[sampler(0)]]) { @@ -121,7 +129,13 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } - return half4(image.sample(image_sampler, in.tex) * (in.opacity * in.fade_opacity)); +#if defined(HAS_UNIFORM_u_opacity) + const half opacity = half(paint.opacity); +#else + const half opacity = in.opacity; +#endif + + return half4(image.sample(image_sampler, in.tex) * (opacity * in.fade_opacity)); } )"; }; diff --git a/include/mbgl/shaders/mtl/symbol_sdf.hpp b/include/mbgl/shaders/mtl/symbol_sdf.hpp index bff393a78..a2d540f91 100644 --- a/include/mbgl/shaders/mtl/symbol_sdf.hpp +++ b/include/mbgl/shaders/mtl/symbol_sdf.hpp @@ -13,6 +13,7 @@ struct ShaderSource { static constexpr auto name = "SymbolSDFIconShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; @@ -26,22 +27,43 @@ struct VertexStage { float3 projected_pos [[attribute(3)]]; float fade_opacity [[attribute(4)]]; +#if !defined(HAS_UNIFORM_u_fill_color) float4 fill_color [[attribute(5)]]; +#endif +#if !defined(HAS_UNIFORM_u_halo_color) float4 halo_color [[attribute(6)]]; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float opacity [[attribute(7)]]; +#endif +#if !defined(HAS_UNIFORM_u_halo_width) float halo_width [[attribute(8)]]; +#endif +#if !defined(HAS_UNIFORM_u_halo_blur) float halo_blur [[attribute(9)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; - float4 fill_color; - float4 halo_color; - float halo_width; - float halo_blur; - float opacity; float2 data0; float3 data1; + +#if !defined(HAS_UNIFORM_u_fill_color) + half4 fill_color; +#endif +#if !defined(HAS_UNIFORM_u_halo_color) + half4 halo_color; +#endif +#if !defined(HAS_UNIFORM_u_opacity) + half opacity; +#endif +#if !defined(HAS_UNIFORM_u_halo_width) + half halo_width; +#endif +#if !defined(HAS_UNIFORM_u_halo_blur) + half halo_blur; +#endif }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], @@ -53,12 +75,6 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const SymbolPermutationUBO& permutation [[buffer(15)]], device const ExpressionInputsUBO& expr [[buffer(16)]]) { - const auto fill_color = colorFor(permutation.fill_color, paint.fill_color, vertx.fill_color, interp.fill_color_t, expr); - const auto halo_color = colorFor(permutation.halo_color, paint.halo_color, vertx.halo_color, interp.halo_color_t, expr); - const auto opacity = valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr); - const auto halo_width = valueFor(permutation.halo_width, paint.halo_width, vertx.halo_width, interp.halo_width_t, expr); - const auto halo_blur = valueFor(permutation.halo_blur, paint.halo_blur, vertx.halo_blur, interp.halo_blur_t, expr); - const float2 a_pos = vertx.pos_offset.xy; const float2 a_offset = vertx.pos_offset.zw; @@ -128,11 +144,21 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], return { .position = position, - .fill_color = fill_color, - .halo_color = halo_color, - .halo_width = halo_width, - .halo_blur = halo_blur, - .opacity = opacity, +#if !defined(HAS_UNIFORM_u_fill_color) + .fill_color = half4(colorFor(permutation.fill_color, paint.fill_color, vertx.fill_color, interp.fill_color_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_halo_color) + .halo_color = half4(colorFor(permutation.halo_color, paint.halo_color, vertx.halo_color, interp.halo_color_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_halo_width) + .halo_width = half(valueFor(permutation.halo_width, paint.halo_width, vertx.halo_width, interp.halo_width_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_halo_blur) + .halo_blur = half(valueFor(permutation.halo_blur, paint.halo_blur, vertx.halo_blur, interp.halo_blur_t, expr)), +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = half(valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr)), +#endif .data0 = a_tex / drawable.texsize, .data1 = float3(gamma_scale, size, interpolated_fade_opacity), }; @@ -141,6 +167,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const SymbolDrawableUBO& drawable [[buffer(10)]], device const SymbolDynamicUBO& dynamic [[buffer(11)]], + device const SymbolDrawablePaintUBO& paint [[buffer(12)]], device const SymbolDrawableTilePropsUBO& props [[buffer(13)]], device const SymbolPermutationUBO& permutation [[buffer(15)]], texture2d image [[texture(0)]], @@ -149,29 +176,47 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } - const float EDGE_GAMMA = 0.105 / dynamic.device_pixel_ratio; +#if defined(HAS_UNIFORM_u_fill_color) + const half4 fill_color = half4(paint.fill_color); +#else + const half4 fill_color = in.fill_color; +#endif +#if defined(HAS_UNIFORM_u_halo_color) + const half4 halo_color = half4(paint.halo_color); +#else + const half4 halo_color = in.halo_color; +#endif +#if defined(HAS_UNIFORM_u_opacity) + const half opacity = half(paint.opacity); +#else + const half opacity = in.opacity; +#endif +#if defined(HAS_UNIFORM_u_halo_width) + const half halo_width = half(paint.halo_width); +#else + const half halo_width = in.halo_width; +#endif +#if defined(HAS_UNIFORM_u_halo_blur) + const half halo_blur = half(paint.halo_blur); +#else + const half halo_blur = in.halo_blur; +#endif + const float EDGE_GAMMA = 0.105 / dynamic.device_pixel_ratio; const float2 tex = in.data0.xy; const float gamma_scale = in.data1.x; const float size = in.data1.y; const float fade_opacity = in.data1[2]; - const float fontScale = props.is_text ? size / 24.0 : size; - - float4 color = in.fill_color; - float gamma = EDGE_GAMMA / (fontScale * drawable.gamma_scale); - float buff = (256.0 - 64.0) / 256.0; - if (props.is_halo) { - color = in.halo_color; - gamma = (in.halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * drawable.gamma_scale); - buff = (6.0 - in.halo_width / fontScale) / SDF_PX; - } - + const float fontGamma = fontScale * drawable.gamma_scale; + const half4 color = props.is_halo ? halo_color : fill_color; + const float gamma = ((props.is_halo ? (halo_blur * 1.19 / SDF_PX) : 0) + EDGE_GAMMA) / fontGamma; + const float buff = props.is_halo ? (6.0 - halo_width / fontScale) / SDF_PX : (256.0 - 64.0) / 256.0; const float dist = image.sample(image_sampler, tex).a; const float gamma_scaled = gamma * gamma_scale; const float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); - return half4(color * (alpha * in.opacity * fade_opacity)); + return half4(color * (alpha * opacity * fade_opacity)); } )"; }; diff --git a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp index 51c234efa..a57c1cb76 100644 --- a/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp +++ b/include/mbgl/shaders/mtl/symbol_text_and_icon.hpp @@ -13,6 +13,7 @@ struct ShaderSource static constexpr auto name = "SymbolTextAndIconShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; + static constexpr auto hasPermutations = true; static const std::array attributes; static const std::array uniforms; @@ -28,22 +29,43 @@ struct VertexStage { float3 projected_pos [[attribute(2)]]; float fade_opacity [[attribute(3)]]; +#if !defined(HAS_UNIFORM_u_fill_color) float4 fill_color [[attribute(4)]]; +#endif +#if !defined(HAS_UNIFORM_u_halo_color) float4 halo_color [[attribute(5)]]; +#endif +#if !defined(HAS_UNIFORM_u_opacity) float opacity [[attribute(6)]]; +#endif +#if !defined(HAS_UNIFORM_u_halo_width) float halo_width [[attribute(7)]]; +#endif +#if !defined(HAS_UNIFORM_u_halo_blur) float halo_blur [[attribute(8)]]; +#endif }; struct FragmentStage { float4 position [[position, invariant]]; - float4 fill_color; - float4 halo_color; - float halo_width; - float halo_blur; - float opacity; float4 data0; float4 data1; + +#if !defined(HAS_UNIFORM_u_fill_color) + half4 fill_color; +#endif +#if !defined(HAS_UNIFORM_u_halo_color) + half4 halo_color; +#endif +#if !defined(HAS_UNIFORM_u_opacity) + half opacity; +#endif +#if !defined(HAS_UNIFORM_u_halo_width) + half halo_width; +#endif +#if !defined(HAS_UNIFORM_u_halo_blur) + half halo_blur; +#endif }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], @@ -55,12 +77,6 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const SymbolPermutationUBO& permutation [[buffer(14)]], device const ExpressionInputsUBO& expr [[buffer(15)]]) { - const auto fill_color = colorFor(permutation.fill_color, paint.fill_color, vertx.fill_color, interp.fill_color_t, expr); - const auto halo_color = colorFor(permutation.halo_color, paint.halo_color, vertx.halo_color, interp.halo_color_t, expr); - const auto opacity = valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr); - const auto halo_width = valueFor(permutation.halo_width, paint.halo_width, vertx.halo_width, interp.halo_width_t, expr); - const auto halo_blur = valueFor(permutation.halo_blur, paint.halo_blur, vertx.halo_blur, interp.halo_blur_t, expr); - const float2 a_pos = vertx.pos_offset.xy; const float2 a_offset = vertx.pos_offset.zw; @@ -130,19 +146,31 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], return { .position = position, - .fill_color = fill_color, - .halo_color = halo_color, - .halo_width = halo_width, - .halo_blur = halo_blur, - .opacity = opacity, .data0 = float4(a_tex / drawable.texsize, a_tex / drawable.texsize_icon), .data1 = float4(gamma_scale, size, interpolated_fade_opacity, is_sdf), + +#if !defined(HAS_UNIFORM_u_fill_color) + .fill_color = half(colorFor(permutation.fill_color, paint.fill_color, vertx.fill_color, interp.fill_color_t, expr)); +#endif +#if !defined(HAS_UNIFORM_u_halo_color) + .halo_color = half(colorFor(permutation.halo_color, paint.halo_color, vertx.halo_color, interp.halo_color_t, expr)); +#endif +#if !defined(HAS_UNIFORM_u_opacity) + .opacity = half(valueFor(permutation.opacity, paint.opacity, vertx.opacity, interp.opacity_t, expr)); +#endif +#if !defined(HAS_UNIFORM_u_halo_width) + .halo_width = half(valueFor(permutation.halo_width, paint.halo_width, vertx.halo_width, interp.halo_width_t, expr)); +#endif +#if !defined(HAS_UNIFORM_u_halo_blur) + .halo_blur = half(valueFor(permutation.halo_blur, paint.halo_blur, vertx.halo_blur, interp.halo_blur_t, expr)); +#endif }; } half4 fragment fragmentMain(FragmentStage in [[stage_in]], device const SymbolDrawableUBO& drawable [[buffer(9)]], device const SymbolDynamicUBO& dynamic [[buffer(10)]], + device const SymbolDrawablePaintUBO& paint [[buffer(11)]], device const SymbolDrawableTilePropsUBO& props [[buffer(12)]], device const SymbolPermutationUBO& permutation [[buffer(14)]], texture2d glyph_image [[texture(0)]], @@ -153,36 +181,54 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]], return half4(1.0); } +#if defined(HAS_UNIFORM_u_fill_color) + const half4 fill_color = half4(paint.fill_color); +#else + const half4 fill_color = in.fill_color; +#endif +#if defined(HAS_UNIFORM_u_halo_color) + const half4 halo_color = half4(paint.halo_color); +#else + const half4 halo_color = in.halo_color; +#endif +#if defined(HAS_UNIFORM_u_opacity) + const half opacity = half(paint.opacity); +#else + const half opacity = in.opacity; +#endif +#if defined(HAS_UNIFORM_u_halo_width) + const half halo_width = half(paint.halo_width); +#else + const half halo_width = in.halo_width; +#endif +#if defined(HAS_UNIFORM_u_halo_blur) + const half halo_blur = half(paint.halo_blur); +#else + const half halo_blur = in.halo_blur; +#endif + const float fade_opacity = in.data1[2]; if (in.data1.w == ICON) { const float2 tex_icon = in.data0.zw; - const float alpha = in.opacity * fade_opacity; + const float alpha = opacity * fade_opacity; return half4(icon_image.sample(icon_sampler, tex_icon) * alpha); } const float2 tex = in.data0.xy; const float EDGE_GAMMA = 0.105 / dynamic.device_pixel_ratio; - const float gamma_scale = in.data1.x; const float size = in.data1.y; - const float fontScale = size / 24.0; - - float4 color = in.fill_color; - float gamma = EDGE_GAMMA / (fontScale * drawable.gamma_scale); - float buff = (256.0 - 64.0) / 256.0; - if (props.is_halo) { - color = in.halo_color; - gamma = (in.halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * drawable.gamma_scale); - buff = (6.0 - in.halo_width / fontScale) / SDF_PX; - } - + const half4 color = props.is_halo ? halo_color : fill_color; + const float fontGamma = fontScale * drawable.gamma_scale; + const float gamma = ((props.is_halo ? (halo_blur * 1.19 / SDF_PX) : 0) + EDGE_GAMMA) / fontGamma; + const float buff = props.is_halo ? (6.0 - halo_width / fontScale) / SDF_PX : (256.0 - 64.0) / 256.0; const float dist = glyph_image.sample(glyph_sampler, tex).a; const float gamma_scaled = gamma * gamma_scale; const float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); - return half4(color * (alpha * in.opacity * fade_opacity)); + return half4(color * (alpha * opacity * fade_opacity)); } )"; }; diff --git a/src/mbgl/mtl/context.cpp b/src/mbgl/mtl/context.cpp index 4b5a210ad..bb075bfc7 100644 --- a/src/mbgl/mtl/context.cpp +++ b/src/mbgl/mtl/context.cpp @@ -210,6 +210,13 @@ bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer, } } +const BufferResource& Context::getEmptyBuffer() { + if (!emptyBuffer) { + emptyBuffer.emplace(const_cast(*this), nullptr, 0, MTL::ResourceStorageModeShared, false, false); + } + return *emptyBuffer; +} + const BufferResource& Context::getTileVertexBuffer() { if (!tileVertexBuffer) { const auto vertices = RenderStaticData::tileVertices(); diff --git a/src/mbgl/mtl/drawable.cpp b/src/mbgl/mtl/drawable.cpp index 00b0fa667..a4ecd2654 100644 --- a/src/mbgl/mtl/drawable.cpp +++ b/src/mbgl/mtl/drawable.cpp @@ -344,8 +344,14 @@ void Drawable::bindAttributes(RenderPass& renderPass) const noexcept { if (buffer && buffer->get()) { assert(binding->vertexStride * impl->vertexCount <= getBufferSize(binding->vertexBufferResource)); renderPass.bindVertex(buffer->get(), /*offset=*/0, attributeIndex); - } else if (impl->noBindingBuffer) { - renderPass.bindVertex(impl->noBindingBuffer->get(), /*offset=*/0, attributeIndex); + } else { + const auto* shaderMTL = static_cast(shader.get()); + if (impl->noBindingBuffer && shaderMTL && shaderMTL->getBindMissingAttributes()) { + renderPass.bindVertex(impl->noBindingBuffer->get(), /*offset=*/0, attributeIndex); + } else { + auto& context = renderPass.getCommandEncoder().getContext(); + renderPass.bindVertex(context.getEmptyBuffer(), /*offset=*/0, attributeIndex); + } } attributeIndex += 1; } @@ -536,6 +542,7 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) { for (auto& binding : impl->attributeBindings) { if (!binding) { assert("Missing attribute binding"); + index += 1; continue; } diff --git a/src/mbgl/renderer/sources/render_tile_source.cpp b/src/mbgl/renderer/sources/render_tile_source.cpp index 49f88a0cd..84ae38bbe 100644 --- a/src/mbgl/renderer/sources/render_tile_source.cpp +++ b/src/mbgl/renderer/sources/render_tile_source.cpp @@ -124,7 +124,8 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr }; // build a set of tiles to cover - std::unordered_set newTiles; + mbgl::unordered_set newTiles; + newTiles.reserve(renderTiles->size()); for (auto& tile : *renderTiles) { newTiles.insert(tile.getOverscaledTileID()); } diff --git a/src/mbgl/shaders/mtl/fill.cpp b/src/mbgl/shaders/mtl/fill.cpp index 589cb330a..487d5b692 100644 --- a/src/mbgl/shaders/mtl/fill.cpp +++ b/src/mbgl/shaders/mtl/fill.cpp @@ -3,15 +3,10 @@ namespace mbgl { namespace shaders { -const std::array ShaderSource::attributes = { +const std::array ShaderSource::attributes = { AttributeInfo{0, gfx::AttributeDataType::Short2, 1, "a_pos"}, AttributeInfo{1, gfx::AttributeDataType::Float4, 1, "a_color"}, AttributeInfo{2, gfx::AttributeDataType::Float2, 1, "a_opacity"}, - - // This shader doesn't use it, but we need this so that the layer can assign the same - // attributes to all the drawables. If/When `VertexAttributeArray::resolve` allows it, - // this can be removed. - AttributeInfo{8, gfx::AttributeDataType::Float2, 1, "a_outline_color"}, }; const std::array ShaderSource::uniforms = { UniformBlockInfo{3, true, false, sizeof(FillDrawableUBO), "FillDrawableUBO"}, @@ -22,13 +17,10 @@ const std::array ShaderSource ShaderSource::textures = {}; -const std::array ShaderSource::attributes = { +const std::array ShaderSource::attributes = { AttributeInfo{0, gfx::AttributeDataType::Short2, 1, "a_pos"}, AttributeInfo{1, gfx::AttributeDataType::Float4, 1, "a_outline_color"}, AttributeInfo{2, gfx::AttributeDataType::Float2, 1, "a_opacity"}, - - // See `a_outline_color` in FillShader - AttributeInfo{8, gfx::AttributeDataType::Float2, 1, "a_color"}, }; const std::array ShaderSource::uniforms = { UniformBlockInfo{3, true, false, sizeof(FillOutlineDrawableUBO), "FillOutlineDrawableUBO"}, @@ -47,7 +39,7 @@ const std::array ShaderSource ShaderSource::uniforms = { UniformBlockInfo{4, true, true, sizeof(FillPatternDrawableUBO), "FillPatternDrawableUBO"}, - UniformBlockInfo{5, true, false, sizeof(FillPatternTilePropsUBO), "FillPatternTilePropsUBO"}, + UniformBlockInfo{5, true, true, sizeof(FillPatternTilePropsUBO), "FillPatternTilePropsUBO"}, UniformBlockInfo{6, true, true, sizeof(FillPatternEvaluatedPropsUBO), "FillPatternEvaluatedPropsUBO"}, UniformBlockInfo{7, true, false, sizeof(FillPatternInterpolateUBO), "FillPatternInterpolateUBO"}, UniformBlockInfo{8, true, true, sizeof(FillPatternPermutationUBO), "FillPatternPermutationUBO"}, @@ -67,7 +59,7 @@ const std::array const std::array ShaderSource::uniforms = { UniformBlockInfo{4, true, true, sizeof(FillOutlinePatternDrawableUBO), "FillOutlinePatternDrawableUBO"}, - UniformBlockInfo{5, true, false, sizeof(FillOutlinePatternTilePropsUBO), "FillOutlinePatternTilePropsUBO"}, + UniformBlockInfo{5, true, true, sizeof(FillOutlinePatternTilePropsUBO), "FillOutlinePatternTilePropsUBO"}, UniformBlockInfo{6, true, true, sizeof(FillOutlinePatternEvaluatedPropsUBO), "FillOutlinePatternEvaluatedPropsUBO"}, UniformBlockInfo{7, true, false, sizeof(FillOutlinePatternInterpolateUBO), "FillOutlinePatternInterpolateUBO"}, UniformBlockInfo{8, true, true, sizeof(FillOutlinePatternPermutationUBO), "FillOutlinePatternPermutationUBO"}, diff --git a/src/mbgl/shaders/mtl/line.cpp b/src/mbgl/shaders/mtl/line.cpp index 4b492546f..901177b99 100644 --- a/src/mbgl/shaders/mtl/line.cpp +++ b/src/mbgl/shaders/mtl/line.cpp @@ -15,7 +15,7 @@ const std::array ShaderSource ShaderSource::uniforms = { UniformBlockInfo{8, true, true, sizeof(LineUBO), "LineUBO"}, - UniformBlockInfo{9, true, false, sizeof(LinePropertiesUBO), "LinePropertiesUBO"}, + UniformBlockInfo{9, true, true, sizeof(LinePropertiesUBO), "LinePropertiesUBO"}, UniformBlockInfo{10, true, false, sizeof(LineInterpolationUBO), "LineInterpolationUBO"}, UniformBlockInfo{11, true, true, sizeof(LinePermutationUBO), "LinePermutationUBO"}, UniformBlockInfo{12, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, @@ -35,9 +35,9 @@ const std::array ShaderSource ShaderSource::uniforms = { UniformBlockInfo{9, true, true, sizeof(LinePatternUBO), "LinePatternUBO"}, - UniformBlockInfo{10, true, false, sizeof(LinePatternPropertiesUBO), "LinePatternPropertiesUBO"}, + UniformBlockInfo{10, true, true, sizeof(LinePatternPropertiesUBO), "LinePatternPropertiesUBO"}, UniformBlockInfo{11, true, false, sizeof(LinePatternInterpolationUBO), "LinePatternInterpolationUBO"}, - UniformBlockInfo{12, true, false, sizeof(LinePatternTilePropertiesUBO), "LinePatternTilePropertiesUBO"}, + UniformBlockInfo{12, true, true, sizeof(LinePatternTilePropertiesUBO), "LinePatternTilePropertiesUBO"}, UniformBlockInfo{13, true, true, sizeof(LinePermutationUBO), "LinePermutationUBO"}, UniformBlockInfo{14, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, }; @@ -58,7 +58,7 @@ const std::array ShaderSource ShaderSource::uniforms = { UniformBlockInfo{9, true, true, sizeof(LineSDFUBO), "LineSDFUBO"}, - UniformBlockInfo{10, true, false, sizeof(LineSDFPropertiesUBO), "LineSDFPropertiesUBO"}, + UniformBlockInfo{10, true, true, sizeof(LineSDFPropertiesUBO), "LineSDFPropertiesUBO"}, UniformBlockInfo{11, true, false, sizeof(LineSDFInterpolationUBO), "LineSDFInterpolationUBO"}, UniformBlockInfo{12, true, true, sizeof(LinePermutationUBO), "LinePermutationUBO"}, UniformBlockInfo{13, true, false, sizeof(ExpressionInputsUBO), "ExpressionInputsUBO"}, @@ -73,7 +73,7 @@ const std::array ShaderSource ShaderSource::uniforms = { UniformBlockInfo{2, true, true, sizeof(LineBasicUBO), "LineBasicUBO"}, - UniformBlockInfo{3, true, false, sizeof(LineBasicPropertiesUBO), "LineBasicPropertiesUBO"}, + UniformBlockInfo{3, true, true, sizeof(LineBasicPropertiesUBO), "LineBasicPropertiesUBO"}, }; const std::array ShaderSource::textures = {}; diff --git a/src/mbgl/shaders/mtl/shader_program.cpp b/src/mbgl/shaders/mtl/shader_program.cpp index 3176cb025..01d90cfec 100644 --- a/src/mbgl/shaders/mtl/shader_program.cpp +++ b/src/mbgl/shaders/mtl/shader_program.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -103,6 +104,8 @@ ShaderProgram::ShaderProgram(std::string name, RendererBackend& backend_, MTLFun vertexFunction(std::move(vert)), fragmentFunction(std::move(frag)) {} +ShaderProgram::~ShaderProgram() noexcept = default; + MTLRenderPipelineStatePtr ShaderProgram::getRenderPipelineState(const gfx::Renderable& renderable, const MTLVertexDescriptorPtr& vertexDescriptor, const gfx::ColorMode& colorMode) const { diff --git a/src/mbgl/shaders/mtl/symbol_sdf.cpp b/src/mbgl/shaders/mtl/symbol_sdf.cpp index a4d1276d2..98d5e39a6 100644 --- a/src/mbgl/shaders/mtl/symbol_sdf.cpp +++ b/src/mbgl/shaders/mtl/symbol_sdf.cpp @@ -23,7 +23,7 @@ const std::array ShaderSource ShaderSource::uniforms = { UniformBlockInfo{9, true, true, sizeof(SymbolDrawableUBO), "SymbolDrawableUBO"}, UniformBlockInfo{10, true, true, sizeof(SymbolDynamicUBO), "SymbolDynamicUBO"}, - UniformBlockInfo{11, true, false, sizeof(SymbolDrawablePaintUBO), "SymbolDrawablePaintUBO"}, + UniformBlockInfo{11, true, true, sizeof(SymbolDrawablePaintUBO), "SymbolDrawablePaintUBO"}, UniformBlockInfo{12, true, true, sizeof(SymbolDrawableTilePropsUBO), "SymbolDrawableTilePropsUBO"}, UniformBlockInfo{13, true, false, sizeof(SymbolDrawableInterpolateUBO), "SymbolDrawableInterpolateUBO"}, UniformBlockInfo{14, true, true, sizeof(SymbolPermutationUBO), "SymbolPermutationUBO"}, diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp index ab820ac67..885d8c2cc 100644 --- a/test/api/custom_layer.test.cpp +++ b/test/api/custom_layer.test.cpp @@ -1,3 +1,5 @@ +#if MLN_RENDER_BACKEND_OPENGL + #include #include @@ -109,3 +111,5 @@ TEST(CustomLayer, Basic) { test::checkImage("test/fixtures/custom_layer/basic", frontend.render(map).image, 0.0006, 0.1); } + +#endif diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp index c53b027de..cf3d8e434 100644 --- a/test/gl/bucket.test.cpp +++ b/test/gl/bucket.test.cpp @@ -1,3 +1,5 @@ +#if MLN_RENDER_BACKEND_OPENGL + #include #include @@ -380,3 +382,5 @@ TEST(Buckets, RasterBucketMaskComplex) { expectedSegments.emplace_back(0, 0, 24, 36); EXPECT_EQ(expectedSegments, bucket.segments); } + +#endif diff --git a/test/gl/context.test.cpp b/test/gl/context.test.cpp index e04d08f3b..8aff86645 100644 --- a/test/gl/context.test.cpp +++ b/test/gl/context.test.cpp @@ -1,3 +1,4 @@ +#if MLN_RENDER_BACKEND_OPENGL #include #include @@ -122,3 +123,5 @@ TEST(GLContextMode, Shared) { test::checkImage("test/fixtures/shared_context", frontend.render(map).image, 0.5, 0.1); } + +#endif diff --git a/test/gl/enum.test.cpp b/test/gl/enum.test.cpp index ab4a95153..2ba162121 100644 --- a/test/gl/enum.test.cpp +++ b/test/gl/enum.test.cpp @@ -1,3 +1,5 @@ +#if MLN_RENDER_BACKEND_OPENGL + #include #include #include @@ -176,3 +178,5 @@ TEST(GL, sizedFor) { ASSERT_EQ(GL_INVALID_ENUM, Enum::sizedFor(TexturePixelType::RGBA, static_cast(-1))); } + +#endif diff --git a/test/gl/gl_functions.test.cpp b/test/gl/gl_functions.test.cpp index 229be2164..9f22d229e 100644 --- a/test/gl/gl_functions.test.cpp +++ b/test/gl/gl_functions.test.cpp @@ -1,3 +1,5 @@ +#if MLN_RENDER_BACKEND_OPENGL + #include #include @@ -257,3 +259,5 @@ TEST(GLFunctions, OpenGLES) { EXPECT_NE(glTexStorage3D, nullptr); EXPECT_NE(glGetInternalformativ, nullptr); } + +#endif diff --git a/test/gl/object.test.cpp b/test/gl/object.test.cpp index 2eceba848..0567b2ed4 100644 --- a/test/gl/object.test.cpp +++ b/test/gl/object.test.cpp @@ -1,3 +1,4 @@ +#if MLN_RENDER_BACKEND_OPENGL #include #include @@ -66,3 +67,5 @@ TEST(GLObject, Store) { context.reset(); EXPECT_TRUE(context.empty()); } + +#endif diff --git a/test/renderer/backend_scope.test.cpp b/test/renderer/backend_scope.test.cpp index 18b130913..78b8fe6cb 100644 --- a/test/renderer/backend_scope.test.cpp +++ b/test/renderer/backend_scope.test.cpp @@ -1,3 +1,4 @@ +#if MLN_RENDER_BACKEND_OPENGL #include #include @@ -126,3 +127,4 @@ TEST(BackendScope, ChainedScopes) { ASSERT_FALSE(activatedA); ASSERT_FALSE(activatedB); } +#endif diff --git a/test/util/hash.test.cpp b/test/util/hash.test.cpp index 203dacd39..b297df7be 100644 --- a/test/util/hash.test.cpp +++ b/test/util/hash.test.cpp @@ -9,6 +9,30 @@ #include #include +#if MLN_RENDER_BACKEND_METAL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + using namespace mbgl; TEST(OrderIndependentHash, Permutations) { @@ -18,37 +42,118 @@ TEST(OrderIndependentHash, Permutations) { constexpr int iterations = 10; // Use values up to this large constexpr size_t maxVal = 10; + // Repeat all that with different random seeds + constexpr int seeds = 5; - std::seed_seq seed{0xf00dLL}; - std::default_random_engine engine(seed); - std::uniform_int_distribution distribution(0, maxVal); + for (int curSeed = 0; curSeed < seeds; ++curSeed) { + std::seed_seq seed{0xf00dLL + curSeed}; + std::default_random_engine engine(seed); + std::uniform_int_distribution distribution(0, maxVal); - for (int curSize = 1; curSize <= maxSize; ++curSize) { - for (int ii = 0; ii < iterations; ++ii) { - // Generate `curSize` random values - std::vector values; - for (int jj = 0; jj < curSize; ++jj) { - values.push_back(distribution(engine)); - } + for (int curSize = 1; curSize <= maxSize; ++curSize) { + for (int ii = 0; ii < iterations; ++ii) { + // Generate `curSize` random values + std::vector values; + for (int jj = 0; jj < curSize; ++jj) { + values.push_back(distribution(engine)); + } - // Generate the hash for each permutation, comparing subsequent values to the first one. - bool isFirst = true; - size_t initialValue = 0; - do { - const auto curValue = util::order_independent_hash(values.begin(), values.end()); - if (isFirst || curValue != initialValue) { - for (auto v : values) { - std::cout << v << ","; + // Generate the hash for each permutation, comparing subsequent values to the first one. + bool isFirst = true; + size_t initialValue = 0; + do { + const auto curValue = util::order_independent_hash(values.begin(), values.end()); + if (isFirst) { + initialValue = curValue; + isFirst = false; + } else { + EXPECT_EQ(initialValue, curValue); } - std::cout << " - " << std::hex << curValue << std::endl; - } - if (isFirst) { - initialValue = curValue; - isFirst = false; - } else { - EXPECT_EQ(initialValue, curValue); - } - } while (std::next_permutation(values.begin(), values.end())); + } while (std::next_permutation(values.begin(), values.end())); + } } } } + +#if MLN_RENDER_BACKEND_METAL + +using namespace mbgl::shaders; + +template +void each_subset(const TSet& sofar, TIter beg, TIter end, TFunc f) { + if (beg == end) { + f(sofar.cbegin(), sofar.cend()); + } else { + TSet with = sofar; + with.insert(*beg); + each_subset(with, std::next(beg), end, f); + each_subset(sofar, std::next(beg), end, f); + } +} + +/// Ensure that no combination of attributes used by a shader definition produce a hash conflict +template +void checkShaderHashes() { + using AttribSet = mbgl::unordered_set; + AttribSet attributes; + for (const auto& attrib : ShaderType::attributes) { + attributes.insert(attrib.nameID); + } + + std::set subsetHashes; + each_subset(AttribSet{}, attributes.cbegin(), attributes.cend(), [&](auto beg, auto end) { + if (beg != end) { + std::vector subset(beg, end); + std::optional firstHash; + do { + const auto hash = util::order_independent_hash(subset.begin(), subset.end()); + if (firstHash) { + // All permutations of the subset must yield the same hash key as the first + EXPECT_EQ(*firstHash, hash); + } else { + firstHash.emplace(hash); + // All subset hashes must be unique + EXPECT_TRUE(subsetHashes.insert(hash).second); + } + } while (std::next_permutation(subset.begin(), subset.end())); + } + }); + EXPECT_EQ(static_cast(std::pow(2, attributes.size())), 1 + subsetHashes.size()); +} + +template +void checkShaderHashes() { + // For OpenGL, we would need to do reflection + //(checkShaderHashes>(),...); + (checkShaderHashes>(), ...); +} + +TEST(OrderIndependentHash, Shaders) { + checkShaderHashes(); +} + +#endif diff --git a/test/util/offscreen_texture.test.cpp b/test/util/offscreen_texture.test.cpp index 282579101..04228eb53 100644 --- a/test/util/offscreen_texture.test.cpp +++ b/test/util/offscreen_texture.test.cpp @@ -1,3 +1,4 @@ +#if MLN_RENDER_BACKEND_OPENGL #include #include @@ -203,3 +204,5 @@ TEST(OffscreenTexture, ClearRenderPassColor) { auto image = offscreenTexture->readStillImage(); test::checkImage("test/fixtures/offscreen_texture/clear-render-pass-color", image, 0, 0); } + +#endif