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