Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HDR Support #112

Merged
merged 12 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions include/aquamarine/backend/DRM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../allocator/Swapchain.hpp"
#include "../output/Output.hpp"
#include "../input/Input.hpp"
#include <drm_mode.h>
UjinT34 marked this conversation as resolved.
Show resolved Hide resolved
#include <hyprutils/memory/WeakPtr.hpp>
#include <wayland-client.h>
#include <xf86drmMode.h>
Expand Down Expand Up @@ -247,15 +248,18 @@ namespace Aquamarine {
bool test = false;
drmModeModeInfo modeInfo;
std::optional<Hyprutils::Math::Mat3x3> ctm;
std::optional<hdr_output_metadata> hdrMetadata;

struct {
uint32_t gammaLut = 0;
uint32_t fbDamage = 0;
uint32_t modeBlob = 0;
uint32_t ctmBlob = 0;
uint32_t hdrBlob = 0;
bool blobbed = false;
bool gammad = false;
bool ctmd = false;
bool hdrd = false;
} atomic;

void calculateMode(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
Expand All @@ -269,7 +273,7 @@ namespace Aquamarine {
void disconnect();
Hyprutils::Memory::CSharedPointer<SDRMCRTC> getCurrentCRTC(const drmModeConnector* connector);
drmModeModeInfo* getCurrentMode();
void parseEDID(std::vector<uint8_t> data);
IOutput::ParsedEDID parseEDID(std::vector<uint8_t> data);
bool commitState(SDRMConnectorCommitData& data);
void applyCommit(const SDRMConnectorCommitData& data);
void rollbackCommit(const SDRMConnectorCommitData& data);
Expand Down Expand Up @@ -315,17 +319,29 @@ namespace Aquamarine {
uint32_t vrr_capable; // not guaranteed to exist
uint32_t subconnector; // not guaranteed to exist
uint32_t non_desktop;
uint32_t panel_orientation; // not guaranteed to exist
uint32_t content_type; // not guaranteed to exist
uint32_t max_bpc; // not guaranteed to exist
uint32_t panel_orientation; // not guaranteed to exist
uint32_t content_type; // not guaranteed to exist
uint32_t max_bpc; // not guaranteed to exist
uint32_t Colorspace; // not guaranteed to exist
vaxerski marked this conversation as resolved.
Show resolved Hide resolved
uint32_t hdr_output_metadata; // not guaranteed to exist

// atomic-modesetting only

uint32_t crtc_id;
};
uint32_t props[4] = {0};
uint32_t props[13] = {0};
};
UDRMConnectorProps props;

union UDRMConnectorColorspace {
struct {
uint32_t Default;
vaxerski marked this conversation as resolved.
Show resolved Hide resolved
uint32_t BT2020_RGB;
uint32_t BT2020_YCC;
};
uint32_t props[3] = {0};
};
UDRMConnectorColorspace colorspace;
};

class IDRMImplementation {
Expand Down
33 changes: 33 additions & 0 deletions include/aquamarine/output/Output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ namespace Aquamarine {
AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE = (1 << 8),
AQ_OUTPUT_STATE_EXPLICIT_OUT_FENCE = (1 << 9),
AQ_OUTPUT_STATE_CTM = (1 << 10),
AQ_OUTPUT_STATE_HDR = (1 << 11),
};

struct SInternalState {
Expand All @@ -70,6 +71,8 @@ namespace Aquamarine {
Hyprutils::Memory::CSharedPointer<IBuffer> buffer;
int32_t explicitInFence = -1, explicitOutFence = -1;
Hyprutils::Math::Mat3x3 ctm;
bool wideColorGamut = false;
hdr_output_metadata hdrMetadata;
};

const SInternalState& state();
Expand All @@ -88,6 +91,8 @@ namespace Aquamarine {
void enableExplicitOutFenceForNextCommit();
void resetExplicitFences();
void setCTM(const Hyprutils::Math::Mat3x3& ctm);
void setWideColorGamut(bool wcg);
void setHDRMetadata(hdr_output_metadata& metadata);
vaxerski marked this conversation as resolved.
Show resolved Hide resolved

private:
SInternalState internalState;
Expand Down Expand Up @@ -119,6 +124,33 @@ namespace Aquamarine {
AQ_SCHEDULE_ANIMATION_DAMAGE,
};

struct HDRMetadata {
UjinT34 marked this conversation as resolved.
Show resolved Hide resolved
bool supported = false;
double desiredContentMinLuminance;
std::optional<double> desiredContentMaxLuminance;
std::optional<double> desiredMaxFrameAverageLuminance;
bool supportsPQ;
bool supportsBT2020;
UjinT34 marked this conversation as resolved.
Show resolved Hide resolved
};

struct xy {
double x = 0;
double y = 0;
};
vaxerski marked this conversation as resolved.
Show resolved Hide resolved

struct ChromaticityCoords {
UjinT34 marked this conversation as resolved.
Show resolved Hide resolved
xy red;
xy green;
xy blue;
xy white;
};

struct ParsedEDID {
UjinT34 marked this conversation as resolved.
Show resolved Hide resolved
std::string make, serial, model;
std::optional<HDRMetadata> hdrMetadata;
std::optional<ChromaticityCoords> chromaticityCoords;
};

virtual bool commit() = 0;
virtual bool test() = 0;
virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend() = 0;
Expand All @@ -133,6 +165,7 @@ namespace Aquamarine {
virtual bool destroy(); // not all backends allow this!!!

std::string name, description, make, model, serial;
ParsedEDID parsedEDID;
Hyprutils::Math::Vector2D physicalSize;
bool enabled = false;
bool nonDesktop = false;
Expand Down
80 changes: 73 additions & 7 deletions src/backend/drm/DRM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extern "C" {
#include <libudev.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <libdisplay-info/cta.h>
#include <libdisplay-info/cvt.h>
#include <libdisplay-info/info.h>
#include <libdisplay-info/edid.h>
Expand Down Expand Up @@ -1082,6 +1083,8 @@ bool Aquamarine::SDRMConnector::init(drmModeConnector* connector) {

if (!getDRMConnectorProps(backend->gpu->fd, id, &props))
return false;
if (props.Colorspace)
getDRMConnectorColorspace(backend->gpu->fd, props.Colorspace, &colorspace);

auto name = drmModeGetConnectorTypeName(connector->connector_type);
if (!name)
Expand Down Expand Up @@ -1147,11 +1150,12 @@ drmModeModeInfo* Aquamarine::SDRMConnector::getCurrentMode() {
return modeInfo;
}

void Aquamarine::SDRMConnector::parseEDID(std::vector<uint8_t> data) {
auto info = di_info_parse_edid(data.data(), data.size());
IOutput::ParsedEDID Aquamarine::SDRMConnector::parseEDID(std::vector<uint8_t> data) {
auto info = di_info_parse_edid(data.data(), data.size());
IOutput::ParsedEDID parsed = {};
if (!info) {
backend->backend->log(AQ_LOG_ERROR, "drm: failed to parse edid");
return;
return parsed;
}

auto edid = di_info_get_edid(info);
Expand All @@ -1168,7 +1172,60 @@ void Aquamarine::SDRMConnector::parseEDID(std::vector<uint8_t> data) {
model = mod ? mod : "";
serial = ser ? ser : "";

parsed.make = make;
parsed.model = model;
parsed.serial = serial;

// copied from kwin
UjinT34 marked this conversation as resolved.
Show resolved Hide resolved
const auto chromaticity = di_edid_get_chromaticity_coords(edid);
if (chromaticity) {
parsed.chromaticityCoords = IOutput::ChromaticityCoords{
IOutput::xy{chromaticity->red_x, chromaticity->red_y},
IOutput::xy{chromaticity->green_x, chromaticity->green_y},
IOutput::xy{chromaticity->blue_x, chromaticity->blue_y},
IOutput::xy{chromaticity->white_x, chromaticity->white_y},
};
} else {
parsed.chromaticityCoords.reset();
}
const di_edid_cta* cta = nullptr;
const di_edid_ext* const* exts = di_edid_get_extensions(edid);
const di_cta_hdr_static_metadata_block* hdr_static_metadata = nullptr;
const di_cta_colorimetry_block* colorimetry = nullptr;
for (; *exts != nullptr; exts++) {
if (!cta && (cta = di_edid_ext_get_cta(*exts))) {
continue;
}
}
if (cta) {
const di_cta_data_block* const* blocks = di_edid_cta_get_data_blocks(cta);
for (; *blocks != nullptr; blocks++) {
if (!hdr_static_metadata && (hdr_static_metadata = di_cta_data_block_get_hdr_static_metadata(*blocks))) {
continue;
}
if (!colorimetry && (colorimetry = di_cta_data_block_get_colorimetry(*blocks))) {
continue;
}
}
if (hdr_static_metadata) {
parsed.hdrMetadata = IOutput::HDRMetadata{
.desiredContentMinLuminance = hdr_static_metadata->desired_content_min_luminance,
.desiredContentMaxLuminance =
hdr_static_metadata->desired_content_max_luminance > 0 ? std::make_optional(hdr_static_metadata->desired_content_max_luminance) : std::nullopt,
.desiredMaxFrameAverageLuminance = hdr_static_metadata->desired_content_max_frame_avg_luminance > 0 ?
std::make_optional(hdr_static_metadata->desired_content_max_frame_avg_luminance) :
std::nullopt,
.supportsPQ = hdr_static_metadata->eotfs->pq,
.supportsBT2020 = colorimetry && colorimetry->bt2020_rgb,
};
} else {
parsed.hdrMetadata.reset();
}
}

di_info_destroy(info);

return parsed;
}

void Aquamarine::SDRMConnector::recheckCRTCProps() {
Expand All @@ -1188,6 +1245,11 @@ void Aquamarine::SDRMConnector::recheckCRTCProps() {
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Explicit sync {}", output->supportsExplicit ? "supported" : "unsupported"));

backend->backend->log(AQ_LOG_DEBUG, std::format("drm: connector {} crtc {} CTM", szName, (crtc->props.ctm ? "supports" : "doesn't support")));

backend->backend->log(AQ_LOG_DEBUG,
std::format("drm: connector {} crtc {} HDR ({})", szName, (props.hdr_output_metadata ? "supports" : "doesn't support"), props.hdr_output_metadata));

backend->backend->log(AQ_LOG_DEBUG, std::format("drm: connector {} crtc {} Colorspace ({})", szName, (props.Colorspace ? "supports" : "doesn't support"), props.Colorspace));
}

void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
Expand Down Expand Up @@ -1270,16 +1332,17 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
uint8_t* edidData = (uint8_t*)getDRMPropBlob(backend->gpu->fd, id, props.edid, &edidLen);

std::vector<uint8_t> edid{edidData, edidData + edidLen};
parseEDID(edid);
auto parsedEDID = parseEDID(edid);

free(edidData);
edid = {};

// TODO: subconnectors

output->make = make;
output->model = model;
output->serial = serial;
output->make = parsedEDID.make;
output->model = parsedEDID.model;
output->serial = parsedEDID.serial;
output->parsedEDID = parsedEDID;
output->description = std::format("{} {} {} ({})", make, model, serial, szName);
output->needsFrame = true;

Expand Down Expand Up @@ -1590,6 +1653,9 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
if (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_CTM)
data.ctm = STATE.ctm;

if (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_HDR)
data.hdrMetadata = STATE.hdrMetadata;

data.blocking = BLOCKING || formatMismatch;
data.modeset = NEEDS_RECONFIG || lastCommitNoBuffer || formatMismatch;
data.flags = flags;
Expand Down
28 changes: 28 additions & 0 deletions src/backend/drm/Props.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ struct prop_info {
static const struct prop_info connector_info[] = {
#define INDEX(name) (offsetof(SDRMConnector::UDRMConnectorProps, name) / sizeof(uint32_t))
{"CRTC_ID", INDEX(crtc_id)},
{"Colorspace", INDEX(Colorspace)},
{"DPMS", INDEX(dpms)},
{"EDID", INDEX(edid)},
{"HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata)},
{"PATH", INDEX(path)},
{"content type", INDEX(content_type)},
{"link-status", INDEX(link_status)},
Expand All @@ -34,6 +36,12 @@ static const struct prop_info connector_info[] = {
#undef INDEX
};

static const struct prop_info colorspace_info[] = {
#define INDEX(name) (offsetof(SDRMConnector::UDRMConnectorColorspace, name) / sizeof(uint32_t))
{"BT2020_RGB", INDEX(BT2020_RGB)}, {"BT2020_YCC", INDEX(BT2020_YCC)}, {"Default", INDEX(Default)}
UjinT34 marked this conversation as resolved.
Show resolved Hide resolved
#undef INDEX
};

static const struct prop_info crtc_info[] = {
#define INDEX(name) (offsetof(SDRMCRTC::UDRMCRTCProps, name) / sizeof(uint32_t))
{"ACTIVE", INDEX(active)}, {"CTM", INDEX(ctm)},
Expand Down Expand Up @@ -95,10 +103,30 @@ namespace Aquamarine {
return true;
}

static bool scanPropertyEnum(int fd, uint32_t propertyId, uint32_t* result, const prop_info* info, size_t info_len) {
drmModePropertyRes* prop = drmModeGetProperty(fd, propertyId);
if (!prop)
return false;

for (uint32_t i = 0; i < prop->count_enums; ++i) {
const prop_info* p = (prop_info*)bsearch(prop->enums[i].name, info, info_len, sizeof(info[0]), comparePropInfo);
if (p)
result[p->index] = prop->enums[i].value;
}

drmModeFreeProperty(prop);

return true;
}

bool getDRMConnectorProps(int fd, uint32_t id, SDRMConnector::UDRMConnectorProps* out) {
return scanProperties(fd, id, DRM_MODE_OBJECT_CONNECTOR, out->props, connector_info, sizeof(connector_info) / sizeof(connector_info[0]));
}

bool getDRMConnectorColorspace(int fd, uint32_t id, SDRMConnector::UDRMConnectorColorspace* out) {
return scanPropertyEnum(fd, id, out->props, colorspace_info, sizeof(colorspace_info) / sizeof(colorspace_info[0]));
}

bool getDRMCRTCProps(int fd, uint32_t id, SDRMCRTC::UDRMCRTCProps* out) {
return scanProperties(fd, id, DRM_MODE_OBJECT_CRTC, out->props, crtc_info, sizeof(crtc_info) / sizeof(crtc_info[0]));
}
Expand Down
1 change: 1 addition & 0 deletions src/backend/drm/Props.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Aquamarine {
bool getDRMConnectorProps(int fd, uint32_t id, SDRMConnector::UDRMConnectorProps* out);
bool getDRMConnectorColorspace(int fd, uint32_t id, SDRMConnector::UDRMConnectorColorspace* out);
bool getDRMCRTCProps(int fd, uint32_t id, SDRMCRTC::UDRMCRTCProps* out);
bool getDRMPlaneProps(int fd, uint32_t id, SDRMPlane::UDRMPlaneProps* out);
bool getDRMProp(int fd, uint32_t obj, uint32_t prop, uint64_t* ret);
Expand Down
19 changes: 19 additions & 0 deletions src/backend/drm/impl/Atomic.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <aquamarine/backend/drm/Atomic.hpp>
#include <cstring>
#include <drm_mode.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <sys/mman.h>
Expand Down Expand Up @@ -103,6 +104,12 @@ void Aquamarine::CDRMAtomicRequest::addConnector(Hyprutils::Memory::CSharedPoint
add(connector->crtc->id, connector->crtc->props.active, enable);

if (enable) {
if (connector->props.Colorspace && connector->colorspace.BT2020_RGB)
add(connector->id, connector->props.Colorspace, STATE.wideColorGamut ? connector->colorspace.BT2020_RGB : connector->colorspace.Default);

if (connector->props.hdr_output_metadata && data.atomic.hdrd)
add(connector->id, connector->props.hdr_output_metadata, data.atomic.hdrBlob);

if (connector->output->supportsExplicit && STATE.committed & COutputState::AQ_OUTPUT_STATE_EXPLICIT_OUT_FENCE)
add(connector->crtc->id, connector->crtc->props.out_fence_ptr, (uintptr_t)&STATE.explicitOutFence);

Expand Down Expand Up @@ -295,6 +302,18 @@ bool Aquamarine::CDRMAtomicImpl::prepareConnector(Hyprutils::Memory::CSharedPoin
}
}

if ((STATE.committed & COutputState::AQ_OUTPUT_STATE_HDR) && data.hdrMetadata.has_value()) {
if (!connector->props.hdr_output_metadata)
connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to commit hdr metadata: no HDR_OUTPUT_METADATA prop support");
else {
if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, &data.hdrMetadata.value(), sizeof(hdr_output_metadata), &data.atomic.hdrBlob)) {
connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a hdr metadata blob");
data.atomic.hdrBlob = 0;
} else
data.atomic.hdrd = true;
}
}

if ((STATE.committed & COutputState::AQ_OUTPUT_STATE_DAMAGE) && connector->crtc->primary->props.fb_damage_clips && MODE) {
if (STATE.damage.empty())
data.atomic.fbDamage = 0;
Expand Down
9 changes: 9 additions & 0 deletions src/output/Output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ void Aquamarine::COutputState::setCTM(const Hyprutils::Math::Mat3x3& ctm) {
internalState.committed |= AQ_OUTPUT_STATE_CTM;
}

void Aquamarine::COutputState::setWideColorGamut(bool wcg) {
internalState.wideColorGamut = wcg;
}

void Aquamarine::COutputState::setHDRMetadata(hdr_output_metadata& metadata) {
internalState.hdrMetadata = metadata;
internalState.committed |= AQ_OUTPUT_STATE_HDR;
}

void Aquamarine::COutputState::onCommit() {
internalState.committed = 0;
internalState.damage.clear();
Expand Down
Loading