diff --git a/CMakeLists.txt b/CMakeLists.txt index 58d4008..5a98c58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) endif() option(USE_FRAMEBUFFER "Use Linux framebuffer instead of SDL2" OFF) +option(USE_RGB565_BACKBUFFER "Use RGB565 internal backbuffer to eliminate present conversion on RGB565 fb0" OFF) set(CORE_SOURCES src/Core/Asset/ObjLoader.cpp @@ -58,6 +59,13 @@ target_include_directories(imx6u_core PUBLIC ${CORE_INCLUDE_DIRS}) if(USE_FRAMEBUFFER) target_compile_definitions(imx6u_core PUBLIC USE_FRAMEBUFFER) +endif() + +if(USE_RGB565_BACKBUFFER) + target_compile_definitions(imx6u_core PUBLIC USE_RGB565_BACKBUFFER) +endif() + +if(USE_FRAMEBUFFER) else() if(WIN32) if(CMAKE_SIZEOF_VOID_P EQUAL 8) @@ -106,6 +114,15 @@ function(imx6u_configure_app_target target_name) "${SDL2_DLL}" "$" ) + elseif(SDL2_image_FOUND) + add_executable(SpriteAssetTool ${SPRITE_ASSET_TOOL_SOURCES}) + target_include_directories(SpriteAssetTool PRIVATE + src/Core/Asset + src/Core/RenderData + ) + target_link_libraries(SpriteAssetTool PRIVATE SDL2::SDL2 SDL2_image::SDL2_image) + else() + message(STATUS "SpriteAssetTool disabled: SDL2_image was not found") endif() if(MSVC) diff --git a/src/Core/Core/DepthBuffer.cpp b/src/Core/Core/DepthBuffer.cpp index 4fea6ad..839ff28 100644 --- a/src/Core/Core/DepthBuffer.cpp +++ b/src/Core/Core/DepthBuffer.cpp @@ -1,34 +1,51 @@ #include "DepthBuffer.h" -#include -#include -#include +#include namespace Core { - void DepthBuffer::clear(const float depth) + DepthBuffer::DepthBuffer(int32_t w, int32_t h) + : width(w), height(h), buffer(static_cast(w) * h, 0xFFFFu) { - std::fill(buffer.begin(), buffer.end(), depth); } - float DepthBuffer::get_depth(const int32_t x, const int32_t y) const + void DepthBuffer::clear(uint16_t depth) + { + // 对 0x0000 或 0xFFFF 走 memset 快速路径 + if (depth == 0xFFFFu || depth == 0x0000u) + { + std::memset(buffer.data(), depth == 0xFFFFu ? 0xFF : 0x00, buffer.size() * sizeof(uint16_t)); + } + else + { + std::fill(buffer.begin(), buffer.end(), depth); + } + } + + uint16_t DepthBuffer::get_depth_unsafe(int32_t x, int32_t y) const + { + return buffer[static_cast(y) * width + x]; + } + + void DepthBuffer::set_depth_unsafe(int32_t x, int32_t y, uint16_t depth) + { + buffer[static_cast(y) * width + x] = depth; + } + + uint16_t DepthBuffer::get_depth(int32_t x, int32_t y) const { if (x < 0 || x >= width || y < 0 || y >= height) { - return INFINITY; + return 0xFFFFu; } - // Row-major layout with y = 0 on the first row, matching a top-left screen origin. - size_t index = static_cast(y) * width + x; - return buffer.at(index); + return get_depth_unsafe(x, y); } - void DepthBuffer::set_depth(const int32_t x, const int32_t y, const float depth) + void DepthBuffer::set_depth(int32_t x, int32_t y, uint16_t depth) { if (x < 0 || x >= width || y < 0 || y >= height) { return; } - // Row-major layout with y = 0 on the first row, matching a top-left screen origin. - size_t index = static_cast(y) * width + x; - buffer.at(index) = depth; + set_depth_unsafe(x, y, depth); } } diff --git a/src/Core/Core/DepthBuffer.h b/src/Core/Core/DepthBuffer.h index 711553e..a9ebeff 100644 --- a/src/Core/Core/DepthBuffer.h +++ b/src/Core/Core/DepthBuffer.h @@ -2,7 +2,6 @@ #include #include #include "Vector2.h" -#include #include namespace Core @@ -12,7 +11,7 @@ namespace Core private: int32_t width; int32_t height; - std::vector buffer; + std::vector buffer; public: int32_t get_width() const { return width; } @@ -23,16 +22,20 @@ namespace Core void* get_buffer() const { return (void*)buffer.data(); } - DepthBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector(width* height, INFINITY)) {} + DepthBuffer(int32_t width, int32_t height); - void clear(const float depth = INFINITY); + // 默认清为 0xFFFF(最远),语义保持"越小越近" + void clear(uint16_t depth = 0xFFFFu); - float get_depth(const Math::Vector2Int position) const { return get_depth(position.x, position.y); } + // 热路径快路径:无边界检查,调用方需确保坐标合法 + uint16_t get_depth_unsafe(int32_t x, int32_t y) const; + void set_depth_unsafe(int32_t x, int32_t y, uint16_t depth); - float get_depth(const int32_t x, const int32_t y) const; + // 安全版本:带边界检查,越界返回最远深度 + uint16_t get_depth(const Math::Vector2Int position) const { return get_depth(position.x, position.y); } + uint16_t get_depth(int32_t x, int32_t y) const; - void set_depth(const Math::Vector2Int position, const float depth) { set_depth(position.x, position.y, depth); } - - void set_depth(const int32_t x, const int32_t y, const float depth); + void set_depth(const Math::Vector2Int position, uint16_t depth) { set_depth(position.x, position.y, depth); } + void set_depth(int32_t x, int32_t y, uint16_t depth); }; -}; +} diff --git a/src/Core/Core/FrameBuffer.cpp b/src/Core/Core/FrameBuffer.cpp index 9b42cbc..48961c0 100644 --- a/src/Core/Core/FrameBuffer.cpp +++ b/src/Core/Core/FrameBuffer.cpp @@ -6,18 +6,16 @@ namespace Core { void FrameBuffer::clear(const uint32_t color) { - std::fill(buffer.begin(), buffer.end(), color); + std::fill(buffer.begin(), buffer.end(), rgba_to_frame_pixel(color)); } - void FrameBuffer::set_pixel(const int32_t x, const int32_t y, const uint32_t color) - { - if (x < 0 || x >= width || y < 0 || y >= height) - { - return; - } - // Row-major layout with y = 0 on the first row, matching a top-left screen origin. - size_t index = static_cast(y) * width + x; - buffer[index] = color; - } -} + void FrameBuffer::set_pixel(const int32_t x, const int32_t y, const uint32_t color) + { + if (x < 0 || x >= width || y < 0 || y >= height) + { + return; + } + set_pixel_unsafe(x, y, color); + } +} diff --git a/src/Core/Core/FrameBuffer.h b/src/Core/Core/FrameBuffer.h index 58d3694..1610755 100644 --- a/src/Core/Core/FrameBuffer.h +++ b/src/Core/Core/FrameBuffer.h @@ -7,12 +7,26 @@ namespace Core { +#ifdef USE_RGB565_BACKBUFFER + typedef uint16_t FramePixel; + inline uint16_t rgba_to_frame_pixel(uint32_t rgba) + { + const uint32_t r = (rgba >> 24) & 0xFFu; + const uint32_t g = (rgba >> 16) & 0xFFu; + const uint32_t b = (rgba >> 8) & 0xFFu; + return static_cast(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3)); + } +#else + typedef uint32_t FramePixel; + inline uint32_t rgba_to_frame_pixel(uint32_t rgba) { return rgba; } +#endif + class FrameBuffer { private: int32_t width; int32_t height; - std::vector buffer; + std::vector buffer; public: int32_t get_width() const { return width; } @@ -23,14 +37,15 @@ namespace Core void* get_buffer() const { return (void*)buffer.data(); } - FrameBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector(width * height, 0)) {} + FrameBuffer(int32_t width, int32_t height) + : width(width), height(height), buffer(static_cast(width) * height, 0) {} void clear(const RenderData::Color& color) { clear(color.to_rgba()); } - void clear(const uint32_t color); + void clear(uint32_t color); void set_pixel(const int32_t x, const int32_t y, const RenderData::Color& color) { @@ -43,5 +58,11 @@ namespace Core } void set_pixel(const int32_t x, const int32_t y, const uint32_t color); + + // 热路径快路径:无边界检查,调用方需确保坐标在 [0, width) x [0, height) 内 + void set_pixel_unsafe(int32_t x, int32_t y, uint32_t color) + { + buffer[static_cast(y) * width + x] = rgba_to_frame_pixel(color); + } }; } diff --git a/src/Core/Draw2D/DrawContext.cpp b/src/Core/Draw2D/DrawContext.cpp index 5511128..e8351eb 100644 --- a/src/Core/Draw2D/DrawContext.cpp +++ b/src/Core/Draw2D/DrawContext.cpp @@ -143,7 +143,7 @@ namespace Core if (has_key && pixel == key) continue; - frameBuffer->set_pixel(dst_x + sx, dst_y_abs, pixel); + frameBuffer->set_pixel_unsafe(dst_x + sx, dst_y_abs, pixel); } } return; @@ -164,7 +164,7 @@ namespace Core if (has_key && pixel == key) continue; - frameBuffer->set_pixel(dst_x + dx_abs, dst_y_abs, pixel); + frameBuffer->set_pixel_unsafe(dst_x + dx_abs, dst_y_abs, pixel); } } } @@ -263,11 +263,23 @@ namespace Core void DrawContext::fill_rect(int32_t x, int32_t y, int32_t w, int32_t h, const RenderData::Color& color) { const uint32_t rgba = color.to_rgba(); - for (int32_t row = y; row < y + h; ++row) + const int32_t fb_w = frameBuffer->get_width(); + const int32_t fb_h = frameBuffer->get_height(); + + int32_t x0 = std::max(0, x); + int32_t y0 = std::max(0, y); + int32_t x1 = std::min(fb_w, x + w); + int32_t y1 = std::min(fb_h, y + h); + + if (x0 >= x1 || y0 >= y1) return; + + uint32_t* buf = static_cast(frameBuffer->get_buffer()); + for (int32_t row = y0; row < y1; ++row) { - for (int32_t col = x; col < x + w; ++col) + uint32_t* dst = buf + row * fb_w + x0; + for (int32_t i = 0; i < x1 - x0; ++i) { - frameBuffer->set_pixel(col, row, rgba); + dst[i] = rgba; } } } @@ -282,6 +294,8 @@ namespace Core const int32_t ch = font.char_h; const int32_t atlas_w = font.atlas.width; const uint32_t* src = font.atlas.pixels; + const int32_t screen_w = frameBuffer->get_width(); + const int32_t screen_h = frameBuffer->get_height(); int32_t cursor_x = x; for (const char* p = text; *p; ++p) @@ -294,15 +308,29 @@ namespace Core const int32_t src_x = col * cw; const int32_t src_y = row * ch; - for (int32_t sy = 0; sy < ch; ++sy) + // 整字符在屏幕外则跳过 + if (cursor_x + cw <= 0 || cursor_x >= screen_w || y + ch <= 0 || y >= screen_h) + { + cursor_x += cw; + continue; + } + + int32_t sy_start = 0, sy_end = ch; + int32_t sx_start = 0, sx_end = cw; + if (y < 0) sy_start = -y; + if (y + ch > screen_h) sy_end = screen_h - y; + if (cursor_x < 0) sx_start = -cursor_x; + if (cursor_x + cw > screen_w) sx_end = screen_w - cursor_x; + + for (int32_t sy = sy_start; sy < sy_end; ++sy) { const uint32_t* atlas_row = src + (src_y + sy) * atlas_w; const int32_t dst_y_abs = y + sy; - for (int32_t sx = 0; sx < cw; ++sx) + for (int32_t sx = sx_start; sx < sx_end; ++sx) { const uint32_t pixel = atlas_row[src_x + sx]; if ((pixel & 0xFF) == 0) continue; - frameBuffer->set_pixel(cursor_x + sx, dst_y_abs, rgba); + frameBuffer->set_pixel_unsafe(cursor_x + sx, dst_y_abs, rgba); } } cursor_x += cw; diff --git a/src/Core/Platform/FBDisplay.cpp b/src/Core/Platform/FBDisplay.cpp index 5c0fc33..a168cc7 100644 --- a/src/Core/Platform/FBDisplay.cpp +++ b/src/Core/Platform/FBDisplay.cpp @@ -119,6 +119,23 @@ namespace Platform if (is_rgb565) { +#ifdef USE_RGB565_BACKBUFFER + // backbuffer 内部已是 RGB565,直接行拷贝提交 + const uint8_t* src_bytes = static_cast(framebuffer->get_buffer()); + if (static_cast(finfo.line_length) == dst_width * 2) + { + std::memcpy(fb_mem, src_bytes, static_cast(dst_height) * dst_width * 2); + } + else + { + for (int y = 0; y < dst_height; ++y) + { + std::memcpy(fb_mem + y * finfo.line_length, + src_bytes + y * src_width * 2, + dst_width * 2); + } + } +#else for (int y = 0; y < dst_height; ++y) { const uint32_t* src_row = src + y * src_width; @@ -128,6 +145,7 @@ namespace Platform dst_row[x] = rgba_to_rgb565(src_row[x]); } } +#endif return; } diff --git a/src/Core/Platform/SDLDisplay.cpp b/src/Core/Platform/SDLDisplay.cpp index a305f6b..665f371 100644 --- a/src/Core/Platform/SDLDisplay.cpp +++ b/src/Core/Platform/SDLDisplay.cpp @@ -42,9 +42,16 @@ namespace Platform return false; } +#ifdef USE_RGB565_BACKBUFFER + const Uint32 sdl_pixel_format = SDL_PIXELFORMAT_RGB565; + const int pixel_bytes = 2; +#else + const Uint32 sdl_pixel_format = SDL_PIXELFORMAT_RGBA8888; + const int pixel_bytes = 4; +#endif texture = SDL_CreateTexture( renderer, - SDL_PIXELFORMAT_RGBA8888, + sdl_pixel_format, SDL_TEXTUREACCESS_STREAMING, width, height @@ -60,7 +67,12 @@ namespace Platform void SDLDisplay::present(const Core::FrameBuffer* framebuffer) { - SDL_UpdateTexture(texture, nullptr, framebuffer->get_buffer(), width * sizeof(uint32_t)); +#ifdef USE_RGB565_BACKBUFFER + const int pixel_bytes = 2; +#else + const int pixel_bytes = 4; +#endif + SDL_UpdateTexture(texture, nullptr, framebuffer->get_buffer(), width * pixel_bytes); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, nullptr, nullptr); SDL_RenderPresent(renderer); diff --git a/src/Core/Rasterizer/TriangleRasterizer.cpp b/src/Core/Rasterizer/TriangleRasterizer.cpp index 7efc132..a478790 100644 --- a/src/Core/Rasterizer/TriangleRasterizer.cpp +++ b/src/Core/Rasterizer/TriangleRasterizer.cpp @@ -6,6 +6,19 @@ #include #include +namespace +{ + // 临时边界:将 float 深度映射到 uint16_t 定点深度。 + // 约定:0.0 ~ 1.0 映射到 0 ~ 65535,越小越近。 + // TODO: 等 TriangleRasterizer 重构为定点后删除此转换。 + inline uint16_t temp_float_depth_to_uint16(float z) + { + if (z <= 0.0f) return 0u; + if (z >= 1.0f) return 0xFFFFu; + return static_cast(z * 65535.0f + 0.5f); + } +} + namespace Rasterizer { void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color) @@ -42,13 +55,14 @@ namespace Rasterizer if (depthBuffer) { + const uint16_t depth_u16 = temp_float_depth_to_uint16(depth); // 深度越小离相机越近 - if (depthBuffer->get_depth(x, y) < depth) + if (depthBuffer->get_depth(x, y) < depth_u16) { continue; } - depthBuffer->set_depth(x, y, depth); + depthBuffer->set_depth(x, y, depth_u16); frameBuffer->set_pixel(x, y, color.to_rgba()); } else