diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index a567475c789e9..af5cb6c9bbfa7 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -335,5 +335,37 @@ TEST(GeometryTest, RectContainsPoint) { } } +TEST(GeometryTest, RectContainsRect) { + { + Rect a(100, 100, 100, 100); + ASSERT_TRUE(a.Contains(a)); + } + { + Rect a(100, 100, 100, 100); + Rect b(0, 0, 0, 0); + ASSERT_FALSE(a.Contains(b)); + } + { + Rect a(100, 100, 100, 100); + Rect b(150, 150, 20, 20); + ASSERT_TRUE(a.Contains(b)); + } + { + Rect a(100, 100, 100, 100); + Rect b(150, 150, 100, 100); + ASSERT_FALSE(a.Contains(b)); + } + { + Rect a(100, 100, 100, 100); + Rect b(50, 50, 100, 100); + ASSERT_FALSE(a.Contains(b)); + } + { + Rect a(100, 100, 100, 100); + Rect b(0, 0, 300, 300); + ASSERT_FALSE(a.Contains(b)); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 00a0ae6ddd255..96fc41fd8f1f3 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -84,6 +84,10 @@ struct TRect { p.y < origin.y + size.height; } + constexpr bool Contains(const TRect& o) const { + return Union(o).size == size; + } + constexpr bool IsZero() const { return size.IsZero(); } constexpr bool IsEmpty() const { return size.IsEmpty(); } diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index f50389a2d7d04..9279d16f2671d 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -428,6 +428,28 @@ static bool Bind(PassBindingsCache& pass, : MTLWindingCounterClockwise]; [encoder setCullMode:MTLCullModeNone]; [encoder setStencilReferenceValue:command.stencil_reference]; + if (command.viewport.has_value()) { + auto v = command.viewport.value(); + MTLViewport viewport = { + .originX = v.rect.origin.x, + .originY = v.rect.origin.y, + .width = v.rect.size.width, + .height = v.rect.size.height, + .znear = v.znear, + .zfar = v.zfar, + }; + [encoder setViewport:viewport]; + } + if (command.scissor.has_value()) { + auto s = command.scissor.value(); + MTLScissorRect scissor = { + .x = static_cast(s.origin.x), + .y = static_cast(s.origin.y), + .width = static_cast(s.size.width), + .height = static_cast(s.size.height), + }; + [encoder setScissorRect:scissor]; + } if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { return false; } @@ -472,6 +494,15 @@ static bool Bind(PassBindingsCache& pass, return false; } + if (command.scissor.has_value()) { + auto target_rect = IRect({}, render_target_.GetRenderTargetSize()); + if (!target_rect.Contains(command.scissor.value())) { + VALIDATION_LOG << "Cannot apply a scissor that lies outside the bounds " + "of the render target."; + return false; + } + } + commands_.emplace_back(std::move(command)); return true; } diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 4890d6ffaefe1..30c2d4cfe662c 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -6,10 +6,12 @@ #include #include +#include #include #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" +#include "impeller/geometry/rect.h" #include "impeller/renderer/buffer_view.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/pipeline.h" @@ -69,6 +71,19 @@ struct Command { PrimitiveType primitive_type = PrimitiveType::kTriangle; WindingOrder winding = WindingOrder::kClockwise; uint32_t stencil_reference = 0u; + //---------------------------------------------------------------------------- + /// The viewport coordinates that the rasterizer linearly maps normalized + /// device coordinates to. + /// If unset, the viewport is the size of the render target with a zero + /// origin, znear=0, and zfar=1. + /// + std::optional viewport; + //---------------------------------------------------------------------------- + /// The scissor rect to use for clipping writes to the render target. The + /// scissor rect must lie entirely within the render target. + /// If unset, no scissor is applied. + /// + std::optional scissor; bool BindVertices(const VertexBuffer& buffer); diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 0d420ee1097da..94e7d94012287 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -11,6 +11,8 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" +#include "impeller/geometry/rect.h" +#include "impeller/geometry/scalar.h" #include "impeller/geometry/color.h" namespace impeller { @@ -149,6 +151,12 @@ enum class PrimitiveType { // checks. Hence, they are not supported here. }; +struct Viewport { + Rect rect; + Scalar znear = 0.0f; + Scalar zfar = 1.0f; +}; + enum class MinMagFilter { /// Select nearest to the sample point. Most widely supported. kNearest,