• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be found
3 // in the LICENSE file.
4 //
5 // Portions Copyright (c) 2018 Daktronics with the following MIT License:
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 
17 #include "tests/cefclient/browser/osr_d3d11_win.h"
18 
19 #include <iomanip>  // For std::setw.
20 
21 #if OS_WIN && ARCH_CPU_ARM_FAMILY
22 #define __prefetch(x) x
23 #endif
24 #include <d3dcompiler.h>
25 #include <directxmath.h>
26 
27 #include "include/base/cef_logging.h"
28 #include "include/internal/cef_string.h"
29 #include "tests/shared/browser/util_win.h"
30 
31 namespace client {
32 namespace d3d11 {
33 
34 namespace {
35 
36 // Wrap a raw COM pointer in a shared_ptr for auto Release().
37 template <class T>
to_com_ptr(T * obj)38 std::shared_ptr<T> to_com_ptr(T* obj) {
39   return std::shared_ptr<T>(obj, [](T* p) {
40     if (p)
41       p->Release();
42   });
43 }
44 
45 }  // namespace
46 
47 struct SimpleVertex {
48   DirectX::XMFLOAT3 pos;
49   DirectX::XMFLOAT2 tex;
50 };
51 
Context(ID3D11DeviceContext * ctx)52 Context::Context(ID3D11DeviceContext* ctx) : ctx_(to_com_ptr(ctx)) {}
53 
flush()54 void Context::flush() {
55   ctx_->Flush();
56 }
57 
SwapChain(IDXGISwapChain * swapchain,ID3D11RenderTargetView * rtv,ID3D11SamplerState * sampler,ID3D11BlendState * blender)58 SwapChain::SwapChain(IDXGISwapChain* swapchain,
59                      ID3D11RenderTargetView* rtv,
60                      ID3D11SamplerState* sampler,
61                      ID3D11BlendState* blender)
62     : sampler_(to_com_ptr(sampler)),
63       blender_(to_com_ptr(blender)),
64       swapchain_(to_com_ptr(swapchain)),
65       rtv_(to_com_ptr(rtv)),
66       width_(0),
67       height_(0) {}
68 
bind(const std::shared_ptr<Context> & ctx)69 void SwapChain::bind(const std::shared_ptr<Context>& ctx) {
70   ctx_ = ctx;
71   ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
72 
73   ID3D11RenderTargetView* rtv[1] = {rtv_.get()};
74   d3d11_ctx->OMSetRenderTargets(1, rtv, nullptr);
75 
76   // Set default blending state.
77   if (blender_) {
78     float factor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
79     d3d11_ctx->OMSetBlendState(blender_.get(), factor, 0xffffffff);
80   }
81 
82   // Set default sampler state.
83   if (sampler_) {
84     ID3D11SamplerState* samplers[1] = {sampler_.get()};
85     d3d11_ctx->PSSetSamplers(0, 1, samplers);
86   }
87 }
88 
unbind()89 void SwapChain::unbind() {
90   ctx_.reset();
91 }
92 
clear(float red,float green,float blue,float alpha)93 void SwapChain::clear(float red, float green, float blue, float alpha) {
94   ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
95   CHECK(d3d11_ctx);
96 
97   FLOAT color[4] = {red, green, blue, alpha};
98   d3d11_ctx->ClearRenderTargetView(rtv_.get(), color);
99 }
100 
present(int sync_interval)101 void SwapChain::present(int sync_interval) {
102   swapchain_->Present(sync_interval, 0);
103 }
104 
resize(int width,int height)105 void SwapChain::resize(int width, int height) {
106   if (width <= 0 || height <= 0 || width == width_ || height == height_) {
107     return;
108   }
109   width_ = width;
110   height_ = height;
111 
112   ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
113   CHECK(d3d11_ctx);
114 
115   d3d11_ctx->OMSetRenderTargets(0, 0, 0);
116   rtv_.reset();
117 
118   DXGI_SWAP_CHAIN_DESC desc;
119   swapchain_->GetDesc(&desc);
120   auto hr = swapchain_->ResizeBuffers(0, width, height, desc.BufferDesc.Format,
121                                       desc.Flags);
122   if (FAILED(hr)) {
123     LOG(ERROR) << "d3d11: Failed to resize swapchain (" << width << "x"
124                << height << ")";
125     return;
126   }
127 
128   ID3D11Texture2D* buffer = nullptr;
129   hr = swapchain_->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&buffer);
130   if (FAILED(hr)) {
131     LOG(ERROR) << "d3d11: Failed to resize swapchain (" << width << "x"
132                << height << ")";
133     return;
134   }
135 
136   ID3D11Device* dev = nullptr;
137   d3d11_ctx->GetDevice(&dev);
138   if (dev) {
139     D3D11_RENDER_TARGET_VIEW_DESC vdesc = {};
140     vdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
141     vdesc.Texture2D.MipSlice = 0;
142     vdesc.Format = desc.BufferDesc.Format;
143 
144     ID3D11RenderTargetView* view = nullptr;
145     hr = dev->CreateRenderTargetView(buffer, &vdesc, &view);
146     if (SUCCEEDED(hr)) {
147       rtv_ = to_com_ptr(view);
148       d3d11_ctx->OMSetRenderTargets(1, &view, nullptr);
149     }
150     dev->Release();
151   }
152   buffer->Release();
153 
154   D3D11_VIEWPORT vp;
155   vp.Width = static_cast<float>(width);
156   vp.Height = static_cast<float>(height);
157   vp.MinDepth = D3D11_MIN_DEPTH;
158   vp.MaxDepth = D3D11_MAX_DEPTH;
159   vp.TopLeftX = 0;
160   vp.TopLeftY = 0;
161   d3d11_ctx->RSSetViewports(1, &vp);
162 }
163 
Effect(ID3D11VertexShader * vsh,ID3D11PixelShader * psh,ID3D11InputLayout * layout)164 Effect::Effect(ID3D11VertexShader* vsh,
165                ID3D11PixelShader* psh,
166                ID3D11InputLayout* layout)
167     : vsh_(to_com_ptr(vsh)),
168       psh_(to_com_ptr(psh)),
169       layout_(to_com_ptr(layout)) {}
170 
bind(const std::shared_ptr<Context> & ctx)171 void Effect::bind(const std::shared_ptr<Context>& ctx) {
172   ctx_ = ctx;
173   ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
174 
175   d3d11_ctx->IASetInputLayout(layout_.get());
176   d3d11_ctx->VSSetShader(vsh_.get(), nullptr, 0);
177   d3d11_ctx->PSSetShader(psh_.get(), nullptr, 0);
178 }
179 
unbind()180 void Effect::unbind() {}
181 
Geometry(D3D_PRIMITIVE_TOPOLOGY primitive,uint32_t vertices,uint32_t stride,ID3D11Buffer * buffer)182 Geometry::Geometry(D3D_PRIMITIVE_TOPOLOGY primitive,
183                    uint32_t vertices,
184                    uint32_t stride,
185                    ID3D11Buffer* buffer)
186     : primitive_(primitive),
187       vertices_(vertices),
188       stride_(stride),
189       buffer_(to_com_ptr(buffer)) {}
190 
bind(const std::shared_ptr<Context> & ctx)191 void Geometry::bind(const std::shared_ptr<Context>& ctx) {
192   ctx_ = ctx;
193   ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
194 
195   // TODO: Handle offset.
196   uint32_t offset = 0;
197 
198   ID3D11Buffer* buffers[1] = {buffer_.get()};
199   d3d11_ctx->IASetVertexBuffers(0, 1, buffers, &stride_, &offset);
200   d3d11_ctx->IASetPrimitiveTopology(primitive_);
201 }
202 
unbind()203 void Geometry::unbind() {}
204 
draw()205 void Geometry::draw() {
206   ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
207   CHECK(d3d11_ctx);
208 
209   // TODO: Handle offset.
210   d3d11_ctx->Draw(vertices_, 0);
211 }
212 
Texture2D(ID3D11Texture2D * tex,ID3D11ShaderResourceView * srv)213 Texture2D::Texture2D(ID3D11Texture2D* tex, ID3D11ShaderResourceView* srv)
214     : texture_(to_com_ptr(tex)), srv_(to_com_ptr(srv)) {
215   share_handle_ = nullptr;
216 
217   IDXGIResource* res = nullptr;
218   if (SUCCEEDED(texture_->QueryInterface(__uuidof(IDXGIResource),
219                                          reinterpret_cast<void**>(&res)))) {
220     res->GetSharedHandle(&share_handle_);
221     res->Release();
222   }
223 
224   // Are we using a keyed mutex?
225   IDXGIKeyedMutex* mutex = nullptr;
226   if (SUCCEEDED(texture_->QueryInterface(__uuidof(IDXGIKeyedMutex),
227                                          (void**)&mutex))) {
228     keyed_mutex_ = to_com_ptr(mutex);
229   }
230 }
231 
width() const232 uint32_t Texture2D::width() const {
233   D3D11_TEXTURE2D_DESC desc;
234   texture_->GetDesc(&desc);
235   return desc.Width;
236 }
237 
height() const238 uint32_t Texture2D::height() const {
239   D3D11_TEXTURE2D_DESC desc;
240   texture_->GetDesc(&desc);
241   return desc.Height;
242 }
243 
format() const244 DXGI_FORMAT Texture2D::format() const {
245   D3D11_TEXTURE2D_DESC desc;
246   texture_->GetDesc(&desc);
247   return desc.Format;
248 }
249 
has_mutex() const250 bool Texture2D::has_mutex() const {
251   return (keyed_mutex_.get() != nullptr);
252 }
253 
lock_key(uint64_t key,uint32_t timeout_ms)254 bool Texture2D::lock_key(uint64_t key, uint32_t timeout_ms) {
255   if (keyed_mutex_) {
256     const auto hr = keyed_mutex_->AcquireSync(key, timeout_ms);
257     return (hr == S_OK);
258   }
259   return true;
260 }
261 
unlock_key(uint64_t key)262 void Texture2D::unlock_key(uint64_t key) {
263   if (keyed_mutex_) {
264     keyed_mutex_->ReleaseSync(key);
265   }
266 }
267 
bind(const std::shared_ptr<Context> & ctx)268 void Texture2D::bind(const std::shared_ptr<Context>& ctx) {
269   ctx_ = ctx;
270   ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
271   if (srv_) {
272     ID3D11ShaderResourceView* views[1] = {srv_.get()};
273     d3d11_ctx->PSSetShaderResources(0, 1, views);
274   }
275 }
276 
unbind()277 void Texture2D::unbind() {}
278 
share_handle() const279 void* Texture2D::share_handle() const {
280   return share_handle_;
281 }
282 
copy_from(const std::shared_ptr<Texture2D> & other)283 void Texture2D::copy_from(const std::shared_ptr<Texture2D>& other) {
284   ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
285   CHECK(d3d11_ctx);
286   if (other) {
287     d3d11_ctx->CopyResource(texture_.get(), other->texture_.get());
288   }
289 }
290 
Device(ID3D11Device * pdev,ID3D11DeviceContext * pctx)291 Device::Device(ID3D11Device* pdev, ID3D11DeviceContext* pctx)
292     : device_(to_com_ptr(pdev)), ctx_(std::make_shared<Context>(pctx)) {
293   lib_compiler_ = LoadLibrary(L"d3dcompiler_47.dll");
294 }
295 
296 // static
create()297 std::shared_ptr<Device> Device::create() {
298   UINT flags = 0;
299 #ifdef _DEBUG
300   flags |= D3D11_CREATE_DEVICE_DEBUG;
301 #endif
302 
303   D3D_FEATURE_LEVEL feature_levels[] = {
304       D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,
305       D3D_FEATURE_LEVEL_10_0,
306       // D3D_FEATURE_LEVEL_9_3,
307   };
308   UINT num_feature_levels = sizeof(feature_levels) / sizeof(feature_levels[0]);
309 
310   ID3D11Device* pdev = nullptr;
311   ID3D11DeviceContext* pctx = nullptr;
312 
313   D3D_FEATURE_LEVEL selected_level;
314   HRESULT hr = D3D11CreateDevice(
315       nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, feature_levels,
316       num_feature_levels, D3D11_SDK_VERSION, &pdev, &selected_level, &pctx);
317 
318   if (hr == E_INVALIDARG) {
319     // DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1
320     // so we need to retry without it.
321     hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags,
322                            &feature_levels[1], num_feature_levels - 1,
323                            D3D11_SDK_VERSION, &pdev, &selected_level, &pctx);
324   }
325 
326   if (SUCCEEDED(hr)) {
327     const auto dev = std::make_shared<Device>(pdev, pctx);
328 
329     LOG(INFO) << "d3d11: Selected adapter " << dev->adapter_name()
330               << " and feature level 0x" << std::setw(4) << std::hex
331               << selected_level;
332 
333     return dev;
334   }
335 
336   return nullptr;
337 }
338 
adapter_name() const339 std::string Device::adapter_name() const {
340   IDXGIDevice* dxgi_dev = nullptr;
341   auto hr = device_->QueryInterface(__uuidof(dxgi_dev), (void**)&dxgi_dev);
342   if (SUCCEEDED(hr)) {
343     IDXGIAdapter* dxgi_adapt = nullptr;
344     hr = dxgi_dev->GetAdapter(&dxgi_adapt);
345     dxgi_dev->Release();
346     if (SUCCEEDED(hr)) {
347       DXGI_ADAPTER_DESC desc;
348       hr = dxgi_adapt->GetDesc(&desc);
349       dxgi_adapt->Release();
350       if (SUCCEEDED(hr)) {
351         return CefString(desc.Description);
352       }
353     }
354   }
355 
356   return "n/a";
357 }
358 
immedidate_context()359 std::shared_ptr<Context> Device::immedidate_context() {
360   return ctx_;
361 }
362 
create_swapchain(HWND window,int width,int height)363 std::shared_ptr<SwapChain> Device::create_swapchain(HWND window,
364                                                     int width,
365                                                     int height) {
366   HRESULT hr;
367   IDXGIFactory1* dxgi_factory = nullptr;
368 
369   // Default size to the window size unless specified.
370   RECT rc_bounds;
371   GetClientRect(window, &rc_bounds);
372   if (width <= 0) {
373     width = rc_bounds.right - rc_bounds.left;
374   }
375   if (height <= 0) {
376     height = rc_bounds.bottom - rc_bounds.top;
377   }
378 
379   {
380     IDXGIDevice* dxgi_dev = nullptr;
381     hr = device_->QueryInterface(__uuidof(IDXGIDevice),
382                                  reinterpret_cast<void**>(&dxgi_dev));
383     if (FAILED(hr)) {
384       return nullptr;
385     }
386 
387     IDXGIAdapter* adapter = nullptr;
388     hr = dxgi_dev->GetAdapter(&adapter);
389     dxgi_dev->Release();
390     if (FAILED(hr)) {
391       return nullptr;
392     }
393 
394     hr = adapter->GetParent(__uuidof(IDXGIFactory1),
395                             reinterpret_cast<void**>(&dxgi_factory));
396     adapter->Release();
397   }
398 
399   if (!dxgi_factory) {
400     return nullptr;
401   }
402 
403   IDXGISwapChain* swapchain = nullptr;
404 
405   // Create swap chain.
406   IDXGIFactory2* dxgi_factory2 = nullptr;
407   hr = dxgi_factory->QueryInterface(__uuidof(IDXGIFactory2),
408                                     reinterpret_cast<void**>(&dxgi_factory2));
409   if (dxgi_factory2) {
410     DXGI_SWAP_CHAIN_DESC1 sd;
411     ZeroMemory(&sd, sizeof(sd));
412     sd.Width = width;
413     sd.Height = height;
414     sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
415     sd.SampleDesc.Count = 1;
416     sd.SampleDesc.Quality = 0;
417     sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
418     sd.BufferCount = 1;
419 
420     IDXGISwapChain1* swapchain1 = nullptr;
421     hr = dxgi_factory2->CreateSwapChainForHwnd(device_.get(), window, &sd,
422                                                nullptr, nullptr, &swapchain1);
423     if (SUCCEEDED(hr)) {
424       hr = swapchain1->QueryInterface(__uuidof(IDXGISwapChain),
425                                       reinterpret_cast<void**>(&swapchain));
426       swapchain1->Release();
427     }
428 
429     dxgi_factory2->Release();
430   } else {
431     // DirectX 11.0 systems.
432     DXGI_SWAP_CHAIN_DESC sd;
433     ZeroMemory(&sd, sizeof(sd));
434     sd.BufferCount = 1;
435     sd.BufferDesc.Width = width;
436     sd.BufferDesc.Height = height;
437     sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
438     sd.BufferDesc.RefreshRate.Numerator = 60;
439     sd.BufferDesc.RefreshRate.Denominator = 1;
440     sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
441     sd.OutputWindow = window;
442     sd.SampleDesc.Count = 1;
443     sd.SampleDesc.Quality = 0;
444     sd.Windowed = TRUE;
445 
446     hr = dxgi_factory->CreateSwapChain(device_.get(), &sd, &swapchain);
447   }
448 
449   // We don't handle full-screen swapchains so we block the ALT+ENTER shortcut.
450   dxgi_factory->MakeWindowAssociation(window, DXGI_MWA_NO_ALT_ENTER);
451   dxgi_factory->Release();
452 
453   if (!swapchain) {
454     return nullptr;
455   }
456 
457   ID3D11Texture2D* back_buffer = nullptr;
458   hr = swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D),
459                             reinterpret_cast<void**>(&back_buffer));
460   if (FAILED(hr)) {
461     swapchain->Release();
462     return nullptr;
463   }
464 
465   ID3D11RenderTargetView* rtv = nullptr;
466   hr = device_->CreateRenderTargetView(back_buffer, nullptr, &rtv);
467   back_buffer->Release();
468   if (FAILED(hr)) {
469     swapchain->Release();
470     return nullptr;
471   }
472 
473   const auto ctx = (ID3D11DeviceContext*)(*ctx_);
474 
475   ctx->OMSetRenderTargets(1, &rtv, nullptr);
476 
477   // Setup the viewport.
478   D3D11_VIEWPORT vp;
479   vp.Width = (FLOAT)width;
480   vp.Height = (FLOAT)height;
481   vp.MinDepth = 0.0f;
482   vp.MaxDepth = 1.0f;
483   vp.TopLeftX = 0;
484   vp.TopLeftY = 0;
485   ctx->RSSetViewports(1, &vp);
486 
487   // Create a default sampler to use.
488   ID3D11SamplerState* sampler = nullptr;
489   {
490     D3D11_SAMPLER_DESC desc = {};
491     desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
492     desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
493     desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
494     desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
495     desc.MinLOD = 0.0f;
496     desc.MaxLOD = D3D11_FLOAT32_MAX;
497     desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
498     device_->CreateSamplerState(&desc, &sampler);
499   }
500 
501   // Create a default blend state to use (pre-multiplied alpha).
502   ID3D11BlendState* blender = nullptr;
503   {
504     D3D11_BLEND_DESC desc;
505     desc.AlphaToCoverageEnable = FALSE;
506     desc.IndependentBlendEnable = FALSE;
507     const auto count = sizeof(desc.RenderTarget) / sizeof(desc.RenderTarget[0]);
508     for (size_t n = 0; n < count; ++n) {
509       desc.RenderTarget[n].BlendEnable = TRUE;
510       desc.RenderTarget[n].SrcBlend = D3D11_BLEND_ONE;
511       desc.RenderTarget[n].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
512       desc.RenderTarget[n].SrcBlendAlpha = D3D11_BLEND_ONE;
513       desc.RenderTarget[n].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
514       desc.RenderTarget[n].BlendOp = D3D11_BLEND_OP_ADD;
515       desc.RenderTarget[n].BlendOpAlpha = D3D11_BLEND_OP_ADD;
516       desc.RenderTarget[n].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
517     }
518     device_->CreateBlendState(&desc, &blender);
519   }
520 
521   return std::make_shared<SwapChain>(swapchain, rtv, sampler, blender);
522 }
523 
create_quad(float x,float y,float width,float height,bool flip)524 std::shared_ptr<Geometry> Device::create_quad(float x,
525                                               float y,
526                                               float width,
527                                               float height,
528                                               bool flip) {
529   x = (x * 2.0f) - 1.0f;
530   y = 1.0f - (y * 2.0f);
531   width = width * 2.0f;
532   height = height * 2.0f;
533   float z = 1.0f;
534 
535   SimpleVertex vertices[] = {
536 
537       {DirectX::XMFLOAT3(x, y, z), DirectX::XMFLOAT2(0.0f, 0.0f)},
538       {DirectX::XMFLOAT3(x + width, y, z), DirectX::XMFLOAT2(1.0f, 0.0f)},
539       {DirectX::XMFLOAT3(x, y - height, z), DirectX::XMFLOAT2(0.0f, 1.0f)},
540       {DirectX::XMFLOAT3(x + width, y - height, z),
541        DirectX::XMFLOAT2(1.0f, 1.0f)}};
542 
543   if (flip) {
544     DirectX::XMFLOAT2 tmp(vertices[2].tex);
545     vertices[2].tex = vertices[0].tex;
546     vertices[0].tex = tmp;
547 
548     tmp = vertices[3].tex;
549     vertices[3].tex = vertices[1].tex;
550     vertices[1].tex = tmp;
551   }
552 
553   D3D11_BUFFER_DESC desc = {};
554   desc.Usage = D3D11_USAGE_DEFAULT;
555   desc.ByteWidth = sizeof(SimpleVertex) * 4;
556   desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
557   desc.CPUAccessFlags = 0;
558 
559   D3D11_SUBRESOURCE_DATA srd = {};
560   srd.pSysMem = vertices;
561 
562   ID3D11Buffer* buffer = nullptr;
563   const auto hr = device_->CreateBuffer(&desc, &srd, &buffer);
564   if (SUCCEEDED(hr)) {
565     return std::make_shared<Geometry>(
566         D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, 4,
567         static_cast<uint32_t>(sizeof(SimpleVertex)), buffer);
568   }
569 
570   return nullptr;
571 }
572 
open_shared_texture(void * handle)573 std::shared_ptr<Texture2D> Device::open_shared_texture(void* handle) {
574   ID3D11Texture2D* tex = nullptr;
575   auto hr = device_->OpenSharedResource(handle, __uuidof(ID3D11Texture2D),
576                                         (void**)(&tex));
577   if (FAILED(hr)) {
578     return nullptr;
579   }
580 
581   D3D11_TEXTURE2D_DESC td;
582   tex->GetDesc(&td);
583 
584   ID3D11ShaderResourceView* srv = nullptr;
585 
586   if (td.BindFlags & D3D11_BIND_SHADER_RESOURCE) {
587     D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
588     srv_desc.Format = td.Format;
589     srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
590     srv_desc.Texture2D.MostDetailedMip = 0;
591     srv_desc.Texture2D.MipLevels = 1;
592 
593     hr = device_->CreateShaderResourceView(tex, &srv_desc, &srv);
594     if (FAILED(hr)) {
595       tex->Release();
596       return nullptr;
597     }
598   }
599 
600   return std::make_shared<Texture2D>(tex, srv);
601 }
602 
create_texture(int width,int height,DXGI_FORMAT format,const void * data,size_t row_stride)603 std::shared_ptr<Texture2D> Device::create_texture(int width,
604                                                   int height,
605                                                   DXGI_FORMAT format,
606                                                   const void* data,
607                                                   size_t row_stride) {
608   D3D11_TEXTURE2D_DESC td;
609   td.ArraySize = 1;
610   td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
611   td.CPUAccessFlags = 0;
612   td.Format = format;
613   td.Width = width;
614   td.Height = height;
615   td.MipLevels = 1;
616   td.MiscFlags = 0;
617   td.SampleDesc.Count = 1;
618   td.SampleDesc.Quality = 0;
619   td.Usage = D3D11_USAGE_DEFAULT;
620 
621   D3D11_SUBRESOURCE_DATA srd;
622   srd.pSysMem = data;
623   srd.SysMemPitch = static_cast<uint32_t>(row_stride);
624   srd.SysMemSlicePitch = 0;
625 
626   ID3D11Texture2D* tex = nullptr;
627   auto hr = device_->CreateTexture2D(&td, data ? &srd : nullptr, &tex);
628   if (FAILED(hr)) {
629     return nullptr;
630   }
631 
632   D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
633   srv_desc.Format = td.Format;
634   srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
635   srv_desc.Texture2D.MostDetailedMip = 0;
636   srv_desc.Texture2D.MipLevels = 1;
637 
638   ID3D11ShaderResourceView* srv = nullptr;
639   hr = device_->CreateShaderResourceView(tex, &srv_desc, &srv);
640   if (FAILED(hr)) {
641     tex->Release();
642     return nullptr;
643   }
644 
645   return std::make_shared<Texture2D>(tex, srv);
646 }
647 
compile_shader(const std::string & source_code,const std::string & entry_point,const std::string & model)648 std::shared_ptr<ID3DBlob> Device::compile_shader(const std::string& source_code,
649                                                  const std::string& entry_point,
650                                                  const std::string& model) {
651   if (!lib_compiler_) {
652     return nullptr;
653   }
654 
655   typedef HRESULT(WINAPI * PFN_D3DCOMPILE)(
656       LPCVOID, SIZE_T, LPCSTR, const D3D_SHADER_MACRO*, ID3DInclude*, LPCSTR,
657       LPCSTR, UINT, UINT, ID3DBlob**, ID3DBlob**);
658 
659   const auto fnc_compile = reinterpret_cast<PFN_D3DCOMPILE>(
660       GetProcAddress(lib_compiler_, "D3DCompile"));
661   if (!fnc_compile) {
662     return nullptr;
663   }
664 
665   DWORD flags = D3DCOMPILE_ENABLE_STRICTNESS;
666 
667 #if defined(NDEBUG)
668 // flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
669 // flags |= D3DCOMPILE_AVOID_FLOW_CONTROL;
670 #else
671   flags |= D3DCOMPILE_DEBUG;
672   flags |= D3DCOMPILE_SKIP_OPTIMIZATION;
673 #endif
674 
675   ID3DBlob* blob = nullptr;
676   ID3DBlob* blob_err = nullptr;
677 
678   const auto psrc = source_code.c_str();
679   const auto len = source_code.size() + 1;
680 
681   const auto hr =
682       fnc_compile(psrc, len, nullptr, nullptr, nullptr, entry_point.c_str(),
683                   model.c_str(), flags, 0, &blob, &blob_err);
684 
685   if (FAILED(hr)) {
686     if (blob_err) {
687       // TODO: Log the error.
688       blob_err->Release();
689     }
690     return nullptr;
691   }
692 
693   if (blob_err) {
694     blob_err->Release();
695   }
696 
697   return std::shared_ptr<ID3DBlob>(blob, [](ID3DBlob* p) {
698     if (p)
699       p->Release();
700   });
701 }
702 
create_default_effect()703 std::shared_ptr<Effect> Device::create_default_effect() {
704   const auto vsh =
705       R"--(struct VS_INPUT
706 {
707 	float4 pos : POSITION;
708 	float2 tex : TEXCOORD0;
709 };
710 
711 struct VS_OUTPUT
712 {
713 	float4 pos : SV_POSITION;
714 	float2 tex : TEXCOORD0;
715 };
716 
717 VS_OUTPUT main(VS_INPUT input)
718 {
719 	VS_OUTPUT output;
720 	output.pos = input.pos;
721 	output.tex = input.tex;
722 	return output;
723 })--";
724 
725   const auto psh =
726       R"--(Texture2D tex0 : register(t0);
727 SamplerState samp0 : register(s0);
728 
729 struct VS_OUTPUT
730 {
731 	float4 pos : SV_POSITION;
732 	float2 tex : TEXCOORD0;
733 };
734 
735 float4 main(VS_OUTPUT input) : SV_Target
736 {
737 	return tex0.Sample(samp0, input.tex);
738 })--";
739 
740   return create_effect(vsh, "main", "vs_4_0", psh, "main", "ps_4_0");
741 }
742 
create_effect(const std::string & vertex_code,const std::string & vertex_entry,const std::string & vertex_model,const std::string & pixel_code,const std::string & pixel_entry,const std::string & pixel_model)743 std::shared_ptr<Effect> Device::create_effect(const std::string& vertex_code,
744                                               const std::string& vertex_entry,
745                                               const std::string& vertex_model,
746                                               const std::string& pixel_code,
747                                               const std::string& pixel_entry,
748                                               const std::string& pixel_model) {
749   const auto vs_blob = compile_shader(vertex_code, vertex_entry, vertex_model);
750 
751   ID3D11VertexShader* vshdr = nullptr;
752   ID3D11InputLayout* layout = nullptr;
753 
754   if (vs_blob) {
755     device_->CreateVertexShader(vs_blob->GetBufferPointer(),
756                                 vs_blob->GetBufferSize(), nullptr, &vshdr);
757 
758     D3D11_INPUT_ELEMENT_DESC layout_desc[] = {
759         {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
760          D3D11_INPUT_PER_VERTEX_DATA, 0},
761         {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
762          D3D11_INPUT_PER_VERTEX_DATA, 0},
763     };
764 
765     UINT elements = ARRAYSIZE(layout_desc);
766 
767     // Create the input layout.
768     device_->CreateInputLayout(layout_desc, elements,
769                                vs_blob->GetBufferPointer(),
770                                vs_blob->GetBufferSize(), &layout);
771   }
772 
773   const auto ps_blob = compile_shader(pixel_code, pixel_entry, pixel_model);
774   ID3D11PixelShader* pshdr = nullptr;
775   if (ps_blob) {
776     device_->CreatePixelShader(ps_blob->GetBufferPointer(),
777                                ps_blob->GetBufferSize(), nullptr, &pshdr);
778   }
779 
780   return std::make_shared<Effect>(vshdr, pshdr, layout);
781 }
782 
Layer(const std::shared_ptr<Device> & device,bool flip)783 Layer::Layer(const std::shared_ptr<Device>& device, bool flip)
784     : device_(device), flip_(flip) {
785   bounds_.x = bounds_.y = bounds_.width = bounds_.height = 0.0f;
786 }
787 
~Layer()788 Layer::~Layer() {}
789 
attach(const std::shared_ptr<Composition> & parent)790 void Layer::attach(const std::shared_ptr<Composition>& parent) {
791   composition_ = parent;
792 }
793 
composition() const794 std::shared_ptr<Composition> Layer::composition() const {
795   return composition_.lock();
796 }
797 
bounds() const798 Rect Layer::bounds() const {
799   return bounds_;
800 }
801 
move(float x,float y,float width,float height)802 void Layer::move(float x, float y, float width, float height) {
803   bounds_.x = x;
804   bounds_.y = y;
805   bounds_.width = width;
806   bounds_.height = height;
807 
808   // It's not efficient to create the quad everytime we move, but for now we're
809   // just trying to get something on-screen.
810   geometry_.reset();
811 }
812 
tick(double)813 void Layer::tick(double) {
814   // Nothing to update in the base class.
815 }
816 
render_texture(const std::shared_ptr<Context> & ctx,const std::shared_ptr<Texture2D> & texture)817 void Layer::render_texture(const std::shared_ptr<Context>& ctx,
818                            const std::shared_ptr<Texture2D>& texture) {
819   if (!geometry_) {
820     geometry_ = device_->create_quad(bounds_.x, bounds_.y, bounds_.width,
821                                      bounds_.height, flip_);
822   }
823 
824   if (geometry_ && texture) {
825     // We need a shader.
826     if (!effect_) {
827       effect_ = device_->create_default_effect();
828     }
829 
830     // Bind our states/resource to the pipeline.
831     ScopedBinder<Geometry> quad_binder(ctx, geometry_);
832     ScopedBinder<Effect> fx_binder(ctx, effect_);
833     ScopedBinder<Texture2D> tex_binder(ctx, texture);
834 
835     // Draw the quad.
836     geometry_->draw();
837   }
838 }
839 
Composition(const std::shared_ptr<Device> & device,int width,int height)840 Composition::Composition(const std::shared_ptr<Device>& device,
841                          int width,
842                          int height)
843     : width_(width), height_(height), vsync_(true), device_(device) {
844   fps_ = 0.0;
845   time_ = 0.0;
846   frame_ = 0;
847   fps_start_ = GetTimeNow();
848 }
849 
is_vsync() const850 bool Composition::is_vsync() const {
851   return vsync_;
852 }
853 
time() const854 double Composition::time() const {
855   return time_;
856 }
857 
fps() const858 double Composition::fps() const {
859   return fps_;
860 }
861 
add_layer(const std::shared_ptr<Layer> & layer)862 void Composition::add_layer(const std::shared_ptr<Layer>& layer) {
863   if (layer) {
864     layers_.push_back(layer);
865 
866     // Attach ourselves as the parent.
867     layer->attach(shared_from_this());
868   }
869 }
870 
remove_layer(const std::shared_ptr<Layer> & layer)871 bool Composition::remove_layer(const std::shared_ptr<Layer>& layer) {
872   size_t match = 0;
873   if (layer) {
874     for (auto i = layers_.begin(); i != layers_.end();) {
875       if ((*i).get() == layer.get()) {
876         i = layers_.erase(i);
877         match++;
878       } else {
879         i++;
880       }
881     }
882   }
883   return (match > 0);
884 }
885 
resize(bool vsync,int width,int height)886 void Composition::resize(bool vsync, int width, int height) {
887   vsync_ = vsync;
888   width_ = width;
889   height_ = height;
890 }
891 
tick(double t)892 void Composition::tick(double t) {
893   time_ = t;
894   for (const auto& layer : layers_) {
895     layer->tick(t);
896   }
897 }
898 
render(const std::shared_ptr<Context> & ctx)899 void Composition::render(const std::shared_ptr<Context>& ctx) {
900   // Use painter's algorithm and render our layers in order (not doing any dept
901   // or 3D here).
902   for (const auto& layer : layers_) {
903     layer->render(ctx);
904   }
905 
906   frame_++;
907   const auto now = GetTimeNow();
908   if ((now - fps_start_) > 1000000) {
909     fps_ = frame_ / double((now - fps_start_) / 1000000.0);
910     frame_ = 0;
911     fps_start_ = now;
912   }
913 }
914 
FrameBuffer(const std::shared_ptr<Device> & device)915 FrameBuffer::FrameBuffer(const std::shared_ptr<Device>& device)
916     : device_(device) {}
917 
on_paint(void * shared_handle)918 void FrameBuffer::on_paint(void* shared_handle) {
919   // Did the shared texture change?
920   if (shared_buffer_ && shared_handle != shared_buffer_->share_handle()) {
921     shared_buffer_.reset();
922   }
923 
924   // Open the shared texture.
925   if (!shared_buffer_) {
926     shared_buffer_ = device_->open_shared_texture((void*)shared_handle);
927     if (!shared_buffer_) {
928       LOG(ERROR) << "d3d11: Could not open shared texture!";
929     }
930   }
931 }
932 
933 }  // namespace d3d11
934 }  // namespace client
935