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 all 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
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
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
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;
};
vaxerski marked this conversation as resolved.
Show resolved Hide resolved

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
Loading