This commit is contained in:
HP 2026-06-07 18:32:00 +08:00
commit 08088c8413
9 changed files with 179 additions and 51 deletions

View File

@ -9,6 +9,7 @@ if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
endif() endif()
option(USE_FRAMEBUFFER "Use Linux framebuffer instead of SDL2" OFF) 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 set(CORE_SOURCES
src/Core/Asset/ObjLoader.cpp src/Core/Asset/ObjLoader.cpp
@ -58,6 +59,13 @@ target_include_directories(imx6u_core PUBLIC ${CORE_INCLUDE_DIRS})
if(USE_FRAMEBUFFER) if(USE_FRAMEBUFFER)
target_compile_definitions(imx6u_core PUBLIC 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() else()
if(WIN32) if(WIN32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8) if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@ -119,6 +127,15 @@ function(imx6u_configure_app_target target_name)
"${SDL2_DLL}" "${SDL2_DLL}"
"$<TARGET_FILE_DIR:${target_name}>" "$<TARGET_FILE_DIR:${target_name}>"
) )
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() endif()
if(MSVC) if(MSVC)

View File

@ -1,34 +1,51 @@
#include "DepthBuffer.h" #include "DepthBuffer.h"
#include <cstdint> #include <cstring>
#include <algorithm>
#include <cmath>
namespace Core namespace Core
{ {
void DepthBuffer::clear(const float depth) DepthBuffer::DepthBuffer(int32_t w, int32_t h)
: width(w), height(h), buffer(static_cast<size_t>(w) * h, 0xFFFFu)
{
}
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); std::fill(buffer.begin(), buffer.end(), depth);
} }
}
float DepthBuffer::get_depth(const int32_t x, const int32_t y) const uint16_t DepthBuffer::get_depth_unsafe(int32_t x, int32_t y) const
{
return buffer[static_cast<size_t>(y) * width + x];
}
void DepthBuffer::set_depth_unsafe(int32_t x, int32_t y, uint16_t depth)
{
buffer[static_cast<size_t>(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) 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. return get_depth_unsafe(x, y);
size_t index = static_cast<size_t>(y) * width + x;
return buffer.at(index);
} }
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) if (x < 0 || x >= width || y < 0 || y >= height)
{ {
return; return;
} }
// Row-major layout with y = 0 on the first row, matching a top-left screen origin. set_depth_unsafe(x, y, depth);
size_t index = static_cast<size_t>(y) * width + x;
buffer.at(index) = depth;
} }
} }

View File

@ -2,7 +2,6 @@
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
#include "Vector2.h" #include "Vector2.h"
#include <cmath>
#include <cstddef> #include <cstddef>
namespace Core namespace Core
@ -12,7 +11,7 @@ namespace Core
private: private:
int32_t width; int32_t width;
int32_t height; int32_t height;
std::vector<float> buffer; std::vector<uint16_t> buffer;
public: public:
int32_t get_width() const { return width; } int32_t get_width() const { return width; }
@ -23,16 +22,20 @@ namespace Core
void* get_buffer() const { return (void*)buffer.data(); } void* get_buffer() const { return (void*)buffer.data(); }
DepthBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector<float>(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 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);
void set_depth(const int32_t x, const int32_t y, const float depth);
};
}; };
}

View File

@ -6,7 +6,7 @@ namespace Core
{ {
void FrameBuffer::clear(const uint32_t color) 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));
} }
@ -16,8 +16,6 @@ namespace Core
{ {
return; return;
} }
// Row-major layout with y = 0 on the first row, matching a top-left screen origin. set_pixel_unsafe(x, y, color);
size_t index = static_cast<size_t>(y) * width + x;
buffer[index] = color;
} }
} }

View File

@ -7,12 +7,26 @@
namespace Core 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<uint16_t>(((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 class FrameBuffer
{ {
private: private:
int32_t width; int32_t width;
int32_t height; int32_t height;
std::vector<uint32_t> buffer; std::vector<FramePixel> buffer;
public: public:
int32_t get_width() const { return width; } int32_t get_width() const { return width; }
@ -23,14 +37,15 @@ namespace Core
void* get_buffer() const { return (void*)buffer.data(); } void* get_buffer() const { return (void*)buffer.data(); }
FrameBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector<uint32_t>(width * height, 0)) {} FrameBuffer(int32_t width, int32_t height)
: width(width), height(height), buffer(static_cast<size_t>(width) * height, 0) {}
void clear(const RenderData::Color& color) void clear(const RenderData::Color& color)
{ {
clear(color.to_rgba()); 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) 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); 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<size_t>(y) * width + x] = rgba_to_frame_pixel(color);
}
}; };
} }

View File

@ -143,7 +143,7 @@ namespace Core
if (has_key && pixel == key) continue; 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; return;
@ -164,7 +164,7 @@ namespace Core
if (has_key && pixel == key) continue; 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) 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(); 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<uint32_t*>(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 ch = font.char_h;
const int32_t atlas_w = font.atlas.width; const int32_t atlas_w = font.atlas.width;
const uint32_t* src = font.atlas.pixels; 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; int32_t cursor_x = x;
for (const char* p = text; *p; ++p) for (const char* p = text; *p; ++p)
@ -294,15 +308,29 @@ namespace Core
const int32_t src_x = col * cw; const int32_t src_x = col * cw;
const int32_t src_y = row * ch; 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 uint32_t* atlas_row = src + (src_y + sy) * atlas_w;
const int32_t dst_y_abs = y + sy; 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]; const uint32_t pixel = atlas_row[src_x + sx];
if ((pixel & 0xFF) == 0) continue; 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; cursor_x += cw;

View File

@ -119,6 +119,23 @@ namespace Platform
if (is_rgb565) if (is_rgb565)
{ {
#ifdef USE_RGB565_BACKBUFFER
// backbuffer 内部已是 RGB565直接行拷贝提交
const uint8_t* src_bytes = static_cast<const uint8_t*>(framebuffer->get_buffer());
if (static_cast<int>(finfo.line_length) == dst_width * 2)
{
std::memcpy(fb_mem, src_bytes, static_cast<size_t>(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) for (int y = 0; y < dst_height; ++y)
{ {
const uint32_t* src_row = src + y * src_width; const uint32_t* src_row = src + y * src_width;
@ -128,6 +145,7 @@ namespace Platform
dst_row[x] = rgba_to_rgb565(src_row[x]); dst_row[x] = rgba_to_rgb565(src_row[x]);
} }
} }
#endif
return; return;
} }

View File

@ -42,9 +42,16 @@ namespace Platform
return false; 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( texture = SDL_CreateTexture(
renderer, renderer,
SDL_PIXELFORMAT_RGBA8888, sdl_pixel_format,
SDL_TEXTUREACCESS_STREAMING, SDL_TEXTUREACCESS_STREAMING,
width, width,
height height
@ -60,7 +67,12 @@ namespace Platform
void SDLDisplay::present(const Core::FrameBuffer* framebuffer) 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_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, nullptr); SDL_RenderCopy(renderer, texture, nullptr, nullptr);
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);

View File

@ -6,6 +6,19 @@
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
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<uint16_t>(z * 65535.0f + 0.5f);
}
}
namespace Rasterizer namespace Rasterizer
{ {
void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color) void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color)
@ -42,13 +55,14 @@ namespace Rasterizer
if (depthBuffer) 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; continue;
} }
depthBuffer->set_depth(x, y, depth); depthBuffer->set_depth(x, y, depth_u16);
frameBuffer->set_pixel(x, y, color.to_rgba()); frameBuffer->set_pixel(x, y, color.to_rgba());
} }
else else