Skip to content

Commit 22a17d6

Browse files
committed
Support recursive path clip
1 parent 23c6882 commit 22a17d6

15 files changed

+249
-25
lines changed

example/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ target_link_libraries(gl_frame_example skity ${GLFW_LIBRARIES} ${CMAKE_DL_LIBS})
5959
add_executable(gl_svg_example gl_svg_example.cc svg_example.cc $<TARGET_OBJECTS:glad> $<TARGET_OBJECTS:gl_app>)
6060
target_link_libraries(gl_svg_example skity::skity skity::svg ${GLFW_LIBRARIES} ${CMAKE_DL_LIBS})
6161

62+
add_executable(gl_clip_example gl_clip_example.cc clip_example.cc $<TARGET_OBJECTS:glad> $<TARGET_OBJECTS:gl_app>)
63+
target_link_libraries(gl_clip_example skity::skity skity::svg ${GLFW_LIBRARIES} ${CMAKE_DL_LIBS})
64+
6265
# vulkan examples
6366
if (VULKAN_BACKEND)
6467

@@ -85,4 +88,7 @@ if (VULKAN_BACKEND)
8588

8689
add_executable(vulkan_hello_world vulkan_hello_world.cc $<TARGET_OBJECTS:vk_app>)
8790
target_link_libraries(vulkan_hello_world skity ${GLFW_LIBRARIES} Vulkan::Vulkan)
91+
92+
add_executable(vk_clip_example vk_clip_example.cc clip_example.cc $<TARGET_OBJECTS:glad> $<TARGET_OBJECTS:vk_app>)
93+
target_link_libraries(vk_clip_example skity::skity skity::svg ${GLFW_LIBRARIES} Vulkan::Vulkan)
8894
endif()

example/clip_example.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <skity/skity.hpp>
2+
3+
float degree = 0.f;
4+
5+
void draw_clip_demo(skity::Canvas* canvas) {
6+
degree += 0.2f;
7+
skity::Paint clip_paint;
8+
clip_paint.setStyle(skity::Paint::kStroke_Style);
9+
clip_paint.setStrokeWidth(2.f);
10+
11+
skity::Paint stroke_paint;
12+
stroke_paint.setStyle(skity::Paint::kStroke_Style);
13+
stroke_paint.setStrokeWidth(5.f);
14+
stroke_paint.setColor(skity::Color_RED);
15+
16+
skity::Rect rect1 = skity::Rect::MakeXYWH(100, 100, 200, 200);
17+
18+
skity::Rect rect2 = skity::Rect::MakeXYWH(150, 150, 100, 100);
19+
20+
clip_paint.setColor(skity::Color_BLACK);
21+
22+
canvas->drawRect(rect1, clip_paint);
23+
24+
canvas->save();
25+
26+
canvas->clipRect(rect1);
27+
28+
canvas->drawLine(100, 170, 400, 200, stroke_paint);
29+
30+
canvas->rotate(degree, 170.f, 170.f);
31+
32+
clip_paint.setColor(skity::Color_BLUE);
33+
34+
canvas->drawRect(rect2, clip_paint);
35+
36+
canvas->clipRect(rect2);
37+
38+
canvas->rotate(-degree, 170.f, 170.f);
39+
40+
canvas->drawLine(100, 180, 400, 230, stroke_paint);
41+
42+
canvas->restore();
43+
}

example/gl_clip_example.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include "gl/gl_app.hpp"
2+
3+
void draw_clip_demo(skity::Canvas* canvas);
4+
5+
class GLClipExample : public example::GLApp {
6+
public:
7+
GLClipExample()
8+
: example::GLApp(800, 800, "GL Clip Example", {1.f, 1.f, 1.f, 1.f}) {}
9+
~GLClipExample() override = default;
10+
11+
protected:
12+
void OnUpdate(float time) override {
13+
draw_clip_demo(GetCanvas());
14+
15+
GetCanvas()->flush();
16+
}
17+
};
18+
19+
int main(int argc, const char** argv) {
20+
GLClipExample app;
21+
app.Run();
22+
23+
return 0;
24+
}

