引入 Gfx::DrawContext 统一绘制入口,封装FrameBuffer/DepthBuffer/Rasterizer/角形光栅化;main.cpp 改用 DrawContext;同步更新文档路径

This commit is contained in:
SepComet 2026-06-06 17:55:50 +08:00
parent a051f6da4c
commit 3e735e27b0
7 changed files with 174 additions and 59 deletions

View File

@ -12,6 +12,7 @@ set(SOURCES
src/Gfx/Core/DepthBuffer.cpp src/Gfx/Core/DepthBuffer.cpp
src/Gfx/Core/FrameBuffer.cpp src/Gfx/Core/FrameBuffer.cpp
src/Gfx/Core/Renderer.cpp src/Gfx/Core/Renderer.cpp
src/Gfx/Draw2D/DrawContext.cpp
src/Gfx/Rasterizer/Rasterizer.cpp src/Gfx/Rasterizer/Rasterizer.cpp
src/Gfx/Rasterizer/TriangleRasterizer.cpp src/Gfx/Rasterizer/TriangleRasterizer.cpp
src/Gfx/Scene/Camera.cpp src/Gfx/Scene/Camera.cpp
@ -30,6 +31,7 @@ target_include_directories(IMX6U-Game PRIVATE
src/Gfx/Platform src/Gfx/Platform
src/Gfx/Asset src/Gfx/Asset
src/Gfx/Core src/Gfx/Core
src/Gfx/Draw2D
src/Gfx/Math src/Gfx/Math
src/Gfx/Rasterizer src/Gfx/Rasterizer
src/Gfx/RenderData src/Gfx/RenderData

View File

@ -57,7 +57,7 @@ cd IMX6U-Game
仓库已自带 SDL2 开发库(`libs/Win/SDL2`),无需额外安装。 仓库已自带 SDL2 开发库(`libs/Win/SDL2`),无需额外安装。
```bash ```bash
cmake -B build-win . cmake -B build-win .m
cmake --build build-win --config Release cmake --build build-win --config Release
``` ```
@ -150,8 +150,6 @@ Framebuffer 对照版本可能需要 root 权限访问 `/dev/fb0`。SDL2 版本
## 目录结构 ## 目录结构
当前源码仍处于早期验证布局:
```text ```text
IMX6U-Game/ IMX6U-Game/
├─ cmake/ ├─ cmake/
@ -161,27 +159,32 @@ IMX6U-Game/
│ ├─ SDL2/ # Windows 用 SDL2 库(头文件 + lib + DLL │ ├─ SDL2/ # Windows 用 SDL2 库(头文件 + lib + DLL
│ └─ SDL_image/ # SDL2_image 库(头文件 + lib + DLL │ └─ SDL_image/ # SDL2_image 库(头文件 + lib + DLL
├─ src/ ├─ src/
│ ├─ Platform/ # 当前显示后端适配,后续归入 Gfx/Platform │ ├─ Gfx/ # 底层图形库:可复用、无具体游戏规则
│ ├─ Core/ # FrameBuffer、DepthBuffer 等,后续归入 Gfx/Core │ │ ├─ Draw2D/ # DrawContext 统一绘制入口
│ ├─ Math/ # 当前 float 数学类型,后续补定点数并归入 Gfx/Math │ │ ├─ Core/ # FrameBuffer、DepthBuffer
│ ├─ Rasterizer/ # 当前软光栅化代码,后续归入 Gfx/Draw2D 或 Gfx/Rasterizer │ │ ├─ Math/ # 向量、矩阵、数学工具
│ ├─ RenderData/ # Color、Triangle 等基础渲染数据 │ │ ├─ Rasterizer/ # 线段、三角形光栅化
│ ├─ Scene/ # 当前 3D demo 场景数据 │ │ ├─ RenderData/ # Color、Triangle 等数据结构
│ ├─ Asset/ # 资源加载预留 │ │ ├─ Scene/ # Camera、Transform、Mesh
│ ├─ Shading/ # 着色预留 │ │ ├─ Shading/ # 着色器(预留)
│ ├─ test_fb.cpp # 独立 fb 测试(最小示例) │ │ ├─ Platform/ # IDisplay、SDLDisplay、FBDisplay
│ └─ main.cpp # 当前 demo 入口,后续拆成 App 主循环 │ │ └─ Asset/ # ObjLoader 等资源加载
│ ├─ Apps/
│ │ └─ Demo/ # 当前 3D 立方体 demo 入口
│ └─ test_fb.cpp # 独立 fb 测试(最小示例)
├─ docs/ ├─ docs/
│ ├─ DEVELOPMENT_GUIDELINES.md # IMX6U 性能红线 │ ├─ DEVELOPMENT_GUIDELINES.md # IMX6U 性能红线
│ └─ APP_AND_GFX_ARCHITECTURE.md # 应用层与图形库分层 │ ├─ APP_AND_GFX_ARCHITECTURE.md # 应用层与图形库分层
│ └─ CONVENTIONS.md # 坐标系、矩阵、深度等数学约定
├─ CMakeLists.txt ├─ CMakeLists.txt
└─ README.md └─ README.md
``` ```
目标布局见 `docs/APP_AND_GFX_ARCHITECTURE.md`:后续会收敛为 `src/Gfx`、`src/Apps/Launcher`、`src/Apps/GameA`、`src/Apps/GameB`、`src/Shared`。
## 模块说明 ## 模块说明
### Draw2D
- **DrawContext**:统一绘制入口,封装 FrameBuffer、DepthBuffer、Rasterizer、TriangleRasterizer对外提供 `clear`、`draw_line`、`draw_triangle`、`present` 接口
### Core ### Core
- **FrameBuffer**CPU 侧颜色缓冲,渲染结果先写在这里 - **FrameBuffer**CPU 侧颜色缓冲,渲染结果先写在这里
- **DepthBuffer**:深度测试用 Z-buffer - **DepthBuffer**:深度测试用 Z-buffer
@ -204,6 +207,8 @@ IMX6U-Game/
**已完成:** **已完成:**
- 可旋转立方体的 3D 渲染MVP 变换、背面剔除、扫描线填充、深度测试) - 可旋转立方体的 3D 渲染MVP 变换、背面剔除、扫描线填充、深度测试)
- 双平台显示后端SDL2 / Framebuffer - 双平台显示后端SDL2 / Framebuffer
- Gfx 目录规范化,代码收敛到 `src/Gfx/`
- `Gfx::DrawContext` 统一绘制入口,封装现有绘制能力
- C++11 兼容代码 - C++11 兼容代码
- CMake 跨平台构建 - CMake 跨平台构建

