Closed Scalibq closed 3 months ago
You can share the code if you want. I can't reproduce it locally. It could be related to the logic you handling locks or something else with vsync / swapchain.
CEF creates its own D3D instance
That's not true. CEF doesn't create D3D in OSR. It passes whatever chromium gives to you, and you just can't pass a D3D instance to chromium.
This seems especially bad when the application is in fullscreen mode. Create a fullscreen D3D application.
I'm just wondering why you can conclude this is a CEF bug despite all rendering code are implemented by yourself.
It has to contend with the D3D object that is rendering to screen. The D3D instances might be starving each other for resources.
Where does this come from? Once you opened the handle and copied to your own using your own device and context, it have nothing to do with that.
Since you are very confident, you could just give a reproducable code as you're the issue creator.
Not to blame, but I don't quite understand why you post a link to your fork, but then say it was not your code and being aggresive. Where else do you expect me to verify the problem?
There're cefclient
target to test OSR function with args:
"args": [
"--off-screen-rendering-enabled",
"--shared-texture-enabled",
"--transparent-painting-enabled",
"--enable-chrome-runtime",
"--url=https://www.youtube.com/watch?v=Cyxixzi2dgQ"
]
This doesn't use fullscreen, but vsync is enabled for that swapchain by default. And it can play the video smoothly.
I also tried to add a fullscreen code from your fork. The video first shows the smooth video in the original cefclient window, later is a fullscreen exclusive (when the clock looks stretched) footage. Both seems smooth to me.
https://github.com/user-attachments/assets/1d726645-a4f0-4cb4-a89b-10421e18afcb
Here's what I done:
Create a new Window and a new Fullscreen swapchain
// Create a D3D11 swapchain for the window.
swap_chain_ = device_->create_swapchain(hwnd());
DCHECK(swap_chain_);
if (!swap_chain_) {
return false;
}
// Create a second swap chain
auto hwnd_fs = ::CreateWindowEx(
0, L"Client_OsrWindow", nullptr,
WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
100 ,100, 1920,1080,
nullptr, nullptr, nullptr, nullptr);
CHECK(hwnd_fs);
swap_chain_full_ = device_->create_swapchain(hwnd_fs, 0, 0, true);
// Create the browser layer.
browser_layer_ = std::make_shared<BrowserLayer>(device_);
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsd = { 0 };
if (fullscreen)
{
fsd.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
fsd.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
fsd.Windowed = true;
}
IDXGISwapChain1* swapchain1 = nullptr;
hr = dxgi_factory2->CreateSwapChainForHwnd(device_.get(), window, &sd,
fullscreen ? &fsd : nullptr, nullptr, &swapchain1);
if (SUCCEEDED(hr)) {
hr = swapchain1->QueryInterface(__uuidof(IDXGISwapChain),
reinterpret_cast<void**>(&swapchain));
swapchain1->Release();
}
if (message == WM_SYSKEYDOWN && event.modifiers & EVENTFLAG_ALT_DOWN && IsKeyDown(VK_RETURN))
{
render_handler_->SetFullscreenState(true);
}
void OsrRenderHandlerWinD3D11::SetFullscreenState(bool fullscreen)
{
swap_chain_full_->fullscreen(fullscreen);
}
void SwapChain::fullscreen(bool fullscreen) {
swapchain_->SetFullscreenState(fullscreen, nullptr);
}
Draw to that swapchain, and Present with VSync.
// Present to window.
swap_chain_->present(send_begin_frame() ? 0 : 1);
// Draw fullscreen
swap_chain_full_->bind(ctx);
swap_chain_full_->resize(texture_size.first, texture_size.second);
swap_chain_full_->clear(0.0f, 0.0f, 1.0f, 1.0f);
composition_->render(ctx);
swap_chain_full_->present(1);
I don't know if this can fully reproduce what your application is, the difference might be your application is heavier or the browser's texture is not rendered like this.
If I create a separate thread and tick the Present there at 60fps, will it be same with your senario?
@Scalibq I'm using OnAccelerated path in 2d engine fully baked by D3D11 pipeline with preserving VSYNC which is always bounded to display refresh rate. My animation quality is butter smooth no single jerkiness.
My guess is, you are missing asynchronous nature of any gpu framework. All commands pushed to gpu pipeline are executed asynchronously till Present will not be called when those all operations needs to complete before swapping backbuffers etc etc...
So in your case if in OnAcceleratedPaint method you are not Presenting because your scenario is more advanced you need to assure that SharedHandle copy was completed before you will exit from OnAcceleratedCallback. It must be done, because you are accessing shared resources between devices, otherwise you will observe black flickering because when you are presenting shared texture copy may not be completed.
So double check if you don't have d3dCtx->Flush() call after copying shared resource. Because it guarantees that operation is completed. If you have a flush call you are stalling all gpu command buffer till everything will be completed. And you are loosing parallel computation benefit. Also if you have this Flush call jerkiness is guaranteed and this is source of your problem.
Instead of flushing context you need to inject query event and wait in your OnAcceleratedPaint till this event will be completed. That way you will avoid jerkiness and copy operation will be completed.
@reitowo once more thank you for your work on this. Excellent job mate!
Also, it wouldn't explain why I could also reproduce the issue with the modified cef-mixer, which doesn't even copy the textures at all, it just creates an SRV directly on the shared texture. So in that case, there is no asynchronous issue with copying not being completed.
If you mean your fork, it doesn't even copy, so chromium writes next frame to it and makes it jerky. If you don't Flush the context and let the callback return, the async rendering would also happen after chromium reuse that resource.
Again, the best way works is
wow, never seen any people being so ignorant and impossible to communicate with, as you wish and have fun with your marvelous code. 😆 @Scalibq
There is a literally 0 chance anyone will jump here to help you solve it, considering your communication culture.
My last advice and take it seriously. I watched your issue on YT. It's not a problem with CEF/Chromium it's 100% problem in your code. Learn, figure out, improve and fix it.
Please note that OnAcceleratedPaint behavior changes a lot since CEF 124. It no longer accepts an external texture, and it gives out sequential textures owned by chromium. It will be recycled once the callback returns.
You have to copy the texture to your own texture buffer before the callback function returns.
https://github.com/Scalibq/cef-mixer/blob/29e4358e8dd4b7705c817be328f270162c677ac0/src/web_layer.cpp#L326
From you code I think you just opened the handle, but not copy it before callback returns. So, it will be junky when you access it later cause next frames may writing to the same resource by chromium.