《OpenCL編程指南》之 與Direct3D互操作

来源:http://www.cnblogs.com/betterwgo/archive/2017/06/11/6947043.html
-Advertisement-
Play Games

介紹OpenCL與D3D 10之間的互操作。 1.初始化OpenCL上下文實現Direct3D互操作 OpenCL共用由pragma cl_khr_d3d10_sharing啟用: #pragma OPENCL EXTENSION cl_khr_d3d10_sharing: enable 啟用D3D ...


    介紹OpenCL與D3D 10之間的互操作。

1.初始化OpenCL上下文實現Direct3D互操作

    OpenCL共用由pragma cl_khr_d3d10_sharing啟用:

    #pragma OPENCL EXTENSION cl_khr_d3d10_sharing: enable

    啟用D3D共用時,很多OpenCL函數會有所擴展,將接受一些處理D3D10共用的參數類型和值。

    可以用D3D互操作屬性來創建OpenCL上下文:

    ·CL_CONTEXT_D3D10_DEVICE_KHR   在clCreateContext和clCreateContextFromtype的屬性參數中作為一個屬性名。

    函數可以查詢D3D互操作特定的對象參數:

    ·CL_CONTEXT_D3D10_PREFER_SHARED_RESOURCES_KHR  作為clGetContextInfo的param_name參數值。

    ·CL_MEM_D3D10_RESOURCE_KHR 作為clGetMemObjectInfo的param_name參數值。

    ·CL_IMAGE_D3D10_SUBRESOURCE_KHR 作為clGetImageInfo的param_name參數值。

    ·CL_COMMAND_ACQUIRE_D3D10_OBJECTS_KHR CL_COMMAND_RELEASE_D3D10_OBJECTS_KHR 當param_name為CL_ENCENT_COMMAND_TYPE時,在clGetEventInfo的參數param_value中返回。

    OpenCL D3D10互操作函數在頭文件cl_d3d10.h中。D3D10的Khronos擴展可以從Khronos網站得到。對於某些發佈版本,可能需要下載這個擴展。

    初始化OpenCL的過程與平常基本相同,只有幾點細小差別。首先平臺可以使用clGetPlatformIDs函數列出。由於我們在搜索一個支持D3D共用的平臺,要在各個平臺上使用clGetPlatformInfo()調用來查詢它支持的擴展。如果擴展串中包含cl_khr_d3d10_sharing,說明可以選用這個平臺來實現D3D共用。

    給定一個支持D3D共用的cl_platform_id,可以在這個平臺上使用clGetDeviceIDsFromD3D10KHR()查詢相應的OpenCL設備ID:

cl_int clGetDeviceIDsFromD3D10KHR(
    cl_platform_id             platform,
    cl_d3d10_device_source_khr d3d_device_source,
    void *                     d3d_object,
    cl_d3d10_device_set_khr    d3d_device_set,
    cl_uint                    num_entries,
    cl_device_id *             devices,
    cl_uint *                  num_devices)

例如:

errNum = clGetDeviceIDsFromD3D10KHR(
    platformIds[index_platform],
    CL_D3D10_DEVICE_KHR,
    g_pD3DDevice,
    CL_PREFERRED_DEVICES_FOR_D3D10_KHR,
    1,
    &cdDevice,
    &num_devices);

if (errNum == CL_INVALID_PLATFORM) {
    printf("Invalid Platform: Specified platform is not valid\n");
} else if( errNum == CL_INVALID_VALUE) {
    printf("Invalid Value: d3d_device_source, d3d_device_set is not valid or num_entries = 0 and devices != NULL or num_devices == devices == NULL\n");
} else if( errNum == CL_DEVICE_NOT_FOUND) {
    printf("No OpenCL devices corresponding to the d3d_object were found\n");
}

代碼為選擇的OpenCL平臺(platformIds[index_platform])獲取一個OpenCL設備ID(cdDevice)。常量CL_D3D10_DEVICE_KHR指示發送的D3D10對象(g_pD3DDevice)是一個D3D10設備,通過CL_PREFERRED_DEVICES_FOR_D3D10_KHR來選擇該平臺的期望設備。這會返回與平臺和D3D10設備關聯的期望OpenCL設備。

    這個函數返回的設備ID可以用來創建一個支持D3D共用的上下文。創建OpenCL上下文時,clCreateContext*()調用中的cl_context_properties域應當包括要共用的D3D10設備的指針。例如:

