Используйте циклический буфер с общей памятью для обмена данными между процессами. Это может быть реализовано с использованием межпроцессного взаимодействия в качестве DLL C++, а затем эта 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");}
//~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);
// generate frame data
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);
//Set size
shm.truncate(shmem_size);
shm_info.truncate(sizeof(shmem_info));
//Map the whole shared memory in this process
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)
{
// get pointer to the beginning of shared memory
uint8_t *ptr = static_cast<uint8_t*>(region.get_address());
// move pointer to the next frame
ptr += f*frame_size;
// modify first data point for testing purposes
frame[0] = f;
frame[1] = f + 1;
frame[2] = f + 2;
// copy data to shared memory
memcpy(ptr, &frame[0], frame_size);
// update the position each "frames" number, doing that too frequently kills the performance
if (f % frames == 0)
{
// this will lock access to the pos for the time of updating the pos only
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);
// stop run
{
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;
//Open already created shared memory object.
shared_memory_object shm(open_only, "MySharedMemory", read_write);
shared_memory_object shm_info(open_only, "MySharedMemoryInfo", read_write);
//Map the whole shared memory in this process
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;
// first wait for processing to be started
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)
{
// wait for a new data
{
scoped_lock<interprocess_mutex> lock(info->mutex);
run = info->run;
if (info->pos == pos)
{
winapi::Sleep(1);
continue;
}
// we've got the new data
shm_pos = info->pos;
}
while (pos < shm_pos)
{
// get pointer to the beginning of shared memory
uint8_t *ptr = static_cast<uint8_t*>(region.get_address());
// calculate the frame position in circular buffer and move pointer to that frame
ptr += (pos%shmem_frames)*frame_size;
// copy data from shared memory
memcpy(&frame[0], ptr, frame_size);
//winapi::Sleep(1);
++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; // producer waits 2 seconds before sets run=false
std::cout << (double(pos*1000) / double(ms)) << " fps." << std::endl;
return 0;
}
Я использовал Boost 1.58, первый круг всегда медленный, вы можете запустить разминку, прежде чем начать использовать общую память. Данные необходимо скопировать в разделяемую память, но для чтения указатель shmem на фрейм может быть передан приложению .Net. Затем вам нужно убедиться, что ваше приложение .Net считывает данные вовремя, прежде чем они будут перезаписаны.
Полезные ссылки:
повысить межпотоковое взаимодействие
Простой пример а>
EDIT: я изменил исходный код, чтобы показать количество кадров в секунду, которое примерно может быть достигнуто. На моей машине это 190+ кадров в секунду, поэтому я ожидаю, что она будет выше требуемых 60 кадров в секунду, принимая во внимание накладные расходы на передачу данных/указателя между приложением .Net и С++ dll.
Приведенный выше код должен дать вам хорошее начало, вам нужно реорганизовать код общей памяти производителя и потребителя в общий класс и сделать его dll. Существует несколько способов импорта dll С++ в .Net. Как-to-Marshal-aC-Class объясняет некоторые из них довольно хорошо.
Теперь к вашим вопросам:
Может кто-нибудь объяснить, как я могу:
отправить некоторые данные объекта (например, System.Drawing.Bitmap) или лучший указатель на эти данные из приложения VB в приложение C++
Вам нужно будет получить HBITMAP
из Bitmap
с помощью метода GetHbitmap()
и передать его в c++ dll. Затем в С++ dll скопируйте данные пикселей и другую растровую информацию, если это необходимо, в общую память (указатель на данные не будет работать). Как это сделать между .Net и С++, см. c-get-raw-pixel- данные из hbitmap. Особенно полезным будет этот ответ.
получить эти данные в приложении С++
Затем, чтобы прочитать данные из общей памяти, вам, вероятно, потребуется сначала создать пустой Bitmap
того же размера, что и в общей памяти, и передать его HBITMAP в C++ dll для заполнения данных пикселей.
запустить некоторую функцию С++ (с каким-то событием?), когда данные получены/готовы
Вам просто нужно постоянно опрашивать общую память на наличие новых данных, как в приведенном выше коде.
person
doqtor
schedule
20.07.2015