blob: d453bc9b6384118308218a9a6365323bd6773bed [file]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/gpu/render_pass.h"
#include "gtest/gtest.h"
#include "flutter/lib/gpu/render_pipeline.h"
#include "flutter/lib/gpu/shader.h"
#include "fml/memory/ref_ptr.h"
namespace flutter::gpu {
namespace {
fml::RefPtr<Shader> MakeShader(impeller::ShaderStage stage) {
return Shader::Make("library", "Entrypoint", stage,
/*code_mapping=*/nullptr, /*inputs=*/{}, /*layouts=*/{},
/*uniform_structs=*/{}, /*uniform_textures=*/{},
/*descriptor_set_layouts=*/{});
}
fml::RefPtr<RenderPipeline> MakeRenderPipeline() {
auto vertex = MakeShader(impeller::ShaderStage::kVertex);
auto fragment = MakeShader(impeller::ShaderStage::kFragment);
return fml::MakeRefCounted<RenderPipeline>(vertex, fragment,
vertex->CreateVertexDescriptor());
}
// Regression test for https://github.com/flutter/flutter/issues/188712:
// SetDepthWriteEnable must honor its argument. It previously ignored the
// argument and always enabled depth writes, so disabling depth writes (for
// example to keep overlapping translucent draws from self-occluding) had no
// effect.
TEST(FlutterGpuRenderPassTest, SetDepthWriteEnableHonorsArgument) {
auto render_pass = fml::MakeRefCounted<RenderPass>();
InternalFlutterGpu_RenderPass_SetDepthWriteEnable(render_pass.get(), true);
EXPECT_TRUE(render_pass->GetDepthAttachmentDescriptor().depth_write_enabled);
InternalFlutterGpu_RenderPass_SetDepthWriteEnable(render_pass.get(), false);
EXPECT_FALSE(render_pass->GetDepthAttachmentDescriptor().depth_write_enabled);
}
// Draws memoize the built pipeline until the pipeline-affecting state
// changes, so every state mutation must mark the state dirty (a missed
// mutation would silently draw with a stale pipeline).
TEST(FlutterGpuRenderPassTest, PipelineStateMutationsMarkStateDirty) {
auto render_pass = fml::MakeRefCounted<RenderPass>();
// A fresh pass must build a pipeline on first draw.
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
render_pass->ClearPipelineStateDirtyForTesting();
InternalFlutterGpu_RenderPass_SetDepthWriteEnable(render_pass.get(), true);
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
render_pass->ClearPipelineStateDirtyForTesting();
InternalFlutterGpu_RenderPass_SetColorBlendEnable(render_pass.get(), 0, true);
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
render_pass->ClearPipelineStateDirtyForTesting();
InternalFlutterGpu_RenderPass_SetDepthCompareOperation(render_pass.get(), 0);
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
render_pass->ClearPipelineStateDirtyForTesting();
render_pass->SetCullMode(impeller::CullMode::kBackFace);
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
render_pass->ClearPipelineStateDirtyForTesting();
render_pass->SetWindingOrder(impeller::WindingOrder::kCounterClockwise);
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
render_pass->ClearPipelineStateDirtyForTesting();
render_pass->SetPrimitiveType(impeller::PrimitiveType::kTriangleStrip);
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
render_pass->ClearPipelineStateDirtyForTesting();
render_pass->SetPolygonMode(impeller::PolygonMode::kLine);
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
render_pass->ClearPipelineStateDirtyForTesting();
render_pass->SetPipeline(MakeRenderPipeline());
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
}
// Callers re-send the same fixed-function state and rebind the same pipeline
// ahead of most draws. Dirtying on those redundant assignments would rebuild
// the pipeline for every draw and leave the memoization doing nothing.
TEST(FlutterGpuRenderPassTest, RedundantPipelineStateAssignmentsAreIgnored) {
auto render_pass = fml::MakeRefCounted<RenderPass>();
auto pipeline = MakeRenderPipeline();
render_pass->SetCullMode(impeller::CullMode::kBackFace);
render_pass->SetWindingOrder(impeller::WindingOrder::kCounterClockwise);
render_pass->SetPrimitiveType(impeller::PrimitiveType::kTriangleStrip);
render_pass->SetPolygonMode(impeller::PolygonMode::kLine);
render_pass->SetPipeline(pipeline);
render_pass->ClearPipelineStateDirtyForTesting();
render_pass->SetCullMode(impeller::CullMode::kBackFace);
render_pass->SetWindingOrder(impeller::WindingOrder::kCounterClockwise);
render_pass->SetPrimitiveType(impeller::PrimitiveType::kTriangleStrip);
render_pass->SetPolygonMode(impeller::PolygonMode::kLine);
render_pass->SetPipeline(pipeline);
EXPECT_FALSE(render_pass->IsPipelineStateDirtyForTesting());
// A real change still dirties.
render_pass->SetCullMode(impeller::CullMode::kFrontFace);
EXPECT_TRUE(render_pass->IsPipelineStateDirtyForTesting());
}
} // namespace
} // namespace flutter::gpu