1 /*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10 #include "test/win/d3d_renderer.h"
11
12 #include "common_video/libyuv/include/webrtc_libyuv.h"
13 #include "rtc_base/checks.h"
14
15 namespace webrtc {
16 namespace test {
17
18 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
19
20 struct D3dCustomVertex {
21 float x, y, z;
22 float u, v;
23 };
24
25 const char kD3DClassName[] = "d3d_renderer";
26
CreatePlatformRenderer(const char * window_title,size_t width,size_t height)27 VideoRenderer* VideoRenderer::CreatePlatformRenderer(const char* window_title,
28 size_t width,
29 size_t height) {
30 return D3dRenderer::Create(window_title, width, height);
31 }
32
D3dRenderer(size_t width,size_t height)33 D3dRenderer::D3dRenderer(size_t width, size_t height)
34 : width_(width),
35 height_(height),
36 hwnd_(NULL),
37 d3d_(NULL),
38 d3d_device_(NULL),
39 texture_(NULL),
40 vertex_buffer_(NULL) {
41 RTC_DCHECK_GT(width, 0);
42 RTC_DCHECK_GT(height, 0);
43 }
44
~D3dRenderer()45 D3dRenderer::~D3dRenderer() {
46 Destroy();
47 }
48
WindowProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)49 LRESULT WINAPI D3dRenderer::WindowProc(HWND hwnd,
50 UINT msg,
51 WPARAM wparam,
52 LPARAM lparam) {
53 if (msg == WM_DESTROY || (msg == WM_CHAR && wparam == VK_RETURN)) {
54 PostQuitMessage(0);
55 return 0;
56 }
57
58 return DefWindowProcA(hwnd, msg, wparam, lparam);
59 }
60
Destroy()61 void D3dRenderer::Destroy() {
62 texture_ = NULL;
63 vertex_buffer_ = NULL;
64 d3d_device_ = NULL;
65 d3d_ = NULL;
66
67 if (hwnd_ != NULL) {
68 DestroyWindow(hwnd_);
69 RTC_DCHECK(!IsWindow(hwnd_));
70 hwnd_ = NULL;
71 }
72 }
73
Init(const char * window_title)74 bool D3dRenderer::Init(const char* window_title) {
75 hwnd_ = CreateWindowA(kD3DClassName, window_title, WS_OVERLAPPEDWINDOW, 0, 0,
76 static_cast<int>(width_), static_cast<int>(height_),
77 NULL, NULL, NULL, NULL);
78
79 if (hwnd_ == NULL) {
80 Destroy();
81 return false;
82 }
83
84 d3d_ = Direct3DCreate9(D3D_SDK_VERSION);
85 if (d3d_ == NULL) {
86 Destroy();
87 return false;
88 }
89
90 D3DPRESENT_PARAMETERS d3d_params = {};
91
92 d3d_params.Windowed = TRUE;
93 d3d_params.SwapEffect = D3DSWAPEFFECT_COPY;
94
95 IDirect3DDevice9* d3d_device;
96 if (d3d_->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd_,
97 D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3d_params,
98 &d3d_device) != D3D_OK) {
99 Destroy();
100 return false;
101 }
102 d3d_device_ = d3d_device;
103 d3d_device->Release();
104
105 IDirect3DVertexBuffer9* vertex_buffer;
106 const int kRectVertices = 4;
107 if (d3d_device_->CreateVertexBuffer(kRectVertices * sizeof(D3dCustomVertex),
108 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED,
109 &vertex_buffer, NULL) != D3D_OK) {
110 Destroy();
111 return false;
112 }
113 vertex_buffer_ = vertex_buffer;
114 vertex_buffer->Release();
115
116 d3d_device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
117 d3d_device_->SetRenderState(D3DRS_LIGHTING, FALSE);
118 Resize(width_, height_);
119
120 ShowWindow(hwnd_, SW_SHOWNOACTIVATE);
121 d3d_device_->Present(NULL, NULL, NULL, NULL);
122
123 return true;
124 }
125
Create(const char * window_title,size_t width,size_t height)126 D3dRenderer* D3dRenderer::Create(const char* window_title,
127 size_t width,
128 size_t height) {
129 static ATOM wc_atom = 0;
130 if (wc_atom == 0) {
131 WNDCLASSA wc = {};
132
133 wc.style = CS_HREDRAW | CS_VREDRAW;
134 wc.lpfnWndProc = WindowProc;
135 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
136 wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
137 wc.lpszClassName = kD3DClassName;
138
139 wc_atom = RegisterClassA(&wc);
140 if (wc_atom == 0)
141 return nullptr;
142 }
143
144 D3dRenderer* d3d_renderer = new D3dRenderer(width, height);
145 if (!d3d_renderer->Init(window_title)) {
146 delete d3d_renderer;
147 return nullptr;
148 }
149
150 return d3d_renderer;
151 }
152
Resize(size_t width,size_t height)153 void D3dRenderer::Resize(size_t width, size_t height) {
154 width_ = width;
155 height_ = height;
156 IDirect3DTexture9* texture;
157
158 d3d_device_->CreateTexture(static_cast<UINT>(width_),
159 static_cast<UINT>(height_), 1, 0, D3DFMT_A8R8G8B8,
160 D3DPOOL_MANAGED, &texture, NULL);
161 texture_ = texture;
162 texture->Release();
163
164 // Vertices for the video frame to be rendered to.
165 static const D3dCustomVertex rect[] = {
166 {-1.0f, -1.0f, 0.0f, 0.0f, 1.0f},
167 {-1.0f, 1.0f, 0.0f, 0.0f, 0.0f},
168 {1.0f, -1.0f, 0.0f, 1.0f, 1.0f},
169 {1.0f, 1.0f, 0.0f, 1.0f, 0.0f},
170 };
171
172 void* buf_data;
173 if (vertex_buffer_->Lock(0, 0, &buf_data, 0) != D3D_OK)
174 return;
175
176 memcpy(buf_data, &rect, sizeof(rect));
177 vertex_buffer_->Unlock();
178 }
179
OnFrame(const webrtc::VideoFrame & frame)180 void D3dRenderer::OnFrame(const webrtc::VideoFrame& frame) {
181 if (static_cast<size_t>(frame.width()) != width_ ||
182 static_cast<size_t>(frame.height()) != height_) {
183 Resize(static_cast<size_t>(frame.width()),
184 static_cast<size_t>(frame.height()));
185 }
186
187 D3DLOCKED_RECT lock_rect;
188 if (texture_->LockRect(0, &lock_rect, NULL, 0) != D3D_OK)
189 return;
190
191 ConvertFromI420(frame, VideoType::kARGB, 0,
192 static_cast<uint8_t*>(lock_rect.pBits));
193 texture_->UnlockRect(0);
194
195 d3d_device_->BeginScene();
196 d3d_device_->SetFVF(D3DFVF_CUSTOMVERTEX);
197 d3d_device_->SetStreamSource(0, vertex_buffer_, 0, sizeof(D3dCustomVertex));
198 d3d_device_->SetTexture(0, texture_);
199 d3d_device_->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
200 d3d_device_->EndScene();
201
202 d3d_device_->Present(NULL, NULL, NULL, NULL);
203 }
204 } // namespace test
205 } // namespace webrtc
206