blob: aeb68bd76da080c543a0659dd7a6713859f54024 [file] [log] [blame]
// 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 "texture_contents.h"
#include <memory>
#include <optional>
#include <utility>
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/texture_fill.frag.h"
#include "impeller/entity/texture_fill.vert.h"
#include "impeller/geometry/constants.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/renderer/formats.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/sampler_library.h"
#include "impeller/tessellator/tessellator.h"
namespace impeller {
TextureContents::TextureContents() = default;
TextureContents::~TextureContents() = default;
std::shared_ptr<TextureContents> TextureContents::MakeRect(Rect destination) {
auto contents = std::make_shared<TextureContents>();
contents->path_ = PathBuilder{}.AddRect(destination).TakePath();
contents->rect_ = destination;
return contents;
}
void TextureContents::SetLabel(std::string label) {
label_ = std::move(label);
}
void TextureContents::SetPath(const Path& path) {
path_ = path;
rect_ = std::nullopt;
}
void TextureContents::SetTexture(std::shared_ptr<Texture> texture) {
texture_ = std::move(texture);
}
std::shared_ptr<Texture> TextureContents::GetTexture() const {
return texture_;
}
void TextureContents::SetOpacity(Scalar opacity) {
opacity_ = opacity;
}
void TextureContents::SetStencilEnabled(bool enabled) {
stencil_enabled_ = enabled;
}
bool TextureContents::CanAcceptOpacity(const Entity& entity) const {
return true;
}
void TextureContents::SetInheritedOpacity(Scalar opacity) {
opacity_ = opacity_ * opacity;
}
Scalar TextureContents::GetOpacity() const {
return opacity_;
}
std::optional<Rect> TextureContents::GetCoverage(const Entity& entity) const {
if (opacity_ == 0) {
return std::nullopt;
}
return path_.GetTransformedBoundingBox(entity.GetTransformation());
};
std::optional<Snapshot> TextureContents::RenderToSnapshot(
const ContentContext& renderer,
const Entity& entity,
const std::optional<SamplerDescriptor>& sampler_descriptor,
bool msaa_enabled) const {
auto bounds = path_.GetBoundingBox();
if (!bounds.has_value()) {
return std::nullopt;
}
// Passthrough textures that have simple rectangle paths and complete source
// rects.
if (rect_.has_value() &&
source_rect_ == Rect::MakeSize(texture_->GetSize()) &&
(opacity_ >= 1 - kEhCloseEnough || defer_applying_opacity_)) {
auto scale = Vector2(bounds->size / Size(texture_->GetSize()));
return Snapshot{
.texture = texture_,
.transform = entity.GetTransformation() *
Matrix::MakeTranslation(bounds->origin) *
Matrix::MakeScale(scale),
.sampler_descriptor = sampler_descriptor.value_or(sampler_descriptor_),
.opacity = opacity_};
}
return Contents::RenderToSnapshot(
renderer, entity, sampler_descriptor.value_or(sampler_descriptor_));
}
static TextureFillVertexShader::PerVertexData ComputeVertexData(
const Point& vtx,
const Rect& coverage_rect,
const ISize& texture_size,
const Rect& source_rect) {
TextureFillVertexShader::PerVertexData data;
data.position = vtx;
auto coverage_coords = (vtx - coverage_rect.origin) / coverage_rect.size;
data.texture_coords =
(source_rect.origin + source_rect.size * coverage_coords) / texture_size;
return data;
}
bool TextureContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
if (texture_ == nullptr) {
return true;
}
using VS = TextureFillVertexShader;
using FS = TextureFillFragmentShader;
const auto coverage_rect = path_.GetBoundingBox();
if (!coverage_rect.has_value()) {
return true;
}
if (coverage_rect->size.IsEmpty()) {
return true;
}
const auto texture_size = texture_->GetSize();
if (texture_size.IsEmpty()) {
return true;
}
if (source_rect_.IsEmpty()) {
return true;
}
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
if (!rect_.has_value()) {
const auto tess_result = renderer.GetTessellator()->Tessellate(
path_.GetFillType(), path_.CreatePolyline(1.0f),
[this, &vertex_builder, &coverage_rect, &texture_size](
const float* vertices, size_t vertices_size,
const uint16_t* indices, size_t indices_size) {
for (auto i = 0u; i < vertices_size; i += 2) {
vertex_builder.AppendVertex(ComputeVertexData(
{vertices[i], vertices[i + 1]}, coverage_rect.value(),
texture_size, source_rect_));
}
FML_DCHECK(vertex_builder.GetVertexCount() == vertices_size / 2);
for (auto i = 0u; i < indices_size; i++) {
vertex_builder.AppendIndex(indices[i]);
}
return true;
});
if (tess_result == Tessellator::Result::kInputError) {
return true;
}
if (tess_result == Tessellator::Result::kTessellationError) {
return false;
}
} else {
for (const auto vtx : rect_->GetPoints()) {
vertex_builder.AppendVertex(ComputeVertexData(
vtx, coverage_rect.value(), texture_size, source_rect_));
}
}
if (!vertex_builder.HasVertices()) {
return true;
}
auto& host_buffer = pass.GetTransientsBuffer();
VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation();
frame_info.texture_sampler_y_coord_scale = texture_->GetYCoordScale();
FS::FragInfo frag_info;
frag_info.alpha = opacity_;
Command cmd;
cmd.label = "Texture Fill";
if (!label_.empty()) {
cmd.label += ": " + label_;
}
auto pipeline_options = OptionsFromPassAndEntity(pass, entity);
if (!stencil_enabled_) {
pipeline_options.stencil_compare = CompareFunction::kAlways;
}
if (rect_.has_value()) {
pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
}
cmd.pipeline = renderer.GetTexturePipeline(pipeline_options);
cmd.stencil_reference = entity.GetStencilDepth();
cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer));
VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
FS::BindTextureSampler(cmd, texture_,
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
sampler_descriptor_));
pass.AddCommand(std::move(cmd));
return true;
}
void TextureContents::SetSourceRect(const Rect& source_rect) {
source_rect_ = source_rect;
}
const Rect& TextureContents::GetSourceRect() const {
return source_rect_;
}
void TextureContents::SetSamplerDescriptor(SamplerDescriptor desc) {
sampler_descriptor_ = std::move(desc);
}
const SamplerDescriptor& TextureContents::GetSamplerDescriptor() const {
return sampler_descriptor_;
}
void TextureContents::SetDeferApplyingOpacity(bool defer_applying_opacity) {
defer_applying_opacity_ = defer_applying_opacity;
}
} // namespace impeller