cl_context_properties contextProperties[] =
{
    CL_CONTEXT_D3D10_DEVICE_KHR, 
    (cl_context_properties)g_pD3DDevice,
    CL_CONTEXT_PLATFORM,
    (cl_context_properties)platformIds[index_platform],
    0
};


context = clCreateContextFromType( contextProperties, CL_DEVICE_TYPE_GPU, NULL, NULL, &errNum ) ;

這個示例代碼中,會從D3D10CreateDeviceAndSwapChain()調用返回D3D10設備g_pD3DDevice的指針。

2.從D3D緩衝區和紋理創建OpenCL記憶體對象

    可以使用clCreateFromD3D10*KHR() OpenCL函數由現有的D3D緩衝區對象和紋理創建OpenCL緩衝區和圖像對象。

    可以使用clCreateFromD3D10BufferKHR()由現有的D3D緩衝區創建OpenCL記憶體對象:

cl_mem clCreateFromD3D10BufferKHR(
    cl_context     context,
    cl_mem_flags   flags,
    ID3D10Buffer * resource,
    cl_int *       errcode_ret)

    所返回的OpenCL緩衝區對象的大小與resource的大小相同。這個調用將使resource上的內部Direct3D引用計數增1.所返回OpenCL記憶體對象上的OpenCL引用計數減至0時,resource上的內部Direct3D引用計數會減1.

    緩衝區與紋理都可以與OpenCL共用。

     在D3D10中,紋理可以如下創建:

// 2D texture
D3D10_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof(D3D10_TEXTURE2D_DESC) );
desc.Width = g_WindowWidth;
desc.Height = g_WindowHeight;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
if (FAILED(g_pD3DDevice->CreateTexture2D( &desc, NULL, &g_pTexture2D)))
    return E_FAIL;

這個共用的紋理格式為DXGI_FORMAT_R8G8B8A8_UNORM。然後可以使用

cl_mem clCreateFromD3D10Texture2DKHR(
    cl_context        context,
    cl_mem_flags      flags,
    ID3D10Texture2D * resource,
    UINT              subresource,
    cl_int *          errcode_ret)

創建一個OpenCL圖像對象。所返回的OpenCL圖像對象的寬度、高度和深度由resource得子資源subresource的寬度、高度、深度決定。所返回的OpenCL圖像對象的通道類型和次序由resource的格式確定。

    這個調用將使resource上的內部Direct3D引用計數增1.所返回的OpenCL記憶體對象上的OpenCL引用計數減至0時,resource上的內部Direct3D引用計數減1.

    類似有3D的,

cl_mem clCreateFromD3D10Texture3DKHR(
    cl_context        context,
    cl_mem_flags      flags,
    ID3D10Texture3D * resource,
    UINT              subresource,
    cl_int *          errcode_ret)

 

微信圖片_20170606080720

微信圖片_20170606080731

3.OpenCL中獲取和釋放Direct3D對象

        在opencl中處理之前必須先獲取direct3d對象,在由direct3d使用之前必須先釋放direct3d對象。

cl_int clEnqueueAcquireD3D10ObjectsKHR(
    cl_command_queue command_queue,
    cl_uint          num_objects,
    const cl_mem *   mem_objects,
    cl_uint          num_events_in_wait_list,
    const cl_event * event_wait_list,
    cl_event *       event)

這會獲得由D3D10資源創建的的OpenCL記憶體對象。

cl_int clEnqueueAcquireD3D10ObjectsKHR(
    cl_command_queue command_queue,
    cl_uint          num_objects,
    const cl_mem *   mem_objects,
    cl_uint          num_events_in_wait_list,
    const cl_event * event_wait_list,
    cl_event *       event)

這會獲得由Direct3D 10資源創建OpenCL記憶體對象。clEnqueueAcquireD3D10ObjectsKHR()提供了同步保證,在調用clEnqueueAcquireD3D10ObjectsKHR()之前做出的所有D3D 10調用都必須先完全執行,之後event才能報告完成,command_queue中的所有後續OpenCL工作才能開始執行。

       釋放函數為:

cl_int clEnqueueReleaseD3D10ObjectsKHR(
    cl_command_queue command_queue,
    cl_uint          num_objects,
    const cl_mem *   mem_objects,
    cl_uint          num_events_in_wait_list,
    const cl_event * event_wait_list,
    cl_event *       event)