example/vk_clip_example.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include "vk/vk_app.hpp"
2+
3+
void draw_clip_demo(skity::Canvas* canvas);
4+
5+
class VKClipExample : public example::VkApp {
6+
public:
7+
VKClipExample()
8+
: example::VkApp(800, 800, "Vulkan Clip Example", {1.f, 1.f, 1.f, 1.f}) {}
9+
~VKClipExample() override = default;
10+
11+
protected:
12+
void OnUpdate(float elapsed_time) override {
13+
draw_clip_demo(GetCanvas());
14+
15+
GetCanvas()->flush();
16+
}
17+
};
18+
19+
int main(int argc, const char** argv) {
20+
VKClipExample app;
21+
app.Run();
22+
23+
return 0;
24+
}

src/render/hw/gl/gl_pipeline.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ static GLenum hw_stencil_func_to_gl(HWStencilFunc func) {
1616
return GL_LESS;
1717
case HWStencilFunc::LESS_OR_EQUAL:
1818
return GL_LEQUAL;
19+
case HWStencilFunc::GREAT_OR_EQUAL:
20+
return GL_GEQUAL;
1921
}
2022

2123
return 0;

src/render/hw/hw_canvas.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ void HWCanvas::onClipPath(const Path& path, ClipOp op) {
178178

179179
raster.FlushRaster();
180180

181+
bool has_clip = state_.HasClip();
182+
181183
if (full_rect_start_ == -1) {
182184
// For recursive clip path, need to do full scene check
183185
// to make stencil buffer right
@@ -207,7 +209,7 @@ void HWCanvas::onClipPath(const Path& path, ClipOp op) {
207209
}
208210

209211
auto draw = std::make_unique<HWDraw>(GetPipeline(),
210-
false, // no need to handle clip mask
212+
has_clip, // no need to handle clip mask
211213
true);
212214

213215
if (raster.StencilBackCount() == 0 && raster.StencilFrontCount() == 0) {

src/render/hw/hw_canvas_state.cc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,22 @@ void HWCanvasState::Concat(const Matrix &matrix) {
6868
}
6969

7070
void HWCanvasState::SaveClipPath(HWDrawRange const &front_range,
71-
HWDrawRange const &back_range,
72-
HWDrawRange const &bound_range,
73-
Matrix const &matrix) {
74-
71+
HWDrawRange const &back_range,
72+
HWDrawRange const &bound_range,
73+
Matrix const &matrix) {
7574
ClipStackValue value{};
7675
value.stack_depth = matrix_state_.size();
7776
value.front_range = front_range;
7877
value.back_range = back_range;
7978
value.bound_range = bound_range;
8079
value.stack_matrix = matrix;
8180

82-
clip_stack_.emplace_back(value);
81+
if (clip_stack_.empty() ||
82+
clip_stack_.back().stack_depth < value.stack_depth) {
83+
clip_stack_.emplace_back(value);
84+
} else {
85+
clip_stack_.back() = value;
86+
}
8387
}
8488

8589
bool HWCanvasState::ClipStackEmpty() { return clip_stack_.empty(); }

src/render/hw/hw_draw.cc

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,6 @@ void HWDraw::DoStencilBufferMove() {
171171
pipeline_->UpdateStencilMask(0xFF);
172172
pipeline_->UpdateStencilOp(HWStencilOp::REPLACE);
173173
pipeline_->SetPipelineColorMode(HWPipelineColorMode::kStencil);
174-
if (clear_stencil_clip_) {
175-
// clear stencil clip value
176-
pipeline_->UpdateStencilFunc(HWStencilFunc::ALWAYS, 0x00, 0x0F);
177-
} else {
178-
if (stencil_front_range_.count == 0 && stencil_back_range_.count == 0) {
179-
// this is a convexity polygon clip and no stencil discard
180-
pipeline_->UpdateStencilFunc(HWStencilFunc::ALWAYS, 0x10, 0x0F);
181-
} else {
182-
// mark bit 9 if lower 8 bit is not zero
183-
pipeline_->UpdateStencilFunc(HWStencilFunc::NOT_EQUAL, 0x10, 0x0F);
184-
}
185-
}
186174

187175
auto current_matrix = pipeline_->GetModelMatrix();
188176
bool need_reset = current_matrix != glm::identity<glm::mat4>();
@@ -191,11 +179,7 @@ void HWDraw::DoStencilBufferMove() {
191179
pipeline_->SetModelMatrix(glm::identity<glm::mat4>());
192180
}
193181

194-
if (color_range_.count > 0) {
195-
pipeline_->DrawIndex(color_range_.start, color_range_.count);
196-
} else {
197-
LOG_ERROR("Clip stencil movement error no color range is set");
198-
}
182+
DoStencilBufferMoveInternal();
199183

200184
if (need_reset) {
201185
pipeline_->SetModelMatrix(current_matrix);
@@ -207,4 +191,35 @@ void HWDraw::DoStencilBufferMove() {
207191
pipeline_->EnableColorOutput();
208192
}
209193

194+
void HWDraw::DoStencilBufferMoveInternal() {
195+
if (color_range_.count == 0) {
196+
return;
197+
}
198+
199+
if (clear_stencil_clip_) {
200+
// clear stencil clip value
201+
pipeline_->UpdateStencilFunc(HWStencilFunc::ALWAYS, 0x00, 0x0F);
202+
pipeline_->DrawIndex(color_range_.start, color_range_.count);
203+
} else if (!has_clip_) {
204+
// mark bit 9 if lower 8 bit is not zero
205+
pipeline_->UpdateStencilFunc(HWStencilFunc::NOT_EQUAL, 0x10, 0x0F);
206+
207+
pipeline_->DrawIndex(color_range_.start, color_range_.count);
208+
} else {
209+
// recursive clip path
210+
211+
// step 1 clear all 0x10 value
212+
pipeline_->UpdateStencilOp(HWStencilOp::DECR_WRAP);
213+
pipeline_->UpdateStencilMask(0xFF);
214+
pipeline_->UpdateStencilFunc(HWStencilFunc::EQUAL, 0x10, 0x1F);
215+
pipeline_->DrawIndex(color_range_.start, color_range_.count);
216+
217+
// step 2 replace all great than 0x10 to 0x10
218+
pipeline_->UpdateStencilOp(HWStencilOp::REPLACE);
219+
pipeline_->UpdateStencilMask(0x0F);
220+
pipeline_->UpdateStencilFunc(HWStencilFunc::NOT_EQUAL, 0x00, 0x0F);
221+
pipeline_->DrawIndex(color_range_.start, color_range_.count);
222+
}
223+
}
224+
210225
} // namespace skity

src/render/hw/hw_draw.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class HWDraw {
6060
void DoColorFill();
6161
void DoStencilBufferMove();
6262

63+
void DoStencilBufferMoveInternal();
64+
6365
private:
6466
HWPipeline* pipeline_;
6567
bool has_clip_;

src/render/hw/hw_pipeline.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ enum class HWStencilFunc {
2727
NOT_EQUAL,
2828
LESS,
2929
LESS_OR_EQUAL,
30+
GREAT_OR_EQUAL,
3031
ALWAYS,
3132
};
3233

src/render/hw/vk/pipelines/stencil_pipeline.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ VKPipelineWrapper::CreateStencilClipBackPipeline(GPUVkContext* ctx) {
5050
}();
5151
}
5252

53+
std::unique_ptr<VKPipelineWrapper>
54+
VKPipelineWrapper::CreateStencilRecClipBackPipeline(GPUVkContext* ctx) {
55+
return PipelineBuilder<StencilRecursiveClipBackPipeline>{
56+
(const char*)vk_common_vert_spv,
57+
vk_common_vert_spv_size,
58+
(const char*)vk_stencil_discard_frag_spv,
59+
vk_stencil_discard_frag_spv_size,
60+
ctx,
61+
}();
62+
}
63+
5364
std::unique_ptr<VKPipelineWrapper> VKPipelineWrapper::CreateStencilClipPipeline(
5465
GPUVkContext* ctx) {
5566
return PipelineBuilder<StencilClipPipeline>{
@@ -61,6 +72,17 @@ std::unique_ptr<VKPipelineWrapper> VKPipelineWrapper::CreateStencilClipPipeline(
6172
}();
6273
}
6374

75+
std::unique_ptr<VKPipelineWrapper>
76+
VKPipelineWrapper::CreateStencilRecClipPipeline(GPUVkContext* ctx) {
77+
return PipelineBuilder<StencilRecursiveClipPipeline>{
78+
(const char*)vk_common_vert_spv,
79+
vk_common_vert_spv_size,
80+
(const char*)vk_stencil_discard_frag_spv,
81+
vk_stencil_discard_frag_spv_size,
82+
ctx,
83+
}();
84+
}
85+
6486
std::unique_ptr<VKPipelineWrapper>
6587
VKPipelineWrapper::CreateStencilReplacePipeline(GPUVkContext* ctx) {
6688
return PipelineBuilder<StencilReplacePipeline>{
@@ -162,6 +184,23 @@ StencilClipBackPipeline::GetDepthStencilStateCreateInfo() {
162184
return depth_stencil_state;
163185
}
164186

187+
VkPipelineDepthStencilStateCreateInfo
188+
StencilRecursiveClipBackPipeline::GetDepthStencilStateCreateInfo() {
189+
auto depth_stencil_state = VKUtils::PipelineDepthStencilStateCreateInfo(
190+
VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL);
191+
192+
depth_stencil_state.stencilTestEnable = VK_TRUE;
193+
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
194+
depth_stencil_state.front.passOp = VK_STENCIL_OP_DECREMENT_AND_WRAP;
195+
depth_stencil_state.front.compareOp = VK_COMPARE_OP_EQUAL;
196+
depth_stencil_state.front.compareMask = 0xFF;
197+
depth_stencil_state.front.writeMask = 0xFF;
198+
depth_stencil_state.front.reference = 0x10;
199+
depth_stencil_state.back = depth_stencil_state.front;
200+
201+
return depth_stencil_state;
202+
}
203+
165204
VkPipelineDepthStencilStateCreateInfo
166205
StencilClipPipeline::GetDepthStencilStateCreateInfo() {
167206
auto depth_stencil_state = VKUtils::PipelineDepthStencilStateCreateInfo(
@@ -179,6 +218,23 @@ StencilClipPipeline::GetDepthStencilStateCreateInfo() {
179218
return depth_stencil_state;
180219
}
181220

221+
VkPipelineDepthStencilStateCreateInfo
222+
StencilRecursiveClipPipeline::GetDepthStencilStateCreateInfo() {
223+
auto depth_stencil_state = VKUtils::PipelineDepthStencilStateCreateInfo(
224+
VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL);
225+
226+
depth_stencil_state.stencilTestEnable = VK_TRUE;
227+
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
228+
depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE;
229+
depth_stencil_state.front.compareOp = VK_COMPARE_OP_NOT_EQUAL;
230+
depth_stencil_state.front.compareMask = 0x0F;
231+
depth_stencil_state.front.writeMask = 0x0F;
232+
depth_stencil_state.front.reference = 0x00;
233+
depth_stencil_state.back = depth_stencil_state.front;
234+
235+
return depth_stencil_state;
236+
}
237+
182238
void StencilReplacePipeline::UpdateStencilInfo(uint32_t reference,
183239
GPUVkContext* ctx) {
184240
VK_CALL(vkCmdSetStencilReference, ctx->GetCurrentCMD(),

src/render/hw/vk/pipelines/stencil_pipeline.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ class StencilClipBackPipeline : public StencilPipeline {
6262
override;
6363
};
6464

65+
class StencilRecursiveClipBackPipeline : public StencilPipeline {
66+
public:
67+
StencilRecursiveClipBackPipeline(size_t push_const_size)
68+
: StencilPipeline(push_const_size) {}
69+
70+
~StencilRecursiveClipBackPipeline() override = default;
71+
72+
protected:
73+
VkPipelineDepthStencilStateCreateInfo GetDepthStencilStateCreateInfo()
74+
override;
75+
};
76+
6577
// Used for move stencil buffer for clip
6678
class StencilClipPipeline : public StencilPipeline {
6779
public:
@@ -74,6 +86,17 @@ class StencilClipPipeline : public StencilPipeline {
7486
override;
7587
};
7688

89+
class StencilRecursiveClipPipeline : public StencilPipeline {
90+
public:
91+
StencilRecursiveClipPipeline(size_t push_const_size)
92+
: StencilPipeline(push_const_size) {}
93+
~StencilRecursiveClipPipeline() override = default;
94+
95+
protected:
96+
VkPipelineDepthStencilStateCreateInfo GetDepthStencilStateCreateInfo()
97+
override;
98+
};
99+
77100
// Used for clip buffer clear or convex polygon clip
78101
class StencilReplacePipeline : public StencilPipeline {
79102
public:

0 commit comments

Comments
 (0)