Skip to content

Commit

Permalink
core/drm: Add HDR Support (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
UjinT34 authored Jan 5, 2025
1 parent f7082be commit aeab812
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 34 deletions.
46 changes: 33 additions & 13 deletions include/aquamarine/backend/DRM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,16 @@ namespace Aquamarine {
uint32_t gamma_lut;
uint32_t gamma_lut_size;
uint32_t ctm;
uint32_t degamma_lut;
uint32_t degamma_lut_size;

// atomic-modesetting only

uint32_t active;
uint32_t mode_id;
uint32_t out_fence_ptr;
};
uint32_t props[7] = {0};
uint32_t props[9] = {0};
};
UDRMCRTCProps props;
};
Expand All @@ -203,6 +205,7 @@ namespace Aquamarine {
virtual void setCursorVisible(bool visible);
virtual Hyprutils::Math::Vector2D cursorPlaneSize();
virtual size_t getGammaSize();
virtual size_t getDeGammaSize();
virtual std::vector<SDRMFormat> getRenderFormats();

int getConnectorID();
Expand Down Expand Up @@ -247,15 +250,20 @@ 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;
bool blobbed = false;
bool gammad = false;
bool ctmd = false;
uint32_t gammaLut = 0;
uint32_t degammaLut = 0;
uint32_t fbDamage = 0;
uint32_t modeBlob = 0;
uint32_t ctmBlob = 0;
uint32_t hdrBlob = 0;
bool blobbed = false;
bool gammad = false;
bool degammad = false;
bool ctmd = false;
bool hdrd = false;
} atomic;

void calculateMode(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
Expand All @@ -269,7 +277,7 @@ namespace Aquamarine {
void disconnect();
Hyprutils::Memory::CSharedPointer<SDRMCRTC> getCurrentCRTC(const drmModeConnector* connector);
drmModeModeInfo* getCurrentMode();
void parseEDID(std::vector<uint8_t> data);
IOutput::SParsedEDID 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 +323,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
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;
uint32_t BT2020_RGB;
uint32_t BT2020_YCC;
};
uint32_t props[3] = {0};
};
UDRMConnectorColorspace colorspace;
};

class IDRMImplementation {
Expand Down
38 changes: 37 additions & 1 deletion include/aquamarine/output/Output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ 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),
AQ_OUTPUT_STATE_DEGAMMA_LUT = (1 << 12),
};

