Turn on full-screen 4xMSAA by default and additional render target validations.

This commit is contained in:
Chinmay Garde 2021-11-30 14:23:48 -08:00 committed by Dan Field
parent 7ebaf2b1f7
commit f9d781df9b
18 changed files with 310 additions and 30 deletions

View File

@ -127,6 +127,17 @@ TEST_F(AiksTest, CanRenderGroupOpacity) {
canvas.Restore();
// ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_F(AiksTest, CanPerformFullScreenMSAA) {
Canvas canvas;
Paint red;
red.color = Color::Red();
canvas.DrawCircle({250, 250}, 125, red);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

View File

@ -91,6 +91,15 @@ void Canvas::DrawPath(Path path, Paint paint) {
GetCurrentPass().AddEntity(std::move(entity));
}
void Canvas::DrawRect(Rect rect, Paint paint) {
DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint));
}
void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) {
DrawPath(PathBuilder{}.AddCircle(center, radius).CreatePath(),
std::move(paint));
}
void Canvas::SaveLayer(Paint paint, std::optional<Rect> bounds) {
GetCurrentPass().SetDelegate(
std::make_unique<PaintPassDelegate>(std::move(paint)));
@ -189,10 +198,6 @@ size_t Canvas::GetStencilDepth() const {
return xformation_stack_.back().stencil_depth;
}
void Canvas::DrawRect(Rect rect, Paint paint) {
DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint));
}
void Canvas::Save(bool create_subpass) {
auto entry = CanvasStackEntry{};
if (create_subpass) {

View File

@ -53,6 +53,8 @@ class Canvas {
void DrawRect(Rect rect, Paint paint);
void DrawCircle(Point center, Scalar radius, Paint paint);
void DrawImage(std::shared_ptr<Image> image, Point offset, Paint paint);
void DrawImageRect(std::shared_ptr<Image> image,

View File

@ -12,6 +12,8 @@ impeller_component("base") {
"config.h",
"strings.cc",
"strings.h",
"validation.cc",
"validation.h",
]
}

View File

@ -5,3 +5,4 @@
#pragma once
#include "impeller/base/strings.h"
#include "impeller/base/validation.h"

View File

@ -0,0 +1,28 @@
// 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/impeller/base/validation.h"
#include "flutter/fml/logging.h"
namespace impeller {
ValidationLog::ValidationLog() = default;
ValidationLog::~ValidationLog() {
FML_LOG(ERROR) << stream_.str();
ImpellerValidationBreak();
}
std::ostream& ValidationLog::GetStream() {
return stream_;
}
void ImpellerValidationBreak() {
// Nothing to do. Exists for the debugger.
FML_LOG(ERROR) << "Break on " << __FUNCTION__
<< " to inspect point of failure.";
}
} // namespace impeller

View File

@ -0,0 +1,31 @@
// 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 <sstream>
#include "flutter/fml/macros.h"
namespace impeller {
class ValidationLog {
public:
ValidationLog();
~ValidationLog();
std::ostream& GetStream();
private:
std::ostringstream stream_;
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ValidationLog);
};
void ImpellerValidationBreak();
} // namespace impeller
#define VALIDATION_LOG ::impeller::ValidationLog{}.GetStream()

View File

@ -144,23 +144,44 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) {
return false;
}
TextureDescriptor color0_tex;
color0_tex.format = PixelFormat::kB8G8R8A8UNormInt;
color0_tex.size = {
TextureDescriptor msaa_tex_desc;
msaa_tex_desc.type = TextureType::k2DMultisample;
msaa_tex_desc.sample_count = SampleCount::kCount4;
msaa_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt;
msaa_tex_desc.size = {
static_cast<ISize::Type>(current_drawable.texture.width),
static_cast<ISize::Type>(current_drawable.texture.height)};
color0_tex.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget);
msaa_tex_desc.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget);
auto msaa_tex =
renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture(
StorageMode::kDeviceTransient, msaa_tex_desc);
if (!msaa_tex) {
FML_LOG(ERROR) << "Could not allocate MSAA resolve texture.";
return false;
}
msaa_tex->SetLabel("PlaygroundMainColor4xMSAA");
TextureDescriptor onscreen_tex_desc;
onscreen_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt;
onscreen_tex_desc.size = msaa_tex_desc.size;
onscreen_tex_desc.usage =
static_cast<uint64_t>(TextureUsage::kRenderTarget);
ColorAttachment color0;
color0.texture =
std::make_shared<TextureMTL>(color0_tex, current_drawable.texture);
color0.texture = msaa_tex;
color0.clear_color = Color::DarkSlateGray();
color0.load_action = LoadAction::kClear;
color0.store_action = StoreAction::kStore;
color0.store_action = StoreAction::kMultisampleResolve;
color0.resolve_texture = std::make_shared<TextureMTL>(
onscreen_tex_desc, current_drawable.texture);
TextureDescriptor stencil0_tex;
stencil0_tex.type = TextureType::k2DMultisample;
stencil0_tex.sample_count = SampleCount::kCount4;
stencil0_tex.format = PixelFormat::kD32FloatS8UNormInt;
stencil0_tex.size = color0_tex.size;
stencil0_tex.size = msaa_tex_desc.size;
stencil0_tex.usage =
static_cast<TextureUsageMask>(TextureUsage::kRenderTarget);
auto stencil_texture =

