ResourceManager::GetVirtualDisplayPipeline fails because CRTC belongs to different pipeline
I loaded vkms
and configured it as the DRM display driver. Then, I tried to use its write-back property to read the content of the display from memory. However, the function DrmHwc::CreateVirtualDisplay
fails.
Further debugging reveals the following call stack:
ComposerClient::createVirtualDisplay (hwc3/ComposerClient.cpp line 347)
→ DrmHwc::CreateVirtualDisplay (drm/DrmHwc.cpp line 143)
→ ResourceManager::GetVirtualDisplayPipeline (drm/ResourceManager.cpp line 203)
→ DrmDisplayPipeline::CreatePipeline (drm/DrmDisplayPipeline.cpp line 140)
→ TryCreatePipelineUsingEncoder (drm/DrmDisplayPipeline.cpp line 113)
→ TryCreatePipeline (drm/DrmDisplayPipeline.cpp line 61)
→ PipelineBindable<O>::BindPipeline (drm/DrmDisplayPipeline.cpp line 36)
The issue is that BindPipeline
returns an empty object {}
instead of a DrmCrtc
object:
static std::unique_ptr<DrmDisplayPipeline> TryCreatePipeline(DrmDevice &dev, DrmConnector &connector,
DrmEncoder &enc, DrmCrtc &crtc) {
/* Check if resources are available */
auto pipe = std::make_unique<DrmDisplayPipeline>();
pipe->device = &dev;
pipe->connector = connector.BindPipeline(pipe.get());
pipe->encoder = enc.BindPipeline(pipe.get());
pipe->crtc = crtc.BindPipeline(pipe.get()); // Source of error
if (!pipe->connector || !pipe->encoder || !pipe->crtc) {
return {}; // Cascade failure
}
// ...
}
This caused the function TryCreatePipeline
to fail, which in turn triggered a cascade of failures in subsequent functions and the eventual failure.
An empty DRM object is returned by the BindPipeline
function only if the DRM object already exists and at least one of the following applies:
- The DRM object belongs to a different pipeline;
- The value of
return_object_if_bound
is false.
There are two issues here:
- When creating a virtual pipeline (writeback), the CRTC object is shared between both the virtual and regular display pipelines. With the current implementation, since the CRTC object already exists and belongs to a different pipeline, we get an empty object back from
BindPipeline
. - The
BindPipeline
function defines the parameterreturn_object_if_bound
with a default value of false. TheTryCreatePipeline
function, which callsBindPipeline
, doesn't explicitly provide a value for this parameter, and accordingly,BindPipeline
uses the default value of false when called byTryCreatePipeline
.
Because of (1) and (2), we always fail the if condition below and return an empty object.
template <class O>
auto PipelineBindable<O>::BindPipeline(DrmDisplayPipeline *pipeline, bool return_object_if_bound)
-> std::shared_ptr<BindingOwner<O>> {
auto owner_object = owner_object_.lock();
if (owner_object) {
if (bound_pipeline_ == pipeline && return_object_if_bound) {
return owner_object;
}
return {};
}
// ...
}
As a side note, hacking to just return the Crtc object if it exists and belongs to an existing pipeline works.
I am sure this was implemented that way for a reason, so any insight would be greatly appreciated.