遗漏的部分

This commit is contained in:
qing committed 2026-08-02 21:21:03 +08:00
1 parent 84fb370da8
commit 0493383ee8
63 files changed
+28237 -4

No files matched your search

+79
View File
@@ -0,0 +1,79 @@
#include <cstdlib>
#include "lci/utf8.h"
#include "lci/ansi.h"
#include "lci/Drawable.h"
#include "lci/Area.h"
namespace qing {
/* FIXME: 区域类持有可绘制类的引用 */
Area::Area(Drawable *d, int w, int h, int x, int y, int rotate = 0, int fontsize=18)
:Drawable(w, h, x, y, rotate, fontsize) {
this->d = d;
}
/* 获取持有可绘制对象的颜色 */
Color Area::_get(int x, int y) {
return d->get_color(this->x + x, this->y + y);
}
/* 绘制到持有可绘制对象 */
void Area::_p(int x, int y, Color &clr) {
d->point(this->x + x, this->y + y, clr);
}
/* 持有可绘制对象区域移动 */
// void Area::_up(int x, int y, int w, int h, int level) {
// d->up(this->x + x, this->y + y, w, h, level);
// }
//
// void Area::_down(int x, int y, int w, int h, int level) {
// d->down(this->x + x, this->y + y, w, h, level);
// }
//
// void Area::_left(int x, int y, int w, int h, int level) {
// d->left(this->x + x, this->y + y, w, h, level);
// }
//
// void Area::_right(int x, int y, int w, int h, int level) {
// d->right(this->x + x, this->y + y, w, h, level);
// }
/* 将另一个可绘制对象刷新到这里 */
void Area::flush(Drawable *n, bool border) {
Rectangle size = n->get_size();
Rectangle pos = n->get_pos();
Color &clr = n->getFontsColor();
if (border) {
rectangle(pos.w-1, pos.h-1, size.w+1, size.h+1, clr);
}
/* FIXME: Area无需复制?如果是两块区域还是需要的 */
Area *p = dynamic_cast<Area*>(n);
if (p != nullptr) return;
// too slow
// 太慢了
for (int x = 0; x < size.w; ++x) {
for (int y = 0; y < size.h; ++y) {
Color color = n->get_color(x, y);
point(pos.w + x, pos.h + y, color);
}
}
}
/* FIXME: 边界检查应该加在父类方法中 不超出边界 */
/* FIXME: 将border写入成员中,如果有border连同它一起移动 */
/* FIXME: rotate=1时不能正常移动 */
void Area::move(int x, int y) {
// if (x >= 0)
// d->right(this->x, this->y, this->w+x, this->h, x);
// else if (x < 0)
// d->left(this->x+x, this->y, this->w-x, this->h, x);
// if (y >= 0)
// d->down(this->x+x, this->y, this->w, this->h+y, y);
// else if (y <0)
// d->up(this->x+x, this->y+y, this->w, this->h-y, y);
this->x += x;
this->y += y;
}
}
+219
View File
@@ -0,0 +1,219 @@
/*
* FIXME: 现在输入框无法触底
*/
#include <cstring>
#include <vector>
#include "lci/CmdArea.h"
#include "lci/utf8.h"
#include "lci/ansi.h"
constexpr static auto H = 2;
namespace qing {
/* FIXME(20260113): 将输入框做成大部分时候和输入行在同一行 */
CmdArea::CmdArea(Drawable *d, int w, int h, int x, int y, int rotate, int fontsize, bool hasBox)
: Area(d, w, h, x, y, rotate, fontsize), hasBox(hasBox) {
//auto w_std = w - 2 * x;
auto w_std = 5 * fontsize;
auto w_tar = 5 * fontsize;
if (hasBox) {
area = std::make_unique<InputBox>(this, (w_tar < w_std) ? w_tar : w_std, H*fontsize, x, fontsize*2, rotate, fontsize);
}
}
void CmdArea::Input(char c) {
std::unique_lock<std::mutex> lck(this->lock);
this->input += c;
}
void CmdArea::ClearInput() {
std::unique_lock<std::mutex> lck(this->lock);
this->input = "";
}
void CmdArea::update_input() {
std::unique_lock<std::mutex> lck(this->lock);
if (hasBox)
area->print((char*)this->input.c_str(), 0);
}
void CmdArea::clearBox() {
std::unique_lock<std::mutex> lck(this->lock);
auto clr = black;
if (hasBox){
auto size = area->get_size();
area->rectangle_fill(0, 0, size.w-1, size.h, clr);
}
}
void CmdArea::delete_input() {
std::unique_lock<std::mutex> lck(this->lock);
auto l = this->input.length();
this->input = this->input.substr(0, l - 1);
}
std::string CmdArea::get_input_and_clear() {
std::unique_lock<std::mutex> lck(this->lock);
auto cur = this->input;
this->input = "";
return cur;
}
void CmdArea::print(char *utf8, int d) {
auto color = black;
auto fontsize = FontSize_get();
auto half_f = fontsize / 2;
this->lock.lock();
auto subpos =(hasBox) ? area->get_pos() : Rectangle{0, 0};
auto subsize = (hasBox) ? area->get_size() : Rectangle{0, 0};
if (hasBox)
rectangle_fill(subpos.w-1, subpos.h-1, subsize.w+2, subsize.h+2, color);
char *ansi = utf8::to_ansi(utf8);
char *p = ansi;
Rectangle l = get_size();
while (1) {
//int size = ansi::size_gb18030((unsigned char*)p, 4);
/**
* 这个老的矢量字库应该是 GB2312 的
*/
int size = ansi::size_gb2312(*(unsigned char *)p);
if (*p == '\n') {
x_f = 0;
y_f += fontsize;
} else if (*p == '\0') {
break;
} else if (*p == '\t') {
int n = x_f / half_f;
int m = 8 - n % 8;
x_f += m * half_f;
} else if (*p == '\r') {
;
} else if (*p == '\033') {
p++;
if (*p != '[' && *p != ']') continue;
if (*p == ']') {
p++;
while (*p != '\007') {
p++;
}
}
else if (*p == '[') {
p++;
int top = 0, t2 = 0;
char buffer[32], buf2[16];
while((!(*p > 'a' && *p < 'z') && !(*p >'A' && *p < 'Z')) && (*p != '\0')) {
buffer[top++] = *(p++);
}
if (*p == '\0') break;
buffer[top++] = *p;
buffer[top] = '\0';
//printf("%s\n", buffer);
int i = 0;
while (i <= top) {
if (buffer[i] == ';') {
buf2[t2] = '\0';
handle_ansi_code(buf2);
buf2[0] = '\0';
t2 = 0;
} else {
buf2[t2++] = buffer[i];
}
i++;
}
buf2[t2] = '\0';
handle_ansi_code(buf2);
}
} else { // 正常的字
char c;
if (size == 1) {
c = *(p + 1);
*(p + 1) = '\0';
}
drawvfont(x_f, y_f, *(unsigned short*)p, clr);
if (size == 1) {
*(p + 1) = c;
}
x_f += (size == 1) ? fontsize / 2 : fontsize;
}
if (x_f >= l.w - fontsize) {
y_f += fontsize;
x_f = 0;
}
if (y_f >= l.h - (d + 3) * fontsize) { /* FIXME: d加上输入框高度 */
int level = (y_f % fontsize == 0) ? fontsize : y_f % fontsize;
y_f -= level;
up(0, 0, l.w, l.h - d * fontsize, level);
}
p += size;
}
free(ansi);
if (hasBox) {
subpos.w = ((x_f + fontsize + subsize.w) > l.w - fontsize) ? l.w - fontsize - subsize.w : x_f + fontsize;
subpos.h = y_f + fontsize;
area->set_pos(subpos);
this->lock.unlock();
this->update_input();
flush(area.get(), true);
}
}
void CmdArea::handle_ansi_code(char *buffer) {
static std::vector<std::string> v;
std::string cur = buffer;
char c = '\0';
v.push_back(cur);
if (!cur.empty() && std::isalpha((c = cur.back()))) {
for ( const auto& elem: v ) {
if (elem == "32m") {
Color clr = green;
set_color(clr);
} else if (elem == "34m") {
Color clr = blue;
set_color(clr);
} else if (elem == "0m" || elem == "00m") {
Color clr = white;
set_color(clr);
} else if (elem == "1m" || elem == "01m") {
if (v[0] == "40") {
;
}
if (v[1] == "33") {
//Color clr = yellow;
//set_color(clr);
}
}
std::cout << elem << " ";
}
v.clear();
std::cout << std::endl;
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#include "lci/Color.h"
namespace qing {
const Color black=Color(0,0,0);
const Color white=Color(255,255,255);
const Color green=Color(0,255,0);
const Color blue =Color(0,0,255);
const Color red =Color(255,0,0);
}
+102
View File
@@ -0,0 +1,102 @@
#include <unistd.h>
#include "lci/CommonThread.h"
namespace qing {
namespace lci{
void CommonThread::thread_main() {
while (chk() != SSHUT) {
/*进入回字形循环,如果不是进入关闭态
内层循环会一直进行。*/
if (chk() == SSTOP){
/*内层循环,静止状态*/
StopEvent();
}
if (chk() == SSTART) {
/*由静止状态退出时,如果进入开始状态
则进入单次的设置操作*/
WakeEvent();
}
if (chk() == SRUNNING) {
/*由设置阶段退出时,如果是运行状态,则发出提示*/
}
while (chk() == SRUNNING) {
//进入运行态
LoopEvent();
}
/*循环末尾,进行一次清理
由于之前可能是由静止态直接切换到关闭工作
在清理时需要检查成员变量是否是被设置过的*/
ClearEvent();
}
}//入口
CommonThread::CommonThread(std::string name) {
/*构造函数,完成数据域的设置。*/
this->label = name;
//创建线程的函数
this->thread = new std::thread(&CommonThread::thread_main, this);
}
//清除线程
//使线程自主退出的函数必须放到外面或者子类叶子上,
//否则会导致关闭时子类已经被释放,进而导致调用不了
CommonThread::~CommonThread(){
if (this->thread){
delete this->thread;//删除线程类
}
} //销毁线程
//-----------------------------------------------------------
//访问器函数
void CommonThread::SetLabel(std::string name) {
//对线程名字的设置
this->label = name;
}
std::string& CommonThread::GetLabel() {
//对线程名字的获取
return this->label;
}
//-----------------------------------------------------------
//等待线程启动完毕
//线程启动失败返回失败。线程启动成功或者现程结束返回成功。
bool CommonThread::WaitStart(unsigned long n) {
for(unsigned long i=0; i<n; i++){
enum Stat stat = this->chk();
if (stat == SSTOP) return false;
else if (stat == SRUNNING || stat == SSHUT) return true;
else if (stat == SSTART) usleep(1);
}//按次循环
return false;
}//等线程开始
//等线程关闭
void CommonThread::WaitClose() {
thread->join();
}
//-----------------------------------------------------------
void CommonThread::StopEvent(){
this->suspend();
}
void CommonThread::WakeEvent(){
this->run();
}
void CommonThread::LoopEvent(){
usleep(10000);
}
void CommonThread::ClearEvent() {
}
}//lci
}
+359
View File
@@ -0,0 +1,359 @@
#include <cmath>
#include "lci/Drawable.h"
namespace qing {
/* FIXME: 字体宽高分开传入 */
Drawable::Drawable(int w, int h, int x, int y, int rotate, int fontsize) /* 构造可绘制对象 */
: w(w), h(h), x(x), y(y), rotate(rotate), font_w(fontsize), font_h(fontsize) {
zn = zzhinumber; /* 初始化字库 */
vennum = ttunumber;
}
Rectangle Drawable::get_size() { /* 获取表示尺寸的矩形 */
if (rotate == 0 || rotate == 2) {
return Rectangle(w, h);
} else if (rotate == 1 || rotate == 3) {
return Rectangle(h, w);
}
throw Drawable::InvalidRotateMode();
}
Rectangle Drawable::get_pos() { /* 获取表示位置的矩形 */
return Rectangle(x, y);
}
void Drawable::set_pos(Rectangle rect) { /* 设置表示位置的矩形 */
x = rect.w;
y= rect.h;
}
Color Drawable::get_color(int x, int y) { /* 获取颜色 */
if (rotate == 0)
{
return _get(x, y);
}
else if (rotate == 1)
{
return _get(y, h - x - 1);
}
else if (rotate == 2)
{
return _get(w - x - 1, h - y - 1);
}
else if (rotate == 3)
{
return _get(w - y - 1 , x);
}
throw Drawable::InvalidRotateMode();
}
/* 设置颜色 */
void Drawable::set_color(Color &color) {
this->clr = color;
}
/* 设置字体大小 */
void Drawable::FontSize(int fontsize) {
font_w = fontsize;
font_h = fontsize;
}
/* 获取字体大小 */
int Drawable::FontSize_get() {
return font_w;
}
/* 获取字体颜色 */
Color &Drawable::getFontsColor() {
return this->clr;
}
void Drawable::point(int x, int y, Color &clr) { /* 绘制点 */
if (rotate == 0)
{
_p(x, y, clr);
}
else if (rotate == 1)
{
_p(y, h - x - 1, clr);
}
else if (rotate == 2)
{
_p(w - x - 1, h - y - 1, clr);
}
else if (rotate == 3) {
_p(w - y - 1 , x, clr);
}
}
void Drawable::line(int x1, int y1, int x2, int y2, Color &clr) /* 绘制线 */
{
double t, dt;
double i, j;
i = x1;
j = y1;
if (std::fabs(x2 - x1) > std::fabs(y2 - y1))
dt = 1 / (std::fabs(x2 - x1) * 2.0);
else
dt = 1 / (std::fabs(y2 - y1) * 2.0);
for (t = 0; t <= 1.0; t += dt) {
point((int)(i+0.5), (int)(j+0.5), clr);
i += dt * (x2 - x1);
j += dt * (y2 - y1);
}
}
void Drawable::rectangle(int x, int y, int w, int h, Color &clr) /* 绘制矩形 */
{
line(x, y, x+w, y, clr);
line(x+w, y, x+w, y+h, clr);
line(x+w, y+h, x, y+h, clr);
line(x, y+h, x, y, clr);
}
void Drawable::rectangle_fill(int x, int y, int w, int h, Color &clr) /* 填充矩形 */
{
for (int i = 0; i < h; ++i)
{
line(x, y+i, x+w, y+i, clr);
}
}
// void Drawable::up(int x, int y, int w, int h, int level) /* 画面上移 */
// {
// switch (rotate)
// {
// case 0: {
// _up(x, y, w, h, level);
// break;
// }
// case 1: {
// _left(y, x, h, w, level);
// break;
// }
// case 2: {
// _down(this->w - x - w, this->h - y - h, w, h, level);
// break;
// }
// case 3: {
// _right(this->w - y - h, x, h, w, level);
// break;
// }
// }
// }
//
// void Drawable::down(int x, int y, int w, int h, int level) /* 画面下移 */
// {
// switch (rotate)
// {
// case 0: {
// _down(x, y, w, h, level);
// break;
// }
// case 1: {
// _right(y, x, h, w, level);
// break;
// }
// case 2: {
// _up(this->w - x - w, this->h - y - h, w, h, level);
// break;
// }
// case 3: {
// _left(this->w - y - h, x, h, w, level);
// break;
// }
// }
// }
//
// void Drawable::left(int x, int y, int w, int h, int level) /* 画面左移 */
// {
// switch (rotate)
// {
// case 0: {
// _left(x, y, w, h, level);
// break;
// }
// case 1: {
// _down(y, x, h, w, level);
// break;
// }
// case 2: {
// _right(this->w - x - w, this-> h - y - h, w, h, level);
// break;
// }
// case 3: {
// _up(this->w - y - h, x, h, w, level);
// break;
// }
// }
// }
//
// void Drawable::right(int x, int y, int w, int h, int level) /* 画面右移 */
// {
// switch (rotate)
// {
// case 0: {
// _right(x, y, w, h, level);
// break;
// }
// case 1: {
// _up(y, x, h, w, level);
// break;
// }
// case 2: {
// _left(this->w - x - w, this->h - y - h, w, h, level);
// break;
// }
// case 3: {
// _down(this->w - y - h, x, h, w, level);
// break;
// }
// }
// }
/* FIXME: 单独放在一个文件里 */
void Drawable::test() { /* 绘制测试图案 */
Rectangle size = get_size();
Color base;
int k1 = -1;
for (int y=0; y < size.h; ++y) {
Color cur = base;
int k2 = -1;
for (int x=0; x < size.w; x++) {
if (cur.r == 0) k2 = 1;
else if (cur.r == 255) k2 = -1;
cur.r += k2;
cur.g += k2;
cur.b += k2;
point(x, y, cur);
}
if (base.r == 0) k1 = 1;
else if (base.r == 255) k1 = -1;
base.r += k1;
base.g += k1;
base.b += k1;
}
}
/* 移动测试 */
// void Drawable::test1()
// {
// Rectangle size = get_size();
// up(0, 0, size.w, size.h, 10);
// down(0, 0, size.w, size.h, 5);
// left(0, 0, size.w, size.h, 10);
// right(0, 0, size.w, size.h, 5);
// }
unsigned char *Drawable::drawvect(int x, int y, unsigned char *vect, int width, int high, Color &clr)
// x, y是左上角坐标,vect指向矢量图数据,width和high分别代表要绘制的图的宽和高(好像宽需要=高)
{
if (!vect) return nullptr;
int i = 0, j = 1; // 指向第一对数字
unsigned char ix, iy; // 从数组取出的第一对坐标放在这
double x1, y1;//, x2, y2; // 绘制直线的实数的坐标
if (high == 0) high = width; // 调节高宽
int ii1, jj1, ii2, jj2; // 最后算出的绘制直线的坐标
int isend = 0; //是否到达曲线尾
int isallend = 0; // 是否结束整个图像
do {
ix = vect[i];
iy = vect[j];
isend = ix & 1; // 奇数代表曲线尾
isallend = iy & 1; // 奇数代表整个图像结束
if (isallend) {
i += 2;
break; // 整个图像结束返回
}
if (isend) { // 如果这里曲线结束什么也不画进到下一条曲线
i += 2;
j += 2;
continue;
}
ix /= 2;
iy /= 2;
x1 = ix;
y1 = iy;
x1 = width * x1 / 128.0;
y1 = high * y1 / 128.0;
ii1 = (int)(x1 + x);
jj1 = (int)(y1 + y);
int k;
for (k = 0; k < 100000; k++) {
//x2 = x1;
//y2 = y1;
ii2 = ii1;
jj2 = jj1;
i += 2;
j += 2;
ix = vect[i];
iy = vect[j];
isend = ix & 1; //奇数代表曲线尾
isallend = iy & 1; //奇数代表整个图像结束
ix /= 2;
iy /= 2;
x1 = ix;
y1 = iy;
x1 = width * x1 / 128.0;
y1 = high * y1 / 128.0;
ii1 = (int)(x1 + x);
jj1 = (int)(y1 + y);
line(ii1, jj1, ii2, jj2, clr);
if (isend || isallend)
break;
}
i += 2; //指向下一条曲线
j += 2;
} while (!isallend);
return vect + i;
}
void Drawable::drawvfont(int x, int y, unsigned short z, Color &clr) {
int i, j, k, ii;
j = -1;
for (i = 0; i < zn; i++) {
if (chss[i] == z) {
j = i;
break;
}
}
if (j < 0) return;
k = 0;
for (i = 0; i < vennum; i++) {
if (k == j)
break;
for (ii =i; ii <vennum; ii++) {
if (vec[2*ii + 1] & 1) {
k++;
i = ii;
break;
}
}
}
if (k < j) return;
drawvect(x, y, vec + 2 * i, font_w, font_h, clr);
}
}
+289
View File
@@ -0,0 +1,289 @@
#include <iostream>
#include <cstring>
#include <cstdint>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "lci/FrameBuf.h"
#include "lci/Area.h"
namespace qing {
/* 这个构造函数太长,应该还可以再提炼一下 */
FrameBuf::FrameBuf(const char *path, int rotate, int fontsize)
: Drawable(0, 0, 0, 0, rotate, fontsize) {
//int open_dev(const char *path)
{
if (d != -1) {
throw std::runtime_error("Device already opening.");
}
if ((d = open(path, O_RDWR)) == -1) {
throw std::runtime_error("Error openning framebuffer device");
}
}
//void init_info()
{
if (ioctl(d, FBIOGET_VSCREENINFO, &var_info) < 0) {
close(d);
d=-1;
throw std::runtime_error("Error reading screen variable information");
}
if (ioctl(d, FBIOGET_FSCREENINFO, &fix_info) < 0) {
close(d);
d=-1;
throw std::runtime_error("Error reading fixed information");
}
}
std::cout << "res:\t\t" << var_info.xres << "," << var_info.yres << std::endl;
std::cout << "res_virtual:\t" << var_info.xres_virtual << "," << var_info.yres_virtual << std::endl;
std::cout << "bpp:\t\t" << var_info.bits_per_pixel << std::endl;
w = var_info.xres_virtual;
h = var_info.yres_virtual;
/*
* 不知道用不用虚拟尺寸,目前是用,在虚拟机上的效果不清楚。我在虚拟机上见过虚拟比实际小的。
*
* 而我的机器人上则是虚拟尺寸比物理尺寸大。
*/
//long v_size = (long)var_info.xres * var_info.yres * var_info.bits_per_pixel / 8;
long v_size = (long)var_info.xres_virtual * var_info.yres_virtual * var_info.bits_per_pixel / 8;
long logic_size = (long)fix_info.smem_len;
int delta = logic_size - v_size;
// 如果实长大于虚长,那么就证明这个缓冲区存在过量
double delta_x = (double)delta / var_info.xres;
double delta_y = (double)delta / var_info.yres;
double delta_bpp = (double)delta / var_info.bits_per_pixel;
std::cout << "virtual size:\t" << v_size << std::endl;
std::cout << "logic size:\t" << logic_size<< std::endl;
std::cout << "delta: \t" << delta<< std::endl;
std::cout << "delta x: \t" << delta_x<< std::endl;
std::cout << "delta y: \t" << delta_y<< std::endl;
std::cout << "delta bpp: \t" << delta_bpp<< std::endl;
if (delta == 0) {
// 如果实长和虚长是相等的,那么就证明这个缓冲区无过量
x_over = 0;
}
else if (delta > 0) {
// 是否存在小数差距
if (delta_y - (int)delta_y != 0) {
//close(d);
//d=-1;
//throw std::runtime_error("y_over");
x_over = delta_y * 8 / var_info.bits_per_pixel;
}
else {
// 计算横轴过量
x_over = delta_y * 8 / var_info.bits_per_pixel;
}
}
// 计算行跳量
line_jump = (var_info.xres_virtual + x_over) * var_info.bits_per_pixel / 8;
//
buf = (char*)mmap(buf, fix_info.smem_len, PROT_WRITE, MAP_SHARED, d, fix_info.smem_start);
if (buf == MAP_FAILED) {
close(d);
d=-1;
throw std::runtime_error("Failed mapping of the frame buffer to memory");
}
clear();
}
FrameBuf::~FrameBuf() { /* 析构函数 */
clear();
munmap(buf, fix_info.smem_len);
close(d);
}
/* 获取像素比特 */
int FrameBuf::get_bpp() { return var_info.bits_per_pixel; }
/* 32位像素比特的绘制点 */
void FrameBuf::p32(__u32 ux, __u32 uy, Color &color)
{
if (ux < 0 || ux >= var_info.xres_virtual
|| uy < 0 || uy >= var_info.yres_virtual)
return;
char *p = buf + (uy * line_jump + ux * 4);
*p++ = color.b;
*p++ = color.g;
*p++ = color.r;
}
/* 16位像素比特的绘制点 */
void FrameBuf::p16(__u32 ux, __u32 uy, Color &color) {
if (ux < 0 || ux >= var_info.xres_virtual
|| uy < 0 || uy >= var_info.yres_virtual)
return;
char *p = buf + (uy * line_jump + ux * 2);
uint16_t pixel_value = 0;
pixel_value |= (color.r >> 3) << 11; //5
pixel_value |= (color.g >> 2) << 5; //6
pixel_value |= (color.b >> 3); //5
*reinterpret_cast<uint16_t*>(p) = pixel_value;
}
/* 获取16像素比特时的像素颜色 */
Color FrameBuf::get16(__u32 ux, __u32 uy) {
char *p = buf + uy * line_jump + ux * get_bpp() / 8;
unsigned char r = (*(short*)p >> 11) & 0x1F;
unsigned char g = (*(short*)p >> 5) & 0x3F;
unsigned char b = (*(short*)p) & 0x1F;
return Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2));
}
/* 获取32位像素比特时的像素颜色 */
Color FrameBuf::get32(__u32 ux, __u32 uy) {
char *p = buf + uy * line_jump + ux * get_bpp() / 8;
unsigned char r = *(p++);
unsigned char g = *(p++);
unsigned char b = *(p++);
return Color(r, g, b);
}
/* 覆盖获取点方法 */
Color FrameBuf::_get(int x, int y) {
switch (get_bpp()) {
case 16:
return get16((__u32)x, (__u32)y);
case 32:
return get32((__u32)x, (__u32)y);
}
throw FrameBuf::UnknowBPP();
}
/* 覆盖绘制点方法 */
void FrameBuf::_p(int x, int y, Color &clr) {
int bpp = get_bpp();
if (bpp == 16) {
p16((__u32)x, (__u32)y, clr);
} else if (bpp == 32) {
p32((__u32)x, (__u32)y, clr);
}
}
/* 覆盖上移方法 */
// void FrameBuf::_up(int x, int y, int w, int h, int level){
// int l = get_bpp() / 8;
// int oneline = w * l;
// char *s = buf + y * line_jump + x * l;
// char *d = s + level * line_jump;
// for (int _ = 0; _ < h - level; ++_) {
// memcpy(s, d, oneline);
// s += line_jump;
// d += line_jump;
// }
// for (int _ = 0; _ < level; ++_) {
// memset(s, 0, oneline);
// s += line_jump;
// }
// }
//
// /* 覆盖下移方法 */
// void FrameBuf::_down(int x, int y, int w, int h, int level) {
// int l = get_bpp() / 8;
// int oneline = w * l;
// long d = level * line_jump;
// char *start = buf + y * line_jump + x * l;
// char *src = start + h * line_jump;
// char *dist = src - d;
//
// do {
//
// src -= line_jump;
// dist -= line_jump;
// memcpy(src, dist, oneline);
//
// } while(dist != start);
//
// do {
//
// src -= line_jump;
// memset(src, 0, oneline);
//
// } while (src != start);
//
// }
//
// /* 覆盖左移方法 */
// void FrameBuf::_left(int x, int y, int w, int h, int level) {
//
// int l = get_bpp() / 8;
// int d = level * l;
// int length = (w - level) * l;
// char *start = buf + y * line_jump + x * l;
// char *src = start + h * line_jump;
// do {
// src -= line_jump;
// memcpy(src, src + d, length);
// memset(src + length, 0, d);
// } while (src != start);
//
// }
//
// /* 覆盖右移方法 */
// /*!*/
// void FrameBuf::_right(int x, int y, int w, int h, int level) {
// int l = get_bpp() / 8;
// int d = level * l;
// int length = (w - level) * l;
// char *start = buf + y * line_jump + x * l;
// char *p = start + h * line_jump;
// char *tmp = new char[length];
// do {
// p -= line_jump;
// char *src = p + d;
// memcpy(tmp, p, length);
// memcpy(src, tmp, length);
// memset(p, 0, d);
// } while (p != start);
// delete[] tmp;
// }
/* 清理方法 */
void FrameBuf::clear() {
memset(buf, 0, fix_info.smem_len);
}
/* 将一个别的可绘制区域刷新到这个上面,也许可以绘制标头 */
void FrameBuf::flush(Drawable *n, bool border) {
Color &clr = n->getFontsColor();
Rectangle size = n->get_size();
Rectangle pos = n->get_pos();
if (border) {
rectangle(pos.w-1, pos.h-1, size.w+2-1, size.h+2-1, clr);
rectangle(pos.w-2, pos.h-2, size.w+4-1, size.h+4-1, clr);
rectangle_fill(pos.w-2, pos.h-_HEAD_LEN-2+1, size.w+4-1, _HEAD_LEN, clr);
}
Area *p = dynamic_cast<Area*>(n);
if (p != nullptr) return;
// too slow 太慢
for (int x = 0; x < size.w; ++x) {
for (int y = 0; y < size.h; ++y) {
Color color = n->get_color(x, y);
point(pos.w + x, pos.h + y, color);
}
}
}
}
+57
View File
@@ -0,0 +1,57 @@
#include "lci/utf8.h"
#include "lci/ansi.h"
#include "lci/InputBox.h"
namespace qing {
InputBox::InputBox(Drawable *d, int w, int h, int x, int y, int rotate = 0, int fontsize=18): Area(d, w, h, x, y, rotate, fontsize) {
this->d = d;
}
void InputBox::print(char *utf8, int _line) {
char *ansi = utf8::to_ansi(utf8);
char *p = ansi;
int fontsize = FontSize_get();
int x_f = 0, y_f = _line * fontsize;
Rectangle l = get_size();
while (1) {
//int size = ansi::size_gb18030((unsigned char*)p, 4);
/* 这个汉字库应该是 GB2312 的 */
int size = ansi::size_gb2312(*(unsigned char *)p);
if (*p == '\0') {
break;
} else { // 正常的字
char c;
if (size == 1) {
c = *(p + 1);
*(p + 1) = '\0';
}
drawvfont(x_f, y_f, *(unsigned short*)p, clr);
if (size == 1) {
*(p + 1) = c;
}
x_f += (size == 1) ? fontsize / 2 : fontsize;
}
if (x_f >= l.w - fontsize) {
break;
}
p += size;
}
free(ansi);
int y2 = y_f + fontsize-2;
line(x_f + 2, y2, x_f + fontsize - 4, y2, clr);
}
}
+99
View File
@@ -0,0 +1,99 @@
#include <iostream>
#include <cstring>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "lci/KeyboardEvent.h"
namespace qing {
/* FIXME: 划分代码块 */
KeyboardEvent::KeyboardEvent() {
const char *path = "/dev/input";
struct dirent *dir;
DIR *p = opendir(path);
if(p == NULL) throw std::runtime_error("opendir");
while ((dir = readdir(p)) != NULL) {
if (!is_event_with_number(dir->d_name)) continue;
char full_name[257] = {'\0'};
snprintf(full_name, sizeof(full_name), "%s/%s", path, dir->d_name);
struct stat buf;
if (stat(full_name, &buf) == -1) {
throw std::runtime_error("stat");
}
if(S_ISDIR(buf.st_mode)) continue;
else if (pool.find(std::string(full_name)) == pool.end()){
int d = open(full_name, O_RDONLY);
if (d == -1) {
;
} else {
set_noblocking(d);
pool.insert(std::make_pair(std::string(full_name), d));
}
}
}
closedir(p);
}
KeyboardEvent::~KeyboardEvent() {
std::map<std::string, int>::iterator it;
for (it = pool.begin(); it != pool.end(); ++it) {
close(it->second);
}
}
/* FIXME: 传入堵塞时间 */
struct input_event KeyboardEvent::get() {
fd_set set;
FD_ZERO(&set);
for (auto it = pool.begin(); it != pool.end(); ++it) {
FD_SET(it->second, &set);
}
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 60000;
if (select(this->maxd()+1, &set, NULL, NULL, &timeout) == -1) {
throw std::runtime_error("select");
}
for (auto it = pool.begin(); it != pool.end(); ++it) {
if (it->second != -1 && FD_ISSET(it->second, &set)) {
struct input_event ev;
if (read(it->second, &ev, sizeof(ev)) == sizeof(ev)) {
return ev;
}
}
}
throw NoEvent("");
}
bool KeyboardEvent::is_event_with_number(const char *filename) {
size_t len = strlen(filename);
if (len <= 5) return false;
if (strncmp(filename, "event", 5) != 0)
return false;
for (size_t i=5; i<len; ++i)
if (!isdigit(filename[i]))
return false;
/* 所有条件都满足 */
return true;
}
void KeyboardEvent::set_noblocking(int d) {
int sig = fcntl(d, F_GETFL, 0);
if (sig == -1) throw std::runtime_error("fcntl");
if (fcntl(d, F_SETFL, sig|O_NONBLOCK) == -1)
throw std::runtime_error("fcntl");
}
int KeyboardEvent::maxd() {
int maxValue = pool.begin()->second;
std::map<std::string, int>::iterator it;
for (it = pool.begin(); it != pool.end(); ++it)
if (it->second > maxValue)
maxValue = it->second;
return maxValue;
}
}
+22
View File
@@ -0,0 +1,22 @@
#include "lci/ansi.h"
namespace qing{
/* 这个函数并不完全
int ansi::size_gb18030(unsigned char *p, size_t maxLen) {
if (*p < 0x80) {
return 1;
} else if ((*p > 0x81 && *p <= 0xFE) &&
((*(p+1) >= 0x40 && *(p+1) <= 0x7E) || (*(p+1) >= 0x80 && *(p+1) <= 0xFE))) {
return 2;
} else if (maxLen >= 4 && (*p == 0x90 || *p == 0x91 || *p == 0xE8 || *p == 0xE9 ||
(*p >= 0x8E && *p <= 0xEF) || (*p >= 0xF8 && *p <= 0xFB)) &&
(*(p+1) & 0xC0) == 0x80 && (*(p+2) & 0xC0) == 0x80 && (*(p+3) & 0xC0) == 0x80) {
return 4; //注意这里是简化判断
} else {
throw ansi::WrongLength();
}
}
*/
int ansi::size_gb2312(unsigned char first_byte) {
return (first_byte >= 0xA1 && first_byte <= 0xFE) ? 2 : 1;
}
}
+45
View File
@@ -0,0 +1,45 @@
#include"lci/fsm.h"
namespace qing{
namespace lci{
Fsm::Fsm(){
this->stat = SSTOP;
}
//------------------------------------------------------
enum Stat Fsm::chk() {
std::unique_lock<std::mutex> lck(this->lock);
return this->stat;
}//check
void Fsm::suspend(){
std::unique_lock<std::mutex> lck(this->lock);
cv.wait(lck);
}//suspend
//-------------------------------------------------------
void Fsm::wake() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSTART;
cv.notify_one();
}//wake
void Fsm::stop() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSTOP;
cv.notify_one();
}//stop
void Fsm::shut() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSHUT;
cv.notify_one();
}//close
void Fsm::run() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SRUNNING;
cv.notify_one();
}//run
}//lci
}//qing
+20697
View File
File diff suppressed because it is too large. Load diff
+60
View File
@@ -0,0 +1,60 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iconv.h>
#include "lci/utf8.h"
namespace qing {
int utf8::size(const unsigned char c) {
if ((c >> 7) == 0){
// 0xxxxxxx
return 1;
} else if ((c >> 5) == 0b110) {
// 110xxxxx 10xxxxxx
return 2;
} else if ((c >> 4) == 0b1110) {
// 1110xxxx 10xxxxxx 10xxxxxx
return 3;
} else if ((c >> 3) == 0b11110) {
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
return 4;
} else {
return 0;
}
}
char *utf8::to_ansi(char *utf8String) {
//@20231120:楠舍科技刘仁
//@20240528,qing:输出字符串的长度不确定
iconv_t ic;
ic = iconv_open("GB18030", "UTF-8");
if (ic == (iconv_t)-1){
perror("iconv_open");
return NULL;
}
//设置输出字符串的大小为2倍输入
size_t InputLength = strlen(utf8String);
size_t OutputLength = InputLength * 2;
char *ansiString = (char*)malloc(OutputLength);
if (ansiString == NULL) {
perror("malloc");
iconv_close(ic);
return NULL;
}
char *InputBuffer = (char*)utf8String;
char *OutputBuffer = ansiString;
size_t result = iconv(ic, &InputBuffer, &InputLength, &OutputBuffer, &OutputLength);
if (result == (size_t)-1) {
perror("iconv");
free(ansiString);
iconv_close(ic);
return NULL;
}
iconv_close(ic);
*OutputBuffer = '\0';
return ansiString;
}
}
+85
View File
@@ -0,0 +1,85 @@
#include "nn/ILayer.hpp"
#include <stdexcept>
/* 获取函数对象 - 获取激活函数 */
f_t ILayer::getf(ActivationFunc type) {
switch (type) {
case ActivationFunc::ReLU:
return relu;
case ActivationFunc::Leaky_ReLU:
return leaky_relu;
case ActivationFunc::Sigmoid:
return sigmoid;
case ActivationFunc::Tanh:
return tanh;
default:
throw std::invalid_argument("ActivationFunc function does not support.");
}
}
/* 获取函数对象 - 求激活函数导数 */
f_t ILayer::getfdot(ActivationFunc type) {
switch (type) {
case ActivationFunc::ReLU:
return relu_derivative;
case ActivationFunc::Leaky_ReLU:
return leaky_relu_derivative;
case ActivationFunc::Sigmoid:
return sigmoid_derivative;
case ActivationFunc::Tanh:
return tanh_derivative;
default:
throw std::invalid_argument("ActivationFunc Derivative does not support.");
}
}
const char* ILayer::get_f_type(ActivationFunc& af) {
switch (af) {
case ActivationFunc::Sigmoid:
return "Sigmoid";
case ActivationFunc::ReLU:
return "ReLU";
case ActivationFunc::Leaky_ReLU:
return "Leaky_ReLU";
}
throw std::runtime_error("What activation function");
}
ILayer::ActivationFunc ILayer::parse_ff_type(const std::string& name) {
if(name == "Sigmoid") {
return ActivationFunc::Sigmoid;
} else if (name == "ReLU") {
return ActivationFunc::ReLU;
} else if (name == "Leaky_ReLU") {
return ActivationFunc::Leaky_ReLU;
} else {
throw std::runtime_error("What activation function");
}
}
/* ReLU 激活函数 */
float ILayer::relu(float x) { return (x > 0.0) ? x : 0.0; }
/* ReLU 导数 */
float ILayer::relu_derivative(float x) { return (x > 0) ? 1: 0; }
/*
* leaky ReLU 激活函数
*
* NOTE: alpha太小或者选择使用ReLU函数的情况下,会导致梯度消失
* NOTE: Leaky ReLU 不会使梯度完全消失,但也会使它变得很小
*/
float ILayer::leaky_relu(float x) { return (x > 0) ? x : LEAKY_RELU_ALPHA * x; }
/* leaky ReLU 导数 */
float ILayer::leaky_relu_derivative(float x) { return (x > 0) ? 1.0f : LEAKY_RELU_ALPHA; }
/* Sigmoid 激活函数 */
float ILayer::sigmoid(float x) { return 1.0 / (1.0 + std::exp(-x)); }
/* Sigmoid 导数 */
float ILayer::sigmoid_derivative(float x)
{
double s = sigmoid(x);
return s * (1.0 - s);
}
/* Tanh 激活函数 */
float ILayer::tanh(float x) { return std::tanh(x); }
/* Tanh 导数 */
float ILayer::tanh_derivative(float x) { return 1.0f - x * x; }
+144
View File
@@ -0,0 +1,144 @@
#include "nn/Matrix.hh"
namespace qing {
template <typename T>
Matrx<T>::Matrx(): row(0), col(0) {}
template <typename T>
Matrx<T>::Matrx(const long row, const long col)
: row(row), col(col) { /* 该构造函数用于从头开始构造 */
arr = std::vector<T>(col * row);
}
template<typename T>
Matrx<T>::Matrx(const long row, const long col, const std::vector<T>& src)
: row(row), col(col) { /* 该构造函数用于通过已有的vector进行构造 */
arr = src;
}
template<typename T>
const T& Matrx<T>::get(long row, long col) const { /* 访问器 - 访问矩阵元素 */
return arr[col * this->row + row];
}
template<typename T>
T& Matrx<T>::witch(long row, long col) { /* 访问器 - 访问矩阵元素 */
return arr[col * this->row + row];
}
template<typename T>
const std::vector<T>& Matrx<T>::get_data() const{ /* 访问器 - 用于获取原始数据 */
return arr;
}
template<typename T>
const long Matrx<T>::get_col() const { /* 获取列数 */
return col;
}
template<typename T>
const long Matrx<T>::get_row() const { /* 获取行数 */
return row;
}
template<typename T>
bool Matrx<T>::has_same_shape(const Matrx<T>& other) { /* 两个矩阵是否具有相同形状 */
return other.get_col() == this->get_col() || other.get_row() == this->get_row();
}
template<typename T>
bool Matrx<T>::has_dot_shape(const Matrx<T>& other) { /* 具有矩阵乘法的形状 */
return this->get_col() == other.get_row();
}
template<typename T>
void Matrx<T>::assert_same_shape(const Matrx<T>& other) { /* 断言两个矩阵具有相同形状 */
if (!has_same_shape(other))
throw std::invalid_argument("The shapes of matrix are not same.");
}
template<typename T>
void Matrx<T>::assert_dot_shape(const Matrx<T>& other) { /* 断言两个矩阵具有点积前置形状 */
if (!has_dot_shape(other)) {
throw std::invalid_argument("The shapes of matrix can not be dot.");
}
}
template<typename T>
Matrx<T> Matrx<T>::operator+(const Matrx<T>& other) { /* 加法操作符重定义 - 矩阵加法 */
assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
std::vector<T> v0 = this->get_data();
std::vector<T> vec = other.get_data();
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or ) {
*it += *it_or;
}
return Matrx<T>{this->get_row(), this->get_col(), v0};
}
template<typename T>
Matrx<T> Matrx<T>::operator+(T& item) { /* 矩阵加法广播 */
auto matrix = *this;
for (auto it = arr.begin(); it != arr.end(); ++it) {
*it += item;
}
return matrix;
}
template<typename T>
Matrx<T> Matrx<T>::operator-(const Matrx<T>& other) { /* 操作符重载 - 矩阵减法 */
assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
std::vector<T> v0 = this->get_data();
std::vector <T> vec = other.get_data();
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or ) {
*it -= *it_or;
}
return Matrx{this->get_row(), this->get_col(), v0};
}
template<typename T>
Matrx<T> Matrx<T>::operator-(T& item) { /* 操作符重载 - 矩阵减法广播 */
auto matrix = *this;
for (auto it = arr.begin(); it != arr.end(); ++it) {
*it -= item;
}
return matrix;
}
template<typename T>
const Matrx<T> Matrx<T>::operator*(const Matrx<T>& other) { /* 操作符重载 - 矩阵乘法 */
assert_dot_shape(other); /* 它们的形状是否能够点积 */
auto matrix = Matrx<T>(this->get_row(), other.get_col());
for (long i=0; i<this->get_row(); ++i) {/* 点积运算 */
for (long j=0; j<other.get_col(); ++j) {
T sum{0};
for (long k=0; k<this->get_col(); ++k) {
sum += this->get(i, k) * other.get(k, j);
}
matrix.witch(i, j) = sum;
}
}
return matrix;
}
template<typename T>
Matrx<T> Matrx<T>::operator*(T& item) { /* 操作符重载 - 矩阵乘法广播 */
std::vector<T> vec = this->get_data();
for (auto it = vec.begin(); it != vec.end(); ++it) {
*it *= item;
}
return Matrx<T>{this->get_row(), this->get_col(), vec};
}
template<typename T>
Matrx<T> Matrx<T>::operator/(T& item) { /* 操作符重载 - 矩阵除法广播 */
std::vector<T> vec = this->get_data();
for (auto it = vec.begin(); it != vec.end(); ++it) {
*it /= item;
}
return Matrx<T>(this->get_row(), this->get_col(), vec);
}
template class Matrx<float>; /* 显式实例化 */
template class Matrx<double>;
}
+396
View File
@@ -0,0 +1,396 @@
/*
* MatrixCuda.cu - MatrixCuda类的CUDA实现文件
*
* 这个文件包含MatrixCuda类的具体实现和CUDA内核的显式实例化
* 注意:这个文件需要使用nvcc编译器编译
*/
#include "nn/MatrixCuda.cuh"
#include "nn/Matrix.hh" // 用于与现有Matrx类交互
namespace qing {
// ============================================================================
// 显式实例化CUDA内核函数
// ============================================================================
namespace cuda_kernels {
// float类型内核显式实例化
template __global__ void matrix_add_kernel<float>(
const float* A, const float* B, float* C,
long rows, long cols, long total_elements);
template __global__ void matrix_sub_kernel<float>(
const float* A, const float* B, float* C,
long rows, long cols, long total_elements);
template __global__ void matrix_scalar_mul_kernel<float>(
const float* A, float scalar, float* C, long total_elements);
template __global__ void matrix_scalar_add_kernel<float>(
const float* A, float scalar, float* C, long total_elements);
template __global__ void matrix_transpose_kernel<float>(
const float* A, float* C, long rows, long cols);
template __global__ void matrix_hadamard_kernel<float>(
const float* A, const float* B, float* C, long total_elements);
template __global__ void matrix_copy_kernel<float>(
const float* src, float* dst, long total_elements);
// double类型内核显式实例化
template __global__ void matrix_add_kernel<double>(
const double* A, const double* B, double* C,
long rows, long cols, long total_elements);
template __global__ void matrix_sub_kernel<double>(
const double* A, const double* B, double* C,
long rows, long cols, long total_elements);
template __global__ void matrix_scalar_mul_kernel<double>(
const double* A, double scalar, double* C, long total_elements);
template __global__ void matrix_scalar_add_kernel<double>(
const double* A, double scalar, double* C, long total_elements);
template __global__ void matrix_transpose_kernel<double>(
const double* A, double* C, long rows, long cols);
template __global__ void matrix_hadamard_kernel<double>(
const double* A, const double* B, double* C, long total_elements);
template __global__ void matrix_copy_kernel<double>(
const double* src, double* dst, long total_elements);
} // namespace cuda_kernels
// ============================================================================
// MatrixCuda类与现有Matrx类的交互实现
// ============================================================================
// 从Matrx<float>转换到MatrixCuda<float>
template <>
CUDA_HOST MatrixCuda<float> MatrixCuda<float>::from_matrx(const Matrx<float>& mat) {
long rows = mat.get_row();
long cols = mat.get_col();
const std::vector<float>& data = mat.get_data();
return MatrixCuda<float>(rows, cols, data);
}
// 从MatrixCuda<float>转换到Matrx<float>
template <>
CUDA_HOST Matrx<float> MatrixCuda<float>::to_matrx() const {
sync_device_to_host();
return Matrx<float>(rows_, cols_, host_data_);
}
// 从Matrx<double>转换到MatrixCuda<double>
template <>
CUDA_HOST MatrixCuda<double> MatrixCuda<double>::from_matrx(const Matrx<double>& mat) {
long rows = mat.get_row();
long cols = mat.get_col();
const std::vector<double>& data = mat.get_data();
return MatrixCuda<double>(rows, cols, data);
}
// 从MatrixCuda<double>转换到Matrx<double>
template <>
CUDA_HOST Matrx<double> MatrixCuda<double>::to_matrx() const {
sync_device_to_host();
return Matrx<double>(rows_, cols_, host_data_);
}
// ============================================================================
// 优化的矩阵乘法实现(使用共享内存)
// ============================================================================
/**
* @brief 使用共享内存优化的矩阵乘法内核
*
* 这个内核使用平铺(tiling)技术来提高矩阵乘法的性能
* 每个线程块计算输出矩阵的一个子块
*/
template <typename T>
__global__ void matrix_mul_optimized_kernel(
const T* A, const T* B, T* C,
long A_rows, long A_cols, long B_cols,
long tile_size) {
// 共享内存声明
extern __shared__ T shared_mem[];
T* tile_A = shared_mem;
T* tile_B = &shared_mem[tile_size * tile_size];
// 线程块索引
long block_row = blockIdx.y;
long block_col = blockIdx.x;
// 线程索引
long thread_row = threadIdx.y;
long thread_col = threadIdx.x;
// 输出矩阵中的位置
long row = block_row * tile_size + thread_row;
long col = block_col * tile_size + thread_col;
T sum = T(0);
// 遍历所有平铺
for (long tile_idx = 0; tile_idx < (A_cols + tile_size - 1) / tile_size; ++tile_idx) {
// 从全局内存加载到共享内存
long A_col = tile_idx * tile_size + thread_col;
long B_row = tile_idx * tile_size + thread_row;
if (row < A_rows && A_col < A_cols) {
tile_A[thread_row * tile_size + thread_col] = A[row * A_cols + A_col];
} else {
tile_A[thread_row * tile_size + thread_col] = T(0);
}
if (B_row < A_cols && col < B_cols) {
tile_B[thread_row * tile_size + thread_col] = B[B_row * B_cols + col];
} else {
tile_B[thread_row * tile_size + thread_col] = T(0);
}
__syncthreads();
// 计算平铺内的点积
for (long k = 0; k < tile_size; ++k) {
sum += tile_A[thread_row * tile_size + k] * tile_B[k * tile_size + thread_col];
}
__syncthreads();
}
// 将结果写回全局内存
if (row < A_rows && col < B_cols) {
C[row * B_cols + col] = sum;
}
}
// ============================================================================
// 优化的矩阵乘法方法
// ============================================================================
/**
* @brief 使用优化CUDA内核的矩阵乘法
*
* 这个方法使用共享内存和平铺技术来加速矩阵乘法
* 对于大矩阵,性能比朴素算法有显著提升
*/
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::matmul_optimized(const MatrixCuda<T>& other) const {
assert_dot_shape(other);
MatrixCuda<T> result(rows_, other.cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 设置平铺大小(典型值为16或32)
const long tile_size = 16;
// 计算线程块和网格大小
dim3 block_size(tile_size, tile_size);
dim3 grid_size((other.cols_ + tile_size - 1) / tile_size,
(rows_ + tile_size - 1) / tile_size);
// 计算共享内存大小
size_t shared_mem_size = 2 * tile_size * tile_size * sizeof(T);
// 启动优化内核
matrix_mul_optimized_kernel<T><<<grid_size, block_size, shared_mem_size>>>(
device_data_, other.device_data_, result.device_data_,
rows_, cols_, other.cols_, tile_size
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
// 显式实例化优化矩阵乘法
template MatrixCuda<float> MatrixCuda<float>::matmul_optimized(const MatrixCuda<float>& other) const;
template MatrixCuda<double> MatrixCuda<double>::matmul_optimized(const MatrixCuda<double>& other) const;
// ============================================================================
// 使用cuBLAS的矩阵乘法(如果可用)
// ============================================================================
#ifdef HAVE_CUBLAS
#include <cublas_v2.h>
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::matmul_cublas(const MatrixCuda<T>& other) const {
assert_dot_shape(other);
MatrixCuda<T> result(rows_, other.cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 创建cuBLAS句柄
cublasHandle_t handle;
cublasCreate(&handle);
// 设置cuBLAS参数
T alpha = T(1);
T beta = T(0);
if constexpr (std::is_same_v<T, float>) {
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
other.cols_, rows_, cols_,
&alpha,
other.device_data_, other.cols_,
device_data_, cols_,
&beta,
result.device_data_, other.cols_);
} else if constexpr (std::is_same_v<T, double>) {
cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
other.cols_, rows_, cols_,
&alpha,
other.device_data_, other.cols_,
device_data_, cols_,
&beta,
result.device_data_, other.cols_);
}
// 销毁cuBLAS句柄
cublasDestroy(handle);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
// 显式实例化cuBLAS矩阵乘法
template MatrixCuda<float> MatrixCuda<float>::matmul_cublas(const MatrixCuda<float>& other) const;
template MatrixCuda<double> MatrixCuda<double>::matmul_cublas(const MatrixCuda<double>& other) const;
#endif // HAVE_CUBLAS
// ============================================================================
// 测试函数
// ============================================================================
/**
* @brief 测试MatrixCuda类的功能
*
* 这个函数用于验证MatrixCuda类的正确性
* 可以单独编译和运行进行测试
*/
template <typename T>
CUDA_HOST void test_matrix_cuda() {
std::cout << "Testing MatrixCuda<" << typeid(T).name() << ">..." << std::endl;
try {
// 测试1:创建矩阵
std::cout << "Test 1: Creating matrices..." << std::endl;
MatrixCuda<T> A = MatrixCuda<T>::zeros(3, 3);
MatrixCuda<T> B = MatrixCuda<T>::ones(3, 3);
MatrixCuda<T> C = MatrixCuda<T>::identity(3);
A.print("A (zeros)");
B.print("B (ones)");
C.print("C (identity)");
// 测试2:矩阵加法
std::cout << "\nTest 2: Matrix addition..." << std::endl;
MatrixCuda<T> D = A + B;
D.print("D = A + B");
// 测试3:标量运算
std::cout << "\nTest 3: Scalar operations..." << std::endl;
MatrixCuda<T> E = B * T(2.5);
E.print("E = B * 2.5");
// 测试4:矩阵转置
std::cout << "\nTest 4: Matrix transpose..." << std::endl;
MatrixCuda<T> F(2, 3);
F.fill(T(1));
F.print("F (2x3)");
MatrixCuda<T> FT = F.transpose();
FT.print("F^T (3x2)");
// 测试5:元素对应乘积
std::cout << "\nTest 5: Hadamard product..." << std::endl;
MatrixCuda<T> G = MatrixCuda<T>::random(2, 2, T(0), T(1));
MatrixCuda<T> H = MatrixCuda<T>::random(2, 2, T(0), T(1));
G.print("G");
H.print("H");
MatrixCuda<T> I = G.hadamard_product(H);
I.print("G ⊙ H");
// 测试6:矩阵乘法
std::cout << "\nTest 6: Matrix multiplication..." << std::endl;
MatrixCuda<T> J(2, 3);
MatrixCuda<T> K(3, 2);
J.fill(T(1));
K.fill(T(2));
J.print("J (2x3)");
K.print("K (3x2)");
MatrixCuda<T> L = J * K;
L.print("J * K (2x2)");
std::cout << "All tests passed!" << std::endl;
} catch (const std::exception& e) {
std::cerr << "Test failed: " << e.what() << std::endl;
}
}
// 显式实例化测试函数
template void test_matrix_cuda<float>();
template void test_matrix_cuda<double>();
} // namespace qing
// ============================================================================
// 主测试程序(可选)
// ============================================================================
#ifdef TEST_MATRIX_CUDA
int main() {
std::cout << "=== MatrixCuda Test Program ===" << std::endl;
// 初始化CUDA
cudaError_t err = cudaSetDevice(0);
if (err != cudaSuccess) {
std::cerr << "Failed to set CUDA device: " << cudaGetErrorString(err) << std::endl;
return 1;
}
// 测试float版本
qing::test_matrix_cuda<float>();
std::cout << "\n";
// 测试double版本
qing::test_matrix_cuda<double>();
// 重置CUDA设备
cudaDeviceReset();
std::cout << "\n=== Test Program Finished ===" << std::endl;
return 0;
}
#endif // TEST_MATRIX_CUDA
+48
View File
@@ -0,0 +1,48 @@
#include "nn/NNBuilder.hpp"
namespace qing {
void NNBuilder::add(NeuralNetwork &layer) { nn.push_back(layer); }
std::vector<std::pair<long, long>> NNBuilder::get_shape() const {
std::vector<std::pair<long, long>> shape;
for (long j = 0; j < nn.size(); ++j) {
shape.emplace_back(nn[j].get_inputno(), nn[j].get_outputno());
}
return shape;
}
void NNBuilder::print_shape() const {
auto shape = get_shape();
for (const auto& p : shape) {
std::cout << p.first << " -> " << p.second << std::endl;
}
}
/* 前向反馈 */
std::vector<float> NNBuilder::forward(std::vector<float>& r) {
auto x = r;
for (long j=0; j< nn.size(); ++j) {
x = nn[j].forward(x);
}
return x;
}
/* 反向传播 */
std::vector<float> NNBuilder::backward(std::vector<float>& errs, float discount) {
auto e = errs;
for (long j=nn.size()-1; j>=0;--j) {
e = nn[j].backward(e);
nn[j].update(discount);
}
return e;
}
/* 计算误差 */
std::vector<float> NNBuilder::cal_err(std::vector<float>& r, std::vector<float>& t) {
auto res = r;
for (long j=0; j<r.size(); ++j) {
res[j] = t[j] - r[j];
}
return res;
}
}
+231
View File
@@ -0,0 +1,231 @@
#include "nn/NeuralNetwork.hpp"
namespace qing{
/* 构造函数 使用基本参数构造 */
NeuralNetwork::NeuralNetwork(
const long inputno, const long outputno,
const std::vector<float> &weights,
const std::vector<float> &bias,
const float learning_rate,
const ActivationFunc func_type
): inputno(inputno), outputno(outputno),
weights(inputno, outputno, weights), bias( 1, outputno, bias),
learning_rate(learning_rate),
f_type(func_type) {
;/* 进行形状检查 */
}
/* 获取输入层大小 */
long NeuralNetwork::get_inputno() const {
return this->inputno;
}
/* 获取输出层大小 */
long NeuralNetwork::get_outputno() const {
return this->outputno;
}
/* 获取权重 */
Matrx<float>& NeuralNetwork::get_weights() {
return this->weights;
}
/* 获取偏置 */
Matrx<float>& NeuralNetwork::get_bias() {
return this->bias;
}
/* 正态分布Xavier初始化 */
void NeuralNetwork::xavierNormalInit(std::vector<float> &weights, long fan_in, long fan_out)
{
/* 高质量的随机数生成器 */
static std::mt19937 generator(std::random_device{}());
/* 计算标准差 */
float sigma = std::sqrt(2.0f / (fan_in + fan_out));
/* 创建正态分布对象,均值为0,标准差为sigma */
std::normal_distribution<float> distribution(0.0f, sigma);
/* 遍历权重矩阵,进行赋值 */
long k=0;
for (long i=0; i<fan_in; ++i)
for (long j=0; j<fan_out; ++j)
weights[k++] = distribution(generator);
}
/* 均匀分布Xavier初始化函数 */
void NeuralNetwork::xavierUniformInit(std::vector<float> &weights, int fan_in, int fan_out)
{
/* 使用高质量的随机数生成器 */
static std::mt19937 generator(std::random_device{}());
/* 计算区间范围:sqrt(2 / (fan_in + fan_out)) */
float scale = std::sqrt(2.0f / (fan_in + fan_out));
/* 创建均匀分布对象,范围为[-scale, scale] */
std::uniform_real_distribution<float> distribution(-scale, scale);
/* 遍历权重矩阵,进行赋值 */
long k=0;
for (long i=0; i<fan_in; ++i)
for (long j=0; j<fan_out; ++j)
weights[k++] = distribution(generator);
}
/* 工厂模式 通过形状构建全连接层 */
NeuralNetwork NeuralNetwork::Create_in_Factory(const long inputno, const long outputno, float learning_rate, ActivationFunc func_type)
{
std::vector<float> weights(inputno * outputno);
xavierUniformInit(weights, inputno, outputno);
std::vector<float> bias(outputno);
NeuralNetwork layer(inputno, outputno, weights, bias, learning_rate, func_type);
return layer; /* FIXME: 使用移动语义 */
}
/* 前向反馈 */
std::vector<float> NeuralNetwork::forward(const std::vector<float>& x)
{
/*
* 矩阵乘法(点积)
* wi => weights inputs(column)
* wo => weights outputs(row)
*
* NOTE: 通常矩阵乘法是三层循环,这里我们用两层
*/
//std::vector<float> res(this->outputno); /* (1, outputs) */
//for (long i=0; i<this->outputno; ++i) {
// long wo = i;
// float sum=0.0;
// for (long j=0, wi=wo; j<this->inputno; ++j, wi+=this->outputno) {
// sum += x[j] * this->weights[wi];
// }
// res[i]= sum;
//}
Matrx<float>mx{1, this->inputno, x};
/*
* 为输入进行备份
*
* FIXME: 使用移动语义传入参数,并且储存
*/
this->prev_inputs = mx; /* (1, inputs) */
Matrx<float>r = mx * this->weights; /* (1, outputs) */
/* 偏置操作 */
//for (long i=0; i<this->outputno; ++i) {
// res[i]+=this->bias[i];
//}
r = r + this->bias;
/*
* 为原始计算结果进行备份
*/
this->prev_raw_inputs = r;
/*
* 使用ReLU激活函数
*
* FIXME: 建立一个算法类
*/
f_t f = getf(f_type);
std::vector<float> vec =r.get_data();
for (long i=0; i<this->outputno; ++i) {
float& v = vec[i];
v = f(v);
}
r = Matrx<float>(1, outputno, vec);
/*
* 为输出进行备份
*/
this->prev_outputs = r;
/* 返回前向反馈的结果。 (1, outputs) */
return r.get_data();
}
/* 反向传播 */
std::vector<float> NeuralNetwork::backward(const std::vector<float>& errors) /* errors => (outputs, 1) */
{
/* 匹配激活导数 */
f_t fdot = getfdot(f_type);
std::vector<float> vec = this->prev_raw_inputs.get_data();
/* 将原始输出转化为导数 */
for (long i=0; i<this->outputno; ++i) {
float& v = vec[i];
v = errors[i] * fdot(v);
}
/* 误差乘以导数 */
//std::vector<float> first(this->outputno);
//for (long i=0; i<this->outputno; ++i) {
// first[i] = errors[i] * this->prev_raw_inputs[i];
//}
Matrx<float> first{1, this->outputno, vec};
/* 更新权重梯度 => (errors * 导数) @ this->inputs => (1, outputs ) @ (inputs, 1) = (outputs, inputs) */
//this->weights_grad = std::vector<float>(this->outputno * this->inputno);
//long k = 0;
//for (long i=0; i<this->inputno; ++i) {
// for (long j=0; j<this->outputno; ++j) {
// this->weights_grad[k++] = first[j] * this->prev_inputs[i];
// }
//}
Matrx<float> mx {this->inputno, 1, this->prev_inputs.get_data()}; /* (inputs, 1) */
this->weights_grad = mx * first; /* (inputs, 1) @ (1, outputs) => (inputs, outputs) */
/*
* 更新偏置梯度
*
* NOTE: 在多批次情况下,应该沿着每一样本纵向求平均数
*/
//this->bias_grad = std::vector<float>(this->outputno);
//for (long i=0; i<this->outputno; ++i) {
// this->bias_grad[i] = first[i];
//}
this->bias_grad = first;
/*
* 计算上一层的误差责任分担,然后返回
*
* this->weights @ first => (outputs, inputs) @ (1, outputs) = (inputs, 1)
*/
//std::vector<float> uplevel_errors(this->inputno);
//for (long i=0; i<this->inputno; ++i) {
// long wii = i * this->outputno; /* 每一列的起始点 */
// float sum = 0.0f;
// for (long j=0, wi=wii; j < this->outputno; ++j, ++wi) {
// sum += this->weights[wi] * first[j];
// }
// uplevel_errors[i] = sum;
//}
Matrx<float> first1{this->outputno, 1, first.get_data()};
Matrx<float> uplevel_errs = this->weights * first1; /* (inputs, outputs) @ (outputs, 1) */
return uplevel_errs.get_data();
/* 使用移动语义返回上一层误差 */
//return std::move(uplevel_errors);
}
void NeuralNetwork::update(float discount) {
this->weights = this->weights + (this->weights_grad * this->learning_rate * discount);
this->bias = this->bias + (this->bias_grad * this->learning_rate * discount);
}
}