View File

@ -10,15 +10,20 @@
IMX6U-Game/ IMX6U-Game/
├─ src/ ├─ src/
│ ├─ Gfx/ # 底层图形库:可复用、无具体游戏规则 │ ├─ Gfx/ # 底层图形库:可复用、无具体游戏规则
│ │ ├─ Core/ # FrameBuffer、DepthBuffer、RendererContext │ │ ├─ Draw2D/ # DrawContext 统一绘制入口(✅ 已实现)
│ │ ├─ Draw2D/ # rect、quad、line、sprite、tile 等基础绘制 │ │ ├─ Core/ # FrameBuffer、DepthBuffer✅ 已实现)
│ │ ├─ Math/ # 定点数、向量、矩阵、几何工具 │ │ ├─ Math/ # 向量、矩阵、数学工具(✅ 已实现)
│ │ ├─ RenderData/ # Color、Rect、Quad、Texture、Vertex 等数据结构 │ │ ├─ Rasterizer/ # 线段、三角形光栅化(✅ 已实现)
│ │ └─ Platform/ # SDL2 / fb0 / input / timer 的平台适配 │ │ ├─ RenderData/ # Color、Triangle 等数据结构(✅ 已实现)
│ │ ├─ Scene/ # Camera、Transform✅ 已实现)
│ │ ├─ Shading/ # 着色器(预留)
│ │ ├─ Platform/ # SDL2 / fb0 平台适配(✅ 已实现)
│ │ └─ Asset/ # 资源加载(✅ 已实现)
│ ├─ Apps/ │ ├─ Apps/
│ │ ├─ Launcher/ # 启动器应用 │ │ ├─ Demo/ # 当前 3D 立方体 demo✅ 已实现)
│ │ ├─ GameA/ # 第一个游戏 │ │ ├─ Launcher/ # 启动器应用(待实现)
│ │ └─ GameB/ # 第二个游戏 │ │ ├─ GameA/ # 第一个游戏(待实现)
│ │ └─ GameB/ # 第二个游戏(待实现)
│ └─ Shared/ # 可选:应用层共享但不属于 Gfx 的东西 │ └─ Shared/ # 可选:应用层共享但不属于 Gfx 的东西
│ ├─ Save/ # 存档格式、配置读写 │ ├─ Save/ # 存档格式、配置读写
│ ├─ UI/ # 启动器和游戏共用 UI 组件 │ ├─ UI/ # 启动器和游戏共用 UI 组件
@ -31,7 +36,9 @@ IMX6U-Game/
└─ docs/ └─ docs/
``` ```
当前项目已有 `Core/Math/Platform/Rasterizer/RenderData/Scene` 等目录,短期不必一次性搬迁;但新增代码应按上面的边界收敛。等功能稳定后,再把现有底层代码整体移动到 `src/Gfx/` ~~当前项目已有 `Core/Math/Platform/Rasterizer/RenderData/Scene` 等目录,短期不必一次性搬迁;但新增代码应按上面的边界收敛。等功能稳定后,再把现有底层代码整体移动到 `src/Gfx/`。~~
**已完成**:底层代码已整体迁移到 `src/Gfx/`,包括 Core、Math、Rasterizer、RenderData、Scene、Shading、Platform、Asset 和新增的 Draw2D。Demo 入口位于 `src/Apps/Demo/`
## 2. 四个层级的职责 ## 2. 四个层级的职责
@ -201,13 +208,14 @@ namespace Gfx
## 7. 推荐演进顺序 ## 7. 推荐演进顺序
1. 先抽出统一 `IApp``AppManager`,让当前 demo 成为一个 app。 1. ~~先抽出统一 `IApp``AppManager`,让当前 demo 成为一个 app。~~
2. 把 SDL2 初始化、输入、present 固定在平台层,应用层不直接碰 SDL。 2. ~~把 SDL2 初始化、输入、present 固定在平台层,应用层不直接碰 SDL。~~
3. 建立 `Gfx::DrawContext`,先封装 clear、pixel、line、rect、quad。 3. ~~建立 `Gfx::DrawContext`,先封装 clear、pixel、line、rect、quad。~~ **已完成**`Gfx::DrawContext` 封装了 clear、draw_line、draw_triangle、present
4. 新增 Launcher app只做最小菜单和应用切换。 4. ~~底层代码迁移到 `src/Gfx/`Demo 入口迁移到 `src/Apps/Demo/`。~~ **已完成**
5. 新增 GameA/GameB 空壳,验证三应用切换。 5. 新增 Launcher app只做最小菜单和应用切换。
6. 再逐步把现有 3D demo 或 2D 游戏逻辑迁入对应 Game 目录。 6. 新增 GameA/GameB 空壳,验证三应用切换。
7. 最后重构 CMake`imx6u_gfx` + 应用 target 拆分。 7. 再逐步把现有 3D demo 或 2D 游戏逻辑迁入对应 Game 目录。
8. 最后重构 CMake`imx6u_gfx` + 应用 target 拆分。
## 8. 性能注意事项 ## 8. 性能注意事项