View File

@ -111,6 +111,12 @@ std::shared_ptr<Texture> AllocatorMTL::CreateTexture(
}
auto mtl_texture_desc = ToMTLTextureDescriptor(desc);
if (!mtl_texture_desc) {
FML_DLOG(ERROR) << "Texture descriptor was invalid.";
return nullptr;
}
mtl_texture_desc.storageMode = ToMTLStorageMode(mode);
auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc];
if (!texture) {

View File

@ -206,6 +206,8 @@ constexpr MTLStoreAction ToMTLStoreAction(StoreAction action) {
return MTLStoreActionDontCare;
case StoreAction::kStore:
return MTLStoreActionStore;
case StoreAction::kMultisampleResolve:
return MTLStoreActionMultisampleResolve;
}
return MTLStoreActionDontCare;
}
@ -216,6 +218,8 @@ constexpr StoreAction FromMTLStoreAction(MTLStoreAction action) {
return StoreAction::kDontCare;
case MTLStoreActionStore:
return StoreAction::kStore;
case MTLStoreActionMultisampleResolve:
return StoreAction::kMultisampleResolve;
default:
break;
}
@ -249,6 +253,16 @@ constexpr MTLClearColor ToMTLClearColor(const Color& color) {
return MTLClearColorMake(color.red, color.green, color.blue, color.alpha);
}
constexpr MTLTextureType ToMTLTextureType(TextureType type) {
switch (type) {
case TextureType::k2D:
return MTLTextureType2D;
case TextureType::k2DMultisample:
return MTLTextureType2DMultisample;
}
return MTLTextureType2D;
}
MTLRenderPipelineColorAttachmentDescriptor*
ToMTLRenderPipelineColorAttachmentDescriptor(
ColorAttachmentDescriptor descriptor);

View File

