[Impeller] Add blit pass (flutter/engine#34901)

This commit is contained in:
Brandon DeRosier 2022-07-27 15:50:04 -07:00 committed by GitHub
parent d560fa20d1
commit 92a7cfe151
48 changed files with 1436 additions and 28 deletions

View File

@ -671,6 +671,10 @@ FILE: ../../../flutter/impeller/renderer/allocator.cc
FILE: ../../../flutter/impeller/renderer/allocator.h
FILE: ../../../flutter/impeller/renderer/backend/gles/allocator_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/allocator_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/blit_command_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/blit_command_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/blit_pass_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/blit_pass_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/buffer_bindings_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/capabilities_gles.cc
@ -712,6 +716,10 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.h
FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/blit_command_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/blit_command_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/command_buffer_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/command_buffer_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/context_mtl.h
@ -742,6 +750,8 @@ FILE: ../../../flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/vulkan/allocator_vk.cc
FILE: ../../../flutter/impeller/renderer/backend/vulkan/allocator_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_pass_vk.cc
FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_pass_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc
FILE: ../../../flutter/impeller/renderer/backend/vulkan/capabilities_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/command_buffer_vk.cc
@ -773,6 +783,10 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/texture_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/vertex_descriptor_vk.cc
FILE: ../../../flutter/impeller/renderer/backend/vulkan/vertex_descriptor_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/vk.h
FILE: ../../../flutter/impeller/renderer/blit_command.cc
FILE: ../../../flutter/impeller/renderer/blit_command.h
FILE: ../../../flutter/impeller/renderer/blit_pass.cc
FILE: ../../../flutter/impeller/renderer/blit_pass.h
FILE: ../../../flutter/impeller/renderer/buffer.cc
FILE: ../../../flutter/impeller/renderer/buffer.h
FILE: ../../../flutter/impeller/renderer/buffer_view.cc

View File

@ -22,6 +22,7 @@
#include "impeller/geometry/path_builder.h"
#include "impeller/geometry/scalar.h"
#include "impeller/geometry/vertices.h"
#include "impeller/renderer/formats.h"
#include "impeller/typographer/backends/skia/text_frame_skia.h"
#include "third_party/skia/include/core/SkColor.h"
@ -778,6 +779,11 @@ static impeller::SamplerDescriptor ToSamplerDescriptor(
desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kLinear;
desc.label = "Linear Sampler";
break;
case flutter::DlImageSampling::kMipmapLinear:
desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kLinear;
desc.mip_filter = impeller::MipFilter::kLinear;
desc.label = "Mipmap Linear Sampler";
break;
default:
break;
}

View File

@ -16,6 +16,8 @@ impeller_shaders("shader_fixtures") {
"impeller.vert",
"instanced_draw.frag",
"instanced_draw.vert",
"mipmaps.frag",
"mipmaps.vert",
"simple.vert",
"test_texture.frag",
"test_texture.vert",

View File

@ -0,0 +1,18 @@
// 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.
uniform FragInfo {
float lod;
}
frag_info;
uniform sampler2D tex;
in vec2 v_uv;
out vec4 frag_color;
void main() {
frag_color = textureLod(tex, v_uv, frag_info.lod);
}

View File

@ -0,0 +1,18 @@
// 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.
uniform VertInfo {
mat4 mvp;
}
vert_info;
in vec2 vertex_position;
in vec2 uv;
out vec2 v_uv;
void main() {
gl_Position = vert_info.mvp * vec4(vertex_position, 0.0, 1.0);
v_uv = uv;
}

View File

@ -334,7 +334,8 @@ std::optional<DecompressedImage> Playground::LoadFixtureImageRGBA(
}
std::shared_ptr<Texture> Playground::CreateTextureForFixture(
const char* fixture_name) const {
const char* fixture_name,
bool enable_mipmapping) const {
auto image = LoadFixtureImageRGBA(fixture_name);
if (!image.has_value()) {
return nullptr;
@ -343,7 +344,8 @@ std::shared_ptr<Texture> Playground::CreateTextureForFixture(
auto texture_descriptor = TextureDescriptor{};
texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
texture_descriptor.size = image->GetSize();
texture_descriptor.mip_count = 1u;
texture_descriptor.mip_count =
enable_mipmapping ? image->GetSize().MipCount() : 1u;
auto texture =
renderer_->GetContext()->GetPermanentsAllocator()->CreateTexture(

View File

@ -57,7 +57,8 @@ class Playground : public ::testing::TestWithParam<PlaygroundBackend> {
const char* fixture_name) const;
std::shared_ptr<Texture> CreateTextureForFixture(
const char* fixture_name) const;
const char* fixture_name,
bool enable_mipmapping = false) const;
std::shared_ptr<Texture> CreateTextureCubeForFixture(
std::array<const char*, 6> fixture_names) const;

View File

@ -8,6 +8,10 @@ impeller_component("renderer") {
sources = [
"allocator.cc",
"allocator.h",
"blit_command.cc",
"blit_command.h",
"blit_pass.cc",
"blit_pass.h",
"buffer.cc",
"buffer.h",
"buffer_view.cc",

View File

@ -16,6 +16,10 @@ impeller_component("gles") {
sources = [
"allocator_gles.cc",
"allocator_gles.h",
"blit_command_gles.cc",
"blit_command_gles.h",
"blit_pass_gles.cc",
"blit_pass_gles.h",
"buffer_bindings_gles.cc",
"buffer_bindings_gles.h",
"capabilities_gles.cc",
@ -60,8 +64,11 @@ impeller_component("gles") {
if (!is_android && !is_fuchsia) {
public_configs = [ ":gles_config" ]
sources += [
"//third_party/angle/include/GLES2/gl2.h",
"//third_party/angle/include/GLES2/gl2ext.h",
# The GLES3 API is a superset of GLES2. Although we target GLES2, we use
# some GLES3 features if the driver supports them.
"//third_party/angle/include/GLES3/gl3.h",
]
}

View File

@ -0,0 +1,134 @@
// 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 "impeller/renderer/backend/gles/blit_command_gles.h"
#include "flutter/fml/closure.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/gles/texture_gles.h"
namespace impeller {
BlitEncodeGLES::~BlitEncodeGLES() = default;
static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) {
if (fbo != GL_NONE) {
gl.BindFramebuffer(type, GL_NONE);
gl.DeleteFramebuffers(1u, &fbo);
}
};
static std::optional<GLuint> ConfigureFBO(
const ProcTableGLES& gl,
const std::shared_ptr<Texture>& texture,
GLenum fbo_type) {
auto handle = TextureGLES::Cast(texture.get())->GetGLHandle();
if (!handle.has_value()) {
return std::nullopt;
}
if (TextureGLES::Cast(*texture).IsWrapped()) {
// The texture is attached to the default FBO, so there's no need to
// create/configure one.
gl.BindFramebuffer(fbo_type, 0);
return 0;
}
GLuint fbo;
gl.GenFramebuffers(1u, &fbo);
gl.BindFramebuffer(fbo_type, fbo);
if (!TextureGLES::Cast(*texture).SetAsFramebufferAttachment(
fbo_type, fbo, TextureGLES::AttachmentPoint::kColor0)) {
VALIDATION_LOG << "Could not attach texture to framebuffer.";
DeleteFBO(gl, fbo, fbo_type);
return std::nullopt;
}
if (gl.CheckFramebufferStatus(fbo_type) != GL_FRAMEBUFFER_COMPLETE) {
VALIDATION_LOG << "Could not create a complete frambuffer.";
DeleteFBO(gl, fbo, fbo_type);
return std::nullopt;
}
return fbo;
};
BlitCopyTextureToTextureCommandGLES::~BlitCopyTextureToTextureCommandGLES() =
default;
std::string BlitCopyTextureToTextureCommandGLES::GetLabel() const {
return label;
}
bool BlitCopyTextureToTextureCommandGLES::Encode(
const ReactorGLES& reactor) const {
const auto& gl = reactor.GetProcTable();
// glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
// emulate the blit when it's not available in the driver.
if (!gl.BlitFramebuffer.IsAvailable()) {
// TODO(bdero): Emulate the blit using a raster draw call here.
FML_LOG(ERROR) << "Texture blit fallback not implemented yet for GLES2.";
return false;
}
GLuint read_fbo = GL_NONE;
GLuint draw_fbo = GL_NONE;
fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
});
{
auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
if (!read.has_value()) {
return false;
}
read_fbo = read.value();
}
{
auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
if (!draw.has_value()) {
return false;
}
draw_fbo = draw.value();
}
gl.Disable(GL_SCISSOR_TEST);
gl.Disable(GL_DEPTH_TEST);
gl.Disable(GL_STENCIL_TEST);
gl.BlitFramebuffer(source_region.origin.x, // srcX0
source_region.origin.y, // srcY0
source_region.size.width, // srcX1
source_region.size.height, // srcY1
destination_origin.x, // dstX0
destination_origin.y, // dstY0
source_region.size.width, // dstX1
source_region.size.height, // dstY1
GL_COLOR_BUFFER_BIT, // mask
GL_NEAREST // filter
);
return true;
};
BlitGenerateMipmapCommandGLES::~BlitGenerateMipmapCommandGLES() = default;
std::string BlitGenerateMipmapCommandGLES::GetLabel() const {
return label;
}
bool BlitGenerateMipmapCommandGLES::Encode(const ReactorGLES& reactor) const {
auto texture_gles = TextureGLES::Cast(texture.get());
if (!texture_gles->GenerateMipmaps()) {
return false;
}
return true;
};
} // namespace impeller

View File

@ -0,0 +1,41 @@
// 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.
#pragma once
#include "impeller/base/backend_cast.h"
#include "impeller/renderer/backend/gles/reactor_gles.h"
#include "impeller/renderer/blit_command.h"
namespace impeller {
/// Mixin for dispatching GLES commands.
struct BlitEncodeGLES : BackendCast<BlitEncodeGLES, BlitCommand> {
virtual ~BlitEncodeGLES();
virtual std::string GetLabel() const = 0;
[[nodiscard]] virtual bool Encode(const ReactorGLES& reactor) const = 0;
};
struct BlitCopyTextureToTextureCommandGLES
: public BlitEncodeGLES,
public BlitCopyTextureToTextureCommand {
~BlitCopyTextureToTextureCommandGLES() override;
std::string GetLabel() const override;
[[nodiscard]] bool Encode(const ReactorGLES& reactor) const override;
};
struct BlitGenerateMipmapCommandGLES : public BlitEncodeGLES,
public BlitGenerateMipmapCommand {
~BlitGenerateMipmapCommandGLES() override;
std::string GetLabel() const override;
[[nodiscard]] bool Encode(const ReactorGLES& reactor) const override;
};
} // namespace impeller

View File

@ -0,0 +1,124 @@
// 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 "impeller/renderer/backend/gles/blit_pass_gles.h"
#include <algorithm>
#include <memory>
#include "flutter/fml/trace_event.h"
#include "impeller/base/config.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/gles/blit_command_gles.h"
#include "impeller/renderer/backend/gles/device_buffer_gles.h"
#include "impeller/renderer/backend/gles/formats_gles.h"
#include "impeller/renderer/backend/gles/pipeline_gles.h"
#include "impeller/renderer/backend/gles/proc_table_gles.h"
#include "impeller/renderer/backend/gles/texture_gles.h"
#include "impeller/renderer/formats.h"
namespace impeller {
BlitPassGLES::BlitPassGLES(ReactorGLES::Ref reactor)
: reactor_(std::move(reactor)),
is_valid_(reactor_ && reactor_->IsValid()) {}
// |BlitPass|
BlitPassGLES::~BlitPassGLES() = default;
// |BlitPass|
bool BlitPassGLES::IsValid() const {
return is_valid_;
}
// |BlitPass|
void BlitPassGLES::OnSetLabel(std::string label) {
label_ = std::move(label);
}
[[nodiscard]] bool EncodeCommandsInReactor(
const std::shared_ptr<Allocator>& transients_allocator,
const ReactorGLES& reactor,
const std::vector<std::unique_ptr<BlitEncodeGLES>>& commands,
const std::string& label) {
TRACE_EVENT0("impeller", __FUNCTION__);
if (commands.empty()) {
return true;
}
const auto& gl = reactor.GetProcTable();
fml::ScopedCleanupClosure pop_pass_debug_marker(
[&gl]() { gl.PopDebugGroup(); });
if (!label.empty()) {
gl.PushDebugGroup(label);
} else {
pop_pass_debug_marker.Release();
}
for (const auto& command : commands) {
fml::ScopedCleanupClosure pop_cmd_debug_marker(
[&gl]() { gl.PopDebugGroup(); });
auto label = command->GetLabel();
if (!label.empty()) {
gl.PushDebugGroup(label);
} else {
pop_cmd_debug_marker.Release();
}
if (!command->Encode(reactor)) {
return false;
}
}
return true;
}
// |BlitPass|
bool BlitPassGLES::EncodeCommands(
const std::shared_ptr<Allocator>& transients_allocator) const {
if (!IsValid()) {
return false;
}
if (commands_.empty()) {
return true;
}
return reactor_->AddOperation([transients_allocator, &commands = commands_,
label = label_](const auto& reactor) {
auto result =
EncodeCommandsInReactor(transients_allocator, reactor, commands, label);
FML_CHECK(result) << "Must be able to encode GL commands without error.";
});
}
// |BlitPass|
void BlitPassGLES::OnCopyTextureToTextureCommand(
std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
std::string label) {
auto command = std::make_unique<BlitCopyTextureToTextureCommandGLES>();
command->label = label;
command->source = std::move(source);
command->destination = std::move(destination);
command->source_region = source_region;
command->destination_origin = destination_origin;
commands_.emplace_back(std::move(command));
}
// |BlitPass|
void BlitPassGLES::OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
std::string label) {
auto command = std::make_unique<BlitGenerateMipmapCommandGLES>();
command->label = label;
command->texture = std::move(texture);
commands_.emplace_back(std::move(command));
}
} // namespace impeller

View File

@ -0,0 +1,53 @@
// 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.
#pragma once
#include "flutter/fml/macros.h"
#include "flutter/impeller/renderer/backend/gles/reactor_gles.h"
#include "flutter/impeller/renderer/blit_pass.h"
#include "impeller/renderer/backend/gles/blit_command_gles.h"
namespace impeller {
class BlitPassGLES final : public BlitPass {
public:
// |BlitPass|
~BlitPassGLES() override;
private:
friend class CommandBufferGLES;
std::vector<std::unique_ptr<BlitEncodeGLES>> commands_;
ReactorGLES::Ref reactor_;
std::string label_;
bool is_valid_ = false;
explicit BlitPassGLES(ReactorGLES::Ref reactor);
// |BlitPass|
bool IsValid() const override;
// |BlitPass|
void OnSetLabel(std::string label) override;
// |BlitPass|
bool EncodeCommands(
const std::shared_ptr<Allocator>& transients_allocator) const override;
// |BlitPass|
void OnCopyTextureToTextureCommand(std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
std::string label) override;
// |BlitPass|
void OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
std::string label) override;
FML_DISALLOW_COPY_AND_ASSIGN(BlitPassGLES);
};
} // namespace impeller

View File

@ -5,6 +5,7 @@
#include "impeller/renderer/backend/gles/command_buffer_gles.h"
#include "impeller/base/config.h"
#include "impeller/renderer/backend/gles/blit_pass_gles.h"
#include "impeller/renderer/backend/gles/render_pass_gles.h"
namespace impeller {
@ -54,4 +55,16 @@ std::shared_ptr<RenderPass> CommandBufferGLES::OnCreateRenderPass(
return pass;
}
// |CommandBuffer|
std::shared_ptr<BlitPass> CommandBufferGLES::OnCreateBlitPass() const {
if (!IsValid()) {
return nullptr;
}
auto pass = std::shared_ptr<BlitPassGLES>(new BlitPassGLES(reactor_));
if (!pass->IsValid()) {
return nullptr;
}
return pass;
}
} // namespace impeller

View File

@ -37,6 +37,9 @@ class CommandBufferGLES final : public CommandBuffer {
std::shared_ptr<RenderPass> OnCreateRenderPass(
RenderTarget target) const override;
// |CommandBuffer|
std::shared_ptr<BlitPass> OnCreateBlitPass() const override;
FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferGLES);
};

View File

@ -163,6 +163,18 @@ constexpr std::optional<GLenum> ToVertexAttribType(ShaderType type) {
FML_UNREACHABLE();
}
constexpr GLenum ToTextureType(TextureType type) {
switch (type) {
case TextureType::kTexture2D:
return GL_TEXTURE_2D;
case TextureType::kTexture2DMultisample:
return GL_TEXTURE_2D_MULTISAMPLE;
case TextureType::kTextureCube:
return GL_TEXTURE_CUBE_MAP;
}
FML_UNREACHABLE();
}
constexpr std::optional<GLenum> ToTextureTarget(TextureType type) {
switch (type) {
case TextureType::kTexture2D:

View File

@ -4,6 +4,6 @@
#pragma once
#include "GLES2/gl2.h"
#include "GLES3/gl3.h"
#define GL_GLEXT_PROTOTYPES
#include "GLES2/gl2ext.h"

View File

@ -87,6 +87,7 @@ ProcTableGLES::ProcTableGLES(Resolver resolver) {
reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
proc_ivar.error_fn = error_fn; \
}
FOR_EACH_IMPELLER_GLES3_PROC(IMPELLER_PROC);
FOR_EACH_IMPELLER_EXT_PROC(IMPELLER_PROC);
#undef IMPELLER_PROC

View File

@ -123,6 +123,7 @@ struct GLProc {
PROC(FramebufferTexture2D); \
PROC(FrontFace); \
PROC(GenBuffers); \
PROC(GenerateMipmap); \
PROC(GenFramebuffers); \
PROC(GenRenderbuffers); \
PROC(GenTextures); \
@ -162,6 +163,8 @@ struct GLProc {
PROC(VertexAttribPointer); \
PROC(Viewport);
#define FOR_EACH_IMPELLER_GLES3_PROC(PROC) PROC(BlitFramebuffer);
#define FOR_EACH_IMPELLER_EXT_PROC(PROC) \
PROC(DiscardFramebufferEXT); \
PROC(PushDebugGroupKHR); \
@ -188,6 +191,7 @@ class ProcTableGLES {
GLProc<decltype(gl##name)> name = {"gl" #name, nullptr};
FOR_EACH_IMPELLER_PROC(IMPELLER_PROC);
FOR_EACH_IMPELLER_GLES3_PROC(IMPELLER_PROC);
FOR_EACH_IMPELLER_EXT_PROC(IMPELLER_PROC);
#undef IMPELLER_PROC

View File

@ -174,19 +174,19 @@ struct RenderPassData {
if (auto color = TextureGLES::Cast(pass_data.color_attachment.get())) {
if (!color->SetAsFramebufferAttachment(
fbo, TextureGLES::AttachmentPoint::kColor0)) {
GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kColor0)) {
return false;
}
}
if (auto depth = TextureGLES::Cast(pass_data.depth_attachment.get())) {
if (!depth->SetAsFramebufferAttachment(
fbo, TextureGLES::AttachmentPoint::kDepth)) {
GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kDepth)) {
return false;
}
}
if (auto stencil = TextureGLES::Cast(pass_data.stencil_attachment.get())) {
if (!stencil->SetAsFramebufferAttachment(
fbo, TextureGLES::AttachmentPoint::kStencil)) {
GL_FRAMEBUFFER, fbo, TextureGLES::AttachmentPoint::kStencil)) {
return false;
}
}

View File

@ -7,6 +7,7 @@
#include "impeller/renderer/backend/gles/formats_gles.h"
#include "impeller/renderer/backend/gles/proc_table_gles.h"
#include "impeller/renderer/backend/gles/texture_gles.h"
#include "impeller/renderer/formats.h"
namespace impeller {
@ -18,12 +19,29 @@ bool SamplerGLES::IsValid() const {
return true;
}
static GLint ToParam(MinMagFilter filter) {
switch (filter) {
case MinMagFilter::kNearest:
return GL_NEAREST;
case MinMagFilter::kLinear:
return GL_LINEAR;
static GLint ToParam(MinMagFilter minmag_filter, MipFilter mip_filter) {
switch (mip_filter) {
case MipFilter::kNone:
switch (minmag_filter) {
case MinMagFilter::kNearest:
return GL_NEAREST;
case MinMagFilter::kLinear:
return GL_LINEAR;
}
case MipFilter::kNearest:
switch (minmag_filter) {
case MinMagFilter::kNearest:
return GL_NEAREST_MIPMAP_NEAREST;
case MinMagFilter::kLinear:
return GL_LINEAR_MIPMAP_NEAREST;
}
case MipFilter::kLinear:
switch (minmag_filter) {
case MinMagFilter::kNearest:
return GL_NEAREST_MIPMAP_LINEAR;
case MinMagFilter::kLinear:
return GL_LINEAR_MIPMAP_LINEAR;
}
}
FML_UNREACHABLE();
}
@ -54,9 +72,9 @@ bool SamplerGLES::ConfigureBoundTexture(const TextureGLES& texture,
const auto& desc = GetDescriptor();
gl.TexParameteri(target.value(), GL_TEXTURE_MIN_FILTER,
ToParam(desc.min_filter));
ToParam(desc.min_filter, desc.mip_filter));
gl.TexParameteri(target.value(), GL_TEXTURE_MAG_FILTER,
ToParam(desc.mag_filter));
ToParam(desc.mag_filter, MipFilter::kNone));
gl.TexParameteri(target.value(), GL_TEXTURE_WRAP_S,
ToAddressMode(desc.width_address_mode));
gl.TexParameteri(target.value(), GL_TEXTURE_WRAP_T,

View File

@ -12,6 +12,7 @@
#include "impeller/base/config.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/gles/formats_gles.h"
#include "impeller/renderer/formats.h"
namespace impeller {
@ -349,11 +350,15 @@ void TextureGLES::InitializeContentsIfNecessary() const {
}
}
bool TextureGLES::Bind() const {
std::optional<GLuint> TextureGLES::GetGLHandle() const {
if (!IsValid()) {
return false;
return std::nullopt;
}
auto handle = reactor_->GetGLHandle(handle_);
return reactor_->GetGLHandle(handle_);
}
bool TextureGLES::Bind() const {
auto handle = GetGLHandle();
if (!handle.has_value()) {
return false;
}
@ -375,6 +380,37 @@ bool TextureGLES::Bind() const {
return true;
}
bool TextureGLES::GenerateMipmaps() const {
if (!IsValid()) {
return false;
}
auto type = GetTextureDescriptor().type;
switch (type) {
case TextureType::kTexture2D:
break;
case TextureType::kTexture2DMultisample:
VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
"supported in the GLES backend.";
return false;
case TextureType::kTextureCube:
break;
}
if (!Bind()) {
return false;
}
auto handle = GetGLHandle();
if (!handle.has_value()) {
return false;
}
const auto& gl = reactor_->GetProcTable();
gl.GenerateMipmap(ToTextureType(type));
return true;
}
TextureGLES::Type TextureGLES::GetType() const {
return type_;
}
@ -390,20 +426,21 @@ static GLenum ToAttachmentPoint(TextureGLES::AttachmentPoint point) {
}
}
bool TextureGLES::SetAsFramebufferAttachment(GLuint fbo,
bool TextureGLES::SetAsFramebufferAttachment(GLenum target,
GLuint fbo,
AttachmentPoint point) const {
if (!IsValid()) {
return false;
}
InitializeContentsIfNecessary();
auto handle = reactor_->GetGLHandle(handle_);
auto handle = GetGLHandle();
if (!handle.has_value()) {
return false;
}
const auto& gl = reactor_->GetProcTable();
switch (type_) {
case Type::kTexture:
gl.FramebufferTexture2D(GL_FRAMEBUFFER, // target
gl.FramebufferTexture2D(target, // target
ToAttachmentPoint(point), // attachment
GL_TEXTURE_2D, // textarget
handle.value(), // texture
@ -411,7 +448,7 @@ bool TextureGLES::SetAsFramebufferAttachment(GLuint fbo,
);
break;
case Type::kRenderBuffer:
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, // target
gl.FramebufferRenderbuffer(target, // target
ToAttachmentPoint(point), // attachment
GL_RENDERBUFFER, // render-buffer target
handle.value() // render-buffer

View File

@ -33,14 +33,19 @@ class TextureGLES final : public Texture,
// |Texture|
~TextureGLES() override;
std::optional<GLuint> GetGLHandle() const;
[[nodiscard]] bool Bind() const;
[[nodiscard]] bool GenerateMipmaps() const;
enum class AttachmentPoint {
kColor0,
kDepth,
kStencil,
};
[[nodiscard]] bool SetAsFramebufferAttachment(GLuint fbo,
[[nodiscard]] bool SetAsFramebufferAttachment(GLenum target,
GLuint fbo,
AttachmentPoint point) const;
Type GetType() const;

View File

@ -8,6 +8,10 @@ impeller_component("metal") {
sources = [
"allocator_mtl.h",
"allocator_mtl.mm",
"blit_command_mtl.h",
"blit_command_mtl.mm",
"blit_pass_mtl.h",
"blit_pass_mtl.mm",
"command_buffer_mtl.h",
"command_buffer_mtl.mm",
"context_mtl.h",

View File

@ -0,0 +1,43 @@
// 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.
#pragma once
#include <Metal/Metal.h>
#include "impeller/base/backend_cast.h"
#include "impeller/renderer/blit_command.h"
namespace impeller {
/// Mixin for dispatching Metal commands.
struct BlitEncodeMTL : BackendCast<BlitEncodeMTL, BlitCommand> {
virtual ~BlitEncodeMTL();
virtual std::string GetLabel() const = 0;
[[nodiscard]] virtual bool Encode(
id<MTLBlitCommandEncoder> encoder) const = 0;
};
struct BlitCopyTextureToTextureCommandMTL
: public BlitCopyTextureToTextureCommand,
public BlitEncodeMTL {
~BlitCopyTextureToTextureCommandMTL() override;
std::string GetLabel() const override;
[[nodiscard]] bool Encode(id<MTLBlitCommandEncoder> encoder) const override;
};
struct BlitGenerateMipmapCommandMTL : public BlitGenerateMipmapCommand,
public BlitEncodeMTL {
~BlitGenerateMipmapCommandMTL() override;
std::string GetLabel() const override;
[[nodiscard]] bool Encode(id<MTLBlitCommandEncoder> encoder) const override;
};
} // namespace impeller

View File

@ -0,0 +1,70 @@
// 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 "impeller/renderer/backend/metal/blit_command_mtl.h"
#include "impeller/renderer/backend/metal/texture_mtl.h"
namespace impeller {
BlitEncodeMTL::~BlitEncodeMTL() = default;
BlitCopyTextureToTextureCommandMTL::~BlitCopyTextureToTextureCommandMTL() =
default;
std::string BlitCopyTextureToTextureCommandMTL::GetLabel() const {
return label;
}
bool BlitCopyTextureToTextureCommandMTL::Encode(
id<MTLBlitCommandEncoder> encoder) const {
auto source_mtl = TextureMTL::Cast(*source).GetMTLTexture();
if (!source_mtl) {
return false;
}
auto destination_mtl = TextureMTL::Cast(*destination).GetMTLTexture();
if (!destination_mtl) {
return false;
}
auto source_origin_mtl =
MTLOriginMake(source_region.origin.x, source_region.origin.y, 0);
auto source_size_mtl =
MTLSizeMake(source_region.size.width, source_region.size.height, 1);
auto destination_origin_mtl =
MTLOriginMake(destination_origin.x, destination_origin.y, 0);
[encoder copyFromTexture:source_mtl
sourceSlice:0
sourceLevel:0
sourceOrigin:source_origin_mtl
sourceSize:source_size_mtl
toTexture:destination_mtl
destinationSlice:0
destinationLevel:0
destinationOrigin:destination_origin_mtl];
return true;
};
BlitGenerateMipmapCommandMTL::~BlitGenerateMipmapCommandMTL() = default;
std::string BlitGenerateMipmapCommandMTL::GetLabel() const {
return label;
}
bool BlitGenerateMipmapCommandMTL::Encode(
id<MTLBlitCommandEncoder> encoder) const {
auto texture_mtl = TextureMTL::Cast(*texture).GetMTLTexture();
if (!texture_mtl) {
return false;
}
[encoder generateMipmapsForTexture:texture_mtl];
return true;
};
} // namespace impeller

View File

@ -0,0 +1,56 @@
// 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.
#pragma once
#include <Metal/Metal.h>
#include "flutter/fml/macros.h"
#include "impeller/renderer/backend/metal/blit_command_mtl.h"
#include "impeller/renderer/blit_pass.h"
namespace impeller {
class BlitPassMTL final : public BlitPass {
public:
// |RenderPass|
~BlitPassMTL() override;
private:
friend class CommandBufferMTL;
std::vector<std::unique_ptr<BlitEncodeMTL>> commands_;
id<MTLCommandBuffer> buffer_ = nil;
std::string label_;
bool is_valid_ = false;
explicit BlitPassMTL(id<MTLCommandBuffer> buffer);
// |BlitPass|
bool IsValid() const override;
// |BlitPass|
void OnSetLabel(std::string label) override;
// |BlitPass|
bool EncodeCommands(
const std::shared_ptr<Allocator>& transients_allocator) const override;
bool EncodeCommands(id<MTLBlitCommandEncoder> pass) const;
// |BlitPass|
void OnCopyTextureToTextureCommand(std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
std::string label) override;
// |BlitPass|
void OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
std::string label) override;
FML_DISALLOW_COPY_AND_ASSIGN(BlitPassMTL);
};
} // namespace impeller

View File

@ -0,0 +1,117 @@
// 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 "impeller/renderer/backend/metal/blit_pass_mtl.h"
#include <Metal/Metal.h>
#include <memory>
#include <variant>
#include "flutter/fml/closure.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/backend_cast.h"
#include "impeller/renderer/backend/metal/blit_command_mtl.h"
#include "impeller/renderer/backend/metal/device_buffer_mtl.h"
#include "impeller/renderer/backend/metal/formats_mtl.h"
#include "impeller/renderer/backend/metal/pipeline_mtl.h"
#include "impeller/renderer/backend/metal/sampler_mtl.h"
#include "impeller/renderer/backend/metal/texture_mtl.h"
#include "impeller/renderer/blit_command.h"
#include "impeller/renderer/formats.h"
#include "impeller/renderer/host_buffer.h"
#include "impeller/renderer/shader_types.h"
namespace impeller {
BlitPassMTL::BlitPassMTL(id<MTLCommandBuffer> buffer) : buffer_(buffer) {
if (!buffer_) {
return;
}
is_valid_ = true;
}
BlitPassMTL::~BlitPassMTL() = default;
bool BlitPassMTL::IsValid() const {
return is_valid_;
}
void BlitPassMTL::OnSetLabel(std::string label) {
if (label.empty()) {
return;
}
label_ = std::move(label);
}
bool BlitPassMTL::EncodeCommands(
const std::shared_ptr<Allocator>& transients_allocator) const {
TRACE_EVENT0("impeller", "BlitPassMTL::EncodeCommands");
if (!IsValid()) {
return false;
}
auto blit_command_encoder = [buffer_ blitCommandEncoder];
if (!blit_command_encoder) {
return false;
}
if (!label_.empty()) {
[blit_command_encoder setLabel:@(label_.c_str())];
}
// Success or failure, the pass must end. The buffer can only process one pass
// at a time.
fml::ScopedCleanupClosure auto_end(
[blit_command_encoder]() { [blit_command_encoder endEncoding]; });
return EncodeCommands(blit_command_encoder);
}
bool BlitPassMTL::EncodeCommands(id<MTLBlitCommandEncoder> encoder) const {
fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; };
for (const auto& command : commands_) {
fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker);
auto label = command->GetLabel();
if (!label.empty()) {
[encoder pushDebugGroup:@(label.c_str())];
} else {
auto_pop_debug_marker.Release();
}
if (command->Encode(encoder)) {
return false;
}
}
return true;
}
// |BlitPass|
void BlitPassMTL::OnCopyTextureToTextureCommand(
std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
std::string label) {
auto command = std::make_unique<BlitCopyTextureToTextureCommandMTL>();
command->label = label;
command->source = std::move(source);
command->destination = std::move(destination);
command->source_region = source_region;
command->destination_origin = destination_origin;
commands_.emplace_back(std::move(command));
}
// |BlitPass|
void BlitPassMTL::OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
std::string label) {
auto command = std::make_unique<BlitGenerateMipmapCommandMTL>();
command->label = label;
command->texture = std::move(texture);
commands_.emplace_back(std::move(command));
}
} // namespace impeller

View File

@ -38,6 +38,9 @@ class CommandBufferMTL final : public CommandBuffer {
std::shared_ptr<RenderPass> OnCreateRenderPass(
RenderTarget target) const override;
// |CommandBuffer|
std::shared_ptr<BlitPass> OnCreateBlitPass() const override;
FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferMTL);
};

View File

@ -4,6 +4,7 @@
#include "impeller/renderer/backend/metal/command_buffer_mtl.h"
#include "impeller/renderer/backend/metal/blit_pass_mtl.h"
#include "impeller/renderer/backend/metal/render_pass_mtl.h"
namespace impeller {
@ -202,4 +203,17 @@ std::shared_ptr<RenderPass> CommandBufferMTL::OnCreateRenderPass(
return pass;
}
std::shared_ptr<BlitPass> CommandBufferMTL::OnCreateBlitPass() const {
if (!buffer_) {
return nullptr;
}
auto pass = std::shared_ptr<BlitPassMTL>(new BlitPassMTL(buffer_));
if (!pass->IsValid()) {
return nullptr;
}
return pass;
}
} // namespace impeller

View File

@ -275,6 +275,18 @@ constexpr MTLSamplerMinMagFilter ToMTLSamplerMinMagFilter(MinMagFilter filter) {
return MTLSamplerMinMagFilterNearest;
}
constexpr MTLSamplerMipFilter ToMTLSamplerMipFilter(MipFilter filter) {
switch (filter) {
case MipFilter::kNone:
return MTLSamplerMipFilterNotMipmapped;
case MipFilter::kNearest:
return MTLSamplerMipFilterNearest;
case MipFilter::kLinear:
return MTLSamplerMipFilterLinear;
}
return MTLSamplerMipFilterNotMipmapped;
}
constexpr MTLSamplerAddressMode ToMTLSamplerAddressMode(
SamplerAddressMode mode) {
switch (mode) {

View File

@ -25,6 +25,7 @@ std::shared_ptr<const Sampler> SamplerLibraryMTL::GetSampler(
auto desc = [[MTLSamplerDescriptor alloc] init];
desc.minFilter = ToMTLSamplerMinMagFilter(descriptor.min_filter);
desc.magFilter = ToMTLSamplerMinMagFilter(descriptor.mag_filter);
desc.mipFilter = ToMTLSamplerMipFilter(descriptor.mip_filter);
desc.sAddressMode = ToMTLSamplerAddressMode(descriptor.width_address_mode);
desc.tAddressMode = ToMTLSamplerAddressMode(descriptor.height_address_mode);
desc.rAddressMode = ToMTLSamplerAddressMode(descriptor.depth_address_mode);

View File

@ -8,6 +8,8 @@ impeller_component("vulkan") {
sources = [
"allocator_vk.cc",
"allocator_vk.h",
"blit_pass_vk.cc",
"blit_pass_vk.h",
"capabilities_vk.cc",
"capabilities_vk.h",
"command_buffer_vk.cc",

View File

@ -0,0 +1,11 @@
// 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 "impeller/renderer/backend/vulkan/blit_pass_vk.h"
namespace impeller {
//
} // namespace impeller

View File

@ -0,0 +1,46 @@
// 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.
#pragma once
#include "flutter/fml/macros.h"
#include "impeller/renderer/blit_pass.h"
namespace impeller {
class BlitPassVK final : public BlitPass {
public:
// |BlitPass|
~BlitPassVK() override;
private:
friend class CommandBufferVK;
BlitPassVK();
// |BlitPass|
bool IsValid() const override;
// |BlitPass|
void OnSetLabel(std::string label) override;
// |BlitPass|
bool EncodeCommands(
const std::shared_ptr<Allocator>& transients_allocator) const override;
// |BlitPass|
void OnCopyTextureToTextureCommand(std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
std::string label) override;
// |BlitPass|
void OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
std::string label) override;
FML_DISALLOW_COPY_AND_ASSIGN(BlitPassVK);
};
} // namespace impeller

View File

@ -30,4 +30,8 @@ std::shared_ptr<RenderPass> CommandBufferVK::OnCreateRenderPass(
FML_UNREACHABLE();
}
std::shared_ptr<BlitPass> CommandBufferVK::OnCreateBlitPass() const {
FML_UNREACHABLE();
}
} // namespace impeller

View File

@ -32,6 +32,9 @@ class CommandBufferVK final : public CommandBuffer {
std::shared_ptr<RenderPass> OnCreateRenderPass(
RenderTarget target) const override;
// |CommandBuffer|
std::shared_ptr<BlitPass> OnCreateBlitPass() const override;
FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferVK);
};

View File

@ -0,0 +1,11 @@
// 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 "impeller/renderer/blit_command.h"
namespace impeller {
//
} // namespace impeller

View File

@ -0,0 +1,26 @@
// 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.
#pragma once
#include "impeller/renderer/texture.h"
namespace impeller {
struct BlitCommand {
std::string label;
};
struct BlitCopyTextureToTextureCommand : public BlitCommand {
std::shared_ptr<Texture> source;
std::shared_ptr<Texture> destination;
IRect source_region;
IPoint destination_origin;
};
struct BlitGenerateMipmapCommand : public BlitCommand {
std::shared_ptr<Texture> texture;
};
} // namespace impeller

View File

@ -0,0 +1,91 @@
// 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 "impeller/renderer/blit_pass.h"
#include <memory>
#include "impeller/base/strings.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/blit_command.h"
#include "impeller/renderer/host_buffer.h"
namespace impeller {
BlitPass::BlitPass() : transients_buffer_(HostBuffer::Create()) {}
BlitPass::~BlitPass() = default;
HostBuffer& BlitPass::GetTransientsBuffer() {
return *transients_buffer_;
}
void BlitPass::SetLabel(std::string label) {
if (label.empty()) {
return;
}
transients_buffer_->SetLabel(SPrintF("%s Transients", label.c_str()));
OnSetLabel(std::move(label));
}
bool BlitPass::AddCopy(std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
std::optional<IRect> source_region,
IPoint destination_origin,
std::string label) {
if (!source) {
VALIDATION_LOG << "Attempted to add a texture blit with no source.";
return false;
}
if (!destination) {
VALIDATION_LOG << "Attempted to add a texture blit with no destination.";
return false;
}
if (source->GetTextureDescriptor().sample_count !=
destination->GetTextureDescriptor().sample_count) {
VALIDATION_LOG << SPrintF(
"The source sample count (%d) must match the destination sample count "
"(%d) for blits.",
source->GetTextureDescriptor().sample_count,
destination->GetTextureDescriptor().sample_count);
return false;
}
if (!source_region.has_value()) {
source_region = IRect::MakeSize(source->GetSize());
}
// Clip the source image.
source_region =
source_region->Intersection(IRect::MakeSize(source->GetSize()));
if (!source_region.has_value()) {
return true; // Nothing to blit.
}
// Clip the destination image.
source_region = source_region->Intersection(
IRect(-destination_origin, destination->GetSize()));
if (!source_region.has_value()) {
return true; // Nothing to blit.
}
OnCopyTextureToTextureCommand(std::move(source), std::move(destination),
source_region.value(), destination_origin,
label);
return true;
}
bool BlitPass::GenerateMipmap(std::shared_ptr<Texture> texture,
std::string label) {
if (!texture) {
VALIDATION_LOG << "Attempted to add an invalid mipmap generation command "
"with no texture.";
return false;
}
OnGenerateMipmapCommand(std::move(texture), label);
return true;
}
} // namespace impeller

View File

@ -0,0 +1,105 @@
// 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.
#pragma once
#include <string>
#include <variant>
#include "impeller/renderer/blit_command.h"
#include "impeller/renderer/texture.h"
namespace impeller {
class HostBuffer;
class Allocator;
//------------------------------------------------------------------------------
/// @brief Blit passes encode blit into the underlying command buffer.
///
/// Blit passes can be obtained from the command buffer in which
/// the pass is meant to encode commands into.
///
/// @see `CommandBuffer`
///
class BlitPass {
public:
virtual ~BlitPass();
virtual bool IsValid() const = 0;
void SetLabel(std::string label);
HostBuffer& GetTransientsBuffer();
//----------------------------------------------------------------------------
/// @brief Record a command to copy the contents of one texture to
/// another texture. The blit area is limited by the intersection
/// of the texture coverage with respect the source region and
/// destination origin.
/// No work is encoded into the command buffer at this time.
///
/// @param[in] source The texture to read for copying.
/// @param[in] destination The texture to overwrite using the source
/// contents.
/// @param[in] source_region The optional region of the source texture
/// to use for copying. If not specified, the
/// full size of the source texture is used.
/// @param[in] destination_origin The origin to start writing to in the
/// destination texture.
/// @param[in] label The optional debug label to give the
/// command.
///
/// @return If the command was valid for subsequent commitment.
///
bool AddCopy(std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
std::optional<IRect> source_region = std::nullopt,
IPoint destination_origin = {},
std::string label = "");
//----------------------------------------------------------------------------
/// @brief Record a command to generate all mip levels for a texture.
/// No work is encoded into the command buffer at this time.
///
/// @param[in] texture The texture to generate mipmaps for.
/// @param[in] label The optional debug label to give the command.
///
/// @return If the command was valid for subsequent commitment.
///
bool GenerateMipmap(std::shared_ptr<Texture> texture, std::string label = "");
//----------------------------------------------------------------------------
/// @brief Encode the recorded commands to the underlying command buffer.
///
/// @param transients_allocator The transients allocator.
///
/// @return If the commands were encoded to the underlying command
/// buffer.
///
virtual bool EncodeCommands(
const std::shared_ptr<Allocator>& transients_allocator) const = 0;
protected:
std::shared_ptr<HostBuffer> transients_buffer_;
explicit BlitPass();
virtual void OnSetLabel(std::string label) = 0;
virtual void OnCopyTextureToTextureCommand(
std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
IRect source_region,
IPoint destination_origin,
std::string label) = 0;
virtual void OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
std::string label) = 0;
private:
FML_DISALLOW_COPY_AND_ASSIGN(BlitPass);
};
} // namespace impeller

View File

@ -27,4 +27,13 @@ std::shared_ptr<RenderPass> CommandBuffer::CreateRenderPass(
return nullptr;
}
std::shared_ptr<BlitPass> CommandBuffer::CreateBlitPass() const {
auto pass = OnCreateBlitPass();
if (pass && pass->IsValid()) {
pass->SetLabel("BlitPass");
return pass;
}
return nullptr;
}
} // namespace impeller

View File

@ -8,6 +8,7 @@
#include <memory>
#include "flutter/fml/macros.h"
#include "impeller/renderer/blit_pass.h"
namespace impeller {
@ -65,20 +66,29 @@ class CommandBuffer {
//----------------------------------------------------------------------------
/// @brief Create a render pass to record render commands into.
///
/// @param[in] desc The description of the render target this pass will
/// target.
/// @param[in] render_target The description of the render target this pass
/// will target.
///
/// @return A valid render pass or null.
///
std::shared_ptr<RenderPass> CreateRenderPass(
RenderTarget render_target) const;
//----------------------------------------------------------------------------
/// @brief Create a blit pass to record blit commands into.
///
/// @return A valid blit pass or null.
///
std::shared_ptr<BlitPass> CreateBlitPass() const;
protected:
CommandBuffer();
virtual std::shared_ptr<RenderPass> OnCreateRenderPass(
RenderTarget render_target) const = 0;
virtual std::shared_ptr<BlitPass> OnCreateBlitPass() const = 0;
private:
FML_DISALLOW_COPY_AND_ASSIGN(CommandBuffer);
};

View File

@ -199,6 +199,16 @@ enum class MinMagFilter {
kLinear,
};
enum class MipFilter {
/// Always sample from mip level 0. Other mip levels are ignored.
kNone,
/// Sample from the nearest mip level.
kNearest,
/// Sample from the two nearest mip levels and linearly interpolate between
/// them.
kLinear,
};
enum class SamplerAddressMode {
kClampToEdge,
kRepeat,

View File

@ -4,6 +4,7 @@
#include "flutter/fml/time/time_point.h"
#include "flutter/testing/testing.h"
#include "impeller/base/strings.h"
#include "impeller/fixtures/box_fade.frag.h"
#include "impeller/fixtures/box_fade.vert.h"
#include "impeller/fixtures/colors.frag.h"
@ -12,6 +13,8 @@
#include "impeller/fixtures/impeller.vert.h"
#include "impeller/fixtures/instanced_draw.frag.h"
#include "impeller/fixtures/instanced_draw.vert.h"
#include "impeller/fixtures/mipmaps.frag.h"
#include "impeller/fixtures/mipmaps.vert.h"
#include "impeller/fixtures/test_texture.frag.h"
#include "impeller/fixtures/test_texture.vert.h"
#include "impeller/geometry/path_builder.h"
@ -442,6 +445,237 @@ TEST_P(RendererTest, CanRenderInstanced) {
}
#endif // IMPELLER_ENABLE_METAL
TEST_P(RendererTest, CanBlitTextureToTexture) {
auto context = GetContext();
ASSERT_TRUE(context);
using VS = MipmapsVertexShader;
using FS = MipmapsFragmentShader;
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
desc->SetSampleCount(SampleCount::kCount4);
auto mipmaps_pipeline =
context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get();
ASSERT_TRUE(mipmaps_pipeline);
TextureDescriptor texture_desc;
texture_desc.format = PixelFormat::kR8G8B8A8UNormInt;
texture_desc.size = {800, 600};
texture_desc.mip_count = 1u;
texture_desc.usage =
static_cast<TextureUsageMask>(TextureUsage::kRenderTarget) |
static_cast<TextureUsageMask>(TextureUsage::kShaderRead);
auto texture = context->GetPermanentsAllocator()->CreateTexture(
StorageMode::kHostVisible, texture_desc);
ASSERT_TRUE(texture);
auto bridge = CreateTextureForFixture("bay_bridge.jpg");
auto boston = CreateTextureForFixture("boston.jpg");
ASSERT_TRUE(bridge && boston);
auto sampler = context->GetSamplerLibrary()->GetSampler({});
ASSERT_TRUE(sampler);
// Vertex buffer.
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
vertex_builder.SetLabel("Box");
auto size = Point(boston->GetSize());
vertex_builder.AddVertices({
{{0, 0}, {0.0, 0.0}}, // 1
{{size.x, 0}, {1.0, 0.0}}, // 2
{{size.x, size.y}, {1.0, 1.0}}, // 3
{{0, 0}, {0.0, 0.0}}, // 1
{{size.x, size.y}, {1.0, 1.0}}, // 3
{{0, size.y}, {0.0, 1.0}}, // 4
});
auto vertex_buffer =
vertex_builder.CreateVertexBuffer(*context->GetTransientsAllocator());
ASSERT_TRUE(vertex_buffer);
Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
auto buffer = context->CreateRenderCommandBuffer();
if (!buffer) {
return false;
}
buffer->SetLabel("Playground Command Buffer");
{
auto pass = buffer->CreateBlitPass();
if (!pass) {
return false;
}
pass->SetLabel("Playground Blit Pass");
if (render_target.GetColorAttachments().empty()) {
return false;
}
// Blit `bridge` to the top left corner of the texture.
pass->AddCopy(bridge, texture);
pass->EncodeCommands(context->GetTransientsAllocator());
}
{
auto pass = buffer->CreateRenderPass(render_target);
if (!pass) {
return false;
}
pass->SetLabel("Playground Render Pass");
{
Command cmd;
cmd.label = "Image";
cmd.pipeline = mipmaps_pipeline;
cmd.BindVertices(vertex_buffer);
VS::VertInfo vert_info;
vert_info.mvp = Matrix::MakeOrthographic(pass->GetRenderTargetSize()) *
Matrix::MakeScale(GetContentScale());
VS::BindVertInfo(cmd,
pass->GetTransientsBuffer().EmplaceUniform(vert_info));
FS::FragInfo frag_info;
frag_info.lod = 0;
FS::BindFragInfo(cmd,
pass->GetTransientsBuffer().EmplaceUniform(frag_info));
auto sampler = context->GetSamplerLibrary()->GetSampler({});
FS::BindTex(cmd, texture, sampler);
pass->AddCommand(std::move(cmd));
}
pass->EncodeCommands(context->GetTransientsAllocator());
}
if (!buffer->SubmitCommands()) {
return false;
}
return true;
};
OpenPlaygroundHere(callback);
}
TEST_P(RendererTest, CanGenerateMipmaps) {
auto context = GetContext();
ASSERT_TRUE(context);
using VS = MipmapsVertexShader;
using FS = MipmapsFragmentShader;
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
desc->SetSampleCount(SampleCount::kCount4);
auto mipmaps_pipeline =
context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get();
ASSERT_TRUE(mipmaps_pipeline);
auto boston = CreateTextureForFixture("boston.jpg", true);
ASSERT_TRUE(boston);
// Vertex buffer.
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
vertex_builder.SetLabel("Box");
auto size = Point(boston->GetSize());
vertex_builder.AddVertices({
{{0, 0}, {0.0, 0.0}}, // 1
{{size.x, 0}, {1.0, 0.0}}, // 2
{{size.x, size.y}, {1.0, 1.0}}, // 3
{{0, 0}, {0.0, 0.0}}, // 1
{{size.x, size.y}, {1.0, 1.0}}, // 3
{{0, size.y}, {0.0, 1.0}}, // 4
});
auto vertex_buffer =
vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator());
ASSERT_TRUE(vertex_buffer);
bool first_frame = true;
Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
if (first_frame) {
ImGui::SetNextWindowPos({10, 10});
}
const char* mip_filter_names[] = {"None", "Nearest", "Linear"};
const MipFilter mip_filters[] = {MipFilter::kNone, MipFilter::kNearest,
MipFilter::kLinear};
const char* min_filter_names[] = {"Nearest", "Linear"};
const MinMagFilter min_filters[] = {MinMagFilter::kNearest,
MinMagFilter::kLinear};
// UI state.
static int selected_mip_filter = 2;
static int selected_min_filter = 0;
static float lod = 4.5;
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Mip filter", &selected_mip_filter, mip_filter_names,
sizeof(mip_filter_names) / sizeof(char*));
ImGui::Combo("Min filter", &selected_min_filter, min_filter_names,
sizeof(min_filter_names) / sizeof(char*));
ImGui::SliderFloat("LOD", &lod, 0, boston->GetMipCount() - 1);
ImGui::End();
auto buffer = context->CreateRenderCommandBuffer();
if (!buffer) {
return false;
}
buffer->SetLabel("Playground Command Buffer");
if (first_frame) {
auto pass = buffer->CreateBlitPass();
if (!pass) {
return false;
}
pass->SetLabel("Playground Blit Pass");
pass->GenerateMipmap(boston, "Boston Mipmap");
pass->EncodeCommands(context->GetTransientsAllocator());
}
first_frame = false;
{
auto pass = buffer->CreateRenderPass(render_target);
if (!pass) {
return false;
}
pass->SetLabel("Playground Render Pass");
{
Command cmd;
cmd.label = "Image LOD";
cmd.pipeline = mipmaps_pipeline;
cmd.BindVertices(vertex_buffer);
VS::VertInfo vert_info;
vert_info.mvp = Matrix::MakeOrthographic(pass->GetRenderTargetSize()) *
Matrix::MakeScale(GetContentScale());
VS::BindVertInfo(cmd,
pass->GetTransientsBuffer().EmplaceUniform(vert_info));
FS::FragInfo frag_info;
frag_info.lod = lod;
FS::BindFragInfo(cmd,
pass->GetTransientsBuffer().EmplaceUniform(frag_info));
SamplerDescriptor sampler_desc;
sampler_desc.mip_filter = mip_filters[selected_mip_filter];
sampler_desc.min_filter = min_filters[selected_min_filter];
auto sampler = context->GetSamplerLibrary()->GetSampler(sampler_desc);
FS::BindTex(cmd, boston, sampler);
pass->AddCommand(std::move(cmd));
}
pass->EncodeCommands(context->GetTransientsAllocator());
}
if (!buffer->SubmitCommands()) {
return false;
}
return true;
};
OpenPlaygroundHere(callback);
}
TEST_P(RendererTest, TheImpeller) {
using VS = ImpellerVertexShader;
using FS = ImpellerFragmentShader;

View File

@ -18,6 +18,7 @@ class Context;
struct SamplerDescriptor final : public Comparable<SamplerDescriptor> {
MinMagFilter min_filter = MinMagFilter::kNearest;
MinMagFilter mag_filter = MinMagFilter::kNearest;
MipFilter mip_filter = MipFilter::kNone;
SamplerAddressMode width_address_mode = SamplerAddressMode::kClampToEdge;
SamplerAddressMode height_address_mode = SamplerAddressMode::kClampToEdge;
@ -27,13 +28,15 @@ struct SamplerDescriptor final : public Comparable<SamplerDescriptor> {
// Comparable<SamplerDescriptor>
std::size_t GetHash() const override {
return fml::HashCombine(min_filter, mag_filter, width_address_mode,
height_address_mode, depth_address_mode);
return fml::HashCombine(min_filter, mag_filter, mip_filter,
width_address_mode, height_address_mode,
depth_address_mode);
}
// Comparable<SamplerDescriptor>
bool IsEqual(const SamplerDescriptor& o) const override {
return min_filter == o.min_filter && mag_filter == o.mag_filter &&
mip_filter == o.mip_filter &&
width_address_mode == o.width_address_mode &&
height_address_mode == o.height_address_mode &&
depth_address_mode == o.depth_address_mode;

View File

@ -42,6 +42,10 @@ bool Texture::SetContents(std::shared_ptr<const fml::Mapping> mapping,
return true;
}
size_t Texture::GetMipCount() const {
return GetTextureDescriptor().mip_count;
}
const TextureDescriptor& Texture::GetTextureDescriptor() const {
return desc_;
}

View File

@ -31,6 +31,8 @@ class Texture {
virtual ISize GetSize() const = 0;
size_t GetMipCount() const;
const TextureDescriptor& GetTextureDescriptor() const;
TextureIntent GetIntent() const;