View File

@ -9,7 +9,7 @@
- **先按 IMX6U 运行时成本设计,再按 PC 调试便利包装。** Windows/Linux SDL 版本只是验证与调试入口,不代表最终性能预算。 - **先按 IMX6U 运行时成本设计,再按 PC 调试便利包装。** Windows/Linux SDL 版本只是验证与调试入口,不代表最终性能预算。
- **热路径默认禁止隐式高成本操作。** 每帧、每对象、每顶点、每像素级代码必须避免动态分配、浮点、虚函数链、复杂 STL 算法和异常控制流。 - **热路径默认禁止隐式高成本操作。** 每帧、每对象、每顶点、每像素级代码必须避免动态分配、浮点、虚函数链、复杂 STL 算法和异常控制流。
- **核心逻辑保持 C++11 兼容。** 不引入需要新工具链或重型运行时支持的语言/库特性。 - **核心逻辑保持 C++11 兼容。** 不引入需要新工具链或重型运行时支持的语言/库特性。
- **优先复用已有类型与缓冲。** 新增抽象前先确认 `src/Core`、`src/Math`、`src/RenderData` 中是否已有可复用能力。 - **优先复用已有类型与缓冲。** 新增抽象前先确认 `src/Gfx/Core`、`src/Gfx/Math`、`src/Gfx/RenderData` 中是否已有可复用能力。
- **性能相关例外必须写明边界。** 如果必须违反本文红线,需要在代码附近注释说明原因、调用频率、数据规模和替代方案。 - **性能相关例外必须写明边界。** 如果必须违反本文红线,需要在代码附近注释说明原因、调用频率、数据规模和替代方案。
## 2. 数值计算规范 ## 2. 数值计算规范
@ -124,13 +124,13 @@
- [ ] 是否把平台/显示层 API 类型泄漏进核心逻辑? - [ ] 是否把平台/显示层 API 类型泄漏进核心逻辑?
- [ ] 是否能在 PC 调试版和 ARM release 版分别关闭调试开销? - [ ] 是否能在 PC 调试版和 ARM release 版分别关闭调试开销?
- [ ] 是否保留 C++11 兼容? - [ ] 是否保留 C++11 兼容?
- [ ] 是否需要同步更新 `src/CONVENTIONS.md` 中的坐标/矩阵/深度等约定? - [ ] 是否需要同步更新 `docs/CONVENTIONS.md` 中的坐标/矩阵/深度等约定?
## 9. 推荐的代码结构方向 ## 9. 推荐的代码结构方向
后续如果继续推进性能优化,优先建立这些基础设施: 后续如果继续推进性能优化,优先建立这些基础设施:
1. 统一定点数类型与转换工具,集中放在 `src/Math/`。 1. 统一定点数类型与转换工具,集中放在 `src/Gfx/Math/`。
2. 帧级临时缓冲/工作区,集中管理可复用数组和 scratch memory。 2. 帧级临时缓冲/工作区,集中管理可复用数组和 scratch memory。
3. 渲染数据的运行时紧凑格式,区分“加载期模型数据”和“运行时渲染数据”。 3. 渲染数据的运行时紧凑格式,区分“加载期模型数据”和“运行时渲染数据”。
4. ARM release 配置下的性能开关,关闭日志、调试绘制和昂贵检查。 4. ARM release 配置下的性能开关,关闭日志、调试绘制和昂贵检查。
@ -150,8 +150,8 @@
### 11.1 SDL2 边界 ### 11.1 SDL2 边界
- SDL2 类型和调用只允许出现在 `src/Platform/` 以及明确的平台适配层中。 - SDL2 类型和调用只允许出现在 `src/Gfx/Platform/` 以及明确的平台适配层中。
- `src/Core`、`src/Math`、`src/Rasterizer`、`src/RenderData`、`src/Scene` 不应直接包含 `SDL.h` - `src/Gfx/Core`、`src/Gfx/Math`、`src/Gfx/Rasterizer`、`src/Gfx/RenderData`、`src/Gfx/Scene`、`src/Gfx/Draw2D` 不应直接包含 `SDL.h`
- 游戏逻辑不直接处理 `SDL_Event`,应转换为项目自己的输入状态结构。 - 游戏逻辑不直接处理 `SDL_Event`,应转换为项目自己的输入状态结构。
- 时间源可以来自 SDL但核心逻辑使用整数 tick / fixed timestep不直接依赖 float 秒数。 - 时间源可以来自 SDL但核心逻辑使用整数 tick / fixed timestep不直接依赖 float 秒数。

