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