struct SInternalState {
Expand All @@ -62,14 +64,17 @@ namespace Aquamarine {
bool enabled = false;
bool adaptiveSync = false;
eOutputPresentationMode presentationMode = AQ_OUTPUT_PRESENTATION_VSYNC;
std::vector<uint16_t> gammaLut; // Gamma lut in the format [r,g,b]+
std::vector<uint16_t> gammaLut; // Gamma lut in the format [r,g,b]+
std::vector<uint16_t> degammaLut; // Gamma lut in the format [r,g,b]+
Hyprutils::Math::Vector2D lastModeSize;
Hyprutils::Memory::CWeakPointer<SOutputMode> mode;
Hyprutils::Memory::CSharedPointer<SOutputMode> customMode;
uint32_t drmFormat = DRM_FORMAT_INVALID;
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 @@ -80,6 +85,7 @@ namespace Aquamarine {
void setAdaptiveSync(bool enabled);
void setPresentationMode(eOutputPresentationMode mode);
void setGammaLut(const std::vector<uint16_t>& lut);
void setDeGammaLut(const std::vector<uint16_t>& lut);
void setMode(Hyprutils::Memory::CSharedPointer<SOutputMode> mode);
void setCustomMode(Hyprutils::Memory::CSharedPointer<SOutputMode> mode);
void setFormat(uint32_t drmFormat);
Expand All @@ -88,6 +94,8 @@ namespace Aquamarine {
void enableExplicitOutFenceForNextCommit();
void resetExplicitFences();
void setCTM(const Hyprutils::Math::Mat3x3& ctm);
void setWideColorGamut(bool wcg);
void setHDRMetadata(const hdr_output_metadata& metadata);

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

struct SHDRMetadata {
float desiredContentMaxLuminance = 0;
float desiredMaxFrameAverageLuminance = 0;
float desiredContentMinLuminance = 0;
bool supportsPQ = false;
};

struct xy {
double x = 0;
double y = 0;
};

struct SChromaticityCoords {
xy red;
xy green;
xy blue;
xy white;
};

struct SParsedEDID {
std::string make, serial, model;
std::optional<SHDRMetadata> hdrMetadata;
std::optional<SChromaticityCoords> chromaticityCoords;
bool supportsBT2020 = false;
};

virtual bool commit() = 0;
virtual bool test() = 0;
virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend() = 0;
Expand All @@ -130,9 +164,11 @@ namespace Aquamarine {
virtual Hyprutils::Math::Vector2D cursorPlaneSize(); // -1, -1 means no set size, 0, 0 means error
virtual void scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);
virtual size_t getGammaSize();
virtual size_t getDeGammaSize();
virtual bool destroy(); // not all backends allow this!!!

std::string name, description, make, model, serial;
SParsedEDID parsedEDID;
Hyprutils::Math::Vector2D physicalSize;
bool enabled = false;
bool nonDesktop = false;
Expand Down
101 changes: 94 additions & 7 deletions src/backend/drm/DRM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <aquamarine/backend/drm/Atomic.hpp>
#include <aquamarine/allocator/GBM.hpp>
#include <aquamarine/allocator/DRMDumb.hpp>
#include <cstdint>
#include <format>
#include <hyprutils/string/VarList.hpp>
#include <chrono>
#include <thread>
Expand All @@ -19,6 +21,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 @@ -1101,6 +1104,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 @@ -1166,11 +1171,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::SParsedEDID Aquamarine::SDRMConnector::parseEDID(std::vector<uint8_t> data) {
auto info = di_info_parse_edid(data.data(), data.size());
IOutput::SParsedEDID 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 @@ -1187,7 +1193,64 @@ void Aquamarine::SDRMConnector::parseEDID(std::vector<uint8_t> data) {
model = mod ? mod : "";
serial = ser ? ser : "";

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

const auto chromaticity = di_edid_get_chromaticity_coords(edid);
if (chromaticity) {
parsed.chromaticityCoords = IOutput::SChromaticityCoords{
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},
};
TRACE(backend->backend->log(AQ_LOG_TRACE,
std::format("EDID: chromaticity coords {},{} {},{} {},{} {},{}", parsed.chromaticityCoords->red.x, parsed.chromaticityCoords->red.y,
parsed.chromaticityCoords->green.x, parsed.chromaticityCoords->green.y, parsed.chromaticityCoords->blue.x,
parsed.chromaticityCoords->blue.y, parsed.chromaticityCoords->white.y, parsed.chromaticityCoords->white.y)));
}

auto exts = di_edid_get_extensions(edid);

for (; *exts != nullptr; exts++) {
auto tag = di_edid_ext_get_tag(*exts);
TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("EDID: checking ext {}", (uint32_t)tag)));
if (tag == DI_EDID_EXT_DISPLAYID)
backend->backend->log(AQ_LOG_WARNING, "FIXME: support displayid blocks");

const auto cta = di_edid_ext_get_cta(*exts);
if (cta) {
TRACE(backend->backend->log(AQ_LOG_TRACE, "EDID: found CTA"));
const di_cta_hdr_static_metadata_block* hdr_static_metadata = nullptr;
const di_cta_colorimetry_block* colorimetry = nullptr;
auto 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))) {
TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("EDID: found HDR {}", hdr_static_metadata->eotfs->pq)));
parsed.hdrMetadata = IOutput::SHDRMetadata{
.desiredContentMaxLuminance = hdr_static_metadata->desired_content_max_luminance,
.desiredMaxFrameAverageLuminance = hdr_static_metadata->desired_content_max_frame_avg_luminance,
.desiredContentMinLuminance = hdr_static_metadata->desired_content_min_luminance,
.supportsPQ = hdr_static_metadata->eotfs->pq,
};
continue;
}
if (!colorimetry && (colorimetry = di_cta_data_block_get_colorimetry(*blocks))) {
TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("EDID: found colorimetry {}", colorimetry->bt2020_rgb)));
parsed.supportsBT2020 = colorimetry->bt2020_rgb;
continue;
}
}
break;
}
}

di_info_destroy(info);

TRACE(backend->backend->log(AQ_LOG_TRACE, "EDID: parsed"));

return parsed;
}

void Aquamarine::SDRMConnector::recheckCRTCProps() {
Expand All @@ -1207,6 +1270,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 @@ -1289,16 +1357,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 @@ -1609,6 +1678,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 Expand Up @@ -1787,6 +1859,21 @@ size_t Aquamarine::CDRMOutput::getGammaSize() {
return size;
}

size_t Aquamarine::CDRMOutput::getDeGammaSize() {
if (!backend->atomic) {
backend->log(AQ_LOG_ERROR, "No support for gamma on the legacy iface");
return 0;
}

uint64_t size = 0;
if (!getDRMProp(backend->gpu->fd, connector->crtc->id, connector->crtc->props.degamma_lut_size, &size)) {
backend->log(AQ_LOG_ERROR, "Couldn't get the degamma_size prop");
return 0;
}

return size;
}

std::vector<SDRMFormat> Aquamarine::CDRMOutput::getRenderFormats() {
if (!connector->crtc || !connector->crtc->primary || connector->crtc->primary->formats.empty()) {
backend->log(AQ_LOG_ERROR, "Can't get formats: no crtc");
Expand Down
Loading

0 comments on commit aeab812

Please sign in to comment.