diff --git a/samples/directx/d3d10_opencl.cpp b/samples/directx/d3d10_opencl.cpp new file mode 100644 index 000000000..432c75644 --- /dev/null +++ b/samples/directx/d3d10_opencl.cpp @@ -0,0 +1,397 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include +#include +#include + +#include "opencv2/core.hpp" +#include "opencv2/core/directx.hpp" +#include "opencv2/core/ocl.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/videoio.hpp" +#include "winapp.hpp" + +#pragma comment (lib, "d3d10.lib") + + +class D3D10WinApp : public WinApp +{ +public: + D3D10WinApp(int width, int height, std::string& window_name, cv::VideoCapture& cap) : + WinApp(width, height, window_name) + { + m_shutdown = false; + m_mode = 0; + m_modeStr[0] = cv::String("No processing"); + m_modeStr[1] = cv::String("Processing on CPU"); + m_modeStr[2] = cv::String("Processing on GPU"); + m_disableProcessing = false; + m_cap = cap; + } + + ~D3D10WinApp() {} + + int onClose(void) + { + m_shutdown = true; + cleanup(); + ::DestroyWindow(m_hWnd); + return 0; + } + + virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + { + switch (message) + { + case WM_CHAR: + if (wParam >= '0' && wParam <= '2') + { + m_mode = (char)wParam - '0'; + return 0; + } + else if (wParam == VK_SPACE) + { + m_disableProcessing = !m_disableProcessing; + return 0; + } + else if (wParam == VK_ESCAPE) + { + return onClose(); + } + break; + + case WM_CLOSE: + return onClose(); + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + + return ::DefWindowProc(hWnd, message, wParam, lParam); + } + + static float getFps() + { + static std::queue time_queue; + + int64 now = cv::getTickCount(); + int64 then = 0; + time_queue.push(now); + + if (time_queue.size() >= 2) + then = time_queue.front(); + + if (time_queue.size() >= 25) + time_queue.pop(); + + return time_queue.size() * (float)cv::getTickFrequency() / (now - then); + } + + int init(void) + { + HRESULT r; + + DXGI_SWAP_CHAIN_DESC scd; + + ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC)); + + scd.BufferCount = 1; // one back buffer + scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // use 32-bit color + scd.BufferDesc.Width = m_width; // set the back buffer width + scd.BufferDesc.Height = m_height; // set the back buffer height + scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used + scd.OutputWindow = m_hWnd; // the window to be used + scd.SampleDesc.Count = 1; // how many multisamples + scd.Windowed = TRUE; // windowed/full-screen mode + scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // allow full-screen switching + + r = ::D3D10CreateDeviceAndSwapChain( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + 0, + D3D10_SDK_VERSION, + &scd, + &m_pD3D10SwapChain, + &m_pD3D10Dev); + if (FAILED(r)) + { + return -1; + } + + r = m_pD3D10SwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&m_pBackBuffer); + if (FAILED(r)) + { + return -1; + } + + r = m_pD3D10Dev->CreateRenderTargetView(m_pBackBuffer, NULL, &m_pRenderTarget); + if (FAILED(r)) + { + return -1; + } + + m_pD3D10Dev->OMSetRenderTargets(1, &m_pRenderTarget, NULL); + + D3D10_VIEWPORT viewport; + ZeroMemory(&viewport, sizeof(D3D10_VIEWPORT)); + + viewport.Width = m_width; + viewport.Height = m_height; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 0.0f; + + m_pD3D10Dev->RSSetViewports(1, &viewport); + + D3D10_TEXTURE2D_DESC desc = { 0 }; + + desc.Width = m_width; + desc.Height = m_height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; + desc.Usage = D3D10_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; + + r = m_pD3D10Dev->CreateTexture2D(&desc, NULL, &m_pSurface); + if (FAILED(r)) + { + std::cerr << "Can't create texture with input image" << std::endl; + return -1; + } + + if (cv::ocl::haveOpenCL()) + { + m_oclCtx = cv::directx::ocl::initializeContextFromD3D10Device(m_pD3D10Dev); + } + + m_oclDevName = cv::ocl::useOpenCL() ? + cv::ocl::Context::getDefault().device(0).name() : + "No OpenCL device"; + + return 0; + } // init() + + + int get_surface(ID3D10Texture2D** ppSurface) + { + HRESULT r; + + if (!m_cap.read(m_frame_bgr)) + return -1; + + cv::cvtColor(m_frame_bgr, m_frame_rgba, CV_RGB2BGRA); + + UINT subResource = ::D3D10CalcSubresource(0, 0, 1); + + D3D10_MAPPED_TEXTURE2D mappedTex; + r = m_pSurface->Map(subResource, D3D10_MAP_WRITE_DISCARD, 0, &mappedTex); + if (FAILED(r)) + { + return r; + } + + cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, (int)mappedTex.RowPitch); + // copy video frame data to surface + m_frame_rgba.copyTo(m); + + m_pSurface->Unmap(subResource); + + *ppSurface = m_pSurface; + + return 0; + } + + + void print_info(ID3D10Texture2D* pSurface, int mode, float fps, cv::String oclDevName) + { + HRESULT r; + + UINT subResource = ::D3D10CalcSubresource(0, 0, 1); + + D3D10_MAPPED_TEXTURE2D mappedTex; + r = pSurface->Map(subResource, D3D10_MAP_WRITE_DISCARD, 0, &mappedTex); + if (FAILED(r)) + { + return; + } + + cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, (int)mappedTex.RowPitch); + + cv::String strMode = cv::format("%s", m_modeStr[mode].c_str()); + cv::String strFPS = cv::format("%2.1f", fps); + cv::String strDevName = cv::format("%s", oclDevName.c_str()); + + cv::putText(m, strMode, cv::Point(0, 16), 1, 0.8, cv::Scalar(0, 0, 0)); + cv::putText(m, strFPS, cv::Point(0, 32), 1, 0.8, cv::Scalar(0, 0, 0)); + cv::putText(m, strDevName, cv::Point(0, 48), 1, 0.8, cv::Scalar(0, 0, 0)); + + m_pSurface->Unmap(subResource); + + return; + } + + + int render() + { + try + { + if (m_shutdown) + return 0; + + HRESULT r; + ID3D10Texture2D* pSurface; + + r = get_surface(&pSurface); + if (FAILED(r)) + { + return -1; + } + + switch (m_mode) + { + case 0: + // no processing + break; + + case 1: + { + // process video frame on CPU + UINT subResource = ::D3D10CalcSubresource(0, 0, 1); + + D3D10_MAPPED_TEXTURE2D mappedTex; + r = m_pSurface->Map(subResource, D3D10_MAP_WRITE_DISCARD, 0, &mappedTex); + if (FAILED(r)) + { + return r; + } + + cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, (int)mappedTex.RowPitch); + + if (!m_disableProcessing) + { + // blur D3D10 surface with OpenCV on CPU + cv::blur(m, m, cv::Size(15, 15), cv::Point(-7, -7)); + } + + m_pSurface->Unmap(subResource); + + break; + } + + case 2: + { + // process video frame on GPU + cv::UMat u; + + cv::directx::convertFromD3D10Texture2D(pSurface, u); + + if (!m_disableProcessing) + { + // blur D3D9 surface with OpenCV on GPU with OpenCL + cv::blur(u, u, cv::Size(15, 15), cv::Point(-7, -7)); + } + + cv::directx::convertToD3D10Texture2D(u, pSurface); + + break; + } + + } // switch + + print_info(pSurface, m_mode, getFps(), m_oclDevName); + + // traditional DX render pipeline: + // BitBlt surface to backBuffer and flip backBuffer to frontBuffer + m_pD3D10Dev->CopyResource(m_pBackBuffer, pSurface); + + // present the back buffer contents to the display + // switch the back buffer and the front buffer + r = m_pD3D10SwapChain->Present(0, 0); + if (FAILED(r)) + { + return -1; + } + } + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + return 0; + } + + int cleanup(void) + { + SAFE_RELEASE(m_pSurface); + SAFE_RELEASE(m_pBackBuffer); + SAFE_RELEASE(m_pD3D10SwapChain); + SAFE_RELEASE(m_pRenderTarget); + SAFE_RELEASE(m_pD3D10Dev); + return 0; + } + +private: + bool m_shutdown; + int m_mode; + cv::String m_modeStr[3]; + int m_disableProcessing; + ID3D10Device* m_pD3D10Dev; + IDXGISwapChain* m_pD3D10SwapChain; + ID3D10Texture2D* m_pBackBuffer; + ID3D10Texture2D* m_pSurface; + ID3D10RenderTargetView* m_pRenderTarget; + cv::VideoCapture m_cap; + cv::Mat m_frame_bgr; + cv::Mat m_frame_rgba; + cv::ocl::Context m_oclCtx; + cv::String m_oclPlatformName; + cv::String m_oclDevName; +}; + + +using namespace cv; + +int main(int argc, char** argv) +{ + cv::VideoCapture cap; + + if (argc > 1) + { + cap.open(argv[1]); + } + else + cap.open(0); + + int width = (int)cap.get(CAP_PROP_FRAME_WIDTH); + int height = (int)cap.get(CAP_PROP_FRAME_HEIGHT); + std::string wndname = "D3D10 Window"; + + D3D10WinApp app(width, height, wndname, cap); + + try + { + app.Create(); + return app.run(); + } + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + catch (...) + { + std::cerr << "FATAL ERROR: Unknown exception" << std::endl; + return 11; + } +} diff --git a/samples/directx/d3d11_opencl.cpp b/samples/directx/d3d11_opencl.cpp new file mode 100644 index 000000000..2d87a3465 --- /dev/null +++ b/samples/directx/d3d11_opencl.cpp @@ -0,0 +1,403 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include +#include +#include + +#include "opencv2/core.hpp" +#include "opencv2/core/directx.hpp" +#include "opencv2/core/ocl.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/videoio.hpp" +#include "winapp.hpp" + +#pragma comment (lib, "d3d11.lib") + + +class D3D11WinApp : public WinApp +{ +public: + D3D11WinApp(int width, int height, std::string& window_name, cv::VideoCapture& cap) : + WinApp(width, height, window_name) + { + m_shutdown = false; + m_mode = 0; + m_modeStr[0] = cv::String("No processing"); + m_modeStr[1] = cv::String("Processing on CPU"); + m_modeStr[2] = cv::String("Processing on GPU"); + m_disableProcessing = false; + m_cap = cap; + } + + ~D3D11WinApp() {} + + int onClose(void) + { + m_shutdown = true; + cleanup(); + ::DestroyWindow(m_hWnd); + return 0; + } + + virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + { + switch (message) + { + case WM_CHAR: + if (wParam >= '0' && wParam <= '2') + { + m_mode = (char)wParam - '0'; + return 0; + } + else if (wParam == VK_SPACE) + { + m_disableProcessing = !m_disableProcessing; + return 0; + } + else if (wParam == VK_ESCAPE) + { + return onClose(); + } + break; + + case WM_CLOSE: + return onClose(); + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + + return ::DefWindowProc(hWnd, message, wParam, lParam); + } + + static float getFps() + { + static std::queue time_queue; + + int64 now = cv::getTickCount(); + int64 then = 0; + time_queue.push(now); + + if (time_queue.size() >= 2) + then = time_queue.front(); + + if (time_queue.size() >= 25) + time_queue.pop(); + + return time_queue.size() * (float)cv::getTickFrequency() / (now - then); + } + + int init(void) + { + HRESULT r; + + DXGI_SWAP_CHAIN_DESC scd; + + ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC)); + + scd.BufferCount = 1; // one back buffer + scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // use 32-bit color + scd.BufferDesc.Width = m_width; // set the back buffer width + scd.BufferDesc.Height = m_height; // set the back buffer height + scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used + scd.OutputWindow = m_hWnd; // the window to be used + scd.SampleDesc.Count = 1; // how many multisamples + scd.Windowed = TRUE; // windowed/full-screen mode + scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // allow full-screen switching + + r = ::D3D11CreateDeviceAndSwapChain( + NULL, + D3D_DRIVER_TYPE_HARDWARE, + NULL, + 0, + NULL, + 0, + D3D11_SDK_VERSION, + &scd, + &m_pD3D11SwapChain, + &m_pD3D11Dev, + NULL, + &m_pD3D11Ctx); + if (FAILED(r)) + { + return -1; + } + + r = m_pD3D11SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&m_pBackBuffer); + if (FAILED(r)) + { + return -1; + } + + r = m_pD3D11Dev->CreateRenderTargetView(m_pBackBuffer, NULL, &m_pRenderTarget); + if (FAILED(r)) + { + return -1; + } + + m_pD3D11Ctx->OMSetRenderTargets(1, &m_pRenderTarget, NULL); + + D3D11_VIEWPORT viewport; + ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT)); + + viewport.Width = (float)m_width; + viewport.Height = (float)m_height; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 0.0f; + + m_pD3D11Ctx->RSSetViewports(1, &viewport); + + D3D11_TEXTURE2D_DESC desc = { 0 }; + + desc.Width = m_width; + desc.Height = m_height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + r = m_pD3D11Dev->CreateTexture2D(&desc, NULL, &m_pSurface); + if (FAILED(r)) + { + std::cerr << "Can't create texture with input image" << std::endl; + return -1; + } + + if (cv::ocl::haveOpenCL()) + { + m_oclCtx = cv::directx::ocl::initializeContextFromD3D11Device(m_pD3D11Dev); + } + + m_oclDevName = cv::ocl::useOpenCL() ? + cv::ocl::Context::getDefault().device(0).name() : + "No OpenCL device"; + + return 0; + } // init() + + + int get_surface(ID3D11Texture2D** ppSurface) + { + HRESULT r; + + if (!m_cap.read(m_frame_bgr)) + return -1; + + cv::cvtColor(m_frame_bgr, m_frame_rgba, CV_RGB2BGRA); + + UINT subResource = ::D3D11CalcSubresource(0, 0, 1); + + D3D11_MAPPED_SUBRESOURCE mappedTex; + r = m_pD3D11Ctx->Map(m_pSurface, subResource, D3D11_MAP_WRITE_DISCARD, 0, &mappedTex); + if (FAILED(r)) + { + return r; + } + + cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, (int)mappedTex.RowPitch); + // copy video frame data to surface + m_frame_rgba.copyTo(m); + + m_pD3D11Ctx->Unmap(m_pSurface, subResource); + + *ppSurface = m_pSurface; + + return 0; + } + + + void print_info(ID3D11Texture2D* pSurface, int mode, float fps, cv::String oclDevName) + { + HRESULT r; + + UINT subResource = ::D3D11CalcSubresource(0, 0, 1); + + D3D11_MAPPED_SUBRESOURCE mappedTex; + r = m_pD3D11Ctx->Map(pSurface, subResource, D3D11_MAP_WRITE_DISCARD, 0, &mappedTex); + if (FAILED(r)) + { + return; + } + + cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, (int)mappedTex.RowPitch); + + cv::String strMode = cv::format("%s", m_modeStr[mode].c_str()); + cv::String strFPS = cv::format("%2.1f", fps); + cv::String strDevName = cv::format("%s", oclDevName.c_str()); + + cv::putText(m, strMode, cv::Point(0, 16), 1, 0.8, cv::Scalar(0, 0, 0)); + cv::putText(m, strFPS, cv::Point(0, 32), 1, 0.8, cv::Scalar(0, 0, 0)); + cv::putText(m, strDevName, cv::Point(0, 48), 1, 0.8, cv::Scalar(0, 0, 0)); + + m_pD3D11Ctx->Unmap(pSurface, subResource); + + return; + } + + + int render() + { + try + { + if (m_shutdown) + return 0; + + HRESULT r; + ID3D11Texture2D* pSurface; + + r = get_surface(&pSurface); + if (FAILED(r)) + { + return -1; + } + + switch (m_mode) + { + case 0: + // no processing + break; + + case 1: + { + // process video frame on CPU + UINT subResource = ::D3D11CalcSubresource(0, 0, 1); + + D3D11_MAPPED_SUBRESOURCE mappedTex; + r = m_pD3D11Ctx->Map(m_pSurface, subResource, D3D11_MAP_WRITE_DISCARD, 0, &mappedTex); + if (FAILED(r)) + { + return r; + } + + cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, (int)mappedTex.RowPitch); + + if (!m_disableProcessing) + { + // blur D3D10 surface with OpenCV on CPU + cv::blur(m, m, cv::Size(15, 15), cv::Point(-7, -7)); + } + + m_pD3D11Ctx->Unmap(m_pSurface, subResource); + + break; + } + + case 2: + { + // process video frame on GPU + cv::UMat u; + + cv::directx::convertFromD3D11Texture2D(pSurface, u); + + if (!m_disableProcessing) + { + // blur D3D9 surface with OpenCV on GPU with OpenCL + cv::blur(u, u, cv::Size(15, 15), cv::Point(-7, -7)); + } + + cv::directx::convertToD3D11Texture2D(u, pSurface); + + break; + } + + } // switch + + print_info(pSurface, m_mode, getFps(), m_oclDevName); + + // traditional DX render pipeline: + // BitBlt surface to backBuffer and flip backBuffer to frontBuffer + m_pD3D11Ctx->CopyResource(m_pBackBuffer, pSurface); + + // present the back buffer contents to the display + // switch the back buffer and the front buffer + r = m_pD3D11SwapChain->Present(0, 0); + if (FAILED(r)) + { + return -1; + } + } + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + return 0; + } + + int cleanup(void) + { + SAFE_RELEASE(m_pSurface); + SAFE_RELEASE(m_pBackBuffer); + SAFE_RELEASE(m_pD3D11SwapChain); + SAFE_RELEASE(m_pRenderTarget); + SAFE_RELEASE(m_pD3D11Dev); + SAFE_RELEASE(m_pD3D11Ctx); + return 0; + } + +private: + bool m_shutdown; + int m_mode; + cv::String m_modeStr[3]; + int m_disableProcessing; + ID3D11Device* m_pD3D11Dev; + IDXGISwapChain* m_pD3D11SwapChain; + ID3D11DeviceContext* m_pD3D11Ctx; + ID3D11Texture2D* m_pBackBuffer; + ID3D11Texture2D* m_pSurface; + ID3D11RenderTargetView* m_pRenderTarget; + cv::VideoCapture m_cap; + cv::Mat m_frame_bgr; + cv::Mat m_frame_rgba; + cv::ocl::Context m_oclCtx; + cv::String m_oclPlatformName; + cv::String m_oclDevName; +}; + + +using namespace cv; + +int main(int argc, char** argv) +{ + cv::VideoCapture cap; + + if (argc > 1) + { + cap.open(argv[1]); + } + else + cap.open(0); + + int width = (int)cap.get(CAP_PROP_FRAME_WIDTH); + int height = (int)cap.get(CAP_PROP_FRAME_HEIGHT); + std::string wndname = "D3D11 Window"; + + D3D11WinApp app(width, height, wndname, cap); + + try + { + app.Create(); + return app.run(); + } + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + catch (...) + { + std::cerr << "FATAL ERROR: Unknown exception" << std::endl; + return 11; + } +} diff --git a/samples/directx/d3d9_opencl.cpp b/samples/directx/d3d9_opencl.cpp new file mode 100644 index 000000000..538b185f0 --- /dev/null +++ b/samples/directx/d3d9_opencl.cpp @@ -0,0 +1,396 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include +#include +#include + +#include "opencv2/core.hpp" +#include "opencv2/core/directx.hpp" +#include "opencv2/core/ocl.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/videoio.hpp" +#include "winapp.hpp" + +#pragma comment (lib, "d3d9.lib") + + +class D3D9WinApp : public WinApp +{ +public: + D3D9WinApp(int width, int height, std::string& window_name, cv::VideoCapture& cap) : + WinApp(width, height, window_name) + { + m_shutdown = false; + m_mode = 0; + m_modeStr[0] = cv::String("No processing"); + m_modeStr[1] = cv::String("Processing on CPU"); + m_modeStr[2] = cv::String("Processing on GPU"); + m_disableProcessing = false; + m_cap = cap; + } + + ~D3D9WinApp() {} + + int onClose(void) + { + m_shutdown = true; + cleanup(); + ::DestroyWindow(m_hWnd); + return 0; + } + + virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + { + switch (message) + { + case WM_CHAR: + if (wParam >= '0' && wParam <= '2') + { + m_mode = (char)wParam - '0'; + return 0; + } + else if (wParam == VK_SPACE) + { + m_disableProcessing = !m_disableProcessing; + return 0; + } + else if (wParam == VK_ESCAPE) + { + return onClose(); + } + break; + + case WM_CLOSE: + return onClose(); + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + + return ::DefWindowProc(hWnd, message, wParam, lParam); + } + + static float getFps() + { + static std::queue time_queue; + + int64 now = cv::getTickCount(); + int64 then = 0; + time_queue.push(now); + + if (time_queue.size() >= 2) + then = time_queue.front(); + + if (time_queue.size() >= 25) + time_queue.pop(); + + return time_queue.size() * (float)cv::getTickFrequency() / (now - then); + } + + int init(void) + { + HRESULT r; + + m_pD3D9 = ::Direct3DCreate9(D3D_SDK_VERSION); + if (NULL == m_pD3D9) + { + return -1; + } + + DWORD flags = D3DCREATE_HARDWARE_VERTEXPROCESSING | + D3DCREATE_PUREDEVICE | + D3DCREATE_NOWINDOWCHANGES | + D3DCREATE_MULTITHREADED | + D3DCREATE_FPU_PRESERVE; + + D3DPRESENT_PARAMETERS d3dpp; + ::ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + + d3dpp.Windowed = true; + d3dpp.Flags = 0; + d3dpp.BackBufferCount = 0; + d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; + d3dpp.BackBufferHeight = m_height; + d3dpp.BackBufferWidth = m_width; + d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.hDeviceWindow = m_hWnd; + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; + + r = m_pD3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd, flags, &d3dpp, &m_pD3D9Dev); + if (FAILED(r)) + { + return -1; + } + + r = m_pD3D9Dev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &m_pBackBuffer); + if (FAILED(r)) + { + return -1; + } + + r = m_pD3D9Dev->CreateOffscreenPlainSurface(m_width, m_height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pSurface, NULL); + if (FAILED(r)) + { + std::cerr << "Can't create surface for result" << std::endl; + return -1; + } + + if (cv::ocl::haveOpenCL()) + { + m_oclCtx = cv::directx::ocl::initializeContextFromDirect3DDevice9(m_pD3D9Dev); + } + + m_oclDevName = cv::ocl::useOpenCL() ? + cv::ocl::Context::getDefault().device(0).name() : + "No OpenCL device"; + + return 0; + } // init() + + + int get_surface(LPDIRECT3DSURFACE9* ppSurface) + { + HRESULT r; + + if (!m_cap.read(m_frame_bgr)) + return -1; + + cv::cvtColor(m_frame_bgr, m_frame_rgba, CV_RGB2RGBA); + + D3DLOCKED_RECT memDesc = { 0, NULL }; + RECT rc = { 0, 0, m_width, m_height }; + + r = m_pSurface->LockRect(&memDesc, &rc, 0); + if (FAILED(r)) + { + return r; + } + + cv::Mat m(m_height, m_width, CV_8UC4, memDesc.pBits, memDesc.Pitch); + // copy video frame data to surface + m_frame_rgba.copyTo(m); + + r = m_pSurface->UnlockRect(); + if (FAILED(r)) + { + return r; + } + + *ppSurface = m_pSurface; + + return 0; + } + + + void print_info(LPDIRECT3DSURFACE9 pSurface, int mode, float fps, cv::String oclDevName) + { + HDC hDC; + + HRESULT r = pSurface->GetDC(&hDC); + if (FAILED(r)) + { + return; + } + + HFONT hFont = (HFONT)::GetStockObject(SYSTEM_FONT); + + HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont); + + if (hOldFont) + { + TEXTMETRIC tm; + ::GetTextMetrics(hDC, &tm); + + char buf[256]; + int y = 0; + + buf[0] = 0; + sprintf(buf, "Mode: %s", m_modeStr[mode].c_str()); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + y += tm.tmHeight; + buf[0] = 0; + sprintf(buf, "FPS: %2.1f", fps); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + y += tm.tmHeight; + buf[0] = 0; + sprintf(buf, "OpenCL device: %s", oclDevName.c_str()); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + ::SelectObject(hDC, hOldFont); + } + + r = pSurface->ReleaseDC(hDC); + + return; + } + + + int render() + { + try + { + if (m_shutdown) + return 0; + + HRESULT r; + LPDIRECT3DSURFACE9 pSurface; + + r = get_surface(&pSurface); + if (FAILED(r)) + { + return -1; + } + + switch (m_mode) + { + case 0: + // no processing + break; + + case 1: + { + // process video frame on CPU + D3DLOCKED_RECT memDesc = { 0, NULL }; + RECT rc = { 0, 0, m_width, m_height }; + + r = pSurface->LockRect(&memDesc, &rc, 0); + if (FAILED(r)) + { + return -1; + } + + cv::Mat m(m_height, m_width, CV_8UC4, memDesc.pBits, memDesc.Pitch); + + if (!m_disableProcessing) + { + // blur D3D9 surface with OpenCV on CPU + cv::blur(m, m, cv::Size(15, 15), cv::Point(-7, -7)); + } + + r = pSurface->UnlockRect(); + if (FAILED(r)) + { + return -1; + } + + break; + } + + case 2: + { + // process video frame on GPU + cv::UMat u; + + cv::directx::convertFromDirect3DSurface9(pSurface, u); + + if (!m_disableProcessing) + { + // blur D3D9 surface with OpenCV on GPU with OpenCL + cv::blur(u, u, cv::Size(15, 15), cv::Point(-7, -7)); + } + + cv::directx::convertToDirect3DSurface9(u, pSurface); + + break; + } + + } // switch + + print_info(pSurface, m_mode, getFps(), m_oclDevName); + + // traditional DX render pipeline: + // BitBlt surface to backBuffer and flip backBuffer to frontBuffer + r = m_pD3D9Dev->StretchRect(pSurface, NULL, m_pBackBuffer, NULL, D3DTEXF_NONE); + if (FAILED(r)) + { + return -1; + } + + // present the back buffer contents to the display + r = m_pD3D9Dev->Present(NULL, NULL, NULL, NULL); + if (FAILED(r)) + { + return -1; + } + } + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + return 0; + } + + int cleanup(void) + { + SAFE_RELEASE(m_pSurface); + SAFE_RELEASE(m_pBackBuffer); + SAFE_RELEASE(m_pD3D9Dev); + SAFE_RELEASE(m_pD3D9); + return 0; + } + +private: + bool m_shutdown; + int m_mode; + cv::String m_modeStr[3]; + int m_disableProcessing; + LPDIRECT3D9 m_pD3D9; + LPDIRECT3DDEVICE9 m_pD3D9Dev; + LPDIRECT3DSURFACE9 m_pBackBuffer; + LPDIRECT3DSURFACE9 m_pSurface; + cv::VideoCapture m_cap; + cv::Mat m_frame_bgr; + cv::Mat m_frame_rgba; + cv::ocl::Context m_oclCtx; + cv::String m_oclPlatformName; + cv::String m_oclDevName; +}; + + +using namespace cv; + +int main(int argc, char** argv) +{ + cv::VideoCapture cap; + + if (argc > 1) + { + cap.open(argv[1]); + } + else + cap.open(0); + + int width = (int)cap.get(CAP_PROP_FRAME_WIDTH); + int height = (int)cap.get(CAP_PROP_FRAME_HEIGHT); + std::string wndname = "D3D9 Window"; + + D3D9WinApp app(width, height, wndname, cap); + + try + { + app.Create(); + return app.run(); + } + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + catch (...) + { + std::cerr << "FATAL ERROR: Unknown exception" << std::endl; + return 11; + } +} diff --git a/samples/directx/d3d9ex_opencl.cpp b/samples/directx/d3d9ex_opencl.cpp new file mode 100644 index 000000000..a4f6d312a --- /dev/null +++ b/samples/directx/d3d9ex_opencl.cpp @@ -0,0 +1,396 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include +#include +#include + +#include "opencv2/core.hpp" +#include "opencv2/core/directx.hpp" +#include "opencv2/core/ocl.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/videoio.hpp" +#include "winapp.hpp" + +#pragma comment (lib, "d3d9.lib") + + +class D3D9ExWinApp : public WinApp +{ +public: + D3D9ExWinApp(int width, int height, std::string& window_name, cv::VideoCapture& cap) : + WinApp(width, height, window_name) + { + m_shutdown = false; + m_mode = 0; + m_modeStr[0] = cv::String("No processing"); + m_modeStr[1] = cv::String("Processing on CPU"); + m_modeStr[2] = cv::String("Processing on GPU"); + m_disableProcessing = false; + m_cap = cap; + } + + ~D3D9ExWinApp() {} + + int onClose(void) + { + m_shutdown = true; + cleanup(); + ::DestroyWindow(m_hWnd); + return 0; + } + + virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + { + switch (message) + { + case WM_CHAR: + if (wParam >= '0' && wParam <= '2') + { + m_mode = (char)wParam - '0'; + return 0; + } + else if (wParam == VK_SPACE) + { + m_disableProcessing = !m_disableProcessing; + return 0; + } + else if (wParam == VK_ESCAPE) + { + return onClose(); + } + break; + + case WM_CLOSE: + return onClose(); + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + + return ::DefWindowProc(hWnd, message, wParam, lParam); + } + + static float getFps() + { + static std::queue time_queue; + + int64 now = cv::getTickCount(); + int64 then = 0; + time_queue.push(now); + + if (time_queue.size() >= 2) + then = time_queue.front(); + + if (time_queue.size() >= 25) + time_queue.pop(); + + return time_queue.size() * (float)cv::getTickFrequency() / (now - then); + } + + int init(void) + { + HRESULT r; + + r = ::Direct3DCreate9Ex(D3D_SDK_VERSION, &m_pD3D9Ex); + if (FAILED(r)) + { + return -1; + } + + DWORD flags = D3DCREATE_HARDWARE_VERTEXPROCESSING | + D3DCREATE_PUREDEVICE | + D3DCREATE_NOWINDOWCHANGES | + D3DCREATE_MULTITHREADED | + D3DCREATE_FPU_PRESERVE; + + D3DPRESENT_PARAMETERS d3dpp; + ::ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + + d3dpp.Windowed = true; + d3dpp.Flags = 0; + d3dpp.BackBufferCount = 0; + d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; + d3dpp.BackBufferHeight = m_height; + d3dpp.BackBufferWidth = m_width; + d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.hDeviceWindow = m_hWnd; + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; + + r = m_pD3D9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd, flags, &d3dpp, NULL, &m_pD3D9DevEx); + if (FAILED(r)) + { + return -1; + } + + r = m_pD3D9DevEx->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &m_pBackBuffer); + if (FAILED(r)) + { + return -1; + } + + r = m_pD3D9DevEx->CreateOffscreenPlainSurface(m_width, m_height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pSurface, NULL); + if (FAILED(r)) + { + std::cerr << "Can't create surface for result" << std::endl; + return -1; + } + + if (cv::ocl::haveOpenCL()) + { + m_oclCtx = cv::directx::ocl::initializeContextFromDirect3DDevice9(m_pD3D9DevEx); + } + + m_oclDevName = cv::ocl::useOpenCL() ? + cv::ocl::Context::getDefault().device(0).name() : + "No OpenCL device"; + + return 0; + } // init() + + + int get_surface(LPDIRECT3DSURFACE9* ppSurface) + { + HRESULT r; + + if (!m_cap.read(m_frame_bgr)) + return -1; + + cv::cvtColor(m_frame_bgr, m_frame_rgba, CV_RGB2RGBA); + + D3DLOCKED_RECT memDesc = { 0, NULL }; + RECT rc = { 0, 0, m_width, m_height }; + + r = m_pSurface->LockRect(&memDesc, &rc, 0); + if (FAILED(r)) + { + return r; + } + + cv::Mat m(m_height, m_width, CV_8UC4, memDesc.pBits, memDesc.Pitch); + // copy video frame data to surface + m_frame_rgba.copyTo(m); + + r = m_pSurface->UnlockRect(); + if (FAILED(r)) + { + return r; + } + + *ppSurface = m_pSurface; + + return 0; + } + + + void print_info(LPDIRECT3DSURFACE9 pSurface, int mode, float fps, cv::String oclDevName) + { + HDC hDC; + + HRESULT r = pSurface->GetDC(&hDC); + if (FAILED(r)) + { + return; + } + + HFONT hFont = (HFONT)::GetStockObject(SYSTEM_FONT); + + HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont); + + if (hOldFont) + { + TEXTMETRIC tm; + ::GetTextMetrics(hDC, &tm); + + char buf[256]; + int y = 0; + + buf[0] = 0; + sprintf(buf, "Mode: %s", m_modeStr[mode].c_str()); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + y += tm.tmHeight; + buf[0] = 0; + sprintf(buf, "FPS: %2.1f", fps); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + y += tm.tmHeight; + buf[0] = 0; + sprintf(buf, "OpenCL device: %s", oclDevName.c_str()); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + ::SelectObject(hDC, hOldFont); + } + + r = pSurface->ReleaseDC(hDC); + + return; + } + + + int render() + { + try + { + if (m_shutdown) + return 0; + + HRESULT r; + LPDIRECT3DSURFACE9 pSurface; + + r = get_surface(&pSurface); + if (FAILED(r)) + { + return -1; + } + + switch (m_mode) + { + case 0: + // no processing + break; + + case 1: + { + // process video frame on CPU + D3DLOCKED_RECT memDesc = { 0, NULL }; + RECT rc = { 0, 0, m_width, m_height }; + + r = pSurface->LockRect(&memDesc, &rc, 0); + if (FAILED(r)) + { + return -1; + } + + cv::Mat m(m_height, m_width, CV_8UC4, memDesc.pBits, memDesc.Pitch); + + if (!m_disableProcessing) + { + // blur D3D9 surface with OpenCV on CPU + cv::blur(m, m, cv::Size(15, 15), cv::Point(-7, -7)); + } + + r = pSurface->UnlockRect(); + if (FAILED(r)) + { + return -1; + } + + break; + } + + case 2: + { + // process video frame on GPU + cv::UMat u; + + cv::directx::convertFromDirect3DSurface9(pSurface, u); + + if (!m_disableProcessing) + { + // blur D3D9 surface with OpenCV on GPU with OpenCL + cv::blur(u, u, cv::Size(15, 15), cv::Point(-7, -7)); + } + + cv::directx::convertToDirect3DSurface9(u, pSurface); + + break; + } + + } // switch + + print_info(pSurface, m_mode, getFps(), m_oclDevName); + + // traditional DX render pipeline: + // BitBlt surface to backBuffer and flip backBuffer to frontBuffer + r = m_pD3D9DevEx->StretchRect(pSurface, NULL, m_pBackBuffer, NULL, D3DTEXF_NONE); + if (FAILED(r)) + { + return -1; + } + + // present the back buffer contents to the display + r = m_pD3D9DevEx->Present(NULL, NULL, NULL, NULL); + if (FAILED(r)) + { + return -1; + } + } + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + return 0; + } + + int cleanup(void) + { + SAFE_RELEASE(m_pSurface); + SAFE_RELEASE(m_pBackBuffer); + SAFE_RELEASE(m_pD3D9DevEx); + SAFE_RELEASE(m_pD3D9Ex); + return 0; + } + +private: + bool m_shutdown; + int m_mode; + cv::String m_modeStr[3]; + int m_disableProcessing; + LPDIRECT3D9EX m_pD3D9Ex; + LPDIRECT3DDEVICE9EX m_pD3D9DevEx; + LPDIRECT3DSURFACE9 m_pBackBuffer; + LPDIRECT3DSURFACE9 m_pSurface; + cv::VideoCapture m_cap; + cv::Mat m_frame_bgr; + cv::Mat m_frame_rgba; + cv::ocl::Context m_oclCtx; + cv::String m_oclPlatformName; + cv::String m_oclDevName; +}; + + +using namespace cv; + +int main(int argc, char** argv) +{ + cv::VideoCapture cap; + + if (argc > 1) + { + cap.open(argv[1]); + } + else + cap.open(0); + + int width = (int)cap.get(CAP_PROP_FRAME_WIDTH); + int height = (int)cap.get(CAP_PROP_FRAME_HEIGHT); + std::string wndname = "D3D9Ex Window"; + + D3D9ExWinApp app(width, height, wndname, cap); + + try + { + app.Create(); + return app.run(); + } + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + catch (...) + { + std::cerr << "FATAL ERROR: Unknown exception" << std::endl; + return 11; + } +} diff --git a/samples/directx/winapp.hpp b/samples/directx/winapp.hpp new file mode 100644 index 000000000..fb13c54ba --- /dev/null +++ b/samples/directx/winapp.hpp @@ -0,0 +1,122 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include + + +#define WINCLASS "WinAppWnd" + +#define SAFE_RELEASE(p) if (p) { p->Release(); p = NULL; } + +class WinApp +{ +public: + WinApp(int width, int height, std::string& window_name) + { + m_width = width; + m_height = height; + m_window_name = window_name; + m_hInstance = ::GetModuleHandle(NULL); + } + + virtual ~WinApp() + { + ::UnregisterClass(WINCLASS, m_hInstance); + } + + int Create() + { + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = &WinApp::StaticWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = m_hInstance; + wcex.hIcon = LoadIcon(0, IDI_APPLICATION); + wcex.hCursor = LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = 0; + wcex.lpszMenuName = 0L; + wcex.lpszClassName = WINCLASS; + wcex.hIconSm = 0; + + ATOM wc = ::RegisterClassEx(&wcex); + + RECT rc = { 0, 0, m_width, m_height }; + ::AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, false); + + m_hWnd = ::CreateWindow( + (LPCTSTR)wc, m_window_name.c_str(), + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + rc.right - rc.left, rc.bottom - rc.top, + NULL, NULL, m_hInstance, (void*)this); + + if (!m_hWnd) + return -1; + + ::ShowWindow(m_hWnd, SW_SHOW); + ::UpdateWindow(m_hWnd); + ::SetFocus(m_hWnd); + + return init(); + } + + virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) = 0; + + int run() + { + MSG msg; + + ::ZeroMemory(&msg, sizeof(msg)); + + while (msg.message != WM_QUIT) + { + if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + else + { + render(); + } + } + + return static_cast(msg.wParam); + } + +protected: + static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + { + WinApp* pWnd; + + if (message == WM_NCCREATE) + { + LPCREATESTRUCT pCreateStruct = ((LPCREATESTRUCT)lParam); + pWnd = (WinApp*)(pCreateStruct->lpCreateParams); + ::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pWnd); + } + + pWnd = GetObjectFromWindow(hWnd); + + if (pWnd) + return pWnd->WndProc(hWnd, message, wParam, lParam); + else + return ::DefWindowProc(hWnd, message, wParam, lParam); + } + + inline static WinApp* GetObjectFromWindow(HWND hWnd) + { + return (WinApp*)::GetWindowLongPtr(hWnd, GWLP_USERDATA); + } + + virtual int init() = 0; + virtual int render() = 0; + + HINSTANCE m_hInstance; + HWND m_hWnd; + int m_width; + int m_height; + std::string m_window_name; +}; +