@ -78,8 +78,13 @@ MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor(
}
MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc) {
if (!desc.IsValid()) {
return nil;
}
auto mtl_desc = [[MTLTextureDescriptor alloc] init];
mtl_desc.textureType = ToMTLTextureType(desc.type);
mtl_desc.pixelFormat = ToMTLPixelFormat(desc.format);
mtl_desc.sampleCount = static_cast<NSUInteger>(desc.sample_count);
mtl_desc.width = desc.size.width;
mtl_desc.height = desc.size.height;
mtl_desc.mipmapLevelCount = desc.mip_count;

View File

@ -53,6 +53,8 @@ static MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor(
descriptor.stencilAttachmentPixelFormat =
ToMTLPixelFormat(desc.GetStencilPixelFormat());
descriptor.sampleCount = 4u;
return descriptor;
}

View File

@ -17,6 +17,33 @@
namespace impeller {
static bool ConfigureResolveTextureAttachment(
const Attachment& desc,
MTLRenderPassAttachmentDescriptor* attachment) {
if (desc.store_action == StoreAction::kMultisampleResolve &&
!desc.resolve_texture) {
VALIDATION_LOG << "Resolve store action specified on attachment but no "
"resolve texture was specified.";
return false;
}
if (desc.resolve_texture &&
desc.store_action != StoreAction::kMultisampleResolve) {
VALIDATION_LOG << "Resolve store action specified but there was no "
"resolve attachment.";
return false;
}
if (!desc.resolve_texture) {
return true;
}
attachment.resolveTexture =
TextureMTL::Cast(*desc.resolve_texture).GetMTLTexture();
return true;
}
static bool ConfigureAttachment(const Attachment& desc,
MTLRenderPassAttachmentDescriptor* attachment) {
if (!desc.texture) {
@ -26,6 +53,11 @@ static bool ConfigureAttachment(const Attachment& desc,
attachment.texture = TextureMTL::Cast(*desc.texture).GetMTLTexture();
attachment.loadAction = ToMTLLoadAction(desc.load_action);
attachment.storeAction = ToMTLStoreAction(desc.store_action);
if (!ConfigureResolveTextureAttachment(desc, attachment)) {
return false;
}
return true;
}
@ -69,8 +101,8 @@ static MTLRenderPassDescriptor* ToMTLRenderPassDescriptor(
for (const auto& color : colors) {
if (!ConfigureColorAttachment(color.second,
result.colorAttachments[color.first])) {
FML_DLOG(ERROR) << "Could not configure color attachment at index "
<< color.first;
VALIDATION_LOG << "Could not configure color attachment at index "
<< color.first;
return nil;
}
}
@ -79,7 +111,7 @@ static MTLRenderPassDescriptor* ToMTLRenderPassDescriptor(
if (depth.has_value() &&
!ConfigureDepthAttachment(depth.value(), result.depthAttachment)) {
FML_DLOG(ERROR) << "Could not configure depth attachment.";
VALIDATION_LOG << "Could not configure depth attachment.";
return nil;
}
@ -87,7 +119,7 @@ static MTLRenderPassDescriptor* ToMTLRenderPassDescriptor(
if (stencil.has_value() &&
!ConfigureStencilAttachment(stencil.value(), result.stencilAttachment)) {
FML_DLOG(ERROR) << "Could not configure stencil attachment.";
VALIDATION_LOG << "Could not configure stencil attachment.";
return nil;
}
@ -99,7 +131,7 @@ RenderPassMTL::RenderPassMTL(id<MTLCommandBuffer> buffer, RenderTarget target)
buffer_(buffer),
desc_(ToMTLRenderPassDescriptor(GetRenderTarget())),
transients_buffer_(HostBuffer::Create()) {
if (!buffer_ || !desc_) {
if (!buffer_ || !desc_ || !render_target_.IsValid()) {
return;
}
is_valid_ = true;
@ -202,8 +234,7 @@ struct PassBindingsCache {
[encoder_ setFragmentBufferOffset:offset atIndex:index];
return true;
default:
FML_DCHECK(false)
<< "Cannot update buffer offset of an unknown stage.";
VALIDATION_LOG << "Cannot update buffer offset of an unknown stage.";
return false;
}
return true;
@ -217,7 +248,7 @@ struct PassBindingsCache {
[encoder_ setFragmentBuffer:buffer offset:offset atIndex:index];
return true;
default:
FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage.";
VALIDATION_LOG << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
@ -239,7 +270,7 @@ struct PassBindingsCache {
[encoder_ setFragmentTexture:texture atIndex:index];
return true;
default:
FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage.";
VALIDATION_LOG << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
@ -263,7 +294,7 @@ struct PassBindingsCache {
[encoder_ setFragmentSamplerState:sampler atIndex:index];
return true;
default:
FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage.";
VALIDATION_LOG << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
@ -360,7 +391,7 @@ bool RenderPassMTL::EncodeCommands(Allocator& allocator,
fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; };
for (const auto& command : commands_) {
if (command.index_count == 0u) {
FML_DLOG(ERROR) << "Zero index count in render pass command.";
VALIDATION_LOG << "Zero index count in render pass command.";
continue;
}

View File

@ -89,6 +89,27 @@ enum class LoadAction {
enum class StoreAction {
kDontCare,
kStore,
kMultisampleResolve,
};
enum class TextureType {
k2D,
k2DMultisample,
};
constexpr bool IsMultisampleCapable(TextureType type) {
switch (type) {
case TextureType::k2D:
return false;
case TextureType::k2DMultisample:
return true;
}
return false;
}
enum class SampleCount {
kCount1 = 1,
kCount4 = 4,
};
using TextureUsageMask = uint64_t;
@ -320,7 +341,8 @@ struct StencilAttachmentDescriptor {
};
struct Attachment {
std::shared_ptr<Texture> texture = nullptr;
std::shared_ptr<Texture> texture;
std::shared_ptr<Texture> resolve_texture;
LoadAction load_action = LoadAction::kDontCare;
StoreAction store_action = StoreAction::kStore;

View File

@ -59,11 +59,11 @@ class RenderPass {
virtual bool EncodeCommands(Allocator& transients_allocator) const = 0;
protected:
const RenderTarget render_target_;
RenderPass(RenderTarget target);
private:
const RenderTarget render_target_;
FML_DISALLOW_COPY_AND_ASSIGN(RenderPass);
};

View File

@ -4,7 +4,7 @@
#include "impeller/renderer/render_target.h"
#include "impeller/base/strings.h"
#include "impeller/base/base.h"
#include "impeller/renderer/allocator.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/texture.h"
@ -15,6 +15,92 @@ RenderTarget::RenderTarget() = default;
RenderTarget::~RenderTarget() = default;
bool RenderTarget::IsValid() const {
// Validate that there is a color attachment at zero index.
if (!HasColorAttachment(0u)) {
VALIDATION_LOG
<< "Render target does not have color attachment at index 0.";
return false;
}
// Validate that all attachments are of the same size.
{
std::optional<ISize> size;
bool sizes_are_same = true;
auto iterator = [&](const Attachment& attachment) -> bool {
if (!size.has_value()) {
size = attachment.texture->GetSize();
}
if (size != attachment.texture->GetSize()) {
sizes_are_same = false;
return false;
}
return true;
};
IterateAllAttachments(iterator);
if (!sizes_are_same) {
VALIDATION_LOG
<< "Sizes of all render target attachments are not the same.";
return false;
}
}
// Validate that all attachments are of the same type and sample counts.
{
std::optional<TextureType> texture_type;
std::optional<SampleCount> sample_count;
bool passes_type_validation = true;
auto iterator = [&](const Attachment& attachment) -> bool {
if (!texture_type.has_value() || !sample_count.has_value()) {
texture_type = attachment.texture->GetTextureDescriptor().type;
sample_count = attachment.texture->GetTextureDescriptor().sample_count;
}
if (texture_type != attachment.texture->GetTextureDescriptor().type) {
passes_type_validation = false;
return false;
}
if (sample_count !=
attachment.texture->GetTextureDescriptor().sample_count) {
passes_type_validation = false;
return false;
}
return true;
};
IterateAllAttachments(iterator);
if (!passes_type_validation) {
VALIDATION_LOG << "Render target texture types are not of the same type "
"and sample count.";
return false;
}
}
return true;
}
void RenderTarget::IterateAllAttachments(
std::function<bool(const Attachment& attachment)> iterator) const {
for (const auto& color : colors_) {
if (!iterator(color.second)) {
return;
}
}
if (depth_.has_value()) {
if (!iterator(depth_.value())) {
return;
}
}
if (stencil_.has_value()) {
if (!iterator(stencil_.value())) {
return;
}
}
}
bool RenderTarget::HasColorAttachment(size_t index) const {
if (auto found = colors_.find(index); found != colors_.end()) {
return true;

View File

@ -4,6 +4,7 @@
#pragma once
#include <functional>
#include <map>
#include <optional>
@ -25,6 +26,8 @@ class RenderTarget {
~RenderTarget();
bool IsValid() const;
bool HasColorAttachment(size_t index) const;
ISize GetRenderTargetSize() const;
@ -49,6 +52,9 @@ class RenderTarget {
std::map<size_t, ColorAttachment> colors_;
std::optional<DepthAttachment> depth_;
std::optional<StencilAttachment> stencil_;
void IterateAllAttachments(
std::function<bool(const Attachment& attachment)> iterator) const;
};
} // namespace impeller

View File

@ -13,11 +13,13 @@
namespace impeller {
struct TextureDescriptor {
TextureType type = TextureType::k2D;
PixelFormat format = PixelFormat::kUnknown;
ISize size;
size_t mip_count = 1u; // Size::MipCount is usually appropriate.
TextureUsageMask usage =
static_cast<TextureUsageMask>(TextureUsage::kShaderRead);
SampleCount sample_count = SampleCount::kCount1;
constexpr size_t GetSizeOfBaseMipLevel() const {
if (!IsValid()) {
@ -33,11 +35,16 @@ struct TextureDescriptor {
return size.width * BytesPerPixelForPixelFormat(format);
}
bool IsValid() const {
constexpr bool SamplingOptionsAreValid() const {
const auto count = static_cast<uint64_t>(sample_count);
return IsMultisampleCapable(type) ? count > 1 : count == 1;
}
constexpr bool IsValid() const {
return format != PixelFormat::kUnknown && //
size.IsPositive() && //
mip_count >= 1u //
;
mip_count >= 1u && //
SamplingOptionsAreValid();
}
};