View File

@ -7,14 +7,11 @@
#include "Matrix4x4.h" #include "Matrix4x4.h"
#include "MathUtil.h" #include "MathUtil.h"
#include "Color.h" #include "Color.h"
#include "FrameBuffer.h"
#include "Rasterizer.h"
#include "TriangleRasterizer.h"
#include "Triangle.h" #include "Triangle.h"
#include "Camera.h" #include "Camera.h"
#include <cstdlib> #include <cstdlib>
#include "Vertex.h" #include "Vertex.h"
#include "DepthBuffer.h" #include "DrawContext.h"
#include "Display.h" #include "Display.h"
#ifdef USE_FRAMEBUFFER #ifdef USE_FRAMEBUFFER
@ -117,10 +114,7 @@ int main(int argc, char *argv[])
return -1; return -1;
} }
Core::FrameBuffer *frameBuffer = new Core::FrameBuffer(width, height); Gfx::DrawContext ctx(width, height);
Core::DepthBuffer *depthBuffer = new Core::DepthBuffer(width, height);
Rasterizer::Rasterizer rasterizer(frameBuffer, depthBuffer);
Rasterizer::TriangleRasterizer triangleRasterizer(frameBuffer, depthBuffer);
Scene::Camera camera; Scene::Camera camera;
camera.transform.position = Math::Vector3(0.0f, 0.0f, 3.0f); camera.transform.position = Math::Vector3(0.0f, 0.0f, 3.0f);
@ -155,13 +149,12 @@ int main(int argc, char *argv[])
const RenderData::Color cubeColor(240, 240, 240, 255); const RenderData::Color cubeColor(240, 240, 240, 255);
const float aspectRatio = static_cast<float>(width) / static_cast<float>(height); const float aspectRatio = static_cast<float>(width) / static_cast<float>(height);
bool isRuning = true; bool isRunning = true;
while (isRuning) while (isRunning)
{ {
display->poll_events(isRuning); display->poll_events(isRunning);
frameBuffer->clear(clearColor); ctx.clear(clearColor);
depthBuffer->clear();
const float timeSeconds = static_cast<float>(display->get_time_ms()) * 0.001f; const float timeSeconds = static_cast<float>(display->get_time_ms()) * 0.001f;
const Math::Matrix4x4 model = const Math::Matrix4x4 model =
@ -204,10 +197,6 @@ int main(int argc, char *argv[])
continue; continue;
} }
const Math::Vector3 &viewV0 = viewSpaceVertices[cubeTriangle.vertices[0]];
const Math::Vector3 &viewV1 = viewSpaceVertices[cubeTriangle.vertices[1]];
const Math::Vector3 &viewV2 = viewSpaceVertices[cubeTriangle.vertices[2]];
drawTriangles[drawCommandCount++] = drawTriangles[drawCommandCount++] =
RenderData::Triangle( RenderData::Triangle(
Scene::Vertex(v0.screen), Scene::Vertex(v0.screen),
@ -217,7 +206,7 @@ int main(int argc, char *argv[])
for (size_t i = 0; i < drawCommandCount; ++i) for (size_t i = 0; i < drawCommandCount; ++i)
{ {
triangleRasterizer.DrawTriangle2D(drawTriangles[i], cubeColor); ctx.draw_triangle(drawTriangles[i], cubeColor);
} }
for (size_t faceIndex = 0; faceIndex < cubeFaces.size(); ++faceIndex) for (size_t faceIndex = 0; faceIndex < cubeFaces.size(); ++faceIndex)
@ -239,19 +228,17 @@ int main(int argc, char *argv[])
continue; continue;
} }
rasterizer.DrawLine( ctx.draw_line(
Math::Vector2(start.screen.x, start.screen.y).to_vector2Int(), Math::Vector2(start.screen.x, start.screen.y).to_vector2Int(),
Math::Vector2(end.screen.x, end.screen.y).to_vector2Int(), Math::Vector2(end.screen.x, end.screen.y).to_vector2Int(),
clearColor); clearColor);
} }
} }
display->present(frameBuffer); ctx.present(display);
} }
display->shutdown(); display->shutdown();
delete display; delete display;
delete frameBuffer;
delete depthBuffer;
return 0; return 0;
} }

