一、幾個概念
后臺緩沖surface,前臺surface,交換鏈,離屏surface
后臺緩沖surface和前臺緩沖surface總是成對出現的,當我們進行繪圖操作時,畫面有可能出現閃爍,這是因為當前繪制的一幅影像沒有同時出現在螢屏上導致的,更詳細的可以看這篇文章,前臺surface + 后臺surface就可以解決這個問題,前臺surface表示我們可以看到的畫面,后臺緩沖surface相當于一個緩沖區,每次繪圖是在后臺surface上進行的,然后完了再把兩個surface做一個交換,這樣后臺surface就可以顯示到螢屏上了,
交換鏈:就是將前臺surface和后臺surface做交換的
離屏surface:離屏surface是一個永遠看不到的表面,通常用來存放位圖,并對其中的一些資料做一些處理,
二、d3d9渲染yuv流程
1、創建D3D9:Direct3DCreate9
IDirect3D9 * WINAPI Direct3DCreate9(UINT SDKVersion);
SDKVersion設定為D3D_SDK_VERSION
IDirect3D9是一個顯示3D圖形的物理設備的C++物件,它可以用于獲得物理設備的資訊和創建一個IDirect3DDevice9介面
可以通過它的GetAdapterDisplayMode()函式獲取當前主顯卡輸出的解析度,重繪頻率等引數
D3DDISPLAYMODE d3dDisplayMode;
d3d9->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3dDisplayMode );
2、創建D3D9Device:CreateDevice
HRESULT CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS *pPresentationParameters,
IDirect3DDevice9 **ppReturnedDeviceInterface
);
引數介紹:
Adapter:表示顯示配接器的序號,D3DADAPTER_DEFAULT始終是主要的顯示配接器
DeviceType:D3DDEVTYPE列舉型別的成員,它表示所需的設備型別,如果所需的設備型別不可用,則該方法將失敗
D3DDEVTYPE_HAL :硬體加速
D3DDEVTYPE_REF :
D3DDEVTYPE_SW :軟體
D3DDEVTYPE_NULLREF :
D3DDEVTYPE_FORCE_DWORD :
hFocusWindow:Windows視窗句柄
BehaviorFlags:設定為軟體頂點處理D3DCREATE_SOFTWARE_VERTEXPROCESSING,或者硬體頂點處理D3DCREATE_HARDWARE_VERTEXPROCESSING
pPresentationParameters:用于D3D9Device的一些引數
ppReturnedDeviceInterface:回傳創建的device
下來我們看一下關于 D3DPRESENT_PARAMETERS 的設定
typedef struct _D3DPRESENT_PARAMETERS_
{
UINT BackBufferWidth;
UINT BackBufferHeight;
D3DFORMAT BackBufferFormat;
UINT BackBufferCount;
D3DMULTISAMPLE_TYPE MultiSampleType;
DWORD MultiSampleQuality;
D3DSWAPEFFECT SwapEffect;
HWND hDeviceWindow;
BOOL Windowed;
BOOL EnableAutoDepthStencil;
D3DFORMAT AutoDepthStencilFormat;
DWORD Flags;
/* FullScreen_RefreshRateInHz must be zero for Windowed mode */
UINT FullScreen_RefreshRateInHz;
UINT PresentationInterval;
} D3DPRESENT_PARAMETERS;
成員介紹:
BackBufferWidth:后臺緩沖surface的寬
BackBufferHeight:后臺緩沖surface的高
一般將其設定為frame width和frame height
BackBufferFormat:后臺緩沖surface的像素格式
BackBufferCount:后臺緩沖surface的個數
MultiSampleType:全屏抗鋸齒的型別
MultiSampleQuality:全屏抗鋸齒的質量等級
SwapEffect:指定表面在交換鏈中是如何被交換的
D3DSWAPEFFECT_DISCARD:后臺緩沖表面區的東西被復制到螢屏上后,后臺緩沖表面區的東西就沒有什么用了,可以丟棄了,
D3DSWAPEFFECT_FLIP: 后臺緩沖表面拷貝到前臺表面,保持后臺緩沖表面內容不變,當后臺緩沖表面大于1個時使用,
D3DSWAPEFFECT_COPY: 同上,當后臺緩沖表面等于1個時使用,
一般設定為D3DSWAPEFFECT_DISCARD
hDeviceWindow:視窗句柄
Windowed:設為true則為視窗模式,false則為全屏模式
EnableAutoDepthStencil:設為true,D3D將自動創建深度/模版緩沖
AutoDepthStencilFormat:深度/模版緩沖的格式
Flags:
FullScreen_RefreshRateInHz:重繪率,設定D3DPRESENT_RATE_DEFAULT使用默認重繪率
PresentationInterval:設定重繪的間隔,可以用以下方式:
D3DPRENSENT_INTERVAL_DEFAULT,則說明在顯示一個渲染畫面的時候必要等候顯示幕重繪完一次螢屏,例如顯示幕重繪率設為80Hz的話,則一秒最多可以顯示80個渲染畫面,
D3DPRENSENT_INTERVAL_IMMEDIATE:表示可以以實時的方式來顯示渲染畫面
3、創建D3D9Surface:CreateOffscreenPlainSurface創建一個離屏surface
HRESULT CreateOffscreenPlainSurface(
UINT Width, // 離屏surface的寬
UINT Height, // 離屏surface的高
D3DFORMAT Format, // 離屏surface的格式YV12
D3DPOOL Pool, // D3DPOOL定義了資源對應的記憶體型別
// D3D3POOL_DEFAULT: 默認值,表示存在于顯卡的顯存中,
// D3D3POOL_MANAGED:由Direct3D自由調度記憶體的位置(顯存或者快取中),
// D3DPOOL_SYSTEMMEM: 表示位于記憶體中,
IDirect3DSurface9 **ppSurface, // 要創建的surface
HANDLE *pSharedHandle
);
4、顯示畫面,顯示畫面的流程為:LockRect(獲取離屏的surface) --> 拷貝資料到離屏的surface --> UnlockRect --> Clear --> BeginScene --> GetBackBuffer(獲取后臺緩沖區的buffer) --> StretchRect(將一個矩形區域的像素從設備記憶體的一個Surface轉移到另一個Surface上) --> EndScene --> Present (顯示)--> Release
三、具體代碼
d3d9_player.h
#pragma once
#include <cstdint>
#include <d3d9.h>
enum PixFormat {
I420,
NV12,
NV21
};
class D3D9Player {
public:
D3D9Player();
~D3D9Player();
void Init(uint32_t width, uint32_t height, PixFormat pix_format);
void CreateRenderWindow();
void SetWindow(void* handle);
void Render(uint8_t* data);
private:
void InitRender();
void CreateSurface();
void DesrtoySurface();
void Cleanup();
private:
uint32_t width_{};
uint32_t height_{};
PixFormat pix_format_ = I420;
CRITICAL_SECTION critical_{};
IDirect3D9* d3d9_{};
IDirect3DDevice9* d3d9_device_{};
IDirect3DSurface9* d3d9_surface_{};
RECT view_rect_{};
HWND render_window_{};
};
d3d9_palyer.cpp
#include "d3d9_player.h"
#include <iostream>
#pragma comment(lib, "d3d9.lib")
D3D9Player::D3D9Player() {
}
D3D9Player::~D3D9Player() {
DesrtoySurface();
Cleanup();
}
void D3D9Player::Init(uint32_t width, uint32_t height, PixFormat pix_format) {
width_ = width;
height_ = height;
pix_format_ = pix_format;
view_rect_.left = 0;
view_rect_.top = 0;
view_rect_.right = width;
view_rect_.bottom = height;
}
void D3D9Player::CreateRenderWindow() {
}
void D3D9Player::SetWindow(void* handle) {
render_window_ = (HWND)(handle);
InitRender();
CreateSurface();
}
void D3D9Player::Render(uint8_t* data) {
D3DLOCKED_RECT d3d_rect;
if (d3d9_surface_ == nullptr) {
return;
}
auto hr = d3d9_surface_->LockRect(&d3d_rect, NULL, D3DLOCK_DONOTWAIT);
if (FAILED(hr)) {
std::cout << "lock rect error" << std::endl;
return;
}
int y_stride = d3d_rect.Pitch;
int v_stride = y_stride / 2;
int u_stride = y_stride / 2;
uint8_t* y_dest = (uint8_t*)d3d_rect.pBits;
uint8_t* v_dest = y_dest + height_ * y_stride;
uint8_t* u_dest = v_dest + height_ / 2 * v_stride;
for (uint32_t i = 0; i < height_; i++) {
memcpy(y_dest + i * y_stride, data + i * width_, width_);
}
for (uint32_t i = 0; i < height_ / 2; i++) {
memcpy(v_dest + i * v_stride, data + width_*height_ + i * width_ / 2,
width_ / 2);
}
for (uint32_t i = 0; i < height_ / 2; i++) {
memcpy(u_dest + i * u_stride, data + width_ * height_ * 5 / 4 + i * width_ / 2,
width_ / 2);
}
hr = d3d9_surface_->UnlockRect();
if (FAILED(hr)) {
std::cout << "unlock rect error" << std::endl;
return;
}
d3d9_device_->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0),
1.0f, 0);
d3d9_device_->BeginScene();
IDirect3DSurface9* d3d_surface = NULL;
d3d9_device_->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO,
&d3d_surface);
d3d9_device_->StretchRect(d3d9_surface_, NULL, d3d_surface, &view_rect_,
D3DTEXF_LINEAR);
d3d9_device_->EndScene();
d3d9_device_->Present(NULL, NULL, NULL, NULL);
d3d_surface->Release();
}
void D3D9Player::InitRender() {
std::cout << "init render" << std::endl;
InitializeCriticalSection(&critical_);
Cleanup();
d3d9_ = Direct3DCreate9(D3D_SDK_VERSION);
if (d3d9_ == nullptr) {
return;
}
RECT r;
GetClientRect(render_window_, &r);
int x = GetSystemMetrics(SM_CXSCREEN);
int y = GetSystemMetrics(SM_CYSCREEN);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.hDeviceWindow = (HWND)render_window_;
d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferWidth = width_;
d3dpp.BackBufferHeight = height_;
HRESULT hr = d3d9_->CreateDevice(
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, render_window_,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3d9_device_);
}
void D3D9Player::Cleanup() {
EnterCriticalSection(&critical_);
if (d3d9_surface_) {
d3d9_surface_->Release();
d3d9_surface_ = nullptr;
}
if (d3d9_device_) {
d3d9_device_->Release();
d3d9_device_ = nullptr;
}
if (d3d9_) {
d3d9_->Release();
d3d9_ = nullptr;
}
LeaveCriticalSection(&critical_);
}
void D3D9Player::CreateSurface() {
if (d3d9_device_ == nullptr) {
return;
}
HRESULT hr = d3d9_device_->CreateOffscreenPlainSurface(
width_, height_, (D3DFORMAT)MAKEFOURCC('Y', 'V', '1', '2'),
D3DPOOL_DEFAULT, &d3d9_surface_, NULL);
if (FAILED(hr)) {
std::cout << "create plain surface error" << std::endl;
return;
}
std::cout << "create surface" << std::endl;
}
void D3D9Player::DesrtoySurface() {
EnterCriticalSection(&critical_);
if (d3d9_surface_) {
d3d9_surface_->Release();
d3d9_surface_ = nullptr;
}
LeaveCriticalSection(&critical_);
}
main.cpp
#include <Windows.h>
#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
#include "d3d9_player.h"
HWND CreateMyWindow(uint32_t width, uint32_t height) {
WNDCLASS window;
memset(&window, 0, sizeof(window));
window.lpfnWndProc = (WNDPROC)DefWindowProc;
window.hInstance = GetModuleHandle(NULL);
window.hCursor = LoadCursor(NULL, IDC_ARROW);
window.lpszClassName = "yuv_player";
if (!RegisterClass(&window)) {
return nullptr;
}
DWORD dwStyle = NULL;
HWND wnd = CreateWindowEx(NULL, window.lpszClassName, "YUV Player", dwStyle,
100, 100, width, height, nullptr, nullptr,
GetModuleHandle(NULL), nullptr);
ShowWindow(wnd, SW_SHOWDEFAULT);
UpdateWindow(wnd);
return wnd;
}
int main(int argc,char* argv[]) {
std::string filename = "480x272_yuv420p.yuv";
uint32_t width = 480;
uint32_t height = 272;
std::shared_ptr<D3D9Player> yuv_player = std::make_shared<D3D9Player>();
yuv_player->Init(width, height, I420);
// yuv_player->CreateRenderWindow();
HWND window = CreateMyWindow(width,height);
yuv_player->SetWindow(window);
uint8_t* data = new uint8_t[width * height * 3 / 2];
std::ifstream fin(filename, std::ios::in | std::ios::binary);
while (!fin.eof()) {
fin.read((char*)data, width * height * 3 / 2);
yuv_player->Render(data);
std::this_thread::sleep_for(std::chrono::milliseconds(33));
}
delete[] data;
fin.close();
getchar();
return 0;
}
四、參考資料
https://blog.csdn.net/leixiaohua1020/article/details/40279297
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/163129.html
標籤:其他
上一篇:【測驗框架】cxxtest
