cef/tests/cefclient/browser/osr_d3d11_win.cc
reito 260dd0ca24 osr: Implement shared texture support (fixes #1006, fixes #2575)
Adds support for the OnAcceleratedPaint callback. Verified to work
on macOS and Windows. Linux support is present but not implemented
for cefclient, so it is not verified to work.

To test:
Run `cefclient --off-screen-rendering-enabled --shared-texture-enabled`
2024-04-23 13:03:56 -04:00

941 lines
26 KiB
C++

// Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
//
// Portions Copyright (c) 2018 Daktronics with the following MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
#include "tests/cefclient/browser/osr_d3d11_win.h"
#include <iomanip> // For std::setw.
#if OS_WIN && ARCH_CPU_ARM_FAMILY
#define __prefetch(x) x
#endif
#include <d3dcompiler.h>
#include <directxmath.h>
#include <wrl/client.h>
#include "include/base/cef_logging.h"
#include "include/internal/cef_string.h"
#include "tests/shared/browser/util_win.h"
namespace client::d3d11 {
namespace {
// Wrap a raw COM pointer in a shared_ptr for auto Release().
template <class T>
std::shared_ptr<T> to_com_ptr(T* obj) {
return std::shared_ptr<T>(obj, [](T* p) {
if (p) {
p->Release();
}
});
}
} // namespace
struct SimpleVertex {
DirectX::XMFLOAT3 pos;
DirectX::XMFLOAT2 tex;
};
Context::Context(ID3D11DeviceContext* ctx) : ctx_(to_com_ptr(ctx)) {}
void Context::flush() {
ctx_->Flush();
}
SwapChain::SwapChain(IDXGISwapChain* swapchain,
ID3D11RenderTargetView* rtv,
ID3D11SamplerState* sampler,
ID3D11BlendState* blender)
: sampler_(to_com_ptr(sampler)),
blender_(to_com_ptr(blender)),
swapchain_(to_com_ptr(swapchain)),
rtv_(to_com_ptr(rtv)) {}
void SwapChain::bind(const std::shared_ptr<Context>& ctx) {
ctx_ = ctx;
ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
ID3D11RenderTargetView* rtv[1] = {rtv_.get()};
d3d11_ctx->OMSetRenderTargets(1, rtv, nullptr);
// Set default blending state.
if (blender_) {
float factor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
d3d11_ctx->OMSetBlendState(blender_.get(), factor, 0xffffffff);
}
// Set default sampler state.
if (sampler_) {
ID3D11SamplerState* samplers[1] = {sampler_.get()};
d3d11_ctx->PSSetSamplers(0, 1, samplers);
}
}
void SwapChain::unbind() {
ctx_.reset();
}
void SwapChain::clear(float red, float green, float blue, float alpha) {
ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
CHECK(d3d11_ctx);
FLOAT color[4] = {red, green, blue, alpha};
d3d11_ctx->ClearRenderTargetView(rtv_.get(), color);
}
void SwapChain::present(int sync_interval) {
swapchain_->Present(sync_interval, 0);
}
void SwapChain::resize(int width, int height) {
if (width <= 0 || height <= 0 || width == width_ || height == height_) {
return;
}
width_ = width;
height_ = height;
ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
CHECK(d3d11_ctx);
d3d11_ctx->OMSetRenderTargets(0, nullptr, nullptr);
rtv_.reset();
DXGI_SWAP_CHAIN_DESC desc;
swapchain_->GetDesc(&desc);
auto hr = swapchain_->ResizeBuffers(0, width, height, desc.BufferDesc.Format,
desc.Flags);
if (FAILED(hr)) {
LOG(ERROR) << "d3d11: Failed to resize swapchain (" << width << "x"
<< height << ")";
return;
}
ID3D11Texture2D* buffer = nullptr;
hr = swapchain_->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&buffer);
if (FAILED(hr)) {
LOG(ERROR) << "d3d11: Failed to resize swapchain (" << width << "x"
<< height << ")";
return;
}
ID3D11Device* dev = nullptr;
d3d11_ctx->GetDevice(&dev);
if (dev) {
D3D11_RENDER_TARGET_VIEW_DESC vdesc = {};
vdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
vdesc.Texture2D.MipSlice = 0;
vdesc.Format = desc.BufferDesc.Format;
ID3D11RenderTargetView* view = nullptr;
hr = dev->CreateRenderTargetView(buffer, &vdesc, &view);
if (SUCCEEDED(hr)) {
rtv_ = to_com_ptr(view);
d3d11_ctx->OMSetRenderTargets(1, &view, nullptr);
}
dev->Release();
}
buffer->Release();
D3D11_VIEWPORT vp;
vp.Width = static_cast<float>(width);
vp.Height = static_cast<float>(height);
vp.MinDepth = D3D11_MIN_DEPTH;
vp.MaxDepth = D3D11_MAX_DEPTH;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
d3d11_ctx->RSSetViewports(1, &vp);
}
Effect::Effect(ID3D11VertexShader* vsh,
ID3D11PixelShader* psh,
ID3D11InputLayout* layout)
: vsh_(to_com_ptr(vsh)),
psh_(to_com_ptr(psh)),
layout_(to_com_ptr(layout)) {}
void Effect::bind(const std::shared_ptr<Context>& ctx) {
ctx_ = ctx;
ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
d3d11_ctx->IASetInputLayout(layout_.get());
d3d11_ctx->VSSetShader(vsh_.get(), nullptr, 0);
d3d11_ctx->PSSetShader(psh_.get(), nullptr, 0);
}
void Effect::unbind() {}
Geometry::Geometry(D3D_PRIMITIVE_TOPOLOGY primitive,
uint32_t vertices,
uint32_t stride,
ID3D11Buffer* buffer)
: primitive_(primitive),
vertices_(vertices),
stride_(stride),
buffer_(to_com_ptr(buffer)) {}
void Geometry::bind(const std::shared_ptr<Context>& ctx) {
ctx_ = ctx;
ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
// TODO: Handle offset.
uint32_t offset = 0;
ID3D11Buffer* buffers[1] = {buffer_.get()};
d3d11_ctx->IASetVertexBuffers(0, 1, buffers, &stride_, &offset);
d3d11_ctx->IASetPrimitiveTopology(primitive_);
}
void Geometry::unbind() {}
void Geometry::draw() {
ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
CHECK(d3d11_ctx);
// TODO: Handle offset.
d3d11_ctx->Draw(vertices_, 0);
}
Texture2D::Texture2D(ID3D11Texture2D* tex, ID3D11ShaderResourceView* srv)
: texture_(to_com_ptr(tex)), srv_(to_com_ptr(srv)) {
share_handle_ = nullptr;
IDXGIResource* res = nullptr;
if (SUCCEEDED(texture_->QueryInterface(__uuidof(IDXGIResource),
reinterpret_cast<void**>(&res)))) {
res->GetSharedHandle(&share_handle_);
res->Release();
}
// Are we using a keyed mutex?
IDXGIKeyedMutex* mutex = nullptr;
if (SUCCEEDED(texture_->QueryInterface(__uuidof(IDXGIKeyedMutex),
(void**)&mutex))) {
keyed_mutex_ = to_com_ptr(mutex);
}
}
uint32_t Texture2D::width() const {
D3D11_TEXTURE2D_DESC desc;
texture_->GetDesc(&desc);
return desc.Width;
}
uint32_t Texture2D::height() const {
D3D11_TEXTURE2D_DESC desc;
texture_->GetDesc(&desc);
return desc.Height;
}
DXGI_FORMAT Texture2D::format() const {
D3D11_TEXTURE2D_DESC desc;
texture_->GetDesc(&desc);
return desc.Format;
}
bool Texture2D::has_mutex() const {
return (keyed_mutex_.get() != nullptr);
}
bool Texture2D::lock_key(uint64_t key, uint32_t timeout_ms) {
if (keyed_mutex_) {
const auto hr = keyed_mutex_->AcquireSync(key, timeout_ms);
return (hr == S_OK);
}
return true;
}
void Texture2D::unlock_key(uint64_t key) {
if (keyed_mutex_) {
keyed_mutex_->ReleaseSync(key);
}
}
void Texture2D::bind(const std::shared_ptr<Context>& ctx) {
ctx_ = ctx;
ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
if (srv_) {
ID3D11ShaderResourceView* views[1] = {srv_.get()};
d3d11_ctx->PSSetShaderResources(0, 1, views);
}
}
void Texture2D::unbind() {}
void* Texture2D::share_handle() const {
return share_handle_;
}
void Texture2D::copy_from(const std::shared_ptr<Texture2D>& other) {
ID3D11DeviceContext* d3d11_ctx = (ID3D11DeviceContext*)(*ctx_);
CHECK(d3d11_ctx);
if (other) {
d3d11_ctx->CopyResource(texture_.get(), other->texture_.get());
}
}
Device::Device(ID3D11Device* pdev, ID3D11DeviceContext* pctx)
: device_(to_com_ptr(pdev)), ctx_(std::make_shared<Context>(pctx)) {
lib_compiler_ = LoadLibrary(L"d3dcompiler_47.dll");
}
// static
std::shared_ptr<Device> Device::create() {
UINT flags = 0;
#ifdef _DEBUG
flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
// D3D_FEATURE_LEVEL_9_3,
};
UINT num_feature_levels = sizeof(feature_levels) / sizeof(feature_levels[0]);
ID3D11Device* pdev = nullptr;
ID3D11DeviceContext* pctx = nullptr;
D3D_FEATURE_LEVEL selected_level;
HRESULT hr = D3D11CreateDevice(
nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, feature_levels,
num_feature_levels, D3D11_SDK_VERSION, &pdev, &selected_level, &pctx);
if (hr == E_INVALIDARG) {
// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1
// so we need to retry without it.
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags,
&feature_levels[1], num_feature_levels - 1,
D3D11_SDK_VERSION, &pdev, &selected_level, &pctx);
}
if (SUCCEEDED(hr)) {
const auto dev = std::make_shared<Device>(pdev, pctx);
LOG(INFO) << "d3d11: Selected adapter " << dev->adapter_name()
<< " and feature level 0x" << std::setw(4) << std::hex
<< selected_level;
return dev;
}
return nullptr;
}
std::string Device::adapter_name() const {
IDXGIDevice* dxgi_dev = nullptr;
auto hr = device_->QueryInterface(__uuidof(dxgi_dev), (void**)&dxgi_dev);
if (SUCCEEDED(hr)) {
IDXGIAdapter* dxgi_adapt = nullptr;
hr = dxgi_dev->GetAdapter(&dxgi_adapt);
dxgi_dev->Release();
if (SUCCEEDED(hr)) {
DXGI_ADAPTER_DESC desc;
hr = dxgi_adapt->GetDesc(&desc);
dxgi_adapt->Release();
if (SUCCEEDED(hr)) {
return CefString(desc.Description);
}
}
}
return "n/a";
}
std::shared_ptr<Context> Device::immedidate_context() {
return ctx_;
}
std::shared_ptr<SwapChain> Device::create_swapchain(HWND window,
int width,
int height) {
HRESULT hr;
IDXGIFactory1* dxgi_factory = nullptr;
// Default size to the window size unless specified.
RECT rc_bounds;
GetClientRect(window, &rc_bounds);
if (width <= 0) {
width = rc_bounds.right - rc_bounds.left;
}
if (height <= 0) {
height = rc_bounds.bottom - rc_bounds.top;
}
{
IDXGIDevice* dxgi_dev = nullptr;
hr = device_->QueryInterface(__uuidof(IDXGIDevice),
reinterpret_cast<void**>(&dxgi_dev));
if (FAILED(hr)) {
return nullptr;
}
IDXGIAdapter* adapter = nullptr;
hr = dxgi_dev->GetAdapter(&adapter);
dxgi_dev->Release();
if (FAILED(hr)) {
return nullptr;
}
hr = adapter->GetParent(__uuidof(IDXGIFactory1),
reinterpret_cast<void**>(&dxgi_factory));
adapter->Release();
}
if (!dxgi_factory) {
return nullptr;
}
IDXGISwapChain* swapchain = nullptr;
// Create swap chain.
IDXGIFactory2* dxgi_factory2 = nullptr;
hr = dxgi_factory->QueryInterface(__uuidof(IDXGIFactory2),
reinterpret_cast<void**>(&dxgi_factory2));
if (dxgi_factory2) {
DXGI_SWAP_CHAIN_DESC1 sd;
ZeroMemory(&sd, sizeof(sd));
sd.Width = width;
sd.Height = height;
sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
IDXGISwapChain1* swapchain1 = nullptr;
hr = dxgi_factory2->CreateSwapChainForHwnd(device_.get(), window, &sd,
nullptr, nullptr, &swapchain1);
if (SUCCEEDED(hr)) {
hr = swapchain1->QueryInterface(__uuidof(IDXGISwapChain),
reinterpret_cast<void**>(&swapchain));
swapchain1->Release();
}
dxgi_factory2->Release();
} else {
// DirectX 11.0 systems.
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = window;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
hr = dxgi_factory->CreateSwapChain(device_.get(), &sd, &swapchain);
}
// We don't handle full-screen swapchains so we block the ALT+ENTER shortcut.
dxgi_factory->MakeWindowAssociation(window, DXGI_MWA_NO_ALT_ENTER);
dxgi_factory->Release();
if (!swapchain) {
return nullptr;
}
ID3D11Texture2D* back_buffer = nullptr;
hr = swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D),
reinterpret_cast<void**>(&back_buffer));
if (FAILED(hr)) {
swapchain->Release();
return nullptr;
}
ID3D11RenderTargetView* rtv = nullptr;
hr = device_->CreateRenderTargetView(back_buffer, nullptr, &rtv);
back_buffer->Release();
if (FAILED(hr)) {
swapchain->Release();
return nullptr;
}
const auto ctx = (ID3D11DeviceContext*)(*ctx_);
ctx->OMSetRenderTargets(1, &rtv, nullptr);
// Setup the viewport.
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)width;
vp.Height = (FLOAT)height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Create a default sampler to use.
ID3D11SamplerState* sampler = nullptr;
{
D3D11_SAMPLER_DESC desc = {};
desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
desc.MinLOD = 0.0f;
desc.MaxLOD = D3D11_FLOAT32_MAX;
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
device_->CreateSamplerState(&desc, &sampler);
}
// Create a default blend state to use (pre-multiplied alpha).
ID3D11BlendState* blender = nullptr;
{
D3D11_BLEND_DESC desc;
desc.AlphaToCoverageEnable = FALSE;
desc.IndependentBlendEnable = FALSE;
for (auto& n : desc.RenderTarget) {
n.BlendEnable = TRUE;
n.SrcBlend = D3D11_BLEND_ONE;
n.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
n.SrcBlendAlpha = D3D11_BLEND_ONE;
n.DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
n.BlendOp = D3D11_BLEND_OP_ADD;
n.BlendOpAlpha = D3D11_BLEND_OP_ADD;
n.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
}
device_->CreateBlendState(&desc, &blender);
}
return std::make_shared<SwapChain>(swapchain, rtv, sampler, blender);
}
std::shared_ptr<Geometry> Device::create_quad(float x,
float y,
float width,
float height,
bool flip) {
x = (x * 2.0f) - 1.0f;
y = 1.0f - (y * 2.0f);
width = width * 2.0f;
height = height * 2.0f;
float z = 1.0f;
SimpleVertex vertices[] = {
{DirectX::XMFLOAT3(x, y, z), DirectX::XMFLOAT2(0.0f, 0.0f)},
{DirectX::XMFLOAT3(x + width, y, z), DirectX::XMFLOAT2(1.0f, 0.0f)},
{DirectX::XMFLOAT3(x, y - height, z), DirectX::XMFLOAT2(0.0f, 1.0f)},
{DirectX::XMFLOAT3(x + width, y - height, z),
DirectX::XMFLOAT2(1.0f, 1.0f)}};
if (flip) {
DirectX::XMFLOAT2 tmp(vertices[2].tex);
vertices[2].tex = vertices[0].tex;
vertices[0].tex = tmp;
tmp = vertices[3].tex;
vertices[3].tex = vertices[1].tex;
vertices[1].tex = tmp;
}
D3D11_BUFFER_DESC desc = {};
desc.Usage = D3D11_USAGE_DEFAULT;
desc.ByteWidth = sizeof(SimpleVertex) * 4;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA srd = {};
srd.pSysMem = vertices;
ID3D11Buffer* buffer = nullptr;
const auto hr = device_->CreateBuffer(&desc, &srd, &buffer);
if (SUCCEEDED(hr)) {
return std::make_shared<Geometry>(
D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, 4,
static_cast<uint32_t>(sizeof(SimpleVertex)), buffer);
}
return nullptr;
}
std::shared_ptr<Texture2D> Device::open_shared_texture(void* handle) {
Microsoft::WRL::ComPtr<ID3D11Device1> device1;
HRESULT hr = device_->QueryInterface(IID_PPV_ARGS(&device1));
if (FAILED(hr)) {
LOG(FATAL) << "Failed to open D3D11_1 device. hr=" << std::hex << hr;
return nullptr;
}
// Open texture on device using shared handle
ID3D11Texture2D* tex = nullptr;
hr = device1->OpenSharedResource1(handle, IID_PPV_ARGS(&tex));
if (FAILED(hr)) {
LOG(FATAL) << "Failed to open shared texture. hr=" << std::hex << hr;
return nullptr;
}
D3D11_TEXTURE2D_DESC td;
tex->GetDesc(&td);
ID3D11ShaderResourceView* srv = nullptr;
if (td.BindFlags & D3D11_BIND_SHADER_RESOURCE) {
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
srv_desc.Format = td.Format;
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MostDetailedMip = 0;
srv_desc.Texture2D.MipLevels = 1;
hr = device_->CreateShaderResourceView(tex, &srv_desc, &srv);
if (FAILED(hr)) {
tex->Release();
return nullptr;
}
}
return std::make_shared<Texture2D>(tex, srv);
}
std::shared_ptr<Texture2D> Device::create_texture(int width,
int height,
DXGI_FORMAT format,
const void* data,
size_t row_stride) {
D3D11_TEXTURE2D_DESC td;
td.ArraySize = 1;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
td.CPUAccessFlags = 0;
td.Format = format;
td.Width = width;
td.Height = height;
td.MipLevels = 1;
td.MiscFlags = 0;
td.SampleDesc.Count = 1;
td.SampleDesc.Quality = 0;
td.Usage = D3D11_USAGE_DEFAULT;
D3D11_SUBRESOURCE_DATA srd;
srd.pSysMem = data;
srd.SysMemPitch = static_cast<uint32_t>(row_stride);
srd.SysMemSlicePitch = 0;
ID3D11Texture2D* tex = nullptr;
auto hr = device_->CreateTexture2D(&td, data ? &srd : nullptr, &tex);
if (FAILED(hr)) {
return nullptr;
}
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
srv_desc.Format = td.Format;
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MostDetailedMip = 0;
srv_desc.Texture2D.MipLevels = 1;
ID3D11ShaderResourceView* srv = nullptr;
hr = device_->CreateShaderResourceView(tex, &srv_desc, &srv);
if (FAILED(hr)) {
tex->Release();
return nullptr;
}
return std::make_shared<Texture2D>(tex, srv);
}
std::shared_ptr<ID3DBlob> Device::compile_shader(const std::string& source_code,
const std::string& entry_point,
const std::string& model) {
if (!lib_compiler_) {
return nullptr;
}
typedef HRESULT(WINAPI * PFN_D3DCOMPILE)(
LPCVOID, SIZE_T, LPCSTR, const D3D_SHADER_MACRO*, ID3DInclude*, LPCSTR,
LPCSTR, UINT, UINT, ID3DBlob**, ID3DBlob**);
const auto fnc_compile = reinterpret_cast<PFN_D3DCOMPILE>(
GetProcAddress(lib_compiler_, "D3DCompile"));
if (!fnc_compile) {
return nullptr;
}
DWORD flags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined(NDEBUG)
// flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
// flags |= D3DCOMPILE_AVOID_FLOW_CONTROL;
#else
flags |= D3DCOMPILE_DEBUG;
flags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
ID3DBlob* blob = nullptr;
ID3DBlob* blob_err = nullptr;
const auto psrc = source_code.c_str();
const auto len = source_code.size() + 1;
const auto hr =
fnc_compile(psrc, len, nullptr, nullptr, nullptr, entry_point.c_str(),
model.c_str(), flags, 0, &blob, &blob_err);
if (FAILED(hr)) {
if (blob_err) {
// TODO: Log the error.
blob_err->Release();
}
return nullptr;
}
if (blob_err) {
blob_err->Release();
}
return std::shared_ptr<ID3DBlob>(blob, [](ID3DBlob* p) {
if (p) {
p->Release();
}
});
}
std::shared_ptr<Effect> Device::create_default_effect() {
const auto vsh =
R"--(struct VS_INPUT
{
float4 pos : POSITION;
float2 tex : TEXCOORD0;
};
struct VS_OUTPUT
{
float4 pos : SV_POSITION;
float2 tex : TEXCOORD0;
};
VS_OUTPUT main(VS_INPUT input)
{
VS_OUTPUT output;
output.pos = input.pos;
output.tex = input.tex;
return output;
})--";
const auto psh =
R"--(Texture2D tex0 : register(t0);
SamplerState samp0 : register(s0);
struct VS_OUTPUT
{
float4 pos : SV_POSITION;
float2 tex : TEXCOORD0;
};
float4 main(VS_OUTPUT input) : SV_Target
{
return tex0.Sample(samp0, input.tex);
})--";
return create_effect(vsh, "main", "vs_4_0", psh, "main", "ps_4_0");
}
std::shared_ptr<Effect> Device::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) {
const auto vs_blob = compile_shader(vertex_code, vertex_entry, vertex_model);
ID3D11VertexShader* vshdr = nullptr;
ID3D11InputLayout* layout = nullptr;
if (vs_blob) {
device_->CreateVertexShader(vs_blob->GetBufferPointer(),
vs_blob->GetBufferSize(), nullptr, &vshdr);
D3D11_INPUT_ELEMENT_DESC layout_desc[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
D3D11_INPUT_PER_VERTEX_DATA, 0},
};
UINT elements = ARRAYSIZE(layout_desc);
// Create the input layout.
device_->CreateInputLayout(layout_desc, elements,
vs_blob->GetBufferPointer(),
vs_blob->GetBufferSize(), &layout);
}
const auto ps_blob = compile_shader(pixel_code, pixel_entry, pixel_model);
ID3D11PixelShader* pshdr = nullptr;
if (ps_blob) {
device_->CreatePixelShader(ps_blob->GetBufferPointer(),
ps_blob->GetBufferSize(), nullptr, &pshdr);
}
return std::make_shared<Effect>(vshdr, pshdr, layout);
}
Layer::Layer(const std::shared_ptr<Device>& device, bool flip)
: device_(device), flip_(flip) {
bounds_.x = bounds_.y = bounds_.width = bounds_.height = 0.0f;
}
Layer::~Layer() = default;
void Layer::attach(const std::shared_ptr<Composition>& parent) {
composition_ = parent;
}
std::shared_ptr<Composition> Layer::composition() const {
return composition_.lock();
}
Rect Layer::bounds() const {
return bounds_;
}
void Layer::move(float x, float y, float width, float height) {
bounds_.x = x;
bounds_.y = y;
bounds_.width = width;
bounds_.height = height;
// It's not efficient to create the quad everytime we move, but for now we're
// just trying to get something on-screen.
geometry_.reset();
}
void Layer::tick(double) {
// Nothing to update in the base class.
}
void Layer::render_texture(const std::shared_ptr<Context>& ctx,
const std::shared_ptr<Texture2D>& texture) {
if (!geometry_) {
geometry_ = device_->create_quad(bounds_.x, bounds_.y, bounds_.width,
bounds_.height, flip_);
}
if (geometry_ && texture) {
// We need a shader.
if (!effect_) {
effect_ = device_->create_default_effect();
}
// Bind our states/resource to the pipeline.
ScopedBinder<Geometry> quad_binder(ctx, geometry_);
ScopedBinder<Effect> fx_binder(ctx, effect_);
ScopedBinder<Texture2D> tex_binder(ctx, texture);
// Draw the quad.
geometry_->draw();
}
}
Composition::Composition(const std::shared_ptr<Device>& device,
int width,
int height)
: width_(width), height_(height), device_(device) {
fps_ = 0.0;
time_ = 0.0;
frame_ = 0;
fps_start_ = GetTimeNow();
}
bool Composition::is_vsync() const {
return vsync_;
}
double Composition::time() const {
return time_;
}
double Composition::fps() const {
return fps_;
}
void Composition::add_layer(const std::shared_ptr<Layer>& layer) {
if (layer) {
layers_.push_back(layer);
// Attach ourselves as the parent.
layer->attach(shared_from_this());
}
}
bool Composition::remove_layer(const std::shared_ptr<Layer>& layer) {
size_t match = 0;
if (layer) {
for (auto i = layers_.begin(); i != layers_.end();) {
if ((*i).get() == layer.get()) {
i = layers_.erase(i);
match++;
} else {
i++;
}
}
}
return (match > 0);
}
void Composition::resize(bool vsync, int width, int height) {
vsync_ = vsync;
width_ = width;
height_ = height;
}
void Composition::tick(double t) {
time_ = t;
for (const auto& layer : layers_) {
layer->tick(t);
}
}
void Composition::render(const std::shared_ptr<Context>& ctx) {
// Use painter's algorithm and render our layers in order (not doing any dept
// or 3D here).
for (const auto& layer : layers_) {
layer->render(ctx);
}
frame_++;
const auto now = GetTimeNow();
if ((now - fps_start_) > 1000000) {
fps_ = frame_ / double((now - fps_start_) / 1000000.0);
frame_ = 0;
fps_start_ = now;
}
}
FrameBuffer::FrameBuffer(const std::shared_ptr<Device>& device)
: device_(device) {}
void FrameBuffer::on_paint(void* shared_handle) {
// Did the shared texture change?
if (shared_buffer_ && shared_handle != shared_buffer_->share_handle()) {
shared_buffer_.reset();
}
// Open the shared texture.
if (!shared_buffer_) {
shared_buffer_ = device_->open_shared_texture((void*)shared_handle);
if (!shared_buffer_) {
LOG(ERROR) << "d3d11: Could not open shared texture!";
}
}
}
} // namespace client::d3d11