使用共享内存循环缓冲区在进程之间交换数据。可以使用boost interprocess实现为C++ dll,然后将该dll导入到您的.Net应用程序中。请注意,您需要构建32位和64位版本的boost和共享内存dll。我准备了一个32位和64位应用程序的示例,您可以运行并查看其速度有多快。我认为它应该足够快,但如果不够快,则仍然可以使用多线程。
64位生产者:
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <cstring>
#include <cstdlib>
#include <string>
#include <iostream>
#include <chrono>
struct shmem_info
{
boost::interprocess::interprocess_mutex mutex;
uint64_t pos;
bool run;
};
int main(int argc, char *argv[])
{
using namespace boost::interprocess;
struct shm_remove
{
shm_remove() { shared_memory_object::remove("MySharedMemory"); shared_memory_object::remove("MySharedMemoryInfo");}
} remover;
const size_t width = 1920;
const size_t height = 1080;
const size_t bytes_per_pixel = 3;
const size_t frame_size = width*height*bytes_per_pixel;
const size_t frames = 60;
const size_t shmem_frames = 3 * frames;
const size_t shmem_size = width * height * bytes_per_pixel * shmem_frames;
std::cout << "Generating data ..." << std::endl;
std::vector<uint8_t> frame(frame_size);
for (size_t x = 0; x < width*height; ++x)
for (size_t y = 0; y < bytes_per_pixel; ++y)
frame[x*bytes_per_pixel + y] = (x%252) + y;
std::cout << "Creating shared memory files ..." << std::endl;
shared_memory_object shm(create_only, "MySharedMemory", read_write);
shared_memory_object shm_info(create_only, "MySharedMemoryInfo", read_write);
shm.truncate(shmem_size);
shm_info.truncate(sizeof(shmem_info));
mapped_region region(shm, read_write);
mapped_region region_info(shm_info, read_write);
shmem_info *info = new (region_info.get_address()) shmem_info;
{
scoped_lock<interprocess_mutex> lock(info->mutex);
info->pos = 0;
info->run = true;
}
char c;
std::cout << "Ready. Now start client application and wait for it to be ready." << std::endl;
std::cout << "Then press a key and enter to start" << std::endl;
std::cin >> c;
std::cout << "Running ..." << std::endl;
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
size_t times = 10;
for (size_t t = 0; t < times; ++t)
{
for (size_t f = 0; f < shmem_frames; ++f)
{
uint8_t *ptr = static_cast<uint8_t*>(region.get_address());
ptr += f*frame_size;
frame[0] = f;
frame[1] = f + 1;
frame[2] = f + 2;
memcpy(ptr, &frame[0], frame_size);
if (f % frames == 0)
{
scoped_lock<interprocess_mutex> lock(info->mutex);
info->pos += frames;
std::cout << "write pos = " << info->pos << std::endl;
}
}
}
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
size_t ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << (double(times*shmem_frames*1000) / double(ms)) << " fps." << std::endl;
winapi::sleep(2000);
{
scoped_lock<interprocess_mutex> lock(info->mutex);
info->run = false;
}
return 0;
}
32位消费者:
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <cstring>
#include <cstdlib>
#include <string>
#include <iostream>
#include <chrono>
struct shmem_info
{
boost::interprocess::interprocess_mutex mutex;
uint64_t pos;
bool run;
};
int main(int argc, char *argv[])
{
using namespace boost::interprocess;
const size_t width = 1920;
const size_t height = 1080;
const size_t bytes_per_pixel = 3;
const size_t frame_size = width*height*bytes_per_pixel;
const size_t frames = 60;
const size_t shmem_frames = 3 * frames;
const size_t shmem_size = width * height * bytes_per_pixel * shmem_frames;
std::vector<uint8_t> frame(frame_size);
std::cout << "Opening shared memory files ..." << std::endl;
shared_memory_object shm(open_only, "MySharedMemory", read_write);
shared_memory_object shm_info(open_only, "MySharedMemoryInfo", read_write);
mapped_region region(shm, read_only);
mapped_region region_info(shm_info, read_write);
shmem_info *info = static_cast<shmem_info*>(region_info.get_address());
std::cout << "Ready." << std::endl;
bool run = true;
while (true)
{
{
scoped_lock<interprocess_mutex> lock(info->mutex);
if (info->run)
break;
}
winapi::Sleep(1);
}
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
uint64_t pos = 0;
uint64_t shm_pos = 0;
while(run)
{
{
scoped_lock<interprocess_mutex> lock(info->mutex);
run = info->run;
if (info->pos == pos)
{
winapi::Sleep(1);
continue;
}
shm_pos = info->pos;
}
while (pos < shm_pos)
{
uint8_t *ptr = static_cast<uint8_t*>(region.get_address());
ptr += (pos%shmem_frames)*frame_size;
memcpy(&frame[0], ptr, frame_size);
++pos;
if (pos % frames == 0)
std::cout << "read pos: " << pos << std::endl;
}
}
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
size_t ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
ms -= 2000;
std::cout << (double(pos*1000) / double(ms)) << " fps." << std::endl;
return 0;
}
我使用的是boost 1.58,第一圈总是比较慢,你可能需要在开始使用共享内存之前运行一次热身圈。数据需要复制到共享内存中,但是为了读取帧的shmem指针可以传递给.Net应用程序。然后您需要确保您的.Net应用程序及时读取数据,以防止其被覆盖。
有用的链接:
boost interpocess
简单示例
编辑:我修改了源代码以显示大致可以实现的每秒帧数。在我的机器上,这是190+ fps,因此我期望它仍将超过所需的60 fps,考虑到在.Net应用程序和c++ dll之间传输数据/指针的开销。
上面的代码应该会给你一个良好的开始,你需要将生产者和消费者共享内存的代码重构为一个通用类并将其制作成dll。有几种将c++ dll导入到.Net的方法。
如何封送C ++类 解释得很好。
现在回答你的问题:
有人能解释一下,我怎么才能:
从VB应用程序向C++应用程序发送一些对象数据(例如System.Drawing.Bitmap),或更好的是指向该数据的指针
你需要使用
GetHbitmap()
方法从
Bitmap
获取
HBITMAP
,然后将其传递给c++ dll。然后在c++ dll中复制像素数据和其他位图信息(如果需要),指向数据的指针不起作用。如何在.Net和c++之间进行操作,请参见
c-get-raw-pixel-data-from-hbitmap。特别有用的是
this answer。
在C++应用程序中接收这些数据
然后,要从共享内存中读取数据,您可能需要首先创建与共享内存中相同大小的空的Bitmap
,并将其HBITMAP传递给C++ dll以填充像素数据。
当数据接收/准备好时启动一些C++函数(带有某些事件?)
您只需要像上面的代码一样持续轮询共享内存以获取新数据即可。