commit
						3885c44938
					
				|  | @ -45,6 +45,7 @@ private: | |||
| }  // namespace
 | ||||
| 
 | ||||
| DebugActor::DebugActor() | ||||
|     : frame_buffer_(70 /* pre-alloc for 70 frames */) | ||||
| { | ||||
|     SetName("kiwano-debug-actor"); | ||||
|     SetPosition(Point{ 10, 10 }); | ||||
|  | @ -77,10 +78,10 @@ void DebugActor::OnRender(RenderContext& ctx) | |||
|     ctx.FillRoundedRectangle(GetBounds(), Vec2{ 5.f, 5.f }); | ||||
|     ctx.DrawTextLayout(debug_text_, Point(10, 10)); | ||||
| 
 | ||||
|     frame_time_.push_back(Time::Now()); | ||||
|     while (frame_time_.back() - frame_time_.front() >= Duration::Second) | ||||
|     frame_buffer_.PushBack(Time::Now()); | ||||
|     while (frame_buffer_.Back() - frame_buffer_.Front() >= Duration::Second) | ||||
|     { | ||||
|         frame_time_.erase(frame_time_.begin()); | ||||
|         frame_buffer_.PopFront(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -93,7 +94,7 @@ void DebugActor::OnUpdate(Duration dt) | |||
|     // For formatting integers with commas
 | ||||
|     (void)ss.imbue(comma_locale_); | ||||
| 
 | ||||
|     ss << "Fps: " << frame_time_.size() << std::endl; | ||||
|     ss << "Fps: " << frame_buffer_.Size() << std::endl; | ||||
| 
 | ||||
| #if defined(KGE_DEBUG) | ||||
|     if (ObjectBase::IsTracingLeaks()) | ||||
|  | @ -106,7 +107,7 @@ void DebugActor::OnUpdate(Duration dt) | |||
| 
 | ||||
|     ss << "Render: " << status.duration.Milliseconds() << "ms" << std::endl; | ||||
| 
 | ||||
|     ss << "Primitives / sec: " << std::fixed << status.primitives * frame_time_.size() << std::endl; | ||||
|     ss << "Primitives / sec: " << std::fixed << status.primitives * frame_buffer_.Size() << std::endl; | ||||
| 
 | ||||
|     ss << "Memory: "; | ||||
|     { | ||||
|  |  | |||
|  | @ -24,6 +24,99 @@ | |||
| 
 | ||||
| namespace kiwano | ||||
| { | ||||
| 
 | ||||
| template <typename T> | ||||
| class SimpleRingBuffer | ||||
| { | ||||
| public: | ||||
|     SimpleRingBuffer(size_t capcity) | ||||
|     { | ||||
|         Reserve(capcity); | ||||
|     } | ||||
| 
 | ||||
|     void PushBack(const T& val) | ||||
|     { | ||||
|         if (IsFull()) | ||||
|             Reserve(Capacity() * 2); | ||||
|         buffer_[rear_] = val; | ||||
|         IncreaseRear(); | ||||
|     } | ||||
| 
 | ||||
|     void PopFront() | ||||
|     { | ||||
|         IncreaseFront(); | ||||
|     } | ||||
| 
 | ||||
|     const T& Front() const | ||||
|     { | ||||
|         return buffer_[front_]; | ||||
|     } | ||||
| 
 | ||||
|     T& Front() | ||||
|     { | ||||
|         return buffer_[front_]; | ||||
|     } | ||||
| 
 | ||||
|     const T& Back() const | ||||
|     { | ||||
|         return buffer_[ClampCursor(rear_, 1)]; | ||||
|     } | ||||
| 
 | ||||
|     T& Back() | ||||
|     { | ||||
|         return buffer_[ClampCursor(rear_, 1)]; | ||||
|     } | ||||
| 
 | ||||
|     bool IsEmpty() const noexcept | ||||
|     { | ||||
|         return front_ = rear_; | ||||
|     } | ||||
| 
 | ||||
|     bool IsFull() const noexcept | ||||
|     { | ||||
|         return front_ == (rear_ + 1) % Capacity(); | ||||
|     } | ||||
| 
 | ||||
|     size_t Size() const | ||||
|     { | ||||
|         return ClampCursor(rear_, front_); | ||||
|     } | ||||
| 
 | ||||
|     size_t Capacity() const | ||||
|     { | ||||
|         return buffer_.size(); | ||||
|     } | ||||
| 
 | ||||
|     void Reserve(size_t capacity) | ||||
|     { | ||||
|         buffer_.resize(capacity); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     void IncreaseFront() | ||||
|     { | ||||
|         if (buffer_.empty()) | ||||
|             return; | ||||
|         front_ = (front_ + 1) % Capacity(); | ||||
|     } | ||||
| 
 | ||||
|     void IncreaseRear() | ||||
|     { | ||||
|         rear_ = (rear_ + 1) % Capacity(); | ||||
|     } | ||||
| 
 | ||||
|     size_t ClampCursor(size_t cursor, size_t off) const | ||||
|     { | ||||
|         return (cursor + Capacity() - off) % Capacity(); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     Vector<T> buffer_; | ||||
|     size_t    front_ = 0; | ||||
|     size_t    rear_  = 0; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * \addtogroup Actors | ||||
|  * @{ | ||||
|  | @ -52,7 +145,8 @@ private: | |||
|     BrushPtr    background_brush_; | ||||
|     TextStyle   debug_text_style_; | ||||
|     TextLayout  debug_text_; | ||||
|     List<Time>  frame_time_; | ||||
| 
 | ||||
|     SimpleRingBuffer<Time> frame_buffer_; | ||||
| }; | ||||
| 
 | ||||
| /** @} */ | ||||
|  |  | |||
|  | @ -82,8 +82,7 @@ void Runner::InitSettings() | |||
|     } | ||||
| 
 | ||||
|     // Create game window
 | ||||
|     WindowPtr window = Window::Create(settings_.title, settings_.width, settings_.height, settings_.icon, | ||||
|                                       settings_.resizable, settings_.fullscreen); | ||||
|     WindowPtr window = Window::Create(settings_.window); | ||||
|     SetWindow(window); | ||||
| 
 | ||||
|     // Update renderer settings
 | ||||
|  | @ -131,9 +130,9 @@ bool Runner::MainLoop(Duration dt) | |||
|         app.DispatchEvent(evt.Get()); | ||||
|     } | ||||
| 
 | ||||
|     // Update frame ticker
 | ||||
|     if (frame_ticker_) | ||||
|     { | ||||
|         // Update frame ticker
 | ||||
|         if (frame_ticker_->Tick(dt)) | ||||
|         { | ||||
|             app.UpdateFrame(frame_ticker_->GetDeltaTime()); | ||||
|  |  | |||
|  | @ -40,25 +40,14 @@ KGE_DECLARE_SMART_PTR(Runner); | |||
|  */ | ||||
| struct Settings | ||||
| { | ||||
|     uint32_t width;           ///< 窗口宽度
 | ||||
|     uint32_t height;          ///< 窗口高度
 | ||||
|     String   title;           ///< 窗口标题
 | ||||
|     Icon     icon;            ///< 窗口图标
 | ||||
|     bool     resizable;       ///< 窗口大小可调整
 | ||||
|     bool     fullscreen;      ///< 窗口全屏
 | ||||
|     Color    bg_color;        ///< 窗口背景色
 | ||||
|     WindowConfig window;          ///< ´°żÚÉčÖĂ
 | ||||
|     Color        bg_color;        ///< ąłž°ÉŤ
 | ||||
|     Duration     frame_interval;  ///< Ö¡¼ä¸ô
 | ||||
|     bool         vsync_enabled;   ///< ´¹Ö±Í¬²½
 | ||||
|     bool         debug_mode;      ///< µ÷ÊÔģʽ
 | ||||
| 
 | ||||
|     Settings() | ||||
|         : width(800) | ||||
|         , height(600) | ||||
|         , title("Kiwano") | ||||
|         , icon() | ||||
|         , resizable(false) | ||||
|         , fullscreen(false) | ||||
|         , bg_color(Color::Black) | ||||
|         : bg_color(Color::Black) | ||||
|         , frame_interval(0) | ||||
|         , vsync_enabled(true) | ||||
|         , debug_mode(false) | ||||
|  |  | |||
|  | @ -82,6 +82,21 @@ struct Icon | |||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * \~chinese | ||||
|  * @brief 窗口设置 | ||||
|  */ | ||||
| struct WindowConfig | ||||
| { | ||||
|     uint32_t width  = 640;            ///< 窗口宽度
 | ||||
|     uint32_t height = 480;            ///< 窗口高度
 | ||||
|     String   title  = "Kiwano Game";  ///< 窗口标题
 | ||||
|     Icon     icon;                    ///< 窗口图标
 | ||||
|     bool     resizable  = false;      ///< 窗口大小可调整
 | ||||
|     bool     fullscreen = false;      ///< 窗口全屏
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #if defined(KGE_PLATFORM_WINDOWS) | ||||
| typedef HWND WindowHandle; | ||||
| #endif | ||||
|  | @ -97,15 +112,10 @@ public: | |||
|     /**
 | ||||
|      * \~chinese | ||||
|      * @brief 初始化窗口 | ||||
|      * @param title 标题 | ||||
|      * @param width 宽度 | ||||
|      * @param height 高度 | ||||
|      * @param icon 图标资源ID | ||||
|      * @param resizable 窗口大小可拉伸 | ||||
|      * @param config 窗口设置 | ||||
|      * @throw kiwano::SystemError 窗口创建失败时抛出 | ||||
|      */ | ||||
|     static WindowPtr Create(const String& title, uint32_t width, uint32_t height, Icon icon = Icon(), | ||||
|                             bool resizable = false, bool fullscreen = false); | ||||
|     static WindowPtr Create(const WindowConfig& config); | ||||
| 
 | ||||
|     /**
 | ||||
|      * \~chinese | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ public: | |||
| 
 | ||||
|     virtual ~WindowWin32Impl(); | ||||
| 
 | ||||
|     void Init(const String& title, uint32_t width, uint32_t height, Icon icon, bool resizable, bool fullscreen); | ||||
|     void Init(const WindowConfig& config); | ||||
| 
 | ||||
|     void SetTitle(const String& title) override; | ||||
| 
 | ||||
|  | @ -88,13 +88,12 @@ private: | |||
|     std::array<KeyCode, 256> key_map_; | ||||
| }; | ||||
| 
 | ||||
| WindowPtr Window::Create(const String& title, uint32_t width, uint32_t height, Icon icon, bool resizable, | ||||
|                          bool fullscreen) | ||||
| WindowPtr Window::Create(const WindowConfig& config) | ||||
| { | ||||
|     WindowWin32ImplPtr ptr = memory::New<WindowWin32Impl>(); | ||||
|     if (ptr) | ||||
|     { | ||||
|         ptr->Init(title, width, height, icon, resizable, fullscreen); | ||||
|         ptr->Init(config); | ||||
|     } | ||||
|     return ptr; | ||||
| } | ||||
|  | @ -142,6 +141,28 @@ void AdjustWindow(uint32_t width, uint32_t height, DWORD style, uint32_t* win_wi | |||
|         *win_height = screenh; | ||||
| } | ||||
| 
 | ||||
| HICON Icon2HIcon(const Icon& icon) | ||||
| { | ||||
|     HICON hicon = NULL; | ||||
|     if (icon.resource_id != 0 && IS_INTRESOURCE(icon.resource_id)) | ||||
|     { | ||||
|         HINSTANCE hinstance = ::GetModuleHandle(nullptr); | ||||
| 
 | ||||
|         hicon = (HICON)::LoadImage(hinstance, MAKEINTRESOURCE(icon.resource_id), IMAGE_ICON, 0, 0, | ||||
|                                    LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         String full_path = FileSystem::GetInstance().GetFullPathForFile(icon.file_path); | ||||
|         if (!full_path.empty()) | ||||
|         { | ||||
|             hicon = (HICON)::LoadImageA(NULL, full_path.c_str(), IMAGE_ICON, 0, 0, | ||||
|                                         LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE); | ||||
|         } | ||||
|     } | ||||
|     return hicon; | ||||
| } | ||||
| 
 | ||||
| }  // namespace
 | ||||
| 
 | ||||
| WindowWin32Impl::WindowWin32Impl() | ||||
|  | @ -206,8 +227,7 @@ WindowWin32Impl::~WindowWin32Impl() | |||
|     ::timeEndPeriod(0); | ||||
| } | ||||
| 
 | ||||
| void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, Icon icon, bool resizable, | ||||
|                            bool fullscreen) | ||||
| void WindowWin32Impl::Init(const WindowConfig& config) | ||||
| { | ||||
|     HINSTANCE   hinst  = GetModuleHandle(nullptr); | ||||
|     WNDCLASSEXA wcex   = { 0 }; | ||||
|  | @ -215,7 +235,7 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, | |||
|     wcex.lpszClassName = "KiwanoAppWnd"; | ||||
|     wcex.style         = CS_HREDRAW | CS_VREDRAW /* | CS_DBLCLKS */; | ||||
|     wcex.lpfnWndProc   = WindowWin32Impl::StaticWndProc; | ||||
|     wcex.hIcon         = nullptr; | ||||
|     wcex.hIcon         = Icon2HIcon(config.icon); | ||||
|     wcex.cbClsExtra    = 0; | ||||
|     wcex.cbWndExtra    = sizeof(LONG_PTR); | ||||
|     wcex.hInstance     = hinst; | ||||
|  | @ -223,21 +243,6 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, | |||
|     wcex.lpszMenuName  = nullptr; | ||||
|     wcex.hCursor       = ::LoadCursor(hinst, IDC_ARROW); | ||||
| 
 | ||||
|     if (icon.resource_id != 0 && IS_INTRESOURCE(icon.resource_id)) | ||||
|     { | ||||
|         wcex.hIcon = (HICON)::LoadImage(hinst, MAKEINTRESOURCE(icon.resource_id), IMAGE_ICON, 0, 0, | ||||
|                                         LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         String full_path = FileSystem::GetInstance().GetFullPathForFile(icon.file_path); | ||||
|         if (!full_path.empty()) | ||||
|         { | ||||
|             wcex.hIcon = (HICON)::LoadImageA(NULL, full_path.c_str(), IMAGE_ICON, 0, 0, | ||||
|                                              LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ::RegisterClassExA(&wcex); | ||||
| 
 | ||||
|     // Get the nearest monitor to this window
 | ||||
|  | @ -256,21 +261,19 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, | |||
|     uint32_t screenh = monitor_info_ex.rcWork.bottom - monitor_info_ex.rcWork.top; | ||||
| 
 | ||||
|     uint32_t win_width, win_height; | ||||
|     AdjustWindow(width, height, GetStyle(), &win_width, &win_height); | ||||
|     AdjustWindow(config.width, config.height, GetStyle(), &win_width, &win_height); | ||||
| 
 | ||||
|     int left = monitor_info_ex.rcWork.left + (screenw - win_width) / 2; | ||||
|     int top  = monitor_info_ex.rcWork.top + (screenh - win_height) / 2; | ||||
|     width  = win_width; | ||||
|     height = win_height; | ||||
| 
 | ||||
|     width_         = width; | ||||
|     height_        = height; | ||||
|     resizable_     = resizable; | ||||
|     is_fullscreen_ = fullscreen; | ||||
|     width_         = win_width; | ||||
|     height_        = win_height; | ||||
|     resizable_     = config.resizable; | ||||
|     is_fullscreen_ = config.fullscreen; | ||||
|     resolution_    = Resolution{ width_, height_, 0 }; | ||||
| 
 | ||||
|     handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", title.c_str(), GetStyle(), left, top, width, height, nullptr, | ||||
|                                 nullptr, hinst, nullptr); | ||||
|     handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", config.title.c_str(), GetStyle(), left, top, width_, height_, | ||||
|                                 nullptr, nullptr, hinst, nullptr); | ||||
| 
 | ||||
|     if (handle_ == nullptr) | ||||
|     { | ||||
|  | @ -321,24 +324,7 @@ void WindowWin32Impl::SetIcon(Icon icon) | |||
| { | ||||
|     KGE_ASSERT(handle_); | ||||
| 
 | ||||
|     HICON hicon = NULL; | ||||
|     if (icon.resource_id != 0 && IS_INTRESOURCE(icon.resource_id)) | ||||
|     { | ||||
|         HINSTANCE hinstance = ::GetModuleHandle(nullptr); | ||||
| 
 | ||||
|         hicon = (HICON)::LoadImage(hinstance, MAKEINTRESOURCE(icon.resource_id), IMAGE_ICON, 0, 0, | ||||
|                                    LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         String full_path = FileSystem::GetInstance().GetFullPathForFile(icon.file_path); | ||||
|         if (!full_path.empty()) | ||||
|         { | ||||
|             hicon = (HICON)::LoadImageA(NULL, full_path.c_str(), IMAGE_ICON, 0, 0, | ||||
|                                         LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     HICON hicon = Icon2HIcon(icon); | ||||
|     ::SendMessage(handle_, WM_SETICON, ICON_BIG, (LPARAM)hicon); | ||||
|     ::SendMessage(handle_, WM_SETICON, ICON_SMALL, (LPARAM)hicon); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue