日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DirectX12_入门之三角形

發(fā)布時(shí)間:2023/12/14 编程问答 87 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DirectX12_入门之三角形 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

為了更加深刻的理解圖形API之間的區(qū)別,從此文讓我們正式開始DirectX12的學(xué)習(xí)之旅。之前了解過OpenGL、DX11與Vulkan,我們也簡單的知道了這些圖形API之間的區(qū)別和架構(gòu)上的差異,我們現(xiàn)在來看一下DX12,從使用中了解它與Vulkan的異步架構(gòu)之間的異同。

具體代碼參照DX12龍書github。

一、準(zhǔn)備工作

首先需要先了解:DirectX12_基礎(chǔ)知識;

為了實(shí)現(xiàn)DX12這個(gè)目標(biāo)的大致步驟也跟調(diào)用歷史版本的D3D11、OpenGL、Vulkan中一樣,就是先創(chuàng)建Windows的窗口,接著創(chuàng)建設(shè)備對象、準(zhǔn)備各種資源,再設(shè)置渲染管線的狀態(tài),最終在消息循環(huán)中不斷的調(diào)用OnUpdate和OnRender(我的例子中甚至沒有封裝這兩個(gè)函數(shù),這里只是讓大家先有個(gè)框架概念的認(rèn)識,聰明的你應(yīng)該一點(diǎn)就通了)。當(dāng)然這個(gè)過程和我們以前學(xué)用其他的D3D接口甚至與學(xué)用OpenGL接口的過程都是完全一致的??吹竭@些共同點(diǎn),我們應(yīng)該慶幸,同時(shí)也應(yīng)該信心滿滿的認(rèn)為,至少這世界還盡在我們掌握之中!

而進(jìn)一步我們真正要好好注意和學(xué)習(xí)的就是那些不同點(diǎn)和足以致命的細(xì)節(jié)了,因?yàn)樵贒3D12中加入了“顯存管理”、“多線程渲染”、“異步Draw Call”等的高級概念,所以在具體使用上就有其獨(dú)特的風(fēng)格和復(fù)雜性了。

1.1 頭文件與庫

要調(diào)用D3D12,第一步就是包含其頭文件并鏈接其lib,那么就在你的代碼開頭這樣來寫:

#include <SDKDDKVer.h> #define WIN32_LEAN_AND_MEAN // 從 Windows 頭中排除極少使用的資料 #include <windows.h> #include <tchar.h> #include <wrl.h> //添加WTL支持 方便使用COM #include <strsafe.h> #include <dxgi1_6.h> #include <DirectXMath.h> #include <d3d12.h> #include <d3d12shader.h> #include <d3dcompiler.h> #if defined(_DEBUG) #include <dxgidebug.h> #endifusing namespace Microsoft; using namespace Microsoft::WRL; using namespace DirectX;//linker #pragma comment(lib, "dxguid.lib") #pragma comment(lib, "dxgi.lib") #pragma comment(lib, "d3d12.lib") #pragma comment(lib, "d3dcompiler.lib")#define GRS_WND_CLASS_NAME _T("GRS Game Window Class") #define GRS_WND_TITLE _T("DirectX12 triangle")#define GRS_THROW_IF_FAILED(hr) {HRESULT _hr = (hr);if (FAILED(_hr)){ throw CGRSCOMException(_hr); }}class CGRSCOMException { public:CGRSCOMException(HRESULT hr) : m_hrError(hr){}HRESULT Error() const{return m_hrError;} private:const HRESULT m_hrError; };

首先,代碼中使用了WRL,它給我們帶了基礎(chǔ)安全的便捷性(就是讓我們忘記那些Release調(diào)用,也不會(huì)帶來內(nèi)存泄漏等問題)。

其次,我們包含了<DirectXMath.h>這個(gè)頭文件,這是一個(gè)非常常用的數(shù)學(xué)庫(因?yàn)樗ㄟ^匯編語言幾乎高效利用了所有現(xiàn)代CPU上的SIMD擴(kuò)展指令,并且是內(nèi)聯(lián)函數(shù)形式,是榨干CPU的重要擴(kuò)展庫),可以在GitHub上下載到它的最新版本,當(dāng)然Windows SDK中自帶的也是較新的版本了。

再次,用#pragma comment(lib, “xxxxxx.xxx”)來引用lib庫,對了VS使用的是2017。

又次,項(xiàng)目中包含了"d3dx12.h",這個(gè)文件是將D3D12中的結(jié)構(gòu)都派生(說擴(kuò)展更合適)為簡單的類,以便于使用,它的“封裝”我認(rèn)為應(yīng)該算作是D3D12 sdk的一部分,可以直接從最新的微軟D3D12示例中找到。

最后,GRS_THROW_IF_FAILED這個(gè)宏其實(shí)它就是為了在調(diào)用COM接口時(shí)簡化出錯(cuò)時(shí)處理時(shí)使用的一個(gè)宏,就是為了出錯(cuò)時(shí)拋出一個(gè)異常,因?yàn)橹灰怯挟惓C(jī)制的語言,程序員們都會(huì)使用拋異常來簡化。

接下來我們開始看DX12的初始化主要流程階段:

  • 使用 D3D12CreateDevice 函數(shù)創(chuàng)建 ID3D12Device

  • 創(chuàng)建 ID3D12Fence object 并確認(rèn) descriptor 大小

  • 檢查 4X MSAA quality level 支持

  • 創(chuàng)建 command queue, command list allocator, and main command list

  • 定義并創(chuàng)建 swap chain

  • 創(chuàng)建應(yīng)用需要的 descriptor heaps

  • 重置 back buffer 大小,并為 back buffer 創(chuàng)建 render target view

  • 創(chuàng)建 depth/stencil buffer 和 associated depth/stencil view

  • 設(shè)置 viewport 和 裁剪框

1.2 變量定義

接下來,我們需要進(jìn)行DX12變量定義:

const UINT nFrameBackBufCount = 3u;int iWidth = 1024; int iHeight = 768; UINT nFrameIndex = 0; UINT nFrame = 0;UINT nDXGIFactoryFlags = 0U; UINT nRTVDescriptorSize = 0U;HWND hWnd = nullptr; MSG msg = {};float fAspectRatio = 3.0f;D3D12_VERTEX_BUFFER_VIEW stVertexBufferView = {};UINT64 n64FenceValue = 0ui64; HANDLE hFenceEvent = nullptr;CD3DX12_VIEWPORT stViewPort(0.0f, 0.0f, static_cast<float>(iWidth), static_cast<float>(iHeight)); CD3DX12_RECT stScissorRect(0, 0, static_cast<LONG>(iWidth), static_cast<LONG>(iHeight));ComPtr<IDXGIFactory5> pIDXGIFactory5; ComPtr<IDXGIAdapter1> pIAdapter; ComPtr<ID3D12Device4> pID3DDevice; ComPtr<ID3D12CommandQueue> pICommandQueue; ComPtr<IDXGISwapChain1> pISwapChain1; ComPtr<IDXGISwapChain3> pISwapChain3; ComPtr<ID3D12DescriptorHeap> pIRTVHeap; ComPtr<ID3D12Resource> pIARenderTargets[nFrameBackBufCount]; ComPtr<ID3D12CommandAllocator> pICommandAllocator; ComPtr<ID3D12RootSignature> pIRootSignature; ComPtr<ID3D12PipelineState> pIPipelineState; ComPtr<ID3D12GraphicsCommandList> pICommandList; ComPtr<ID3D12Resource> pIVertexBuffer; ComPtr<ID3D12Fence> pIFence;struct GRS_VERTEX { XMFLOAT3 position; XMFLOAT4 color; };

其中我們目標(biāo)是要畫一個(gè)三角形,所以要先定義我們的3D頂點(diǎn)數(shù)據(jù)結(jié)構(gòu)GRS_VERTEX,當(dāng)然其中的XMFLOAT3和XMFLOAT4來自DirectXMath庫,等價(jià)于float[3]和float[4]。當(dāng)然如果你之前有了解過關(guān)于shader優(yōu)化的一些專題的話,那么在正式的項(xiàng)目中你應(yīng)該至少保持4*sizeof(float)邊界對齊,這樣可以提高GPU訪問這些數(shù)據(jù)的效率。

1.3 創(chuàng)建窗口

// 創(chuàng)建窗口{WNDCLASSEX wcex = {};wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_GLOBALCLASS;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);wcex.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); //防止無聊的背景重繪wcex.lpszClassName = GRS_WND_CLASS_NAME;RegisterClassEx(&wcex);DWORD dwWndStyle = WS_OVERLAPPED | WS_SYSMENU;RECT rtWnd = { 0, 0, iWidth, iHeight };AdjustWindowRect(&rtWnd, dwWndStyle, FALSE);// 計(jì)算窗口居中的屏幕坐標(biāo)INT posX = (GetSystemMetrics(SM_CXSCREEN) - rtWnd.right - rtWnd.left) / 2;INT posY = (GetSystemMetrics(SM_CYSCREEN) - rtWnd.bottom - rtWnd.top) / 2;hWnd = CreateWindowW(GRS_WND_CLASS_NAME, GRS_WND_TITLE, dwWndStyle, posX, posY, rtWnd.right - rtWnd.left, rtWnd.bottom - rtWnd.top, nullptr, nullptr, hInstance, nullptr);if (!hWnd){return FALSE;}ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);}

注意代碼中指定的窗口風(fēng)格WS_OVERLAPPEDWINDOW,這里因?yàn)槲覀儾惶幚鞳nSize事件(眾所周知,窗口重繪需要?jiǎng)討B(tài)修改,以后再詳細(xì)處理吧)。

二、創(chuàng)建DXGI

這些基礎(chǔ)的工作做完了,我們就要開始正式調(diào)用D3D12接口了。根據(jù)前面的簡要描述這里就該創(chuàng)建設(shè)備對象接口了,在D3D12中,一個(gè)重要的概念是將設(shè)備對象概念進(jìn)行了擴(kuò)展。將原來在D3D9中揉在一起的圖形子系統(tǒng)(從硬件子系統(tǒng)角度抽象),顯示器,適配器,3D設(shè)備等對象進(jìn)行了分離,而分離的標(biāo)志就是使用IDXGIFactory來代表整個(gè)圖形子系統(tǒng),它主要的功用之一就是讓我們創(chuàng)建適配器、3D設(shè)備等對象接口用的,因此它的名字就多了個(gè)Factory,這估計(jì)也是暗指Factory設(shè)計(jì)模式之故。這個(gè)對象接口就是我們要?jiǎng)?chuàng)建的第一個(gè)接口:

CreateDXGIFactory2(nDXGIFactoryFlags, IID_PPV_ARGS(&pIDXGIFactory5)); // 關(guān)閉ALT+ENTER鍵切換全屏的功能,因?yàn)槲覀儧]有實(shí)現(xiàn)OnSize處理,所以先關(guān)閉 GRS_THROW_IF_FAILED(pIDXGIFactory5->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER));

在D3D中,如果你了解COM的話,你就會(huì)知道所有D3D12對象接口的初始化創(chuàng)建不能再使用COM規(guī)范的CoCreateInstance函數(shù)了,這是你必須忘記的第一個(gè)招式。這里你要記住的就是D3D12僅僅利用了COM的接口概念而已,其它的都忽略了。這樣我們在使用這些接口時(shí)就可以簡單的理解為是系統(tǒng)提供的只有公有函數(shù)的類的對象指針即可

三、創(chuàng)建設(shè)備對象接口

有了IDXGIFactory的接口,我們就需要枚舉并選擇一個(gè)合適的顯示適配器(顯卡)來創(chuàng)建D3D設(shè)備接口。這里要說明的是為什么我們要選擇枚舉這種啰嗦的方式來創(chuàng)建我們的設(shè)備接口呢?因?yàn)閷τ诂F(xiàn)代的很多PC系統(tǒng)來說CPU中往往集成了顯卡,同時(shí)系統(tǒng)中還會(huì)有一個(gè)獨(dú)立的顯卡。另外大多數(shù)筆記本系統(tǒng)中,為節(jié)能之目的,往往會(huì)把集顯作為默認(rèn)的顯示適配器,而由于集顯功能性能限制的問題,所以在有些示例中可能會(huì)引起一些問題,尤其是將來準(zhǔn)備使用DXR渲染的時(shí)候。所以基于這樣的原因,這里就使用比較繁瑣的枚舉顯卡適配器的方式來創(chuàng)建3D設(shè)備對象。另外這也是為將來使用多顯卡渲染示例的需要做準(zhǔn)備的。代碼如下: for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != pIDXGIFactory5->EnumAdapters1(adapterIndex, &pIAdapter); ++adapterIndex) { DXGI_ADAPTER_DESC1 desc = {}; pIAdapter->GetDesc1(&desc); if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {//軟件虛擬適配器,跳過continue; } //檢查適配器對D3D支持的兼容級別,這里直接要求支持12.1的能力,注意返回接口的那個(gè)參數(shù)被置為了nullptr,這樣 //就不會(huì)實(shí)際創(chuàng)建一個(gè)設(shè)備了,也不用我們啰嗦的再調(diào)用release來釋放接口。這也是一個(gè)重要的技巧,請記住! if (SUCCEEDED(D3D12CreateDevice(pIAdapter.Get(), D3D_FEATURE_LEVEL_12_1, _uuidof(ID3D12Device), nullptr))) {break; } } //創(chuàng)建D3D12.1的設(shè)備 GRS_THROW_IF_FAILED(D3D12CreateDevice( pIAdapter.Get() , D3D_FEATURE_LEVEL_12_1 ,IID_PPV_ARGS( &pID3DDevice )));

特別需要提醒的是,你在代碼的創(chuàng)建循環(huán)中的if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)語句上設(shè)置斷點(diǎn),然后仔細(xì)查看desc中的內(nèi)容,確認(rèn)你用于創(chuàng)建設(shè)備對象的適配器是你系統(tǒng)中最強(qiáng)的一塊顯卡。一般系統(tǒng)中默認(rèn)序號0的設(shè)備是集顯,如果不是獨(dú)顯,那就請你修改adapterIndex這個(gè)循環(huán)初值,比如改為1或2過更高的值試試,當(dāng)然前提是你的系統(tǒng)中確定有那么多適配器(也就是顯卡),直到使用了性能最強(qiáng)的一個(gè)適配器來創(chuàng)建設(shè)備。這樣做的目的不是為了跑性能,而是目前我發(fā)現(xiàn)集顯在運(yùn)行一些高級功能時(shí)會(huì)出現(xiàn)一些問題,很多高級功能是不支持的,用功能比較強(qiáng)的獨(dú)顯是不錯(cuò)的一個(gè)方法。

四、創(chuàng)建命令隊(duì)列接口

再接下去如果你熟悉D3D11的話,我們就需要?jiǎng)?chuàng)建DeviceContext對象及接口了,而在D3D9中有了設(shè)備接口就相當(dāng)于有了一切,直接就可以加載資源,設(shè)置管線狀態(tài),然后開始渲染。

其實(shí)我一直覺得在D3D11中這個(gè)接口對象及名字DeviceContext不是那么直觀。在D3D12中就直接改叫CommandQueue了。這是為什么呢?其實(shí)現(xiàn)代的顯卡上或者說GPU中,已經(jīng)包含多個(gè)可以同時(shí)并行執(zhí)行命令的引擎了,不是游戲引擎,可以理解為執(zhí)行某類指令的專用微核。也請注意這里的概念,一定要理解并行執(zhí)行的引擎這個(gè)概念,因?yàn)閷淼闹匾纠缍嗑€程渲染,多顯卡渲染示例等中還會(huì)用到這個(gè)概念。

這里再舉個(gè)例子來加深理解這個(gè)概念,比如支持D3D12的GPU中至少就有執(zhí)行3D命令的引擎,執(zhí)行復(fù)制命令的引擎(就是從CPU內(nèi)存中復(fù)制內(nèi)容到顯存中或反之或GPU內(nèi)部以及引擎之間),執(zhí)行通用計(jì)算命令的引擎(執(zhí)行Computer Shader的引擎)以及可以進(jìn)行視頻編碼解碼的視頻引擎等。而在D3D12中針對這些不同的引擎,就需要?jiǎng)?chuàng)建不同的命令隊(duì)列接口來代表不同的引擎對象了。這相較于傳統(tǒng)的D3D9或者D3D11設(shè)備接口來說,不但接口被拆分了,而且在對象概念層級上都進(jìn)行了拆分。

因?yàn)槲覀兊哪繕?biāo)是繪制一個(gè)三角形,因此我們第一個(gè)要?jiǎng)?chuàng)建的引擎(命令隊(duì)列)就是3D圖形命令隊(duì)列(暫時(shí)我們也只需要這個(gè))。創(chuàng)建的代碼如下:

D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; GRS_THROW_IF_FAILED(pID3DDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&pICommandQueue)));

這段代碼中較特別的地方之一就是我們需要一個(gè)結(jié)構(gòu)來傳遞參數(shù)給創(chuàng)建函數(shù),這是一種函數(shù)設(shè)計(jì)風(fēng)格,之所以這里要重點(diǎn)強(qiáng)調(diào)這個(gè),是因?yàn)樵贒3D12中這種風(fēng)格被大量使用。比之用參數(shù)列表來調(diào)用函數(shù)的方式,這種方式在可寫性修改性上有很大的改觀,對于不用的參數(shù),不賦值即可。這也是與Vulkan相似的地方。

另一個(gè)需要注意的細(xì)節(jié)就是我們創(chuàng)建3D圖形命令隊(duì)列(引擎)的標(biāo)志是D3D12_COMMAND_LIST_TYPE_DIRECT從其名字幾乎看不出是什么意思,其實(shí)這個(gè)標(biāo)志的真正含義是說,我們創(chuàng)建的不只是能夠執(zhí)行3D圖形命令的隊(duì)列那么簡單,而是說它是圖形設(shè)備的“直接”代表物,本質(zhì)上還可以執(zhí)行幾乎所有的命令,包括圖形命令、復(fù)制命令、計(jì)算命令甚至視頻編解碼命令,還可以執(zhí)行捆綁包(這個(gè)也是以后介紹),因此它是3D圖形命令隊(duì)列(引擎)的超集,基本就是代表了整個(gè)GPU的執(zhí)行能力,固名直接。

五、創(chuàng)建交換鏈

有了命令隊(duì)列對象,接下去我們就可以創(chuàng)建交換鏈了。與之前的D3D版本不同,尤其是與D3D9等古老接口不同,D3D12中交換鏈更加的獨(dú)立了。為了概念上更加清晰,我建議你將交換鏈理解為一個(gè)畫板上有很多頁畫紙,而渲染管線就是畫筆顏料等等,雖然他們要組合在一起才能繪畫,但本質(zhì)上是獨(dú)立的東西,因?yàn)楫嫾埼覀冞€可以使用完全不同的別的筆來寫字或繪畫,比如交換鏈還可以用于D2D、DirectWrite繪制等,只是在這里我們是用來作為3D渲染的目標(biāo)。

另外在D3D12中具體創(chuàng)建交換鏈時(shí)就需要指定一個(gè)命令隊(duì)列,這也是最終呈現(xiàn)畫面前,交換鏈需要確定繪制操作是否完全完成了,也就是需要這個(gè)命令隊(duì)列最終Flush(刷新)一下。創(chuàng)建交換鏈的代碼如下:

DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.BufferCount = nFrameBackBufCount; swapChainDesc.Width = iWidth; swapChainDesc.Height = iHeight; swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.SampleDesc.Count = 1;GRS_THROW_IF_FAILED(pIDXGIFactory5->CreateSwapChainForHwnd(pICommandQueue.Get(), hWnd,&swapChainDesc,nullptr,nullptr,&pISwapChain1));

上面的代碼中沒什么特別的,風(fēng)格上依舊是結(jié)構(gòu)體做函數(shù)的主要參數(shù),要注意的就是SwapEffect參數(shù),目前賦值DXGI_SWAP_EFFECT_FLIP_DISCARD即可,在后面的文章中再細(xì)聊這個(gè)參數(shù)的作用,對于一般的應(yīng)用來說,這樣就已經(jīng)足夠了。

有了交換鏈,那么我們就需要知道當(dāng)前被繪制的后緩沖序號是哪一個(gè)(注意這個(gè)序號是從0開始的,并且每個(gè)后緩沖序號在新的D3D12中是不變的),調(diào)用下面的代碼就可以得到當(dāng)前繪制的后緩沖的序號:

GRS_THROW_IF_FAILED(pISwapChain1.As(&pISwapChain3)); //6、得到當(dāng)前后緩沖區(qū)的序號,也就是下一個(gè)將要呈送顯示的緩沖區(qū)的序號 //注意此處使用了高版本的SwapChain接口的函數(shù) nFrameIndex = pISwapChain3->GetCurrentBackBufferIndex();

這段代碼中我們調(diào)用了WRL::ComPtr的函數(shù)As,其內(nèi)部就是調(diào)用QueryInterface的經(jīng)典COM方法,沒什么稀奇的。我們是使用低版本的SwapChain接口得到了一個(gè)高版本的SwapChain接口。目的是為了調(diào)用GetCurrentBackBufferIndex方法,而從其來自高版本接口可以知道,這是后來擴(kuò)展的方法。主要原因就是現(xiàn)在翻轉(zhuǎn)繪制緩沖區(qū)到顯示緩沖區(qū)的方法更高效了,直接就是將對應(yīng)的后緩沖序號設(shè)置為當(dāng)前顯示的緩沖序號即可,比如原來顯示的是序號為1的緩沖區(qū),那么下一個(gè)要顯示的緩沖區(qū)就是序號2的緩沖區(qū),如果為2的緩沖區(qū)正在顯示,那么下一個(gè)將要顯示的序號就又回到了0,當(dāng)然這里假設(shè)緩沖區(qū)數(shù)量是3,我們的例子中就正好是3個(gè)緩沖區(qū),所以緩沖區(qū)的序號就正好是緩沖區(qū)數(shù)量的余數(shù)(MOD)。其他情況依此類推。

前版本的D3D中,拿到了交換鏈的后緩沖之后,我們就需要?jiǎng)?chuàng)建一個(gè)叫做Render Target View(簡稱RTV,最好背下來)Descriptor渲染目標(biāo)視圖描述符的對象及接口。類似僵尸片中的各種靈符一樣,描述符也有一些神奇的功用,比如拿RTV描述符貼在一塊紋理上,它立刻就變成了RTV。它的作用是讓GPU能夠正確識別和使用渲染目標(biāo)資源,其本質(zhì)就是描述緩沖區(qū)占用的顯存,所以從本質(zhì)上講只要是可以作為一整塊顯存來使用的緩沖都可以作為渲染目標(biāo), 比如有一些高級渲染技法中就需要渲染到紋理上,當(dāng)然我們要做的也很簡單就是給那些紋理貼上RTV符即可。因?yàn)镚PU內(nèi)部本質(zhì)上是一個(gè)巨大的SIMD架構(gòu)的處理器,同時(shí)考慮到很多微核(可以理解為就是GPU中的多個(gè)ALU單元)并行執(zhí)行的需要,所以它在存儲器的使用上是非常細(xì)化的,在使用某段存儲(內(nèi)存或顯存)之前,就需要通過類似描述符之類的概念等價(jià)物說清楚這段存儲的用途、大小、讀寫屬性、GPU/CPU訪問權(quán)限等信息。因?yàn)閯?chuàng)建交換鏈的主要目的是用它的緩沖區(qū)作為3D圖形渲染的目標(biāo),所以我們就需要用渲染目標(biāo)視圖描述符告訴GPU這些緩沖區(qū)是渲染目標(biāo)。

六、創(chuàng)建創(chuàng)建RTV描述符堆和RTV描述符

在D3D12中加入了一個(gè)重要的概念——描述符堆,首先看到堆這個(gè)詞我們應(yīng)當(dāng)聯(lián)想到內(nèi)存管理(如果你想到了數(shù)據(jù)結(jié)構(gòu),說明你基本功還可以,這里我們討論的是D3D12,跟數(shù)據(jù)結(jié)構(gòu)關(guān)系不大,所以應(yīng)當(dāng)正確聯(lián)想到內(nèi)存管理中的堆棧);其次在D3D12中凡是套用了堆(Heap)這個(gè)概念的對象,目前應(yīng)當(dāng)將他們理解為固定大小的數(shù)組對象,而不是真正意義上可以管理任意大小內(nèi)存塊并能夠自動(dòng)伸縮大小的內(nèi)存堆棧。未來不好說D3D中會(huì)不會(huì)實(shí)現(xiàn)全動(dòng)態(tài)的顯存堆管理。在目前我們就理解為它是數(shù)組即可。

這也是D3D12在功能上較之前版本的D3D接口擴(kuò)展出來的重要概念——顯存管理(或稱之為存儲管理更合適,這里用顯存是為了強(qiáng)調(diào)與傳統(tǒng)系統(tǒng)內(nèi)存管理的區(qū)別)的一個(gè)重要表現(xiàn)。

渲染目標(biāo)視圖描述符堆的代碼如下:

D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; rtvHeapDesc.NumDescriptors = nFrameBackBufCount; rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;GRS_THROW_IF_FAILED(pID3DDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&pIRTVHeap))); //得到每個(gè)描述符元素的大小 nRTVDescriptorSize = pID3DDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);

上述代碼在風(fēng)格上依舊是結(jié)構(gòu)體做參數(shù)調(diào)用函數(shù)的老套路。結(jié)構(gòu)體初始化時(shí)Type參數(shù)賦值為D3D12_DESCRIPTOR_HEAP_TYPE_RTV,表示我們將創(chuàng)建的堆(數(shù)組)是用來存儲RTV描述符的堆(數(shù)組)。通過NumDescriptors參數(shù)我們就指定了堆的大小(實(shí)質(zhì)上是數(shù)組元素的個(gè)數(shù)),Flags參數(shù)暫時(shí)不介紹,像這里賦值就OK。堆創(chuàng)建完了之后我們就調(diào)用D3D設(shè)備接口的GetDescriptorHandleIncrementSize方法得到實(shí)際上每個(gè)RTV描述的大小,也就是數(shù)組元素的真實(shí)大小,你可以理解為我們相當(dāng)于調(diào)用了一個(gè)sizeof運(yùn)算符,得到了一個(gè)不知道里面存了些啥的復(fù)雜結(jié)構(gòu)體的大小,當(dāng)然計(jì)量單位是字節(jié)。

有了RTV描述符堆(數(shù)組),那么我們就可以創(chuàng)建RTV描述符了,代碼如下:

CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(pIRTVHeap->GetCPUDescriptorHandleForHeapStart()); for (UINT i = 0; i < nFrameBackBufCount; i++) {GRS_THROW_IF_FAILED(pISwapChain3->GetBuffer(i, IID_PPV_ARGS(&pIARenderTargets[i])));pID3DDevice->CreateRenderTargetView(pIARenderTargets[i].Get(), nullptr, rtvHandle);rtvHandle.Offset(1, nRTVDescriptorSize); }

代碼中首先我們使用了一個(gè)來自D3Dx12.h中擴(kuò)展的類CD3DX12_CPU_DESCRIPTOR_HANDLE來管理描述堆的當(dāng)前元素指針位置,概念上你可以將這個(gè)對象理解為一個(gè)數(shù)組元素迭代器,雖然它的名字被定義成了HANDLE但我覺得使用iterator更確切。

接下來就是一個(gè)循環(huán),循環(huán)上界就是我們創(chuàng)建的交換鏈中的后緩沖個(gè)數(shù),在我們的例子中是3個(gè)后緩沖,因此這個(gè)循環(huán)會(huì)執(zhí)行三次,創(chuàng)建三個(gè)RTV描述符。這里要特別注意的是,緩沖的序號和對應(yīng)描述符的數(shù)組元素序號要保持一致,當(dāng)然代碼中已經(jīng)保證了這一點(diǎn)。循環(huán)最后一行Offset則暴漏了這里其實(shí)是在操作數(shù)組的本質(zhì)。

七、創(chuàng)建根簽名對象接口

再接下來我們就需要?jiǎng)?chuàng)建一個(gè)更重要的對象了,就是根簽名。在這里首先你要為你能堅(jiān)持看到這里給自己點(diǎn)個(gè)贊,因?yàn)镈3D12中為完成渲染加入了太多的概念和對象,當(dāng)然這些概念的加入都是為了提高性能而設(shè)計(jì)的。當(dāng)然能看到這里的前提是再次提醒你已經(jīng)看過我之前寫的關(guān)于D3D12的相關(guān)博客文章了。

從總體上來理解D3D12的話,就是在D3D12中加入了存儲管理、所有的調(diào)用都是異步并行的方式并且為管理異步調(diào)用而加入了同步對象。這里提到的根簽名則是為了整體上統(tǒng)一集中管理之前在D3D11中分散在各個(gè)資源創(chuàng)建函數(shù)參數(shù)中的存儲Slot和對應(yīng)寄存器序號的對象。也就是說在D3D12中我們不用在創(chuàng)建某個(gè)資源時(shí)單獨(dú)在其參數(shù)中指定對應(yīng)哪個(gè)Slot(暫時(shí)翻譯為存儲槽)和寄存器及序號了,而是統(tǒng)一在D3D12中用一個(gè)根簽名就將這些描述清楚。

熟悉Shader的話,你就知道我們在寫shader的時(shí)候有時(shí)候就需要指定每種數(shù)據(jù),比如常量緩沖、頂點(diǎn)寄存器、紋理等資源是對應(yīng)哪個(gè)存儲槽和寄存器,及序號的。對于存儲槽我們可以理解為一條從內(nèi)存向顯存?zhèn)鬏敂?shù)據(jù)的通道,想象成一個(gè)流水槽(如果你懂點(diǎn)點(diǎn)PCIe的話,可以將之理解為PCIe的一條通道)。而對于這里的寄存器就不是指CPU上的寄存器了,而是指GPU上的寄存器。根據(jù)前面的描述現(xiàn)代的GPU在概念上可以理解為一個(gè)巨大的SIMD架構(gòu)的處理器,由于為高效并行執(zhí)行指令的需要,它在存儲管理上是非常細(xì)分的,甚至它的寄存器也是細(xì)化分類的,有常量寄存器、紋理寄存器、采樣器等等,并且每類寄存器都有若干個(gè),以序號來索引使用,所以在我們從CPU加載資源到GPU上時(shí)就需要詳細(xì)指定那些數(shù)據(jù)從哪個(gè)槽上灌入到哪個(gè)序號的寄存器上。

而要達(dá)到這個(gè)目的就需要在兩個(gè)方面明確指定這些參數(shù),一方面是從程序代碼(CPU側(cè))調(diào)用D3D12相關(guān)接口創(chuàng)建資源時(shí)指定傳輸參數(shù)(存儲槽序號,寄存器序號),另一方面在Shader代碼中指定接收參數(shù),并指定Shader代碼中具體訪問哪個(gè)存儲槽,哪個(gè)寄存器中的數(shù)據(jù)。或者更準(zhǔn)確的說一般Shader中就不用管是哪個(gè)Slot了,因?yàn)閿?shù)據(jù)肯定都已經(jīng)到了顯存中,Shader中實(shí)質(zhì)關(guān)心的只是寄存器和其序號。

或者直接的說根簽名就是描述了整個(gè)的用于渲染的資源的存儲布局。在MSDN官方的描述中也是這樣說的:根簽名是一個(gè)綁定約定,由應(yīng)用程序定義,著色器使用它來定位他們需要訪問的資源。

最終在D3D12中之所以要統(tǒng)一管理這些的目的就是為了方便形成一個(gè)統(tǒng)一的管線狀態(tài)對象(Pipeline States Object PSO),有了管線狀態(tài)對象,在渲染時(shí),只要資源加載正確,我們只需要在不同的渲染過程間切換設(shè)置不同的渲染管線狀態(tài)對象即可,而在傳統(tǒng)的D3D管線編碼中,這些工作需要一個(gè)個(gè)設(shè)置管線狀態(tài),然后自己編寫不同的管線狀態(tài)管理代碼來實(shí)現(xiàn),在代碼組織上過于分散和復(fù)雜,同時(shí)也不利于復(fù)雜場景渲染時(shí)快速切換不同渲染管線狀態(tài)的需要。

而根簽名對象則是總領(lǐng)一條管線狀態(tài)對象存儲綁定架構(gòu)的總綱。在我們這里的例子中,因?yàn)槲覀儧]有用到復(fù)雜的數(shù)據(jù),只是為了畫一個(gè)三角形,并且沒有紋理、沒有采樣器等等,所以我們就創(chuàng)建一個(gè)都是0索引(序號是1的意思,搞C的你應(yīng)該明白)的一個(gè)根簽名,代碼如下:

CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc; rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); ComPtr<ID3DBlob> signature; ComPtr<ID3DBlob> error;GRS_THROW_IF_FAILED(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));GRS_THROW_IF_FAILED(pID3DDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&pIRootSignature)));

這段代碼中我們又用到一個(gè)d3dx12.h中擴(kuò)展的類CD3DX12_ROOT_SIGNATURE_DESC來定義一個(gè)根簽名的結(jié)構(gòu),然后編譯一下,再創(chuàng)建根簽名對象及其接口。這里的根簽名參數(shù)已經(jīng)說清楚了我們需要傳遞網(wǎng)格數(shù)據(jù)到管線中進(jìn)行渲染并且要定義對應(yīng)的Input Layout格式對象。

默認(rèn)情況下Slot和寄存器序號都使用0。關(guān)于根簽名的詳細(xì)內(nèi)容我們放在后續(xù)的教程中專門來細(xì)講,這里先理解到這樣就可以了。需要補(bǔ)充的就是,根簽名的創(chuàng)建方式主要有兩種,一種是使用腳本方式來編寫一個(gè)根簽名(VS中是擴(kuò)展名為HLSLi的文件),另一種就是我們這里使用的定義一個(gè)結(jié)構(gòu)體再編譯生成一個(gè)根簽名的方式。我們示例中使用的是第二種方法,我的建議是兩種方法都掌握,這個(gè)我們后面的教程都會(huì)講到。但是我們必須要清晰的認(rèn)識到使用結(jié)構(gòu)體然后調(diào)用編譯函數(shù)在代碼中編譯的方法的巨大優(yōu)勢,因?yàn)檫@種形式很方便我們定義自己的根簽名腳本,也就是腳本化。比如你可以用XML文件定義一個(gè)根簽名結(jié)構(gòu),然后加載使用,這樣就不會(huì)被禁錮于純代碼或純HLSL腳本的方式中。而是可以自己擴(kuò)展更靈活和易轉(zhuǎn)換的方式。

八、編譯Shader及創(chuàng)建渲染管線狀態(tài)對象接口

至此,經(jīng)過了這么多對象創(chuàng)建初始化工作后,我們終于可以看到一點(diǎn)曙光了,接下來我們就要?jiǎng)?chuàng)建渲染管線狀態(tài)對象了,在D3D12以前,雖然有渲染管線狀態(tài)這樣一個(gè)概念,但在接口上它的所有狀態(tài)設(shè)置都是按照渲染階段來分不同的函數(shù)直接放在Device對象接口或Context對象接口中?,F(xiàn)在渲染管線狀態(tài)就被獨(dú)立了成了一個(gè)對象,并用ID3D12PipelineState接口來代表。

從概念上講,渲染管線狀態(tài)就是把原來的Rasterizer State(光柵化狀態(tài))、Depth Stencil State(深度、蠟板狀態(tài))、Blend State(輸出alpha混合狀態(tài))、各階段Shader程序(VS、HS、DS、GS、PS、CS),以及輸入數(shù)據(jù)模型(Input Layout)等組合在一個(gè)對象中,從而形成一個(gè)完整的可重用的Pipeline State Object(PSO 渲染管線狀態(tài)對象)。這樣我們就從每次不同的渲染需要設(shè)置不同的管線狀態(tài)參數(shù)的過程中解放了出來,在實(shí)際使用時(shí),只需要在開始時(shí)初始化一堆PSO,然后根據(jù)不同的渲染需要在管線上設(shè)置不同的PSO即可開始渲染,渲染部分代碼就被大大簡化了,從而使游戲引擎的封裝實(shí)現(xiàn)也大大簡化了。

實(shí)際上這也是符合現(xiàn)代大多數(shù)引擎中關(guān)于渲染部分的封裝思路的。因?yàn)楝F(xiàn)代光柵化渲染的很多理論算法都已經(jīng)很成熟很完備了,完全有條件統(tǒng)一為幾大類不同的主流PSO,然后重用即可。甚至我們現(xiàn)在在使用一些現(xiàn)代化的游戲引擎開發(fā)游戲時(shí)基本都不用關(guān)注渲染部分的組件,引擎自帶的組件已經(jīng)很強(qiáng)悍了。所以這也是很多會(huì)用引擎開發(fā)游戲的開發(fā)人員往往對渲染部分了解和關(guān)注甚少的原因之一。

在D3D12中通過PSO對象,我們也具備了直接封裝實(shí)現(xiàn)具有現(xiàn)代化水平的引擎渲染部件的能力,當(dāng)然這需要你在封裝設(shè)計(jì)上有一定功力,在這里我依然強(qiáng)調(diào)一下,這個(gè)教程中不講封裝,只講基礎(chǔ)。所以我們就直接看看最原始的調(diào)用代碼是什么樣子的:

ComPtr<ID3DBlob> vertexShader; ComPtr<ID3DBlob> pixelShader; #if defined(_DEBUG)UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #elseUINT compileFlags = 0; #endifTCHAR pszShaderFileName[] = _T("D:\\Projects_2018_08\\D3DPipelineTest\\D3D12Trigger\\Shader\\shaders.hlsl");GRS_THROW_IF_FAILED(D3DCompileFromFile(pszShaderFileName, nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr));GRS_THROW_IF_FAILED(D3DCompileFromFile(pszShaderFileName, nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr));D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = {{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }};D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; psoDesc.pRootSignature = pIRootSignature.Get(); psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get()); psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get()); psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); psoDesc.DepthStencilState.DepthEnable = FALSE; psoDesc.DepthStencilState.StencilEnable = FALSE; psoDesc.SampleMask = UINT_MAX; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; psoDesc.NumRenderTargets = 1; psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; psoDesc.SampleDesc.Count = 1;GRS_THROW_IF_FAILED(pID3DDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pIPipelineState)));

這段代碼在結(jié)構(gòu)上比較清晰,也很容易理解,在編譯完Shader程序后,就通過初始化一個(gè)PSO結(jié)構(gòu)體,然后調(diào)用CreateGraphicsPipelineState創(chuàng)建一個(gè)PSO對象及其代表接口ID3D12PipelineState。

從PSO結(jié)構(gòu)體的初始化上,你應(yīng)該看到了D3D12與原來的D3D接口的明顯不同,過去這些參數(shù)首先是通過調(diào)用一個(gè)個(gè)函數(shù)來設(shè)置的,并且按照不同的渲染階段冠以IA、VS、PS、RS、OM等前綴名字,且不說它們看起來有多別扭,光是每次在渲染循環(huán)中調(diào)用一遍就夠復(fù)雜了,如果你少設(shè)置了一個(gè)狀態(tài),有可能就引起奇奇怪怪的后果,尤其是復(fù)雜的場景中,每種物體都可能需要不同的渲染手法,每種渲染手法就需要這堆函數(shù)的不同順序的組合調(diào)用,而每次渲染完一個(gè),你還需要挨個(gè)清理掉,以便不影響后續(xù)的渲染調(diào)用。如果你熟悉Windows GDI的話,你就會(huì)發(fā)現(xiàn)之前的D3D接口在渲染狀態(tài)編碼的風(fēng)格上與其十分類似,都是先設(shè)置一堆狀態(tài),然后繪制,最后再還原這一堆狀態(tài)到之前的樣子。代碼上一樣的令人作嘔,因?yàn)橐粋€(gè)不留神內(nèi)存泄漏不說,并且渲染的結(jié)果通常也不會(huì)正確,所以調(diào)試起來像噩夢一樣。

另一方面,過去的那種通過不同函數(shù)來設(shè)置渲染狀態(tài)參數(shù)的方法,非常不利于固化(存儲或加載)或腳本化封裝(自定義腳本來靈活封裝渲染管線),顯得很不靈活。而這些對于現(xiàn)代化設(shè)計(jì)的游戲引擎來說是非常重要的特征。喜大普奔的是,在D3D12中,正如你所見的這些都只是初始化一個(gè)巨大的結(jié)構(gòu)體即可。

PSO對象還帶來一個(gè)更巨大的好處就是非常有利于多線程渲染,可以在不同的渲染線程之間方便的共享不同的PSO對象,而不用考慮怎樣去靈活的封裝這些渲染狀態(tài)參數(shù)以便于共享。

其中shader簡單可見如下:

struct PSInput {float4 position : SV_POSITION;float4 color : COLOR; };PSInput VSMain(float4 position : POSITION, float4 color : COLOR) {PSInput result;result.position = position;result.color = color;return result; }float4 PSMain(PSInput input) : SV_TARGET {return input.color; }

九、加載待渲染數(shù)據(jù)(三角形頂點(diǎn))

有了PSO我們就可以正式開始加載網(wǎng)格數(shù)據(jù)并開始渲染了。我想現(xiàn)在你應(yīng)該猜到在D3D12中渲染也應(yīng)該沒那么簡單吧?如果你猜到了,那說明通過前面的學(xué)習(xí)你已經(jīng)有點(diǎn)適應(yīng)D3D12為我們帶來的空前的復(fù)雜性了,這很好,這說明你基本已經(jīng)快爬到學(xué)習(xí)曲線的坡頂了。

開始正式渲染之前的最后一步就是加載資源了。因?yàn)槲覀円L制一個(gè)三角形,所以我們就直接在代碼中準(zhǔn)備好這個(gè)三角形的數(shù)據(jù),代碼如下:

// 定義三角形的3D數(shù)據(jù)結(jié)構(gòu),每個(gè)頂點(diǎn)使用三原色之一 GRS_VERTEX triangleVertices[] = {{ { 0.0f, 0.25f * fAspectRatio, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } },{ { 0.25f * fAspectRatio, -0.25f * fAspectRatio, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },{ { -0.25f * fAspectRatio, -0.25f * fAspectRatio, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } } };const UINT vertexBufferSize = sizeof(triangleVertices);GRS_THROW_IF_FAILED(pID3DDevice->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),D3D12_HEAP_FLAG_NONE,&CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize),D3D12_RESOURCE_STATE_GENERIC_READ,nullptr,IID_PPV_ARGS(&pIVertexBuffer)));UINT8* pVertexDataBegin = nullptr; CD3DX12_RANGE readRange(0, 0); GRS_THROW_IF_FAILED(pIVertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin))); memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices)); pIVertexBuffer->Unmap(0, nullptr);stVertexBufferView.BufferLocation = pIVertexBuffer->GetGPUVirtualAddress(); stVertexBufferView.StrideInBytes = sizeof(GRS_VERTEX); stVertexBufferView.SizeInBytes = vertexBufferSize;

上面的代碼中,對于三角形的頂點(diǎn),我們使用了三角形外接圓半徑的形式參數(shù)化定義了它,這樣方便我們調(diào)整三角形大小看效果。

接著一個(gè)重要的概念就又是與存儲管理有關(guān)了,在這里就是資源管理了?;旧显贒3D12渲染過程中需要的數(shù)據(jù)都可以被稱為資源,在這段代碼中我們需要的資源就是三角形的頂點(diǎn)數(shù)據(jù),使用的函數(shù)是CreateCommittedResource。

首先在一般的場合下,比如我們這里的示例中都可以使用這一個(gè)函數(shù)來創(chuàng)建資源。其次這個(gè)函數(shù)是為數(shù)不多的幾個(gè)內(nèi)部還同步的D3D12函數(shù)之一,也就是當(dāng)這個(gè)函數(shù)返回時(shí),實(shí)際的資源也就分配好了,與我們在之前版本D3D中用的方法類似。因此這個(gè)函數(shù)返回后,我們就可以像傳統(tǒng)做法一樣,map然后memcpy數(shù)據(jù)最后unmap就完成了數(shù)據(jù)從CPU內(nèi)存向顯存的傳遞。

幸運(yùn)的是,CD3DX12_RESOURCE_DESC這個(gè)結(jié)構(gòu)體與它在D3D11或D3D9中的前輩很相似,你應(yīng)該已經(jīng)很熟悉它了。在后面的系列教程中我們還會(huì)詳細(xì)講解它的用法。而另一個(gè)類CD3DX12_HEAP_PROPERTIES就先暫時(shí)不要糾結(jié)了,知道在這里它就是為了封裝D3D12_HEAP_TYPE_UPLOAD這個(gè)屬性即可。Upload的意思就是從CPU內(nèi)存上傳到顯存中的意思。

代碼的最后三行,就是通過結(jié)構(gòu)體對象描述清楚了這個(gè)資源的視圖,目的是告訴GPU被描述的資源實(shí)際是Vertex Buffer。這也與之前的D3D版本中的做法有些區(qū)別。在傳統(tǒng)的D3D中,是通過調(diào)用Device接口的函數(shù)明確的創(chuàng)建一個(gè)資源視圖對象的,而在D3D12中只需要在結(jié)構(gòu)體對象中說明即可,主要目的就是為了能夠統(tǒng)一實(shí)現(xiàn)一個(gè)可固化或腳本化的靈活的資源視圖對象,與之前說的PSO對象結(jié)構(gòu)體對象來描述的目的相類似。

十、異步渲染原理及命令列表詳解

我們做完了渲染管線及渲染管線狀態(tài)準(zhǔn)備工作,同時(shí)也“加載”完了我們需要渲染的三角形的數(shù)據(jù),終于可以開始渲染了。當(dāng)然因?yàn)镈3D12本質(zhì)上為了支持多線程渲染而采取了異步設(shè)計(jì)策略的緣故,渲染也與之前版本的D3D有較大的差別。

首先我們需要接觸的有一個(gè)新的概念就是命令列表,它的接口是

ID3D12GraphicsCommandList。其實(shí)在D3D11中它的概念等價(jià)物就是 Deferred Device Context,而相應(yīng)的D3D12中的Command Queues就對應(yīng)D3D11中的Immediate Device Context。

顧名思義,命令列表其實(shí)就是為了記錄CPU發(fā)給GPU的圖形命令的,因此它里面的方法函數(shù)就是一個(gè)個(gè)圖形命令了,我們逐一調(diào)用命令函數(shù),它就按照我們調(diào)用的順序記錄了這些圖形命令。

在D3D12中所有的圖形命令函數(shù)即ID3D12GraphicsCommandList的接口方法都是異步的,就是說一調(diào)用就返回,甚至很多方法連返回值都沒有,調(diào)用時(shí)不能判定函數(shù)調(diào)用是否正確,因?yàn)檎{(diào)用的時(shí)候函數(shù)并沒有真正執(zhí)行,僅是被記錄,這個(gè)過程被稱為錄制(Record)。

最終當(dāng)所有的命令都記錄完畢后,必須發(fā)送至Command Queue中ExecuteCommandList方法后,也就是將命令列表作為參數(shù)傳給這個(gè)函數(shù),一個(gè)命令列表才能去執(zhí)行。

為了安全控制,也就是防止因多線程渲染帶來的不必要沖突,命令列表的狀態(tài)被分為:錄制狀態(tài)和可Execute狀態(tài)(也叫關(guān)閉狀態(tài)),命令列表對象通常處在兩個(gè)狀態(tài)之一。

通常一個(gè)命令列表在被創(chuàng)建時(shí)是默認(rèn)處于錄制狀態(tài)的,此狀態(tài)下是不能被執(zhí)行的。錄制完成后我們調(diào)用命令列表對象的Close方法關(guān)閉它,它就變成了可執(zhí)行狀態(tài),就可以提交給Command Queue(命令隊(duì)列)的ExecuteCommandList方法去執(zhí)行,待執(zhí)行完畢后我們又調(diào)用命令列表的Reset方法使它恢復(fù)到記錄狀態(tài)即可。當(dāng)然Reset之后,命令列表之前記錄的命令也就丟失了,嚴(yán)格來說是這些命令被交給命令隊(duì)列去執(zhí)行了,而命令列表不在記錄原來的命令了。要理解這個(gè)概念,讓我們想象命令列表就是我們?nèi)ワ堭^吃飯時(shí)服務(wù)員用于填寫你點(diǎn)的菜的菜單,而命令隊(duì)列就是飯館的廚房,當(dāng)我們點(diǎn)完菜后服務(wù)員就將菜單交給了廚房,而他的手里就又是新的一頁空白菜單,準(zhǔn)備下一位客戶點(diǎn)菜了。

理解了命令列表,那么我們還需要在剛才那個(gè)飯館點(diǎn)菜的模型示例基礎(chǔ)上進(jìn)一步來思考一下,那就是我們點(diǎn)菜用的菜單紙是要提前準(zhǔn)備的,并且它是需要?jiǎng)討B(tài)分配的,雖然一般的都是固定大小的,但是也會(huì)有客人只點(diǎn)幾樣小菜,而整個(gè)菜單就有很多空白浪費(fèi)了,也有很多客人可能因?yàn)辄c(diǎn)很多菜而使用了幾頁菜單紙。在D3D12中,用于最終記錄這些命令的“菜單紙”就是命令分配器對象,其接口是ID3D12CommandAllocator。這也是D3D12中加入的諸多關(guān)于存儲管理概念對象中的一個(gè)。從本質(zhì)上講,其實(shí)不論什么圖形命令,最終都是需要GPU來執(zhí)行,那么這個(gè)分配器我們可以理解為本質(zhì)上是用來管理記錄命令用的顯存的。不過幸運(yùn)的是目前這個(gè)接口的細(xì)節(jié)在D3D12的調(diào)用過程中是不用過多關(guān)注的,因?yàn)樗壳暗脑O(shè)計(jì)只是為了體現(xiàn)了命令分配這個(gè)概念,實(shí)質(zhì)上并沒有什么具體直接的方法需要我們?nèi)フ{(diào)用,我們要做的只是說需要?jiǎng)?chuàng)建它,并將它和某個(gè)命令列表捆在一起即可。

當(dāng)然命令分配器和命令列表最好是一對一的,用剛才飯館點(diǎn)菜模型示例來說,我們肯定希望每個(gè)服務(wù)員都單獨(dú)拿一份菜單來給你點(diǎn)菜,所以以此來理解的話,你就明白一對一的意義了。我相信誰都不會(huì)喜歡自己點(diǎn)的菜和別人點(diǎn)的菜混在一起吧?

從另一方面說,之所以要這么一個(gè)有點(diǎn)像純概念式的對象接口來表達(dá)分配命令存儲管理的概念的真正意義何在呢?那就是為了多線程渲染。因?yàn)槲乙呀?jīng)不止一次的強(qiáng)調(diào)過D3D12中加入并強(qiáng)化的核心概念就是多線程渲染。注意真正引入多線程渲染是在D3D11中,只不過D3D11中仍然保留了同步化的渲染支持。同時(shí)D3D11的多線程渲染貌似用的并不多,也沒什么名氣,也或者是我孤陋寡聞了。

而在D3D12中,管你喜歡不喜歡,渲染已經(jīng)完全是多線程化了。徹底整明白多線程渲染的基本編碼方式就是這篇教程的核心目的了,這也是我們徹底征服D3D12的基礎(chǔ)中的基礎(chǔ)。所以這里我也要多費(fèi)些篇幅。

了解多線程編程的開發(fā)者,應(yīng)該很清楚一個(gè)概念,那就是,從本質(zhì)上說多線程其實(shí)不難。比如在Windows上調(diào)用CreateThread(推薦__beginthread)就可以創(chuàng)建線程,而真正的難點(diǎn)在于如何安全的在多線程環(huán)境下使用內(nèi)存。而在D3D12中,實(shí)質(zhì)為了徹底實(shí)現(xiàn)強(qiáng)悍的多線程渲染,最終是加入了大量的存儲管理的概念,編程的復(fù)雜性也來源于此。在這里實(shí)際命令分配器就是典型的存儲管理概念的體現(xiàn)。在D3D12中我們一般是為每一個(gè)渲染線程創(chuàng)建一個(gè)命令列表和一個(gè)命令分配器,這樣就像我們舉得飯館點(diǎn)菜的例子中一樣,大一點(diǎn)的上規(guī)模的餐館中,一般每桌都有專門的服務(wù)員為你點(diǎn)菜,并且人手一份菜單,而廚房則通常會(huì)有多位不同的廚師負(fù)責(zé)不同的菜品的烹飪。從這里也可以看出為什么D3D12中非要加入多線程渲染的,那就是為了高效率、高品質(zhì)、高并行,也就是說你不是再像以前一樣開一個(gè)也許只有一兩個(gè)廚師三四個(gè)服務(wù)員的小餐館了,而是像現(xiàn)在這樣要開大型的餐廳了,幾乎每個(gè)餐桌都有專門的服務(wù)員點(diǎn)菜(多線程生成多個(gè)命令列表),后臺的廚房中則是n個(gè)廚師在并行的為不同桌的客人烹制菜品,甚至廚師的分工都被細(xì)化了,有些負(fù)責(zé)烹制西餐、有些負(fù)責(zé)粵菜、有些負(fù)責(zé)涼菜等等(多引擎),想象一下使用了多線程渲染之后,你的引擎也是在異曲同工的繪制復(fù)雜大型的場景的情景。而這一切都在高效的D3D12接口的支持下,有條不紊的進(jìn)行。

十一、創(chuàng)建命令分配器接口、命令列表接口和柵欄對象接口

看過上面那段介紹,在概念上你已經(jīng)消化吸收了多線程渲染的基本原理。當(dāng)然在我們現(xiàn)在的例子中,還沒有真正用起多線程渲染,將來我們就會(huì)用到。提前說這些的真實(shí)目的是因?yàn)镈3D12本質(zhì)全是異步的,所以我們還是需要將我們這個(gè)單線程例子,按照多線程渲染的套路來編寫,這也是D3D12編程比較復(fù)雜的一個(gè)體現(xiàn)。首先我們要像下面這樣創(chuàng)建一個(gè)命令分配器,然后在創(chuàng)建一個(gè)命令列表:

// 12、創(chuàng)建命令列表分配器 GRS_THROW_IF_FAILED(pID3DDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&pICommandAllocator))); // 13、創(chuàng)建圖形命令列表 GRS_THROW_IF_FAILED(pID3DDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, pICommandAllocator.Get(), pIPipelineState.Get(), IID_PPV_ARGS(&pICommandList)));

從上面代碼再結(jié)合我們之前講過的多引擎知識大家應(yīng)該看明白我們創(chuàng)建了一個(gè)“直接”的命令分配器和命令列表,其“直接”的含義與直接命令隊(duì)列的含義對應(yīng),就是用來分配和記錄所有種類的命令的。同時(shí)在創(chuàng)建命令列表時(shí),我們還要指出它對應(yīng)的命令分配器對象的指針,這也就是一一對應(yīng)含義的體現(xiàn)。

接下來因?yàn)槲覀儽举|(zhì)上還是在用異步的思路來調(diào)用渲染的過程,所以我們就還需要?jiǎng)?chuàng)建控制CPU和GPU同步工作的同步對象——柵欄(Fence),代碼如下:

// 14、創(chuàng)建一個(gè)同步對象——圍欄,用于等待渲染完成,因?yàn)楝F(xiàn)在Draw Call是異步的了 GRS_THROW_IF_FAILED(pID3DDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pIFence))); n64FenceValue = 1; // 15、創(chuàng)建一個(gè)Event同步對象,用于等待圍欄事件通知 hFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (hFenceEvent == nullptr) {GRS_THROW_IF_FAILED(HRESULT_FROM_WIN32(GetLastError())); }

上述代碼可以看出創(chuàng)建一個(gè)柵欄很簡單,當(dāng)然為了完成真正的同步控制(這里指CPU與GPU渲染間的同步,以后所說的同步都要結(jié)合上下文明白我們指的是什么同步),我們還要準(zhǔn)備一個(gè)稱為圍欄值的64位無符號整數(shù)變量,在示例中就是n64FenceValue,和一個(gè)Event(事件)系統(tǒng)內(nèi)核對象。在這里我們都是先準(zhǔn)備好他們,后面我們就會(huì)真正用到它。

十二、渲染

終于最后一步就是我們進(jìn)入消息循環(huán)調(diào)用渲染了,代碼如下:

//創(chuàng)建定時(shí)器對象,以便于創(chuàng)建高效的消息循環(huán) HANDLE phWait = CreateWaitableTimer(NULL, FALSE, NULL); LARGE_INTEGER liDueTime = {};liDueTime.QuadPart = -1i64;//1秒后開始計(jì)時(shí) SetWaitableTimer(phWait, &liDueTime, 1, NULL, NULL, 0);//40ms的周期 //開始消息循環(huán),并在其中不斷渲染 DWORD dwRet = 0; BOOL bExit = FALSE; while (!bExit) {dwRet = ::MsgWaitForMultipleObjects(1, &phWait, FALSE, INFINITE, QS_ALLINPUT);switch (dwRet - WAIT_OBJECT_0){case 0:case WAIT_TIMEOUT:{//計(jì)時(shí)器時(shí)間到//開始記錄命令pICommandList->SetGraphicsRootSignature(pIRootSignature.Get());pICommandList->RSSetViewports(1, &stViewPort);pICommandList->RSSetScissorRects(1, &stScissorRect);// 通過資源屏障判定后緩沖已經(jīng)切換完畢可以開始渲染了pICommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pIARenderTargets[nFrameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(pIRTVHeap->GetCPUDescriptorHandleForHeapStart(), nFrameIndex, nRTVDescriptorSize);//設(shè)置渲染目標(biāo)pICommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);// 繼續(xù)記錄命令,并真正開始新一幀的渲染const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };pICommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);pICommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);pICommandList->IASetVertexBuffers(0, 1, &stVertexBufferView);//Draw Call!!!pICommandList->DrawInstanced(3, 1, 0, 0);//又一個(gè)資源屏障,用于確定渲染已經(jīng)結(jié)束可以提交畫面去顯示了pICommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pIARenderTargets[nFrameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));//關(guān)閉命令列表,可以去執(zhí)行了GRS_THROW_IF_FAILED(pICommandList->Close());//執(zhí)行命令列表ID3D12CommandList* ppCommandLists[] = { pICommandList.Get() };pICommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);//提交畫面GRS_THROW_IF_FAILED(pISwapChain3->Present(1, 0));//開始同步GPU與CPU的執(zhí)行,先記錄圍欄標(biāo)記值const UINT64 fence = n64FenceValue;GRS_THROW_IF_FAILED(pICommandQueue->Signal(pIFence.Get(), fence));n64FenceValue++;// 看命令有沒有真正執(zhí)行到圍欄標(biāo)記的這里,沒有就利用事件去等待,注意使用的是命令隊(duì)列對象的指針if (pIFence->GetCompletedValue() < fence){GRS_THROW_IF_FAILED(pIFence->SetEventOnCompletion(fence, hFenceEvent));WaitForSingleObject(hFenceEvent, INFINITE);}//到這里說明一個(gè)命令隊(duì)列完整的執(zhí)行完了,在這里就代表我們的一幀已經(jīng)渲染完了,接著準(zhǔn)備執(zhí)行下一幀//渲染//獲取新的后緩沖序號,因?yàn)镻resent真正完成時(shí)后緩沖的序號就更新了nFrameIndex = pISwapChain3->GetCurrentBackBufferIndex();//命令分配器先Reset一下GRS_THROW_IF_FAILED(pICommandAllocator->Reset());//Reset命令列表,并重新指定命令分配器和PSO對象GRS_THROW_IF_FAILED(pICommandList->Reset(pICommandAllocator.Get(), pIPipelineState.Get()));//GRS_TRACE(_T("第%u幀渲染結(jié)束.\n"), nFrame++);}break;case 1:{//處理消息while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){if (WM_QUIT != msg.message){::TranslateMessage(&msg);::DispatchMessage(&msg);}else{bExit = TRUE;}}}break;default:break;}}

代碼中使用了一個(gè)精確定時(shí)的消息循環(huán)。

這里重點(diǎn)講一下資源屏障的用法和意義。資源屏障的原理在之前Vulkan的文章中已經(jīng)有過很形象的講解了。在這段代碼中有兩處用到資源屏障,我們可以看到資源屏障的運(yùn)用其實(shí)也很簡單,它核心的思想就是追蹤資源權(quán)限的變化,從而同步GPU上前后執(zhí)行命令對訪問資源的操作。代碼中第一處:

pICommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pIARenderTargets[nFrameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

的確切含義就是說我們判定并等待完成渲染目標(biāo)的資源是否完成了從Present(提交)狀態(tài)切換到Render Target(渲染目標(biāo))狀態(tài)了。ResourceBarrier是一個(gè)同步調(diào)用,與一般的同步調(diào)用不同,首先它在命令列表記錄時(shí)也是立即返回的,只是個(gè)同步調(diào)用記錄;其次它的目的是同步GPU上前后命令函數(shù)之間對同一資源的訪問操作的,再次它真正在Execute之中才是同步執(zhí)行的,而我們在CPU的代碼中是感知不到的;我們唯一能確定的就是在Execute一個(gè)命令列表的過程中,如果它被真正執(zhí)行完了之后,那么就完全可以確定被轉(zhuǎn)換狀態(tài)的資源已經(jīng)從其之前命令函數(shù)操作要求的狀態(tài)轉(zhuǎn)換成了之后操作要求的狀態(tài)了。或者形象的理解這個(gè)函數(shù)在正在被執(zhí)行的時(shí)候是不能被“跳過”的。那么這里可能難以理解的是為什么說資源訪問狀態(tài)的切換就可以完成一個(gè)同步的“等待”操作呢?這就又不得不說GPU構(gòu)造的特殊性了,因?yàn)槿缜八鑫覀円呀?jīng)不止一次講到GPU是一個(gè)巨大的SIMD架構(gòu)的處理器了,因此它上面的所謂命令的執(zhí)行,往往是由若干個(gè)ALU(通常是成千上萬個(gè))并行執(zhí)行訪問具體的一個(gè)資源(其實(shí)就是一塊顯存)上不同單元來完成的,而且每種命令對同一塊資源的訪問要求又是完全不同的,比如我們這里就是Present操作,它是只讀的要求,而渲染的命令又要求這塊資源是Render Target,也就是可寫的,所以兩個(gè)操作直接就需要來回控制這種狀態(tài)的切換,而GPU本身知道那個(gè)操作已經(jīng)完成可以執(zhí)行真正的狀態(tài)切換了,而狀態(tài)切換成功就說明之前操作已經(jīng)全部完成,可以進(jìn)行之后的操作了。這樣一來其實(shí)Transition這個(gè)函數(shù)的含義也就明白了。當(dāng)然這里的CD3DX12_RESOURCE_BARRIER類也是來自d3d12.h中,也是其基本結(jié)構(gòu)的擴(kuò)展,真實(shí)的結(jié)構(gòu)體中就是要求我們指明是那塊資源,并且指明之前操作要求的訪問狀態(tài)是什么,以及之后的訪問狀態(tài)是什么,而這個(gè)類的封裝就使初始化這個(gè)結(jié)構(gòu)體更加的簡便和直觀了。

運(yùn)行即可見如下結(jié)果:

總結(jié)

以上是生活随笔為你收集整理的DirectX12_入门之三角形的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

曰本三级在线 | 亚洲一级电影 | 亚洲精品综合一区二区 | 日韩三级精品 | 久久高清免费视频 | 亚洲一区二区视频在线播放 | 欧美日韩一区三区 | 在线免费观看黄色 | 欧美在线一 | 97成人在线 | 亚洲电影自拍 | 91色蜜桃| 免费网站看av片 | 人人干天天射 | 九九99 | 国产精品精品国产婷婷这里av | 丁香五婷 | 国精产品999国精产 久久久久 | 国产精品 视频 | 免费黄色小网站 | 成年美女黄网站色大片免费看 | 国产精品国产亚洲精品看不卡 | 亚洲视频综合在线 | 国产精品高潮久久av | 免费看一级片 | 国产伦理一区 | av在线永久免费观看 | 狠狠干综合 | 久久久久久久国产精品视频 | 久久综合五月婷婷 | 四虎www| 精品在线二区 | 欧美日本不卡高清 | 看av在线| 精品黄色在线 | 久久综合中文色婷婷 | a级一a一级在线观看 | 91av原创| 亚洲三级视频 | 国产一级在线播放 | 日韩在线视频观看免费 | 国产一级片免费视频 | 久久天天躁夜夜躁狠狠85麻豆 | 成人国产一区二区 | 天天干天天射天天插 | 久草网视频 | www操操 | 精品一二三四五区 | 国产精品99久久久久久武松影视 | 国产精品99久久久久久大便 | 国产一卡久久电影永久 | 操碰av | 美女禁18| 在线看小早川怜子av | 奇米影视在线99精品 | 日日草视频 | 超碰人人干人人 | 国产香蕉视频 | 国产一区二区视频在线 | 亚洲精品高清一区二区三区四区 | 国产97在线视频 | 亚洲综合在线发布 | 福利视频导航网址 | 日韩超碰在线 | 成人一区影院 | 亚洲精品永久免费视频 | 色免费在线| 国产一区二三区好的 | 日韩欧美在线免费 | 欧美精品999 | 美女又爽又黄 | 久久精品视频中文字幕 | 中文字幕日本在线观看 | 精品久久久免费 | 亚洲精品国产成人av在线 | 国产成人免费观看久久久 | 在线观看播放av | www.色国产 | 91在线免费观看网站 | 国产精品一区二区av影院萌芽 | 国产精品久久精品国产 | 日黄网站 | 精品久久久久久久久久久久久久久久 | 97视频免费播放 | 超碰在线99 | 国产91亚洲| 大胆欧美gogo免费视频一二区 | 精品视频免费观看 | 毛片播放网站 | 久久久99久久 | av电影免费 | 亚洲片在线 | 少妇bbbb| 最新日韩视频 | 人人超碰免费 | 国产精品免费久久久久影院仙踪林 | 亚洲精品激情 | 日韩二区三区 | 超碰在线最新网址 | 99综合电影在线视频 | 久久福利小视频 | 免费视频一级片 | 久久精品一区二区三区中文字幕 | 黄色av免费在线 | 欧美日韩一区二区三区视频 | a午夜在线 | 二区三区在线观看 | 日本不卡一区二区三区在线观看 | 午夜精品三区 | 99视频偷窥在线精品国自产拍 | 久久亚洲欧美 | 久久黄色片 | 视频一区二区免费 | 91麻豆精品国产91久久久久久久久 | 欧美成人手机版 | 国产一级视屏 | 在线免费观看黄色小说 | 国产精品理论片在线播放 | 日本性动态图 | 91精品久久久久久粉嫩 | 欧美日韩中文在线视频 | 亚洲欧美视频在线 | 亚洲精品456在线播放乱码 | 欧美天天综合网 | 日本中文字幕在线电影 | 99亚洲国产精品 | 久久人人97超碰国产公开结果 | 久久综合网色—综合色88 | 午夜精品av在线 | 欧美日韩中文字幕在线视频 | 日韩av不卡在线观看 | 91精品国产九九九久久久亚洲 | 天天色天天色天天色 | 精品久久片 | 欧美日韩二三区 | 久久久久久久久久久精 | av成人在线看 | 亚洲理论在线观看电影 | 日韩和的一区二在线 | 久久中文字幕视频 | 成人av免费在线看 | 久久国产精品久久精品 | 天天爽夜夜爽精品视频婷婷 | 人人干人人艹 | 在线国产高清 | 亚洲一区二区天堂 | av青草| 中文字幕中文字幕在线中文字幕三区 | 激情av综合 | 综合久久久久 | bbb搡bbb爽爽爽 | 99久久日韩精品免费热麻豆美女 | 深夜免费网站 | 日日干美女 | 国内精品久久久久久久影视麻豆 | 成人午夜精品 | 成年人视频在线免费播放 | 国产一区二三区好的 | 在线影院中文字幕 | 成人黄色一级视频 | 热久久视久久精品18亚洲精品 | 最新日韩在线 | 91少妇精拍在线播放 | 国产精品免费久久久久 | 日日日日日 | 日韩精品一区二区在线观看视频 | 999久久久免费视频 午夜国产在线观看 | 欧美日韩网址 | 久久成年人网站 | 亚洲精品午夜久久久久久久久久久 | 亚洲观看黄色网 | 天天干天天草天天爽 | 成人app在线免费观看 | 日韩高清不卡一区二区三区 | 黄色精品久久久 | 国产亚洲精品久久 | 99久久精品无免国产免费 | 亚洲精品黄色 | 国产精品 日韩 欧美 | 日韩成人精品在线观看 | 国产小视频在线观看免费 | 国内久久精品视频 | 国产精品美女免费看 | 亚洲精品国偷拍自产在线观看 | 毛片在线播放网址 | 久久人人爽爽人人爽人人片av | 国产精品视频永久免费播放 | 91av在线免费视频 | 午夜日b视频 | 久久超碰97 | 久久久精品高清 | 久久美女电影 | 日韩精品中文字幕在线观看 | 亚洲精品一区二区18漫画 | 狠狠干网 | 成人av网站在线 | 四月婷婷在线观看 | 91久久国产综合精品女同国语 | 久久免费视频网 | 日韩视频一区二区三区 | 免费观看性生活大片3 | 免费在线观看毛片网站 | 久草在线资源观看 | 黄色成人av在线 | 久久丁香网 | 黄色网www| 天天做天天爽 | .国产精品成人自产拍在线观看6 | 天天色天天上天天操 | 97超碰中文字幕 | 久草在线手机观看 | 天天操网站 | 天天激情站 | 久久久精品福利视频 | 久久久2o19精品| 五月天狠狠操 | 亚洲免费av在线播放 | 在线免费观看的av | 久久97视频| 成人一区二区三区在线观看 | 久久精品1区 | 99亚洲精品 | 久久午夜国产 | 日批视频在线播放 | 在线只有精品 | 久章草在线观看 | 在线观看视频国产一区 | 免费黄a| 国产一二三四在线视频 | 亚洲欧美一区二区三区孕妇写真 | 成年人电影毛片 | 亚洲国产丝袜在线观看 | 伊人天天狠天天添日日拍 | 久久久久免费 | 国产视频2 | 欧美日韩aa| 五月导航 | 国产黄色大片免费看 | 99精品国产福利在线观看免费 | 91成人蝌蚪 | 久99久视频 | 探花视频免费在线观看 | 97精品国产97久久久久久久久久久久 | 人人网人人爽 | 福利视频一区二区 | 日韩有码在线观看视频 | 丁香六月激情婷婷 | 亚洲欧美日韩中文在线 | 欧洲亚洲女同hd | 久久不射电影网 | 狠狠躁日日躁狂躁夜夜躁av | www.久热 | 就色干综合| 天天操比| 国产中文字幕一区 | 国产馆在线播放 | av片无限看| 日韩精品欧美视频 | 精品久久久久久电影 | 一区二区三区在线免费观看视频 | 欧美精品久久人人躁人人爽 | av短片在线| 天天操网 | 国产区久久 | 奇人奇案qvod | 欧美一级片免费观看 | 丁香婷婷久久 | 精品一区二区6 | 国产又粗又猛又爽又黄的视频免费 | 日本特黄特色aaa大片免费 | 久久字幕精品一区 | 97精品国产97久久久久久免费 | 99热999 | 国产 色| 日韩久久激情 | 免费久久网站 | 天天干,天天操 | 日韩av在线一区二区 | 国产精品久久久久久欧美 | 青草视频在线 | www.99热精品 | 久久超碰97 | 中文字幕视频在线播放 | 日日干天天插 | 中文字幕一区二区三区久久蜜桃 | 91免费黄视频 | 日本最大色倩网站www | 久久久久久久久久网 | 三级大片网站 | 日韩精品一区二区三区三炮视频 | 亚洲国产精品一区二区尤物区 | 色欧美88888久久久久久影院 | 91免费网址| 成人一级片免费看 | 亚洲黄色一级视频 | www.国产视频 | 91在线视频免费91 | 成人羞羞视频在线观看免费 | 天天激情天天干 | www.com黄| 精品美女视频 | 国产日本三级 | 免费合欢视频成人app | 久久不卡视频 | .国产精品成人自产拍在线观看6 | 天堂黄色片 | 国产一区二区在线播放视频 | 麻豆91在线看 | 欧美aa在线 | 日韩中文字幕免费 | 九九欧美视频 | 国产精品入口传媒 | 成人小视频在线观看免费 | 粉嫩av一区二区三区四区 | 欧美三人交 | 91精品久久久久久久久久久久久 | 麻豆小视频在线观看 | 日韩国产欧美视频 | 亚洲精品视频在线观看免费视频 | 久久精品99国产 | 免费在线观看中文字幕 | 日韩网站中文字幕 | 中文字幕在线观看2018 | 欧美日韩观看 | 99视频精品在线 | 久久99免费视频 | 97视频在线观看成人 | 日韩网站在线免费观看 | 激情视频在线观看网址 | 不卡视频国产 | 狠狠色网 | 婷婷在线看 | 国产精品一区二区在线 | 国产视频久久久久 | 欧美一区影院 | 中文字幕网站 | 亚洲精品裸体 | 在线观看国产区 | 久久免费视频6 | 中文字幕在线观看完整 | 亚洲国产片色 | 亚洲精品在线国产 | 麻花传媒mv免费观看 | 中文字幕亚洲精品在线观看 | 999视频网 | 久久99视频 | 国产成人精品综合 | 国内外激情视频 | 国产操在线 | 在线免费三级 | 国产日韩欧美在线影视 | 人人添人人澡 | 中文字幕视频观看 | 欧美一级电影片 | 香蕉网在线观看 | 久久人人爽人人片 | 国色天香第二季 | 黄色a在线观看 | 国产精品久久久久一区二区国产 | 色综合夜色一区 | 99人成在线观看视频 | 香蕉在线影院 | 亚洲精品三级 | 亚洲污视频| 中文字幕一区二区三区四区视频 | 在线观看视频一区二区 | 亚洲欧美在线观看视频 | 成人中文字幕在线观看 | 成人av在线电影 | 2024国产精品视频 | 国产中文字幕视频在线 | av三级在线免费观看 | 久久资源在线 | 91精品欧美一区二区三区 | 亚洲涩涩色 | 日韩av一区在线观看 | 99精品一区二区三区 | 日本中文不卡 | 久久国产精品99久久人人澡 | 五月天天色 | 亚洲91精品在线观看 | 日韩电影精品一区 | 9999免费视频 | 亚洲精品视频久久 | 日一日干一干 | 精品一区中文字幕 | www九九热| 四虎成人网 | 亚洲精品麻豆视频 | 亚洲精品视频免费 | 97在线视频免费播放 | 免费看三级网站 | 亚洲精品国产区 | 午夜18视频在线观看 | 成人免费视频视频在线观看 免费 | 日韩免费福利 | 日日干 天天干 | 久久天天躁狠狠躁夜夜不卡公司 | 免费一区在线 | 在线精品视频免费观看 | 国产精品视频 | 奇米网777| 国产精品一区二区电影 | 黄色大片入口 | 久久精品男人的天堂 | www.天堂av | 国产99久久久精品 | 久久久久久高潮国产精品视 | 欧美视屏一区二区 | 最新日本中文字幕 | 天天操天天爽天天干 | 最近的中文字幕大全免费版 | 国产美女无遮挡永久免费 | 亚洲一级电影 | 国产99久久久国产精品 | 免费91在线 | 美女网站视频久久 | 丁香六月伊人 | 国产精品日韩精品 | 中文字幕日韩免费视频 | 在线黄色国产 | 二区三区在线视频 | 狠狠操影视 | 久久久久久国产精品 | 九九久久精品 | 五月天色婷婷丁香 | 国产日韩欧美在线免费观看 | 成人免费在线观看电影 | 中文字幕在线有码 | 日韩av一区二区在线影视 | 在线观看91久久久久久 | 久久精品91视频 | 精品综合久久久 | 婷婷综合国产 | 亚洲电影久久久 | 欧美日韩亚洲精品在线 | 99精品国产99久久久久久福利 | 精品国产91亚洲一区二区三区www | 在线一区二区三区 | 99国内精品久久久久久久 | 国产精品成人免费一区久久羞羞 | 黄色精品一区 | 人人模人人爽 | 日韩欧美v | 久久精品之 | 国产一区二区三区黄 | 久久精品毛片 | 免费网站在线观看成人 | 99视频在线精品免费观看2 | 国产精品久久久久久久久久新婚 | 久草在线资源观看 | 国产精品黄色影片导航在线观看 | 久久久精品国产免费观看一区二区 | 91视频首页 | 中文字幕在线观看一区二区三区 | 一区二区精品在线 | 亚洲最大免费成人网 | 国产精品系列在线 | 日韩精品一区电影 | 亚洲精品在线视频网站 | 一级黄色大片在线观看 | 久香蕉| 久久精品福利视频 | 国产裸体永久免费视频网站 | 美女黄频在线观看 | 久久久久国产a免费观看rela | 午夜精品一区二区三区在线视频 | 中文区中文字幕免费看 | 久草在线观看资源 | 人人澡人摸人人添学生av | 日韩视频欧美视频 | 国产视频一区在线 | 免费视频成人 | 国产aa精品 | 成人免费网站视频 | 日本最新中文字幕 | 97国产一区二区 | 手机av在线网站 | 中文字幕电影高清在线观看 | av在线电影免费观看 | 综合在线观看 | 91精品国产乱码久久桃 | 在线观看亚洲电影 | av免费电影在线观看 | 日韩二区在线 | 成人一区电影 | 亚洲美女精品 | japanesexxx乱女另类 | 久久精品99久久久久久 | 亚洲精品动漫久久久久 | 韩国一区二区在线观看 | 日韩网站中文字幕 | 96精品在线| 精品国产一二三四区 | 欧美日韩国产一二 | 国产精品久久久久久欧美 | 午夜久久福利视频 | 国产小视频你懂的在线 | 久久久久婷 | www.日本色 | 91成人黄色 | 国产在线观看,日本 | 婷婷六月综合亚洲 | 国产成人精品一区二区三区网站观看 | 国产亚洲精品久久网站 | 久精品视频免费观看2 | 日日久视频 | 亚洲五月婷婷 | 亚洲精品1区2区3区 超碰成人网 | 丁香婷五月 | 美女网站色免费 | 久久久免费少妇 | 日韩精品久久久久久久电影99爱 | 国产精品岛国久久久久久久久红粉 | 伊人伊成久久人综合网小说 | a在线观看国产 | 欧美久久99 | 91丨精品丨蝌蚪丨白丝jk | 亚洲午夜久久久久久久久电影网 | 国产精品国产三级国产不产一地 | 99精品国产99久久久久久福利 | 久久久精品欧美 | 国产成人精品av | 色就干| 国产一区二区精品 | 一区 在线 影院 | 国产日韩精品视频 | 国产九九九精品视频 | 亚洲男男gaygayxxxgv | 成人精品久久久 | 国产黄色片免费看 | 黄色精品久久久 | 中文字幕免费 | 成人va视频 | 色爽网站 | 午夜在线免费观看 | 国产视频中文字幕 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 91传媒在线| 麻豆91在线看 | 久久涩视频| 奇米网8888 | 一区二区三区精品久久久 | 摸bbb搡bbb搡bbbb | 久久福利剧场 | 深夜男人影院 | 久久久www免费电影网 | 国产精品久免费的黄网站 | 免费在线观看的av网站 | 激情五月色播五月 | 国产一区视频免费在线观看 | 国产精品一区二区三区久久久 | 久久久久久久免费看 | 色婷婷综合在线 | 日躁夜躁狠狠躁2001 | 欧美va天堂va视频va在线 | 在线国产99 | 丝袜av一区 | 精品在线免费观看 | 黄色av在| 96视频免费在线观看 | 色综合久久久 | 国产成人精品综合久久久久99 | 亚洲国产中文在线观看 | 97在线观看视频 | 欧美在线观看视频一区二区 | 久久国产一区二区三区 | 91精品在线免费视频 | 视频一区二区免费 | 久久精品—区二区三区 | 日韩1级片 | 国产一级久久 | 美女黄久久 | 狠狠操精品 | 最新影院 | 欧美va电影 | 欧洲精品一区二区 | 天天摸天天操天天舔 | 国产福利91精品一区二区三区 | 噜噜色官网| 一区二区三区www | 中文字幕一区2区3区 | 天天操天天射天天添 | 色丁香久久 | 亚洲视频1区2区 | 精品99免费| www.亚洲| 国产自产在线视频 | av性在线| 91亚州 | 亚洲综合色播 | 麻豆影视在线观看 | 激情综合网色播五月 | 亚洲最新av网站 | 亚洲一区日韩 | 国产 一区二区三区 在线 | 黄色精品在线看 | 欧美国产日韩中文 | 成年人免费在线看 | 国产日产精品久久久久快鸭 | 18久久久久| 亚洲视频资源在线 | 天天射成人 | 精品久久久一区二区 | 亚洲午夜精品久久久 | 日韩草比| 久久久天天操 | 久久亚洲视频 | 日韩网站一区二区 | av大片免费看 | 一区二区不卡视频在线观看 | 国内免费久久久久久久久久久 | 一区二区三区电影在线播 | 激情久久伊人 | 欧美夫妻生活视频 | 久久国产福利 | 免费久久99精品国产 | 91最新地址永久入口 | 久久精品二区 | 亚洲天堂网在线播放 | 狠狠干 狠狠操 | 亚洲精品资源在线观看 | 欧美a在线免费观看 | 四虎国产精品免费 | 天无日天天操天天干 | 国产一区在线免费 | 人人干干人人 | 天天操夜操视频 | 久久精品免费观看 | 欧美亚洲xxx | 草免费视频 | 免费网站v | 一级黄色大片在线观看 | 免费在线观看av网址 | 国产手机在线精品 | 日韩av电影网站在线观看 | a视频免费 | 日韩在线观看视频在线 | 午夜久久影视 | 亚洲午夜精品一区二区三区电影院 | 成人xxxx| 色综合色综合色综合 | 最近免费中文字幕 | 91三级视频| 日日爽视频 | 99热精品免费观看 | 免费看国产精品 | 久久亚洲福利 | 免费午夜av | 中文字幕在线视频第一页 | 97视频在线观看播放 | 成年人视频在线免费播放 | 人人爽人人爽人人片av | 精品一区二区在线免费观看 | 激情欧美丁香 | 久久视精品| 久久a久久 | 日韩va欧美va亚洲va久久 | 中文字幕一区二区三区在线播放 | 97超碰在 | 日韩久久久 | 午夜精品久久久久久中宇69 | 麻豆视频成人 | 日韩一区二区三区视频在线 | 99精品免费久久久久久日本 | 五月天综合色激情 | 激情久久久久久久久久久久久久久久 | 麻豆国产视频下载 | 国产一级在线播放 | 日本三级不卡视频 | 国产亚洲精品免费 | 天天插天天狠天天透 | 日韩午夜小视频 | 久久久国产精品亚洲一区 | 麻豆一区二区三区视频 | 99热日本| 这里只有精品视频在线 | 免费观看一区二区 | 91精品免费看| 亚洲激情综合 | 黄色午夜 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 久久黄色片 | 成年人免费看片网站 | 欧美a√在线 | 久久久久成人精品 | 日韩久久久久久久 | 欧美激情奇米色 | 日韩成人精品在线观看 | 又粗又长又大又爽又黄少妇毛片 | 亚洲欧美日韩精品久久久 | 久久综合久久综合九色 | 国产精品福利在线观看 | 丁香五月亚洲综合在线 | 国产中文字幕视频在线 | 中文av在线播放 | 麻豆精品在线 | av在线免费观看不卡 | 在线视频久| 国产精品网站一区二区三区 | 国产美女主播精品一区二区三区 | 91经典在线 | 日本中文字幕在线播放 | 中文字幕一区二区在线播放 | 91九色国产在线 | 黄色三级在线看 | 五月婷婷视频在线 | 五月开心婷婷 | 66av99精品福利视频在线 | 99这里只有精品99 | 欧美日韩精品区 | 久久视频一区二区 | 在线观看成人福利 | 在线观看中文字幕一区 | 日日爱夜夜爱 | 91精品啪在线观看国产 | 精品国产美女 | 国产一级91 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 在线观看日韩精品 | 久久在现| 国产精品久久久久久69 | 婷婷丁香在线观看 | 成人全视频免费观看在线看 | 国产成年免费视频 | 国产一区二区日本 | 国产字幕在线看 | 色av男人的天堂免费在线 | 国产在线观看你懂得 | av福利网址导航大全 | 日韩成人在线免费观看 | 狠狠综合 | 亚洲aⅴ在线观看 | 最近最新mv字幕免费观看 | 国产一区二区三区免费在线 | 五月婷香蕉久色在线看 | www.狠狠操.com | 免费看片色 | 1024手机在线看 | 免费成人在线观看视频 | 成人免费视频网站 | 一区二区三区手机在线观看 | 人人澡超碰碰 | 91亚洲精品国偷拍 | 99久久精品国产亚洲 | 国产成人精品久久久久蜜臀 | 狠狠色丁香婷婷综合 | 久草视频在线观 | 久久99亚洲精品久久 | 韩国精品福利一区二区三区 | 亚洲国产电影在线观看 | 国产成人一区二区三区影院在线 | 91成人精品国产刺激国语对白 | 久久社区视频 | 日韩中文字幕第一页 | 欧美日韩激情视频8区 | 免费日韩视| 国产一级电影在线 | 欧美污污视频 | 婷婷六月天天 | 99在线视频播放 | 久久久亚洲麻豆日韩精品一区三区 | 毛片.com| 日日夜夜狠狠干 | 欧美日在线 | 91久久人澡人人添人人爽欧美 | 99久久精品免费看国产四区 | 成人超碰在线 | 久久精品久久精品 | 久久久精选 | 啪啪激情网| 久久久99久久 | 久久精品欧美一区二区三区麻豆 | 欧美精品久久久久性色 | 日韩手机在线观看 | 久操视频在线免费看 | 成人国产精品久久久 | 久久精品电影院 | 51久久夜色精品国产麻豆 | 日本中文字幕在线看 | 精品久久久久久久久亚洲 | 黄色大片日本免费大片 | 国产成人精品国内自产拍免费看 | 久草在线资源观看 | 97免费 | 97免费在线视频 | 久久精品一区二区三区中文字幕 | 欧美成人黄色片 | 免费观看成人 | 亚洲视频免费视频 | 不卡的av在线播放 | 激情婷婷久久 | 9999在线 | 一区二区亚洲精品 | 在线观看麻豆av | 婷婷六月色 | 永久免费毛片在线观看 | 97理论片| 婷婷色六月天 | 成人少妇影院yyyy | 色国产在线 | 99国产精品久久久久老师 | 国产xvideos免费视频播放 | 亚洲综合国产精品 | 国产精品永久免费 | 精品不卡视频 | 天天爱天天操天天爽 | 青春草视频在线播放 | 69夜色精品国产69乱 | 国产专区视频 | 色多多视频在线观看 | 国内久久看 | 亚洲精品国产精品国 | 欧美另类视频 | 久久成熟 | 欧美极品xxx| 成人在线视频免费 | 色综合中文字幕 | 午夜美女福利 | 久久国语 | 亚洲免费a| 国产黄色在线观看 | 美女网站黄免费 | av超碰在线| 去看片| 97超视频| 国产91精品看黄网站在线观看动漫 | 99久久激情视频 | 在线观看日韩精品 | 久操视频在线播放 | 欧美成人视 | 91女人18片女毛片60分钟 | 国产视频 亚洲视频 | 黄色在线观看www | 亚洲三级在线免费观看 | 成人天堂网 | 午夜在线观看一区 | 国产精品成人久久久久久久 | 999亚洲国产996395| 国产精品99久久久久的智能播放 | 日韩av一卡二卡三卡 | 欧美精品久久久久久久免费 | 免费在线观看成人小视频 | 91中文字幕一区 | 欧美日韩二三区 | 99久久www免费 | 五月婷婷在线视频观看 | www.888av| 狠狠狠狠狠狠 | av网站播放 | 久久亚洲国产精品 | 亚洲少妇久久 | 成人动漫一区二区三区 | 超碰在线人人草 | 成人av电影在线 | 国产高清福利在线 | 久久免费看视频 | 免费亚洲婷婷 | 国产精品久久久久一区二区国产 | 欧美午夜理伦三级在线观看 | 精品视频免费在线 | 日韩手机视频 | 国产精品一码二码三码在线 | 亚洲影院国产 | 91精品1区2区 | 一区二区三区视频在线 | 精品一区二区精品 | av怡红院 | 成人毛片在线观看视频 | 色播五月婷婷 | 在线观看aa| 99久久这里只有精品 | 久久久久久麻豆 | 久久国产精品久久w女人spa | 久久久久女人精品毛片 | 国产不卡av在线 | 激情五月婷婷综合 | 免费av网址在线观看 | 日韩欧美在线综合网 | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | 欧美午夜精品久久久久 | 久久久色 | 久久免费视频5 | 免费看色的网站 | 亚在线播放中文视频 | 国产黄色一级片 | 亚洲国产影院av久久久久 | 绯色av一区 | 91亚洲精品在线 | 亚洲国产97在线精品一区 | 人人草在线视频 | 国产福利一区二区三区视频 | 国产一区二区精品91 | 久久国产精品99国产 | 国产精品美女视频网站 | 久久人网 | 亚洲区视频在线观看 | 国内久久久 | 看片一区二区三区 | 又黄又色又爽 | 久草电影在线 | 国产精品原创av片国产免费 | 久久超碰在线 | 五月婷婷六月丁香在线观看 | 午夜性盈盈 | 成人中文字幕在线 | 成人av片在线观看 | 国产精品久久久久久久av电影 | 成年人在线观看网站 | 国产免费精彩视频 | 成年人国产视频 | 欧美在线视频一区二区三区 | 国产亚洲精品无 | 国产精品原创av片国产免费 | 久久一线| 国产精品一区二区av影院萌芽 | 国产成人精品久久亚洲高清不卡 | 亚洲激情一区二区三区 | 亚洲精品乱码久久久久 | 黄色在线看网站 | 人人超碰人人 | 97国产视频 | 久久综合中文色婷婷 | 91麻豆精品国产91久久久久久 | 国产.精品.日韩.另类.中文.在线.播放 | 国产亚洲精品久久久久久移动网络 | 91av免费看 | 亚洲精选在线观看 | av丝袜美腿 | 天天爽网站 | 不卡av在线播放 | 91桃色在线免费观看 | 国内一级片在线观看 | 亚洲成人精品久久 | 一区二区三区高清在线观看 | 99精品视频网 | 日韩久久精品一区二区三区下载 | 激情久久久久 | 日韩区在线观看 | 欧美成人精品欧美一级乱 | 在线播放视频一区 | 午夜私人影院 | 日韩免费一级a毛片在线播放一级 | 免费在线观看中文字幕 | 国产在线va | 伊人伊成久久人综合网站 | 日韩中文字幕国产精品 | 美女在线国产 | 永久免费毛片 | 999国内精品永久免费视频 | 国产91小视频 | 久久久国产视频 | 最近中文字幕免费av | 久久99精品国产麻豆宅宅 | 欧美韩国日本在线观看 | 亚洲精品tv久久久久久久久久 | 久久免费视频这里只有精品 | 天天av资源 | 免费黄色网址网站 | 欧美日韩亚洲在线观看 | 在线观看视频福利 | 久久九九国产精品 | 亚洲天堂网在线视频观看 | 国产精品99视频 | 91资源在线 | 欧美一级在线观看视频 | 日韩 在线| 日韩区欠美精品av视频 | 韩日精品中文字幕 | 精品久久一区二区三区 | 中文字幕91 | 黄色软件大全网站 | 日韩在线精品 | 成人av中文字幕在线观看 | avhd高清在线谜片 | 色婷婷天天干 | 啪啪av在线| 综合激情av | 日本久久久久久久久久久 | 丁香六月av | 在线免费观看成人 | 欧美最爽乱淫视频播放 | 国产一级片毛片 | www国产亚洲精品久久麻豆 | 国产精品久久久久久久久久久久午夜 | 五月天综合激情 | 日韩一级黄色大片 | 久久久久免费精品视频 | 久草视频在线播放 | 日韩高清免费电影 | 91麻豆精品国产91久久久使用方法 | 97超碰免费在线 | 亚洲婷婷在线 | 日韩动态视频 | 久久麻豆精品 | 国产18精品乱码免费看 | 国产一区二区在线免费播放 | 久久久网页 |