blob: a5bfde0c1a7e7a3cf5a7f59d654b9c9f161b9270 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 <math.h>
#include <stdio.h>
#include <vector>
#if USE_MOLD
#define DEMO_CLASS_HEADER "common/demo_app_mold.h"
#define DEMO_CLASS_NAME DemoAppMold
#define ONLY_IF_MOLD(...) __VA_ARGS__
#define ONLY_IF_SPINEL(...) /* nothing */
#else // !USE_MOLD
#define DEMO_CLASS_HEADER "common/demo_app_spinel.h"
#define DEMO_CLASS_NAME DemoAppSpinel
#define ONLY_IF_MOLD(...) /* nothing */
#define ONLY_IF_SPINEL(...) __VA_ARGS__
#endif // !USE_MOLD
// This weird include is to work-around the fact that gn --check doesn't
// seem to properly understand conditional includes when they appear in
// #ifdef .. #endif blocks. Note that this is standard cpp behaviour!
#include DEMO_CLASS_HEADER
#include "common/demo_image.h"
#include "common/demo_utils.h"
#include "svg_scene_demo_image.h"
#include "tests/common/affine_transform.h"
#include "tests/common/argparse.h"
#include "tests/common/svg/scoped_svg.h"
#include "tests/common/svg/svg_bounds.h"
#include "tests/common/svg/svg_scene.h"
#include "tests/common/utils.h"
#define DEMO_SURFACE_WIDTH 1024
#define DEMO_SURFACE_HEIGHT 1024
#ifndef PROGRAM_NAME
#define PROGRAM_NAME "svg_ring_demo"
#endif
int
main(int argc, const char ** argv)
{
// clang-format off
#define MY_OPTIONS_LIST(param) \
ARGPARSE_OPTION_INT(param, count, 'c', "count", "Number of SVG instances in ring.") \
ARGPARSE_OPTION_DOUBLE(param, radius, 'r', "radius", "Select inner ring radius in pixels.") \
ARGPARSE_OPTION_DOUBLE(param, scale, 's', "scale", "Apply scale to each SVG instance.") \
ARGPARSE_OPTION_DOUBLE(param, fixed_rotation, 'R', \
"fixed-rotation", "Use a fixed rotation parameter. Value is in degrees.") \
ARGPARSE_OPTION_FLAG(param, debug, 'D', "debug", \
"Enable debug messages and Vulkan validation layers.") \
ARGPARSE_OPTION_STRING(param, window, '\0', "window", "Set window dimensions (e.g. 800x600).") \
ARGPARSE_OPTION_STRING(param, device, '\0', "device", "Device selection (vendor:device) IDs.") \
ARGPARSE_OPTION_STRING(param, format, '\0', "format", "Force pixel format [RGBA, BGRA].") \
ARGPARSE_OPTION_FLAG(param, fps, '\0', "fps", "Print frames per seconds to stdout.") \
ARGPARSE_OPTION_FLAG(param, no_vsync, '\0', "no-vsync", \
"Disable vsync synchronization. Useful for benchmarking. Note that this will disable " \
"presentation on Fuchsia as well.") \
ARGPARSE_OPTION_FLAG(param, no_clear, '\0', "no-clear", \
"Disable image clear before rendering. Useful for benchmarking raw rendering performance.")
// clang-format on
ARGPARSE_DEFINE_OPTIONS_STRUCT(options, MY_OPTIONS_LIST);
if (!ARGPARSE_PARSE_ARGS(argc, argv, options, MY_OPTIONS_LIST))
{
if (options.help_needed)
ARGPARSE_PRINT_HELP(PROGRAM_NAME, "A short demo of Spinel rendering", MY_OPTIONS_LIST);
exit(options.help_needed ? EXIT_SUCCESS : EXIT_FAILURE);
}
uint32_t vendor_id, device_id;
if (!parseDeviceOption(options.device, &vendor_id, &device_id))
return EXIT_FAILURE;
uint32_t window_width, window_height;
if (!parseWindowOption(options.window,
DEMO_SURFACE_WIDTH,
DEMO_SURFACE_HEIGHT,
&window_width,
&window_height))
return EXIT_FAILURE;
uint32_t ring_size = 10;
if (options.count.used)
{
ring_size = (options.count.value < 1) ? 0u : (uint32_t)options.count.value;
}
double ring_radius = 20.;
if (options.radius.used)
{
ring_radius = options.radius.value;
}
double ring_scale = 1.0;
if (options.scale.used)
{
ring_scale = options.scale.value;
}
// Parse the SVG input document.
if (argc < 2)
{
fprintf(stderr, "This program requires an input svg file path!\n");
return EXIT_FAILURE;
}
ScopedSvg svg = ScopedSvg::parseFile(argv[1]);
ASSERT_MSG(svg.get(), "Could not parse input SVG file: %s\n", argv[1]);
VkOffset2D image_center = {};
double svg_xmin, svg_ymin, svg_xmax, svg_ymax;
svg_estimate_bounds(svg.get(), nullptr, &svg_xmin, &svg_ymin, &svg_xmax, &svg_ymax);
if (options.debug)
{
printf("Image bounds min=(%g,%g) max=(%g,%g) width=%g height=%g\n",
svg_xmin,
svg_ymin,
svg_xmax,
svg_ymax,
svg_xmax - svg_xmin,
svg_ymax - svg_ymin);
}
if (svg_xmin >= svg_xmax || svg_ymin >= svg_ymax)
{
fprintf(stderr, "WARNING: Could not compute bounds of SVG document!\n");
}
else
{
image_center.x = (int32_t)((svg_xmin + svg_xmax) / 2.);
image_center.y = (int32_t)((svg_ymin + svg_ymax) / 2.);
}
// Create the application window.
DEMO_CLASS_NAME::Config config = {
.app = {
.app_name = PROGRAM_NAME,
.window_width = window_width,
.window_height = window_height,
.verbose = options.debug,
.debug = options.debug,
.disable_vsync = options.no_vsync,
.print_fps = options.fps,
},
.no_clear = options.no_clear,
};
DEMO_CLASS_NAME demo(config);
VkExtent2D swapchain_extent = demo.window_extent();
// Build ring as an SvgScene, centered around the window center.
double win_center_x = swapchain_extent.width * 0.5;
double win_center_y = swapchain_extent.height * 0.5;
double rotation_center_x = (svg_xmin + svg_xmax) * 0.5;
double rotation_center_y = svg_ymax + ring_radius;
SvgScene svg_scene;
for (uint32_t nn = 0; nn < ring_size; ++nn)
{
affine_transform_t transform =
affine_transform_make_translation(-rotation_center_x, -rotation_center_y);
if (ring_scale != 1.0)
{
transform =
affine_transform_multiply_by_value(affine_transform_make_scale(ring_scale), transform);
}
transform = affine_transform_multiply_by_value(
affine_transform_make_rotation(M_PI * 2 * nn / ring_size),
transform);
transform.tx += win_center_x;
transform.ty += win_center_y;
svg_scene.addSvgDocument(svg.get(), transform);
}
// Determine per-frame transform / animation.
auto per_frame_transform = [options, win_center_x, win_center_y](uint32_t frame_counter) {
double angle = options.fixed_rotation.used ? (options.fixed_rotation.value * M_PI / 180.)
: (frame_counter / 60.) * M_PI;
affine_transform_t affine =
affine_transform_make_rotation_xy(angle, win_center_x, win_center_y);
return spn_transform_t{
.sx = (float)affine.sx,
.shx = (float)affine.shx,
.tx = (float)affine.tx,
.shy = (float)affine.shy,
.sy = (float)affine.sy,
.ty = (float)affine.ty,
};
};
demo.setImageFactory(SvgSceneDemoImage::makeFactory(svg_scene, per_frame_transform));
demo.run();
return EXIT_SUCCESS;
}