Skip to content

Commit 35a1768

Browse files
committed
draft
1 parent 604d0a9 commit 35a1768

6 files changed

Lines changed: 450 additions & 176 deletions

File tree

media_kit_video/windows/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ if(MEDIA_KIT_LIBS_AVAILABLE)
2727
add_library(
2828
${PLUGIN_NAME} SHARED
2929
"d3d11_renderer.cc"
30+
"mailbox_swap_chain.cc"
3031
"media_kit_video_plugin_c_api.cc"
3132
"media_kit_video_plugin.cc"
3233
"video_output_manager.cc"
@@ -46,6 +47,8 @@ if(MEDIA_KIT_LIBS_AVAILABLE)
4647
FLUTTER_PLUGIN_IMPL
4748
)
4849

50+
target_compile_options(${PLUGIN_NAME} PRIVATE "/utf-8")
51+
4952
target_include_directories(
5053
${PLUGIN_NAME} INTERFACE
5154
"${CMAKE_CURRENT_SOURCE_DIR}/include"
Lines changed: 66 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// This file is a part of media_kit
1+
// This file is a part of media_kit
22
// (https://github.com/media-kit/media-kit).
33
//
4-
// Copyright © 2025 & onwards, Predidit.
4+
// Copyright © 2026 Predidit.
55
// All rights reserved.
66
// Use of this source code is governed by MIT license that can be found in the
77
// LICENSE file.
@@ -13,115 +13,68 @@
1313
#pragma comment(lib, "dxgi.lib")
1414
#pragma comment(lib, "d3d11.lib")
1515

16-
#define FAIL(message) \
17-
std::cout << "media_kit: D3D11Renderer: Failure: " << message \
18-
<< std::endl; \
19-
return false
20-
21-
#define CHECK_HRESULT(message) \
22-
if (FAILED(hr)) { \
23-
FAIL(message); \
24-
}
25-
2616
int D3D11Renderer::instance_count_ = 0;
2717

2818
D3D11Renderer::D3D11Renderer(int32_t width, int32_t height)
2919
: width_(width), height_(height) {
30-
mutex_ = ::CreateMutex(NULL, FALSE, NULL);
3120
if (!CreateD3D11Device()) {
3221
throw std::runtime_error("Unable to create Direct3D 11 device.");
3322
}
34-
if (!CreateTexture()) {
35-
throw std::runtime_error("Unable to create Direct3D 11 texture.");
23+
if (!CreateMailbox()) {
24+
throw std::runtime_error("Unable to create mailbox swap chain.");
3625
}
3726
instance_count_++;
3827
}
3928

4029
D3D11Renderer::~D3D11Renderer() {
41-
CleanUp(true);
42-
::ReleaseMutex(mutex_);
43-
::CloseHandle(mutex_);
30+
mailbox_swap_chain_.Reset();
31+
32+
if (d3d_11_device_context_) {
33+
d3d_11_device_context_->Release();
34+
d3d_11_device_context_ = nullptr;
35+
}
36+
if (d3d_11_device_) {
37+
d3d_11_device_->Release();
38+
d3d_11_device_ = nullptr;
39+
}
4440
instance_count_--;
4541
}
4642

4743
void D3D11Renderer::SetSize(int32_t width, int32_t height) {
48-
if (width == width_ && height == height_) {
49-
return;
50-
}
44+
if (width == width_ && height == height_) return;
5145
width_ = width;
5246
height_ = height;
53-
54-
// Release the old texture reference
55-
if (shared_texture_) {
56-
shared_texture_->Release();
57-
shared_texture_ = nullptr;
58-
}
59-
60-
// Resize the swap chain (this will resize the back buffer)
61-
if (swap_chain_) {
62-
auto hr = swap_chain_->ResizeBuffers(1, width_, height_,
63-
DXGI_FORMAT_B8G8R8A8_UNORM, 0);
47+
if (mailbox_swap_chain_) {
48+
const HRESULT hr = mailbox_swap_chain_->Resize(width_, height_);
6449
if (FAILED(hr)) {
65-
std::cout << "media_kit: D3D11Renderer: Failed to resize swap chain"
66-
<< std::endl;
67-
return;
50+
std::cout << "media_kit: D3D11Renderer: Mailbox resize failed (hr=0x"
51+
<< std::hex << hr << std::dec << ")" << std::endl;
6852
}
6953
}
70-
71-
// Recreate the shared texture with the new size
72-
CreateTexture();
7354
}
7455

75-
void D3D11Renderer::CopyTexture() {
76-
::WaitForSingleObject(mutex_, INFINITE);
77-
78-
// With native DXGI rendering, mpv renders directly to the swap chain's back buffer.
79-
// We need to copy the back buffer to our shared texture for Flutter.
80-
if (d3d_11_device_context_ != nullptr && swap_chain_ != nullptr && shared_texture_) {
81-
// Get the back buffer from the swap chain
82-
Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer;
83-
auto hr = swap_chain_->GetBuffer(0, __uuidof(ID3D11Texture2D),
84-
(void**)&back_buffer);
85-
if (SUCCEEDED(hr) && back_buffer) {
86-
// Copy from back buffer to shared texture
87-
d3d_11_device_context_->CopyResource(shared_texture_.Get(), back_buffer.Get());
88-
d3d_11_device_context_->Flush();
89-
}
56+
void D3D11Renderer::ProducerCommit() {
57+
if (mailbox_swap_chain_) {
58+
mailbox_swap_chain_->ProducerCommit();
9059
}
91-
92-
::ReleaseMutex(mutex_);
9360
}
9461

95-
void D3D11Renderer::CleanUp(bool release_device) {
96-
// Release texture
97-
if (shared_texture_) {
98-
shared_texture_->Release();
99-
shared_texture_ = nullptr;
100-
}
101-
102-
// Release swap chain
103-
if (swap_chain_) {
104-
swap_chain_->Release();
105-
swap_chain_ = nullptr;
62+
HANDLE D3D11Renderer::ConsumerAcquire() {
63+
if (mailbox_swap_chain_) {
64+
return mailbox_swap_chain_->ConsumerAcquire();
10665
}
66+
return nullptr;
67+
}
10768

108-
// Release device and context if the instance is being destroyed
109-
if (release_device) {
110-
if (d3d_11_device_context_) {
111-
d3d_11_device_context_->Release();
112-
d3d_11_device_context_ = nullptr;
113-
}
114-
if (d3d_11_device_) {
115-
d3d_11_device_->Release();
116-
d3d_11_device_ = nullptr;
117-
}
69+
HANDLE D3D11Renderer::ReadHandleSnapshot() const {
70+
if (mailbox_swap_chain_) {
71+
return mailbox_swap_chain_->ReadHandleSnapshot();
11872
}
73+
return nullptr;
11974
}
12075

12176
bool D3D11Renderer::CreateD3D11Device() {
122-
if (d3d_11_device_ != nullptr) {
123-
return true; // Already created
124-
}
77+
if (d3d_11_device_) return true;
12578

12679
const D3D_FEATURE_LEVEL feature_levels[] = {
12780
D3D_FEATURE_LEVEL_11_1,
@@ -134,9 +87,7 @@ bool D3D11Renderer::CreateD3D11Device() {
13487
IDXGIAdapter* adapter = nullptr;
13588
D3D_DRIVER_TYPE driver_type = D3D_DRIVER_TYPE_UNKNOWN;
13689

137-
// Automatically selecting adapter on Windows 10 RTM or greater
13890
if (Utils::IsWindows10RTMOrGreater()) {
139-
adapter = nullptr;
14091
driver_type = D3D_DRIVER_TYPE_HARDWARE;
14192
} else {
14293
IDXGIFactory* dxgi = nullptr;
@@ -147,74 +98,45 @@ bool D3D11Renderer::CreateD3D11Device() {
14798
}
14899
}
149100

150-
// Create swap chain descriptor for offscreen rendering
151-
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
152-
swap_chain_desc.BufferDesc.Width = width_;
153-
swap_chain_desc.BufferDesc.Height = height_;
154-
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
155-
swap_chain_desc.BufferDesc.RefreshRate.Numerator = 0;
156-
swap_chain_desc.BufferDesc.RefreshRate.Denominator = 1;
157-
swap_chain_desc.SampleDesc.Count = 1;
158-
swap_chain_desc.SampleDesc.Quality = 0;
159-
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
160-
swap_chain_desc.BufferCount = 1;
161-
// Use desktop window for offscreen rendering
162-
swap_chain_desc.OutputWindow = ::GetDesktopWindow();
163-
swap_chain_desc.Windowed = TRUE;
164-
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
165-
166-
auto hr = ::D3D11CreateDeviceAndSwapChain(
167-
adapter, driver_type, 0, 0, feature_levels,
168-
sizeof(feature_levels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION,
169-
&swap_chain_desc, &swap_chain_,
170-
&d3d_11_device_, nullptr, &d3d_11_device_context_);
171-
172-
CHECK_HRESULT("D3D11CreateDeviceAndSwapChain");
173-
174-
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device = nullptr;
175-
auto dxgi_device_success = d3d_11_device_->QueryInterface(
176-
__uuidof(IDXGIDevice), (void**)&dxgi_device);
177-
if (SUCCEEDED(dxgi_device_success) && dxgi_device != nullptr) {
178-
dxgi_device->SetGPUThreadPriority(5); // Must be in interval [-7, 7]
101+
const HRESULT hr = ::D3D11CreateDevice(
102+
adapter, driver_type, nullptr,
103+
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
104+
feature_levels, static_cast<UINT>(std::size(feature_levels)),
105+
D3D11_SDK_VERSION, &d3d_11_device_, nullptr, &d3d_11_device_context_);
106+
107+
if (adapter) adapter->Release();
108+
109+
if (FAILED(hr)) {
110+
std::cout << "media_kit: D3D11Renderer: D3D11CreateDevice failed (hr=0x"
111+
<< std::hex << hr << std::dec << ")" << std::endl;
112+
return false;
179113
}
180114

181-
auto level = d3d_11_device_->GetFeatureLevel();
115+
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
116+
if (SUCCEEDED(d3d_11_device_->QueryInterface(__uuidof(IDXGIDevice),
117+
(void**)&dxgi_device)) &&
118+
dxgi_device) {
119+
dxgi_device->SetGPUThreadPriority(5);
120+
}
121+
122+
const auto level = d3d_11_device_->GetFeatureLevel();
182123
std::cout << "media_kit: D3D11Renderer: Direct3D Feature Level: "
183-
<< (((unsigned)level) >> 12) << "_"
184-
<< ((((unsigned)level) >> 8) & 0xf) << std::endl;
124+
<< (static_cast<unsigned>(level) >> 12) << "_"
125+
<< ((static_cast<unsigned>(level) >> 8) & 0xfu) << std::endl;
185126

186127
return true;
187128
}
188129

189-
bool D3D11Renderer::CreateTexture() {
190-
// Create a separate shared texture for Flutter
191-
// (The swap chain back buffer cannot be shared directly with Flutter)
192-
D3D11_TEXTURE2D_DESC texture_desc = {0};
193-
texture_desc.Width = width_;
194-
texture_desc.Height = height_;
195-
texture_desc.MipLevels = 1;
196-
texture_desc.ArraySize = 1;
197-
texture_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
198-
texture_desc.SampleDesc.Count = 1;
199-
texture_desc.SampleDesc.Quality = 0;
200-
texture_desc.Usage = D3D11_USAGE_DEFAULT;
201-
texture_desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
202-
texture_desc.CPUAccessFlags = 0;
203-
texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
204-
205-
auto hr = d3d_11_device_->CreateTexture2D(&texture_desc, nullptr,
206-
&shared_texture_);
207-
CHECK_HRESULT("ID3D11Device::CreateTexture2D");
208-
209-
Microsoft::WRL::ComPtr<IDXGIResource> resource;
210-
hr = shared_texture_.As(&resource);
211-
CHECK_HRESULT("ID3D11Texture2D::As<IDXGIResource>");
212-
213-
// Retrieve the shared HANDLE for interop with Flutter
214-
hr = resource->GetSharedHandle(&handle_);
215-
CHECK_HRESULT("IDXGIResource::GetSharedHandle");
216-
217-
shared_texture_->AddRef();
218-
130+
bool D3D11Renderer::CreateMailbox() {
131+
MailboxSwapChain* raw = nullptr;
132+
const HRESULT hr =
133+
MailboxSwapChain::Create(d3d_11_device_, width_, height_, &raw);
134+
if (FAILED(hr)) {
135+
std::cout << "media_kit: D3D11Renderer: MailboxSwapChain::Create failed "
136+
"(hr=0x"
137+
<< std::hex << hr << std::dec << ")" << std::endl;
138+
return false;
139+
}
140+
mailbox_swap_chain_.Attach(raw);
219141
return true;
220-
}
142+
}
Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// This file is a part of media_kit
22
// (https://github.com/media-kit/media-kit).
33
//
4-
// Copyright © 2025 & onwards, Predidit.
4+
// Copyright © Predidit.
55
// All rights reserved.
66
// Use of this source code is governed by MIT license that can be found in the
77
// LICENSE file.
@@ -15,55 +15,66 @@
1515
#include <wrl.h>
1616

1717
#include <cstdint>
18-
#include <functional>
1918
#include <iostream>
2019

20+
#include "mailbox_swap_chain.h"
2121
#include "utils.h"
2222

23-
// |D3D11Renderer| provides an abstraction around Direct3D 11 for video
24-
// rendering with libmpv's native DXGI support.
25-
// This replaces the previous ANGLE-based implementation with a simpler,
26-
// more efficient approach using mpv's built-in D3D11 renderer.
27-
23+
// D3D11Renderer creates a D3D11 device and owns a MailboxSwapChain that
24+
// implements the lock-free triple-buffer mailbox between the libmpv rendering
25+
// thread (producer) and Flutter's render thread (consumer).
26+
//
27+
// The MailboxSwapChain is passed directly to mpv as the IDXGISwapChain* in
28+
// mpv_dxgi_init_params. mpv calls GetBuffer(0, ...) to obtain a render
29+
// target, renders into it, and flushes. The plugin then calls
30+
// ProducerCommit() to atomically publish the frame. Flutter's
31+
// GpuSurfaceTexture callback calls ConsumerAcquire() to receive the DXGI
32+
// shared HANDLE of the newest complete frame — with no copy and no OS lock.
2833
class D3D11Renderer {
2934
public:
30-
const int32_t width() const { return width_; }
31-
const int32_t height() const { return height_; }
32-
const HANDLE handle() const { return handle_; }
35+
int32_t width() const { return width_; }
36+
int32_t height() const { return height_; }
37+
38+
// Raw device pointer used by VideoOutput to populate mpv_dxgi_init_params.
3339
ID3D11Device* device() const { return d3d_11_device_; }
34-
IDXGISwapChain* swap_chain() const { return swap_chain_; }
3540

36-
D3D11Renderer(int32_t width, int32_t height);
41+
// IDXGISwapChain* facade backed by MailboxSwapChain.
42+
// Passed to mpv as mpv_dxgi_init_params::swapchain (void*).
43+
IDXGISwapChain* swap_chain() const { return mailbox_swap_chain_.Get(); }
3744

45+
D3D11Renderer(int32_t width, int32_t height);
3846
~D3D11Renderer();
3947

48+
// Recreates the three mailbox slots at the new dimensions.
49+
// Must be called from the producer thread only.
4050
void SetSize(int32_t width, int32_t height);
4151

42-
void CopyTexture();
52+
// Called from the producer thread (mpv thread pool) after
53+
// mpv_render_context_render returns. Publishes the rendered frame.
54+
void ProducerCommit();
4355

44-
private:
45-
bool CreateD3D11Device();
56+
// Called from the consumer thread (Flutter GpuSurfaceTexture callback).
57+
// Returns the DXGI shared HANDLE of the most recent complete frame.
58+
HANDLE ConsumerAcquire();
4659

47-
bool CreateTexture();
60+
// Returns the DXGI shared HANDLE for the current read slot without
61+
// advancing mailbox state. Used once during texture registration before
62+
// the consumer thread starts.
63+
HANDLE ReadHandleSnapshot() const;
4864

49-
void CleanUp(bool release_device);
65+
private:
66+
bool CreateD3D11Device();
67+
bool CreateMailbox();
5068

5169
int32_t width_ = 1;
5270
int32_t height_ = 1;
53-
HANDLE handle_ = nullptr;
54-
55-
// Sync operations.
56-
HANDLE mutex_ = nullptr;
5771

58-
// D3D 11
5972
ID3D11Device* d3d_11_device_ = nullptr;
6073
ID3D11DeviceContext* d3d_11_device_context_ = nullptr;
61-
IDXGISwapChain* swap_chain_ = nullptr;
6274

63-
// Shared texture for Flutter rendering (created from swap chain back buffer)
64-
Microsoft::WRL::ComPtr<ID3D11Texture2D> shared_texture_;
75+
Microsoft::WRL::ComPtr<MailboxSwapChain> mailbox_swap_chain_;
6576

6677
static int instance_count_;
6778
};
6879

69-
#endif
80+
#endif // D3D11_RENDERER_H_

0 commit comments

Comments
 (0)