這會獲得由Direct3D 10資源創建OpenCL記憶體對象。clEnqueueReleaseD3D10ObjectsKHR()提供了同步保證,在調用clEnqueueReleaseD3D10ObjectsKHR()之後做出的所有D3D 10調用不會立即開始執行,直到event_wait_list中所有事件都已經完成,而且提交到command_queue中的所有工作都已經完成執行之後這些D3D 10調用才會開始。

    另外,與D3D10不同,OpenGL獲取函數不會提供同步保證。另外,獲取和釋放紋理時,最高效的做法是同時獲取和釋放所有共用的紋理和資源。另外,最好在切換回D3D處理之前處理完所有opencl內核。採用這種方式,獲取和釋放調用可以用來構成opencl和D3D處理的邊界。

4.OpenCL中處理D3D紋理

      opencl修改紋理內容:

cl_int computeTexture()
{
    cl_int errNum;

    static cl_int seq =0;
    seq = (seq+1)%(g_WindowWidth*2);

    errNum = clSetKernelArg(tex_kernel, 0, sizeof(cl_mem), &g_clTexture2D);
    errNum = clSetKernelArg(tex_kernel, 1, sizeof(cl_int), &g_WindowWidth);
    errNum = clSetKernelArg(tex_kernel, 2, sizeof(cl_int), &g_WindowHeight);
    errNum = clSetKernelArg(tex_kernel, 3, sizeof(cl_int), &seq);
    
    size_t tex_globalWorkSize[2] = { g_WindowWidth, g_WindowHeight };
    size_t tex_localWorkSize[2] = { 32, 4 } ;

    errNum = clEnqueueAcquireD3D10ObjectsKHR(commandQueue, 1, &g_clTexture2D, 0, NULL, NULL );

    errNum = clEnqueueNDRangeKernel(commandQueue, tex_kernel, 2, NULL,
                                    tex_globalWorkSize, tex_localWorkSize,
                                    0, NULL, NULL);
    if (errNum != CL_SUCCESS)
    {
        std::cerr << "Error queuing kernel for execution." << std::endl;
    }
    errNum = clEnqueueReleaseD3D10ObjectsKHR(commandQueue, 1, &g_clTexture2D, 0, NULL, NULL );
    clFinish(commandQueue);
    return 0;
}

用opencl內核計算生成一個D3D紋理對象的內容:

__kernel void xyz_init_texture_kernel(__write_only image2d_t im, int w, int h, int seq )
{
    int2 coord = { get_global_id(0), get_global_id(1) };
    float4 color =  { 
                      (float)coord.x/(float)w,
                      (float)coord.y/(float)h,
                      (float)abs(seq-w)/(float)w,
                      1.0f};
    write_imagef( im, coord, color );
}

這個紋理使用write_imagef()函數寫至內核。這裡seq是一個序列號變數,在宿主機上每一幀會迴圈遞增,併發送至內核。在內核中,seq變數用於生成紋理顏色值。seq遞增時,顏色會改變來實現紋理動畫。

        另外,代碼中使用了一種渲染技術g_pTechnique。這是一個基本處理管線,會用到一個簡單的頂點著色器,將頂點和紋理坐標傳遞到一個像素著色器:

//
// Vertex Shader
//
PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = input.Pos;
    output.Tex = input.Tex;
        
    return output;
}

technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

        這個技術使用常規的D3D10調用載入。像素著色器再對OpenCL內核修改的紋理完成紋理查找,比提供顯示:

SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

float4 PS( PS_INPUT input) : SV_Target
{
    return txDiffuse.Sample( samLinear, input.Tex );
}

在像素著色器中,samLinear是輸入紋理的一個線性採樣器。對於渲染迴圈的每次迭代,OpenCL在computeTexture()中更新紋理內容,有D3D10顯示更新的紋理。

5.OpenCL中處理D3D頂點數據

     現考慮 使用一個包含頂點數據的D3D緩衝區在屏幕上繪製一個正弦曲線。首先為D3D中的頂點緩衝區定義一個簡單的結構:

struct SimpleSineVertex
{
    D3DXVECTOR4 Pos;
};

可以為這個結構創建一個D3D10緩衝區,這裡緩衝區中包含256個元素:

bd.Usage = D3D10_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleSineVertex ) * 256;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;

hr = g_pD3DDevice->CreateBuffer( &bd, NULL, &g_pSineVertexBuffer );

