blob: b2f271a7ca37822c3202d211aff7e62e59f4a27c [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/math/size.h"
#include "cobalt/renderer/backend/default_graphics_system.h"
#include "cobalt/renderer/backend/graphics_context.h"
#include "cobalt/renderer/backend/graphics_system.h"
#include "cobalt/renderer/backend/render_target.h"
#include "cobalt/renderer/rasterizer/rasterizer.h"
#include "cobalt/renderer/renderer_module.h"
#include "cobalt/renderer/test/scenes/all_scenes_combined_scene.h"
#include "cobalt/system_window/system_window.h"
#include "cobalt/trace_event/benchmark.h"
using cobalt::math::Size;
using cobalt::math::SizeF;
using cobalt::render_tree::AlphaFormat;
using cobalt::render_tree::animations::AnimateNode;
using cobalt::render_tree::ImageData;
using cobalt::render_tree::Node;
using cobalt::render_tree::PixelFormat;
using cobalt::render_tree::ResourceProvider;
using cobalt::renderer::backend::Display;
using cobalt::renderer::backend::GraphicsContext;
using cobalt::renderer::backend::GraphicsSystem;
using cobalt::renderer::backend::RenderTarget;
using cobalt::renderer::backend::SurfaceInfo;
using cobalt::renderer::rasterizer::Rasterizer;
using cobalt::renderer::RendererModule;
using cobalt::renderer::test::scenes::AddBlankBackgroundToScene;
using cobalt::renderer::test::scenes::CreateAllScenesCombinedScene;
using cobalt::system_window::SystemWindow;
namespace {
const int kViewportWidth = 1920;
const int kViewportHeight = 1080;
std::unique_ptr<Rasterizer> CreateDefaultRasterizer(
GraphicsContext* graphics_context) {
RendererModule::Options render_module_options;
return render_module_options.create_rasterizer_function.Run(
graphics_context, render_module_options);
}
// Allow test writers to choose whether the rasterizer results should be drawn
// on-screen or off-screen. The major difference between the two is that when
// drawing to a display, we must wait for v-sync, and thus the framerate will
// be capped at 60fps.
enum OutputSurfaceType {
kOutputSurfaceTypeDisplay,
kOutputSurfaceTypeOffscreen,
};
typedef base::Callback<scoped_refptr<Node>(ResourceProvider*, const SizeF&,
base::TimeDelta)>
SceneCreateFunction;
// RunRenderTreeSceneBenchmark serves as a framework for render tree scene
// based benchmarks. In other words, it makes it easy to test performance
// metrics given different render tree scenes.
void RunRenderTreeSceneBenchmark(SceneCreateFunction scene_create_function,
OutputSurfaceType output_surface_type) {
// Disable tracing so that we can iterate one round without recording
// results. This is to trigger any lazy initialization that may need to
// be done.
base::trace_event::TraceLog::GetInstance()->SetDisabled(
base::trace_event::TraceLog::RECORDING_MODE);
base::EventDispatcher event_dispatcher;
std::unique_ptr<SystemWindow> test_system_window;
if (output_surface_type == kOutputSurfaceTypeDisplay) {
Size view_size(kViewportWidth, kViewportHeight);
test_system_window.reset(
new cobalt::system_window::SystemWindow(&event_dispatcher, view_size));
}
// Setup our graphics system.
std::unique_ptr<GraphicsSystem> graphics_system =
cobalt::renderer::backend::CreateDefaultGraphicsSystem(
test_system_window.get());
std::unique_ptr<GraphicsContext> graphics_context =
graphics_system->CreateGraphicsContext();
// Create the rasterizer using the platform default RenderModule options.
std::unique_ptr<Rasterizer> rasterizer =
CreateDefaultRasterizer(graphics_context.get());
std::unique_ptr<Display> test_display;
scoped_refptr<RenderTarget> test_surface;
if (output_surface_type == kOutputSurfaceTypeDisplay) {
test_display = graphics_system->CreateDisplay(test_system_window.get());
test_surface = test_display->GetRenderTarget();
} else if (output_surface_type == kOutputSurfaceTypeOffscreen) {
// Create our offscreen surface that will be the target of our test
// rasterizations.
const Size kTestOffscreenDimensions(1920, 1080);
test_surface = graphics_context->CreateDownloadableOffscreenRenderTarget(
kTestOffscreenDimensions);
} else {
DLOG(FATAL) << "Unknown output surface type.";
}
scoped_refptr<Node> scene =
scene_create_function.Run(rasterizer->GetResourceProvider(),
test_surface->GetSize(), base::TimeDelta());
const int kRenderIterationCount = 100;
const float kFixedTimeStepInSecondsPerFrame = 0.016f;
for (int i = 0; i < kRenderIterationCount; ++i) {
AnimateNode* animate_node =
base::polymorphic_downcast<AnimateNode*>(scene.get());
scoped_refptr<Node> animated = animate_node
->Apply(base::TimeDelta::FromSecondsD(
i * kFixedTimeStepInSecondsPerFrame))
.animated->source();
// Submit the render tree to be rendered.
rasterizer->Submit(animated, test_surface);
graphics_context->Finish();
if (i == 0) {
// Enable tracing again after one iteration has passed and any lazy
// initializations are out of the way.
base::trace_event::TraceLog::GetInstance()->SetEnabled(
base::trace_event::TraceConfig(),
base::trace_event::TraceLog::RECORDING_MODE);
}
}
}
} // namespace
// Setup a quick macro so that we can measure the same events for each
// render tree builder benchmark.
#define RENDER_TREE_BUILDER_BENCHMARK(test_name) \
TRACE_EVENT_BENCHMARK5( \
test_name, "BuildRenderTree", cobalt::trace_event::IN_SCOPE_DURATION, \
"AnimateNode::Apply()", cobalt::trace_event::IN_SCOPE_DURATION, \
"Rasterizer::Submit()", cobalt::trace_event::FLOW_DURATION, \
"Rasterizer::Submit()", cobalt::trace_event::TIME_BETWEEN_EVENT_STARTS, \
"VisitRenderTree", cobalt::trace_event::IN_SCOPE_DURATION)
// A catch-all test that exercises every different render tree node at the
// same time. This is the same render tree builder used by the renderer
// sandbox.
RENDER_TREE_BUILDER_BENCHMARK(AllScenesCombinedOffscreenBenchmark) {
RunRenderTreeSceneBenchmark(base::Bind(&CreateAllScenesCombinedScene),
kOutputSurfaceTypeOffscreen);
}
RENDER_TREE_BUILDER_BENCHMARK(AllScenesCombinedOnscreenBenchmark) {
RunRenderTreeSceneBenchmark(base::Bind(&CreateAllScenesCombinedScene),
kOutputSurfaceTypeDisplay);
}
// This benchmark tracks how long it takes to load image data into a image
// via the render_tree::ResourceProvider interface provided by our default
// rasterizer.
namespace {
void SynthesizeImageData(ImageData* image_data) {
TRACE_EVENT0("rasterizer_benchmark", "SynthesizeImageData");
// Simply fill the entire image with a single arbitrarily chosen byte value.
const uint8_t kFillValue = 127;
int height = image_data->GetDescriptor().size.height();
int pitch_in_bytes = image_data->GetDescriptor().pitch_in_bytes;
for (int row = 0; row < height; ++row) {
uint8_t* pixel_data = image_data->GetMemory() + row * pitch_in_bytes;
for (int row_byte = 0; row_byte < pitch_in_bytes; ++row_byte) {
pixel_data[row_byte] = kFillValue;
}
}
}
void RunCreateImageViaResourceProviderBenchmark(AlphaFormat alpha_format) {
std::unique_ptr<GraphicsSystem> graphics_system =
cobalt::renderer::backend::CreateDefaultGraphicsSystem();
std::unique_ptr<GraphicsContext> graphics_context =
graphics_system->CreateGraphicsContext();
// Create the rasterizer using the platform default RenderModule options.
std::unique_ptr<Rasterizer> rasterizer =
CreateDefaultRasterizer(graphics_context.get());
ResourceProvider* resource_provider = rasterizer->GetResourceProvider();
if (!resource_provider->AlphaFormatSupported(alpha_format)) {
// Only run the test if the alpha format is supported.
return;
}
const int kIterationCount = 20;
const Size kImageSize(400, 400);
cobalt::render_tree::PixelFormat pixel_format =
cobalt::render_tree::kPixelFormatRGBA8;
if (!resource_provider->PixelFormatSupported(pixel_format)) {
pixel_format = cobalt::render_tree::kPixelFormatBGRA8;
if (!resource_provider->PixelFormatSupported(pixel_format)) {
LOG(ERROR) << "Could not find a supported pixel format on this platform, "
"returning early from benchmark.";
return;
}
}
for (int i = 0; i < kIterationCount; ++i) {
// Repeatedly allocate memory for an image, write to that memory, and then
// submit the image data back to the ResourceProvider to have it create
// an image out of it.
std::unique_ptr<ImageData> image_data =
resource_provider->AllocateImageData(kImageSize, pixel_format,
alpha_format);
SynthesizeImageData(image_data.get());
resource_provider->CreateImage(std::move(image_data));
}
}
} // namespace
TRACE_EVENT_BENCHMARK3(
CreateUnpremultipliedAlphaImageViaResourceProviderBenchmark,
"ResourceProvider::AllocateImageData()",
cobalt::trace_event::IN_SCOPE_DURATION, "ResourceProvider::CreateImage()",
cobalt::trace_event::IN_SCOPE_DURATION, "SynthesizeImageData",
cobalt::trace_event::IN_SCOPE_DURATION) {
RunCreateImageViaResourceProviderBenchmark(
cobalt::render_tree::kAlphaFormatUnpremultiplied);
}
TRACE_EVENT_BENCHMARK3(
CreatePremultipliedAlphaImageViaResourceProviderBenchmark,
"ResourceProvider::AllocateImageData()",
cobalt::trace_event::IN_SCOPE_DURATION, "ResourceProvider::CreateImage()",
cobalt::trace_event::IN_SCOPE_DURATION, "SynthesizeImageData",
cobalt::trace_event::IN_SCOPE_DURATION) {
RunCreateImageViaResourceProviderBenchmark(
cobalt::render_tree::kAlphaFormatPremultiplied);
}