View File

@ -0,0 +1,61 @@
#include "DrawContext.h"
#include "FrameBuffer.h"
#include "DepthBuffer.h"
#include "Rasterizer.h"
#include "TriangleRasterizer.h"
#include "Display.h"
namespace Gfx
{
DrawContext::DrawContext(int32_t width, int32_t height)
{
frameBuffer = new Core::FrameBuffer(width, height);
depthBuffer = new Core::DepthBuffer(width, height);
rasterizer = new Rasterizer::Rasterizer(frameBuffer, depthBuffer);
triangleRasterizer = new Rasterizer::TriangleRasterizer(frameBuffer, depthBuffer);
}
DrawContext::~DrawContext()
{
delete triangleRasterizer;
delete rasterizer;
delete depthBuffer;
delete frameBuffer;
}
int32_t DrawContext::get_width() const
{
return frameBuffer->get_width();
}
int32_t DrawContext::get_height() const
{
return frameBuffer->get_height();
}
void DrawContext::clear(const RenderData::Color& color)
{
frameBuffer->clear(color);
depthBuffer->clear();
}
void DrawContext::clear_depth()
{
depthBuffer->clear();
}
void DrawContext::draw_line(const Math::Vector2Int& from, const Math::Vector2Int& to, const RenderData::Color& color)
{
rasterizer->DrawLine(from, to, color);
}
void DrawContext::draw_triangle(const RenderData::Triangle& triangle, const RenderData::Color& color)
{
triangleRasterizer->DrawTriangle2D(triangle, color);
}
void DrawContext::present(Platform::IDisplay* display)
{
display->present(frameBuffer);
}
}

View File

@ -0,0 +1,52 @@
#pragma once
#include "Color.h"
#include "Vector2.h"
#include "Triangle.h"
#include <cstdint>
namespace Core
{
class FrameBuffer;
class DepthBuffer;
}
namespace Rasterizer
{
class Rasterizer;
class TriangleRasterizer;
}
namespace Platform
{
class IDisplay;
}
namespace Gfx
{
class DrawContext
{
private:
Core::FrameBuffer* frameBuffer;
Core::DepthBuffer* depthBuffer;
Rasterizer::Rasterizer* rasterizer;
Rasterizer::TriangleRasterizer* triangleRasterizer;
public:
DrawContext(int32_t width, int32_t height);
~DrawContext();
DrawContext(const DrawContext&) = delete;
DrawContext& operator=(const DrawContext&) = delete;
int32_t get_width() const;
int32_t get_height() const;
void clear(const RenderData::Color& color);
void clear_depth();
void draw_line(const Math::Vector2Int& from, const Math::Vector2Int& to, const RenderData::Color& color);
void draw_triangle(const RenderData::Triangle& triangle, const RenderData::Color& color);
void present(Platform::IDisplay* display);
};
}