214 lines
5.7 KiB
C++
214 lines
5.7 KiB
C++
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include "Color.h"
|
|
#include <cstdlib>
|
|
#include "Timer.h"
|
|
#include "DrawContext.h"
|
|
#include "Image.h"
|
|
#include "TimeSource.h"
|
|
#include "Timer.h"
|
|
#include "font_atlas.h"
|
|
#include "test_sprite.h"
|
|
|
|
#ifdef USE_FRAMEBUFFER
|
|
#include "FBDisplay.h"
|
|
#else
|
|
#include "SDLDisplay.h"
|
|
#endif
|
|
|
|
const int32_t width = 1024;
|
|
const int32_t height = 600;
|
|
|
|
static void PrintUsage(const char *program_name)
|
|
{
|
|
std::cout
|
|
<< "Usage: " << program_name << " [--fps 30|45|60]\n"
|
|
<< " " << program_name << " [--fps=30|45|60]\n";
|
|
}
|
|
|
|
struct ProgramOptions
|
|
{
|
|
uint32_t target_fps;
|
|
bool show_help;
|
|
|
|
ProgramOptions()
|
|
: target_fps(Core::Timer::DefaultFps),
|
|
show_help(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
static Platform::IDisplay* CreateDisplay()
|
|
{
|
|
#ifdef USE_FRAMEBUFFER
|
|
return new Platform::FBDisplay();
|
|
#else
|
|
return new Platform::SDLDisplay();
|
|
#endif
|
|
}
|
|
|
|
static void PrintUsage(const char* program_name)
|
|
{
|
|
std::cout
|
|
<< "Usage: " << program_name << " [--fps 30|45|60]\n"
|
|
<< " " << program_name << " [--fps=30|45|60]\n";
|
|
}
|
|
|
|
static ProgramOptions ParseProgramOptions(int argc, char* argv[])
|
|
{
|
|
ProgramOptions options;
|
|
|
|
for (int i = 1; i < argc; ++i)
|
|
{
|
|
const char* arg = argv[i];
|
|
const char* fps_value = nullptr;
|
|
|
|
if (std::strcmp(arg, "--help") == 0 || std::strcmp(arg, "-h") == 0)
|
|
{
|
|
options.show_help = true;
|
|
return options;
|
|
}
|
|
else if (std::strcmp(arg, "--fps") == 0)
|
|
{
|
|
if (i + 1 < argc)
|
|
{
|
|
fps_value = argv[++i];
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Missing value for --fps, falling back to 30. Supported: 30, 45, 60.\n";
|
|
}
|
|
}
|
|
else if (std::strncmp(arg, "--fps=", 6) == 0)
|
|
{
|
|
fps_value = arg + 6;
|
|
}
|
|
|
|
if (fps_value != nullptr)
|
|
{
|
|
const uint32_t parsed_fps = static_cast<uint32_t>(std::strtoul(fps_value, nullptr, 10));
|
|
if (Core::Timer::is_supported_fps(parsed_fps))
|
|
{
|
|
options.target_fps = parsed_fps;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Unsupported FPS '" << fps_value << "', falling back to 30. Supported: 30, 45, 60.\n";
|
|
options.target_fps = Core::Timer::DefaultFps;
|
|
}
|
|
}
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
static void SleepRemainingFrameTime(const Core::Timer& timer, const Platform::ITimeSource& time_source)
|
|
{
|
|
const uint32_t sleep_ms = timer.remaining_frame_ms(time_source.get_time_ms());
|
|
if (sleep_ms > 0u)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
const ProgramOptions options = ParseProgramOptions(argc, argv);
|
|
if (options.show_help)
|
|
{
|
|
PrintUsage(argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
Platform::IDisplay* display = CreateDisplay();
|
|
if (!display->init(DemoWidth, DemoHeight))
|
|
{
|
|
delete display;
|
|
return -1;
|
|
}
|
|
|
|
Core::DrawContext ctx(DemoWidth, DemoHeight);
|
|
Core::Timer timer(options.target_fps);
|
|
Platform::SteadyTimeSource time_source;
|
|
|
|
RenderData::BitmapFont font;
|
|
font.atlas = RenderData::Image(font_atlas_pixels, font_atlas_width, font_atlas_height);
|
|
font.char_w = font_char_w;
|
|
font.char_h = font_char_h;
|
|
font.columns = font_columns;
|
|
font.first_char = font_first_char;
|
|
|
|
RenderData::Image sprite_img(test_sprite_pixels, test_sprite_width, test_sprite_height, 0x00000000);
|
|
RenderData::SpriteRegion sprite_region(&sprite_img, 0, 0, sprite_img.width, sprite_img.height);
|
|
|
|
const std::array<uint16_t, 8 * 4> tileIds = {
|
|
0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile,
|
|
RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0,
|
|
0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile,
|
|
RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0, RenderData::Tilemap::EmptyTile, 0};
|
|
RenderData::Tilemap testTilemap(tileIds.data(), 8, 4, &sprite_img, sprite_img.width, sprite_img.height, 1);
|
|
|
|
const RenderData::Color clearColor(18, 18, 24, 255);
|
|
const RenderData::Color fpsColor(0, 255, 80, 255);
|
|
const RenderData::Color fpsBg(0, 0, 0, 200);
|
|
|
|
int32_t fps = 0;
|
|
int32_t frame_count = 0;
|
|
uint32_t last_fps_time = 0;
|
|
uint32_t animation_time_ms = 0;
|
|
char fps_text[32];
|
|
char perf_text[64];
|
|
uint32_t last_frame_ms = 0;
|
|
uint32_t last_present_ms = 0;
|
|
|
|
bool isRunning = true;
|
|
while (isRunning)
|
|
{
|
|
const uint32_t frame_start_ms = time_source.get_time_ms();
|
|
timer.begin_frame(frame_start_ms);
|
|
|
|
display->poll_events(isRunning);
|
|
|
|
ctx.clear_color(clearColor);
|
|
|
|
// sprite 测试
|
|
ctx.draw_sprite(10, 10, sprite_img);
|
|
ctx.draw_sprite_region_ex(30, 10, sprite_region, 2, false, false);
|
|
ctx.draw_sprite_region_ex(10, 30, sprite_region, 3, true, false);
|
|
ctx.draw_tilemap(testTilemap, 650, 500, 96, 48, static_cast<int32_t>(frame_start_ms / 20u) % 32, 0);
|
|
|
|
// FPS 计数
|
|
++frame_count;
|
|
const uint32_t now = time_source.get_time_ms();
|
|
if (now - last_fps_time >= 1000u)
|
|
{
|
|
fps = frame_count;
|
|
frame_count = 0;
|
|
last_fps_time = now;
|
|
}
|
|
|
|
std::snprintf(fps_text, sizeof(fps_text), "FPS: %d", fps);
|
|
ctx.draw_text(font, 4, 4, fpsColor, fpsBg, fps_text);
|
|
std::snprintf(perf_text, sizeof(perf_text), "Frame:%ums Present:%ums", last_frame_ms, last_present_ms);
|
|
ctx.draw_text(font, 4, 4 + font.char_h, fpsColor, fpsBg, perf_text);
|
|
|
|
const uint32_t present_start_ms = time_source.get_time_ms();
|
|
ctx.present(display);
|
|
last_present_ms = time_source.get_time_ms() - present_start_ms;
|
|
last_frame_ms = time_source.get_time_ms() - frame_start_ms;
|
|
SleepRemainingFrameTime(timer, time_source);
|
|
}
|
|
|
|
display->shutdown();
|
|
delete display;
|
|
return 0;
|
|
}
|