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