因為要使用OpenCL設置緩衝區中的數據,所以為第二個參數pInitialData傳入NULL,只分配空間。一旦創建了D3D緩衝區 g_pSineVertexBuffer,可以使用clCreateFromD3D10BufferKHR()函數從g_pSineVertexBuffer創建一個OpenCL緩衝區:

g_clBuffer = clCreateFromD3D10BufferKHR( context, CL_MEM_READ_WRITE, g_pSineVertexBuffer, &errNum );
if( errNum != CL_SUCCESS)
{

    std::cerr << "Error creating buffer from D3D10" << std::endl;
    return E_FAIL;
}

與前類似,g_clBuffer可以作為一個內核參數發送到一個生產數據的OpenCL內核。 在示例代碼中,正弦曲線的頂點位置在內核中生成:

__kernel void init_vbo_kernel(__global float4 *vbo, int w, int h, int seq)
{
    int gid = get_global_id(0);
    float4 linepts;
    float f = 1.0f;
    float a = 0.4f;
    float b = 0.0f;

    linepts.x = gid/(w/2.0f)-1.0f;
    linepts.y = b + a*sin(3.14*2.0*((float)gid/(float)w*f + (float)seq/(float)w));
    linepts.z = 0.5f;
    linepts.w = 0.0f;

    vbo[gid] = linepts;
}

渲染時,設置佈局和緩衝區,並指定一個線條帶。接下來,computeBuffer()調用前面的內核更新緩衝區。激活一個簡單的渲染管線,並繪製256個數據點:

// Set the input layout
g_pD3DDevice->IASetInputLayout( g_pSineVertexLayout );
// Set vertex buffer
stride = sizeof( SimpleSineVertex );
offset = 0;
g_pD3DDevice->IASetVertexBuffers( 0, 1, &g_pSineVertexBuffer, &stride, &offset );

// Set primitive topology
g_pD3DDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP );

computeBuffer();
g_pTechnique->GetPassByIndex( 1 )->Apply( 0 );
g_pD3DDevice->Draw( 256, 0 );

        運行時,程式會應用這個內核生成紋理內容,然後運行D3D管線對紋理採樣,併在屏幕上顯示。然後還會繪製頂點緩衝區,在屏幕上得到一個正弦曲線。

image

 

示例工程源碼:http://download.csdn.net/download/qq_33892166/9867159


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • python numpy csv文件的寫入和存取 寫入csv文件 CSV (Comma‐Separated Value, 逗號分隔值),是一種常見的文件格式,用來存儲批量數據。 寫入csv文件 示例: 得到的文件是這樣的 改變參數,以浮點數寫入 讀取csv文件 讀取csv文件 示例: CSV只能有效 ...
  • 大學四年,即將畢業! 大學期間的最後一篇博客,總結分享下我做的畢業設計。我選的論文命題為《燃氣管網設備儀器進銷存管理系統之後臺設計》,由於我們專業只有我一個走技術路線,所以,我一個人完成了整個系統的設計及開發,總耗時近一個月,最終獲得優的成績。 這裡不討論論文,不寫具體實現細節,主要講如何一步步搭建 ...
  • Base64.java DESUtil.java ...
  • package com.mstf.aes; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmExcep... ...
  • RSAsecurity.java RSAtest.java ...
  • 背景: 線上機器,需要過濾access日誌,發送給另外一個api 期初是單進程,效率太低,改為多進程發送後,查看日誌中偶爾會出現異常錯誤(忘記截圖了。。。) 總之就是埠不夠用了報錯 原因: 每一條日誌都是一次請求發送給api,短連接產生大量time_wait狀態,占用了大量埠 這種高併發導致的大 ...
  • 示例代碼 之前的文章說過,對於MyBatis來說insert、update、delete是一組的,因為對於MyBatis來說它們都是update;select是一組的,因為對於MyBatis來說它就是select。 本文研究一下select的實現流程,示例代碼為: selectMailById方法的 ...
  • 前幾天本來想以debug模式看一下JDK的源碼,進入調試模式時才發現,根本看不到方法裡面變數值的情況。為什麼呢?JDK現在的版本中,編譯過後,去除了裡面的調試信息。解決辦法是,編譯那些類,使其帶有調試信息,使用命令:javac -g 查看了一些相關資料,現將解決方法放到下麵 說明:JDK_HOME表 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...