diff options
| author | Tomi Valkeinen <tomi.valkeinen@iki.fi> | 2015-09-28 01:13:34 +0300 | 
|---|---|---|
| committer | Tomi Valkeinen <tomi.valkeinen@iki.fi> | 2015-09-28 01:13:34 +0300 | 
| commit | 009828beac9bfe9c36d336a4de0d297f90aece52 (patch) | |
| tree | 3ea6b22f2036dcc0c23c459ce53bfc2ad282e3e7 /libkms++ | |
Initial version
Diffstat (limited to 'libkms++')
| -rw-r--r-- | libkms++/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | libkms++/atomicreq.cpp | 64 | ||||
| -rw-r--r-- | libkms++/atomicreq.h | 31 | ||||
| -rw-r--r-- | libkms++/card.cpp | 198 | ||||
| -rw-r--r-- | libkms++/card.h | 49 | ||||
| -rw-r--r-- | libkms++/connector.cpp | 111 | ||||
| -rw-r--r-- | libkms++/connector.h | 49 | ||||
| -rw-r--r-- | libkms++/crtc.cpp | 74 | ||||
| -rw-r--r-- | libkms++/crtc.h | 35 | ||||
| -rw-r--r-- | libkms++/decls.h | 15 | ||||
| -rw-r--r-- | libkms++/drmobject.cpp | 69 | ||||
| -rw-r--r-- | libkms++/drmobject.h | 44 | ||||
| -rw-r--r-- | libkms++/encoder.cpp | 63 | ||||
| -rw-r--r-- | libkms++/encoder.h | 25 | ||||
| -rw-r--r-- | libkms++/framebuffer.cpp | 167 | ||||
| -rw-r--r-- | libkms++/framebuffer.h | 44 | ||||
| -rw-r--r-- | libkms++/helpers.cpp | 23 | ||||
| -rw-r--r-- | libkms++/helpers.h | 12 | ||||
| -rw-r--r-- | libkms++/kms++.h | 10 | ||||
| -rw-r--r-- | libkms++/plane.cpp | 64 | ||||
| -rw-r--r-- | libkms++/plane.h | 32 | ||||
| -rw-r--r-- | libkms++/property.cpp | 36 | ||||
| -rw-r--r-- | libkms++/property.h | 23 | ||||
| -rw-r--r-- | libkms++/utils/color.cpp | 67 | ||||
| -rw-r--r-- | libkms++/utils/color.h | 48 | ||||
| -rw-r--r-- | libkms++/utils/conv.cpp | 138 | ||||
| -rw-r--r-- | libkms++/utils/conv.h | 8 | ||||
| -rw-r--r-- | libkms++/utils/testpat.cpp | 162 | ||||
| -rw-r--r-- | libkms++/utils/testpat.h | 6 | 
29 files changed, 1674 insertions, 0 deletions
| diff --git a/libkms++/CMakeLists.txt b/libkms++/CMakeLists.txt new file mode 100644 index 0000000..e8d14f7 --- /dev/null +++ b/libkms++/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories(${LIBDRM_INCLUDE_DIRS}) +link_directories(${LIBDRM_LIBRARY_DIRS}) + +file(GLOB SRCS "*.cpp" "*.h" "utils/*.cpp" "utils/*.h") +add_library(kms++ ${SRCS}) + +target_include_directories(kms++ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/libkms++/atomicreq.cpp b/libkms++/atomicreq.cpp new file mode 100644 index 0000000..3346af9 --- /dev/null +++ b/libkms++/atomicreq.cpp @@ -0,0 +1,64 @@ +#include <cassert> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "atomicreq.h" +#include "property.h" +#include "card.h" + +#ifndef DRM_CLIENT_CAP_ATOMIC + +#define DRM_MODE_ATOMIC_TEST_ONLY 0 + +struct _drmModeAtomicReq; +typedef struct _drmModeAtomicReq* drmModeAtomicReqPtr; + +static inline drmModeAtomicReqPtr drmModeAtomicAlloc() { return 0; } +static inline void drmModeAtomicFree(drmModeAtomicReqPtr) { } +static inline int drmModeAtomicAddProperty(drmModeAtomicReqPtr, uint32_t, uint32_t, uint64_t) { return 0; } +static inline int drmModeAtomicCommit(int, drmModeAtomicReqPtr, int, void*) { return 0; } + +#endif // DRM_CLIENT_CAP_ATOMIC + +namespace kms +{ +AtomicReq::AtomicReq(Card& card) +	: m_card(card) +{ +	assert(card.has_atomic()); +	m_req = drmModeAtomicAlloc(); +} + +AtomicReq::~AtomicReq() +{ +	drmModeAtomicFree(m_req); +} + +void AtomicReq::add(uint32_t ob_id, uint32_t prop_id, uint64_t value) +{ +	int r = drmModeAtomicAddProperty(m_req, ob_id, prop_id, value); +	if (r <= 0) +		throw std::invalid_argument("foo"); +} + +void AtomicReq::add(DrmObject *ob, Property *prop, uint64_t value) +{ +	add(ob->id(), prop->id(), value); +} + +int AtomicReq::test() +{ +	uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY; + +	return drmModeAtomicCommit(m_card.fd(), m_req, flags, 0); +} + +int AtomicReq::commit() +{ +	uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT; +	void* data = 0; + +	return drmModeAtomicCommit(m_card.fd(), m_req, flags, data); +} +} diff --git a/libkms++/atomicreq.h b/libkms++/atomicreq.h new file mode 100644 index 0000000..9a8a748 --- /dev/null +++ b/libkms++/atomicreq.h @@ -0,0 +1,31 @@ +#pragma once + +#include <cstdint> + +struct _drmModeAtomicReq; + +#include "decls.h" + +namespace kms +{ +class AtomicReq +{ +public: +	AtomicReq(Card& card); +	~AtomicReq(); + +	AtomicReq(const AtomicReq& other) = delete; +	AtomicReq& operator=(const AtomicReq& other) = delete; + +	void add(uint32_t ob_id, uint32_t prop_id, uint64_t value); +	void add(DrmObject *ob, Property *prop, uint64_t value); + +	int test(); +	int commit(); + +private: +	Card& m_card; +	_drmModeAtomicReq* m_req; +}; + +} diff --git a/libkms++/card.cpp b/libkms++/card.cpp new file mode 100644 index 0000000..2ba350d --- /dev/null +++ b/libkms++/card.cpp @@ -0,0 +1,198 @@ +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <utility> +#include <stdexcept> +#include <string.h> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "kms++.h" + +#ifndef DRM_CLIENT_CAP_ATOMIC +#define DRM_CLIENT_CAP_ATOMIC 3 +#endif + +namespace kms +{ + +Card::Card() +{ +	const char *card = "/dev/dri/card0"; + +	int fd = open(card, O_RDWR | O_CLOEXEC); +	if (fd < 0) +		throw std::invalid_argument("foo"); +	m_fd = fd; + +	int r; + +	r = drmSetMaster(fd); +	m_master = r == 0; + +	r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); +	if (r) +		throw std::invalid_argument("foo"); + +	r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1); +	m_has_atomic = r == 0; + +	uint64_t has_dumb; +	r = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb); +	if (r || !has_dumb) +		throw std::invalid_argument("foo"); + +	auto res = drmModeGetResources(m_fd); +	if (!res) +		throw std::invalid_argument("foo"); + +	for (int i = 0; i < res->count_connectors; ++i) { +		uint32_t id = res->connectors[i]; +		m_obmap[id] = new Connector(*this, id, i); +	} + +	for (int i = 0; i < res->count_crtcs; ++i) { +		uint32_t id = res->crtcs[i]; +		m_obmap[id] = new Crtc(*this, id, i); +	} + +	for (int i = 0; i < res->count_encoders; ++i) { +		uint32_t id = res->encoders[i]; +		m_obmap[id] = new Encoder(*this, id); +	} + +	drmModeFreeResources(res); + +	auto planeRes = drmModeGetPlaneResources(m_fd); + +	for (uint i = 0; i < planeRes->count_planes; ++i) { +		uint32_t id = planeRes->planes[i]; +		m_obmap[id] = new Plane(*this, id); +	} + +	drmModeFreePlaneResources(planeRes); + +	// collect all possible props +	for (auto ob : get_objects()) { +		auto props = drmModeObjectGetProperties(m_fd, ob->id(), ob->object_type()); + +		if (props == nullptr) +			continue; + +		for (unsigned i = 0; i < props->count_props; ++i) { +			uint32_t prop_id = props->props[i]; + +			if (m_obmap.find(prop_id) == m_obmap.end()) +				m_obmap[prop_id] = new Property(*this, prop_id); +		} + +		drmModeFreeObjectProperties(props); +	} + +	for (auto pair : m_obmap) +		pair.second->setup(); +} + +Card::~Card() +{ +	for (auto pair : m_obmap) +		delete pair.second; + +	close(m_fd); +} + +template <class T> static void print_obs(const std::map<uint32_t, DrmObject*>& obmap) +{ +	for (auto pair : obmap) { +		auto ob = pair.second; +		if (dynamic_cast<T*>(ob)) { +			ob->print_short(); +			//ob->print_props(); +		} +	} +} + +void Card::print_short() const +{ +	print_obs<Connector>(m_obmap); +	print_obs<Encoder>(m_obmap); +	print_obs<Crtc>(m_obmap); +	print_obs<Plane>(m_obmap); +} + +Property* Card::get_prop(const char *name) const +{ +	for (auto pair : m_obmap) { +		auto prop = dynamic_cast<Property*>(pair.second); +		if (!prop) +			continue; + +		if (strcmp(name, prop->name()) == 0) +			return prop; +	} + +	throw std::invalid_argument("foo"); +} + +Connector* Card::get_first_connected_connector() const +{ +	for(auto pair : m_obmap) { +		auto c = dynamic_cast<Connector*>(pair.second); + +		if (c && c->connected()) +			return c; +	} + +	throw std::invalid_argument("no connected connectors"); +} + +DrmObject* Card::get_object(uint32_t id) const +{ +	return m_obmap.at(id); +} + +std::vector<Connector*> Card::get_connectors() const +{ +	std::vector<Connector*> v; +	for(auto pair : m_obmap) { +		auto p = dynamic_cast<Connector*>(pair.second); +		if (p) +			v.push_back(p); +	} +	return v; +} + +std::vector<Plane*> Card::get_planes() const +{ +	std::vector<Plane*> v; +	for(auto pair : m_obmap) { +		auto p = dynamic_cast<Plane*>(pair.second); +		if (p) +			v.push_back(p); +	} +	return v; +} + +std::vector<DrmObject*> Card::get_objects() const +{ +	std::vector<DrmObject*> v; +	for(auto pair : m_obmap) +		v.push_back(pair.second); +	return v; +} + +Crtc* Card::get_crtc_by_index(uint32_t idx) const +{ +	for(auto pair : m_obmap) { +		auto crtc = dynamic_cast<Crtc*>(pair.second); +		if (crtc && crtc->idx() == idx) +			return crtc; +	} +	throw std::invalid_argument("fob"); +} + +Crtc* Card::get_crtc(uint32_t id) const { return dynamic_cast<Crtc*>(get_object(id)); } +Encoder* Card::get_encoder(uint32_t id) const { return dynamic_cast<Encoder*>(get_object(id)); } +Property* Card::get_prop(uint32_t id) const { return dynamic_cast<Property*>(get_object(id)); } +} diff --git a/libkms++/card.h b/libkms++/card.h new file mode 100644 index 0000000..fb45d04 --- /dev/null +++ b/libkms++/card.h @@ -0,0 +1,49 @@ +#pragma once + +#include <cstdint> +#include <vector> +#include <map> + +#include "decls.h" + +namespace kms +{ + +class Card +{ +public: +	Card(); +	~Card(); + +	Card(const Card& other) = delete; +	Card& operator=(const Card& other) = delete; + +	int fd() const { return m_fd; } + +	Connector* get_first_connected_connector() const; + +	DrmObject* get_object(uint32_t id) const; +	Crtc* get_crtc(uint32_t id) const; +	Crtc* get_crtc_by_index(uint32_t idx) const; +	Encoder* get_encoder(uint32_t id) const; +	Property* get_prop(uint32_t id) const; +	Property* get_prop(const char *name) const; + +	bool master() const { return m_master; } +	bool has_atomic() const { return m_has_atomic; } + +	void print_short() const; + +	std::vector<Connector*> get_connectors() const; +	std::vector<DrmObject*> get_objects() const; +	std::vector<Plane*> get_planes() const; + +private: +	std::map<uint32_t, DrmObject*> m_obmap; + +	int m_fd; +	bool m_master; + +	bool m_has_atomic; +}; +} diff --git a/libkms++/connector.cpp b/libkms++/connector.cpp new file mode 100644 index 0000000..ee14af9 --- /dev/null +++ b/libkms++/connector.cpp @@ -0,0 +1,111 @@ +#include <stdio.h> +#include <iostream> +#include <unistd.h> +#include <fcntl.h> +#include <cassert> + +#include "kms++.h" +#include "helpers.h" + +using namespace std; + +#define DEF_CONN(c) [DRM_MODE_CONNECTOR_##c] = #c + +namespace kms +{ + +static const char * connector_names[] = { +	DEF_CONN(Unknown), +	DEF_CONN(VGA), +	DEF_CONN(DVII), +	DEF_CONN(DVID), +	DEF_CONN(DVIA), +	DEF_CONN(Composite), +	DEF_CONN(SVIDEO), +	DEF_CONN(LVDS), +	DEF_CONN(Component), +	DEF_CONN(9PinDIN), +	DEF_CONN(DisplayPort), +	DEF_CONN(HDMIA), +	DEF_CONN(HDMIB), +	DEF_CONN(TV), +	DEF_CONN(eDP), +	DEF_CONN(VIRTUAL), +	DEF_CONN(DSI), +}; + +static const char *connection_str[] = { +	[0] = "<unknown>", +	[DRM_MODE_CONNECTED] = "Connected", +	[DRM_MODE_DISCONNECTED] = "Disconnected", +	[DRM_MODE_UNKNOWNCONNECTION] = "Unknown", +}; + +struct ConnectorPriv +{ +	drmModeConnectorPtr drm_connector; +}; + +Connector::Connector(Card &card, uint32_t id, uint32_t idx) +	:DrmObject(card, id, DRM_MODE_OBJECT_CONNECTOR, idx) +{ +	m_priv = new ConnectorPriv(); + +	m_priv->drm_connector = drmModeGetConnector(this->card().fd(), this->id()); +	assert(m_priv->drm_connector); + +	auto name = connector_names[m_priv->drm_connector->connector_type]; +	m_fullname = std::string(string(name) + std::to_string(m_priv->drm_connector->connector_type_id)); +} + + +Connector::~Connector() +{ +	drmModeFreeConnector(m_priv->drm_connector); +	delete m_priv; +} + +void Connector::setup() +{ +	if (m_priv->drm_connector->encoder_id != 0) { +		auto enc = card().get_encoder(m_priv->drm_connector->encoder_id); +		if (enc) +			m_current_crtc = enc->get_crtc(); +	} +} + +void Connector::print_short() const +{ +	auto c = m_priv->drm_connector; + +	printf("Connector %d, %s, %dx%dmm, %s\n", id(), m_fullname.c_str(), +	       c->mmWidth, c->mmHeight, connection_str[c->connection]); +} + +Videomode Connector::get_default_mode() const +{ +	drmModeModeInfo drmmode = m_priv->drm_connector->modes[0]; + +	return drm_mode_to_video_mode(drmmode); +} + +bool Connector::connected() +{ +	return m_priv->drm_connector->connection == DRM_MODE_CONNECTED; +} + +vector<Crtc*> Connector::get_possible_crtcs() const +{ +	vector<Crtc*> crtcs; + +	for (int i = 0; i < m_priv->drm_connector->count_encoders; ++i) { +		auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]); + +		auto l = enc->get_possible_crtcs(); + +		crtcs.insert(crtcs.end(), l.begin(), l.end()); +	} + +	return crtcs; +} +} diff --git a/libkms++/connector.h b/libkms++/connector.h new file mode 100644 index 0000000..b7d975c --- /dev/null +++ b/libkms++/connector.h @@ -0,0 +1,49 @@ +#pragma once + +#include <vector> + +#include "drmobject.h" + +namespace kms +{ + +struct ConnectorPriv; + +struct Videomode +{ +	uint32_t clock; +	uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; +	uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + +	uint32_t vrefresh; + +	uint32_t flags; +	uint32_t type; +	char name[32]; // XXX +}; + +class Connector : public DrmObject +{ +public: +	Connector(Card& card, uint32_t id, uint32_t idx); +	~Connector(); + +	void setup(); + +	void print_short() const; + +	Videomode get_default_mode() const; + +	Crtc* get_current_crtc() const { return m_current_crtc; } +	std::vector<Crtc*> get_possible_crtcs() const; + +	bool connected(); + +private: +	ConnectorPriv* m_priv; + +	std::string m_fullname; + +	Crtc* m_current_crtc; +}; +} diff --git a/libkms++/crtc.cpp b/libkms++/crtc.cpp new file mode 100644 index 0000000..e583be8 --- /dev/null +++ b/libkms++/crtc.cpp @@ -0,0 +1,74 @@ +#include <stdio.h> +#include <iostream> +#include <unistd.h> +#include <fcntl.h> +#include <cassert> + +#include "kms++.h" +#include "helpers.h" + +using namespace std; + +namespace kms +{ + +struct CrtcPriv +{ +	drmModeCrtcPtr drm_crtc; +}; + +Crtc::Crtc(Card &card, uint32_t id, uint32_t idx) +	:DrmObject(card, id, DRM_MODE_OBJECT_CRTC, idx) +{ +	m_priv = new CrtcPriv(); +	m_priv->drm_crtc = drmModeGetCrtc(this->card().fd(), this->id()); +	assert(m_priv->drm_crtc); +} + +Crtc::~Crtc() +{ +	drmModeFreeCrtc(m_priv->drm_crtc); +	delete m_priv; +} + +void Crtc::setup() +{ +	for (Plane* plane : card().get_planes()) { +		if (plane->supports_crtc(this)) +			m_possible_planes.push_back(plane); +	} +} + +void Crtc::print_short() const +{ +	auto c  = m_priv->drm_crtc; + +	printf("Crtc %d, %d,%d %dx%d\n", id(), +	       c->x, c->y, c->width, c->height); +} + +int Crtc::set_mode(Connector* conn, Framebuffer& fb, const Videomode& mode) +{ +	uint32_t conns[] = { conn->id() }; +	drmModeModeInfo drmmode = video_mode_to_drm_mode(mode); + +	return drmModeSetCrtc(card().fd(), id(), fb.id(), +			      0, 0, +			      conns, 1, &drmmode); +} + +static inline uint32_t conv(float x) +{ +	// XXX fix the conversion for fractional part +	return ((uint32_t)x) << 16; +} + +int Crtc::set_plane(Plane* plane, Framebuffer& fb, +		    int32_t dst_x, int32_t dst_y, uint32_t dst_w, uint32_t dst_h, +		    float src_x, float src_y, float src_w, float src_h) +{ +	return drmModeSetPlane(card().fd(), plane->id(), id(), fb.id(), 0, +			       dst_x, dst_y, dst_w, dst_h, +			       conv(src_x), conv(src_y), conv(src_w), conv(src_h)); +} +} diff --git a/libkms++/crtc.h b/libkms++/crtc.h new file mode 100644 index 0000000..ac05da9 --- /dev/null +++ b/libkms++/crtc.h @@ -0,0 +1,35 @@ +#pragma once + +#include <vector> + +#include "drmobject.h" + +namespace kms +{ + +struct CrtcPriv; + +class Crtc : public DrmObject +{ +public: +	Crtc(Card& card, uint32_t id, uint32_t idx); +	~Crtc(); + +	void setup(); + +	void print_short() const; + +	const std::vector<Plane*>& get_possible_planes() const { return m_possible_planes; } + +	int set_mode(Connector* conn, Framebuffer& fb, const Videomode& mode); + +	int set_plane(Plane *plane, Framebuffer &fb, +		      int32_t dst_x, int32_t dst_y, uint32_t dst_w, uint32_t dst_h, +		      float src_x, float src_y, float src_w, float src_h); + +private: +	CrtcPriv* m_priv; + +	std::vector<Plane*> m_possible_planes; +}; +} diff --git a/libkms++/decls.h b/libkms++/decls.h new file mode 100644 index 0000000..c2cf09f --- /dev/null +++ b/libkms++/decls.h @@ -0,0 +1,15 @@ +#pragma once + +namespace kms +{ +class AtomicReq; +class Card; +class Connector; +class Crtc; +class Encoder; +class Framebuffer; +class DrmObject; +class Plane; +class Property; +class Videomode; +} diff --git a/libkms++/drmobject.cpp b/libkms++/drmobject.cpp new file mode 100644 index 0000000..ea5d594 --- /dev/null +++ b/libkms++/drmobject.cpp @@ -0,0 +1,69 @@ +#include <string.h> +#include <iostream> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "kms++.h" + +using namespace std; + +namespace kms +{ + +DrmObject::DrmObject(Card& card, uint32_t object_type) +	:m_id(-1), m_card(card), m_object_type(object_type) +{ +} + +DrmObject::DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx) +	:m_id(id), m_card(card), m_object_type(object_type), m_idx(idx) +{ +	refresh_props(); +} + +DrmObject::~DrmObject() +{ + +} + +void DrmObject::refresh_props() +{ +	auto props = drmModeObjectGetProperties(card().fd(), this->id(), this->object_type()); + +	if (props == nullptr) +		return; + +	for (unsigned i = 0; i < props->count_props; ++i) { +		uint32_t prop_id = props->props[i]; +		uint64_t prop_value = props->prop_values[i]; + +		m_prop_values[prop_id] = prop_value; +	} + +	drmModeFreeObjectProperties(props); +} + +void DrmObject::print_props() const +{ +	for (auto it = m_prop_values.begin(); it != m_prop_values.end(); ++it) { +		cout << "\t" << card().get_prop(it->first)->name() << +			" = " << it->second << endl; +	} +} + +uint64_t DrmObject::get_prop_value(uint32_t id) const +{ +	return m_prop_values.at(id); +} + +uint64_t DrmObject::get_prop_value(const char *name) const +{ +	for (auto pair : m_prop_values) { +		auto prop = card().get_prop(pair.first); +		if (strcmp(name, prop->name()) == 0) +			return m_prop_values.at(prop->id()); +	} + +	throw invalid_argument("foo"); +} +} diff --git a/libkms++/drmobject.h b/libkms++/drmobject.h new file mode 100644 index 0000000..5c945f9 --- /dev/null +++ b/libkms++/drmobject.h @@ -0,0 +1,44 @@ +#pragma once + +#include <map> + +#include "decls.h" + +namespace kms +{ + +class DrmObject +{ +public: +	DrmObject(Card& card, uint32_t object_type); +	DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx = 0); +	virtual ~DrmObject(); + +	DrmObject(const DrmObject& other) = delete; +	DrmObject& operator=(const DrmObject& other) = delete; + +	virtual void setup() { }; + +	virtual void print_short() const = 0; +	void print_props() const; + +	uint32_t id() const { return m_id; } +	Card& card() const { return m_card; } + +	uint32_t object_type() const { return m_object_type; } +	uint32_t idx() const { return m_idx; } + +	void refresh_props(); +	uint64_t get_prop_value(uint32_t id) const; +	uint64_t get_prop_value(const char *name) const; + +protected: +	uint32_t m_id;	// protected for Framebuffer... + +private: +	Card& m_card; +	std::map<uint32_t, uint64_t> m_prop_values; +	uint32_t m_object_type; +	uint32_t m_idx; +}; +} diff --git a/libkms++/encoder.cpp b/libkms++/encoder.cpp new file mode 100644 index 0000000..081177b --- /dev/null +++ b/libkms++/encoder.cpp @@ -0,0 +1,63 @@ +#include <stdio.h> +#include <iostream> +#include <unistd.h> +#include <fcntl.h> +#include <cassert> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "kms++.h" + +using namespace std; + +namespace kms +{ + +struct EncoderPriv +{ +	drmModeEncoderPtr drm_encoder; +}; + +Encoder::Encoder(Card &card, uint32_t id) +	:DrmObject(card, id, DRM_MODE_OBJECT_ENCODER) +{ +	m_priv = new EncoderPriv(); +	m_priv->drm_encoder = drmModeGetEncoder(this->card().fd(), this->id()); +	assert(m_priv->drm_encoder); +} + +Encoder::~Encoder() +{ +	drmModeFreeEncoder(m_priv->drm_encoder); +	delete m_priv; +} + +void Encoder::print_short() const +{ +	auto e = m_priv->drm_encoder; + +	printf("Encoder %d, %d\n", id(), +	       e->encoder_type); +} + +Crtc* Encoder::get_crtc() const +{ +	return card().get_crtc(m_priv->drm_encoder->crtc_id); +} + +vector<Crtc*> Encoder::get_possible_crtcs() const +{ +	unsigned bits = m_priv->drm_encoder->possible_crtcs; +	vector<Crtc*> crtcs; + +	for (int idx = 0; bits; idx++, bits >>= 1) { +		if ((bits & 1) == 0) +			continue; + +		auto crtc = card().get_crtc_by_index(idx); +		crtcs.push_back(crtc); +	} + +	return crtcs; +} +} diff --git a/libkms++/encoder.h b/libkms++/encoder.h new file mode 100644 index 0000000..3e9a8e4 --- /dev/null +++ b/libkms++/encoder.h @@ -0,0 +1,25 @@ +#pragma once + +#include <vector> +#include "drmobject.h" + +namespace kms +{ + +struct EncoderPriv; + +class Encoder : public DrmObject +{ +public: +	Encoder(Card& card, uint32_t id); +	~Encoder(); + +	void print_short() const; + +	Crtc* get_crtc() const; +	std::vector<Crtc*> get_possible_crtcs() const; + +private: +	EncoderPriv* m_priv; +}; +} diff --git a/libkms++/framebuffer.cpp b/libkms++/framebuffer.cpp new file mode 100644 index 0000000..5382a6f --- /dev/null +++ b/libkms++/framebuffer.cpp @@ -0,0 +1,167 @@ + +#include <cstring> +#include <stdexcept> +#include <sys/mman.h> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <drm_fourcc.h> +#include <drm.h> +#include <drm_mode.h> + +#include "kms++.h" +#include "utils/testpat.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +namespace kms +{ + +Framebuffer::Framebuffer(Card &card, uint32_t width, uint32_t height, const char* fourcc) +	:DrmObject(card, DRM_MODE_OBJECT_FB) +{ +	uint32_t a, b, c, d; +	a = fourcc[0]; +	b = fourcc[1]; +	c = fourcc[2]; +	d = fourcc[3]; + +	uint32_t code = ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)); + +	Create(width, height, code); +} + +Framebuffer::~Framebuffer() +{ +	Destroy(); +} + +void Framebuffer::print_short() const +{ +	printf("Framebuffer %d\n", id()); +} + +struct FormatPlaneInfo +{ +	uint8_t bitspp;	/* bits per (macro) pixel */ +	uint8_t xsub; +	uint8_t ysub; +}; + +struct FormatInfo +{ +	uint32_t format; +	uint8_t num_planes; +	struct FormatPlaneInfo planes[4]; +}; + +static const FormatInfo format_info_array[] = { +	/* YUV packed */ +	{ DRM_FORMAT_UYVY, 1, { { 32, 2, 1 } }, }, +	{ DRM_FORMAT_YUYV, 1, { { 32, 2, 1 } }, }, +	/* YUV semi-planar */ +	{ DRM_FORMAT_NV12, 2, { { 8, 1, 1, }, { 16, 2, 2 } }, }, +	/* RGB16 */ +	{ DRM_FORMAT_RGB565, 1, { { 16, 1, 1 } }, }, +	/* RGB32 */ +	{ DRM_FORMAT_XRGB8888, 1, { { 32, 1, 1 } }, }, +}; + +static const FormatInfo& find_format(uint32_t format) +{ +	for (uint i = 0; i < ARRAY_SIZE(format_info_array); ++i) { +		if (format == format_info_array[i].format) +			return format_info_array[i]; +	} + +	throw std::invalid_argument("foo"); +} + +void Framebuffer::Create(uint32_t width, uint32_t height, uint32_t format) +{ +	int r; + +	m_width = width; +	m_height = height; +	m_format = format; + +	const FormatInfo& format_info = find_format(format); + +	m_num_planes = format_info.num_planes; + +	for (int i = 0; i < format_info.num_planes; ++i) { +		const FormatPlaneInfo& pi = format_info.planes[i]; +		FramebufferPlane& plane = m_planes[i]; + +		/* create dumb buffer */ +		struct drm_mode_create_dumb creq = drm_mode_create_dumb(); +		creq.width = m_width / pi.xsub; +		creq.height = m_height / pi.ysub; +		creq.bpp = pi.bitspp; +		r = drmIoctl(card().fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq); +		if (r) +			throw std::invalid_argument("foo"); + +		plane.handle = creq.handle; +		plane.stride = creq.pitch; +		plane.size = creq.height * creq.pitch; + +		/* +		printf("buf %d: %dx%d, bitspp %d, stride %d, size %d\n", +			i, creq.width, creq.height, pi->bitspp, plane->stride, plane->size); +		*/ + +		/* prepare buffer for memory mapping */ +		struct drm_mode_map_dumb mreq = drm_mode_map_dumb(); +		mreq.handle = plane.handle; +		r = drmIoctl(card().fd(), DRM_IOCTL_MODE_MAP_DUMB, &mreq); +		if (r) +			throw std::invalid_argument("foo"); + +		/* perform actual memory mapping */ +		m_planes[i].map = (uint8_t *)mmap(0, plane.size, PROT_READ | PROT_WRITE, MAP_SHARED, +						  card().fd(), mreq.offset); +		if (plane.map == MAP_FAILED) +			throw std::invalid_argument("foo"); + +		/* clear the framebuffer to 0 */ +		memset(plane.map, 0, plane.size); +	} + +	/* create framebuffer object for the dumb-buffer */ +	uint32_t bo_handles[4] = { m_planes[0].handle, m_planes[1].handle }; +	uint32_t pitches[4] = { m_planes[0].stride, m_planes[1].stride }; +	uint32_t offsets[4] = { 0 }; +	uint32_t id; +	r = drmModeAddFB2(card().fd(), m_width, m_height, format, +			  bo_handles, pitches, offsets, &id, 0); +	if (r) +		throw std::invalid_argument("foo"); + +	m_id = id; +} + +void Framebuffer::Destroy() +{ +	/* delete framebuffer */ +	drmModeRmFB(card().fd(), id()); + +	for (uint i = 0; i < m_num_planes; ++i) { +		FramebufferPlane& plane = m_planes[i]; + +		/* unmap buffer */ +		munmap(plane.map, plane.size); + +		/* delete dumb buffer */ +		struct drm_mode_destroy_dumb dreq = drm_mode_destroy_dumb(); +		dreq.handle = plane.handle; +		drmIoctl(card().fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + +	} +} + +void Framebuffer::clear() +{ +	for (unsigned i = 0; i < m_num_planes; ++i) +		memset(m_planes[i].map, 0, m_planes[i].size); +} +} diff --git a/libkms++/framebuffer.h b/libkms++/framebuffer.h new file mode 100644 index 0000000..2710528 --- /dev/null +++ b/libkms++/framebuffer.h @@ -0,0 +1,44 @@ +#pragma once + +#include "drmobject.h" + +namespace kms +{ + +class Framebuffer : public DrmObject +{ +public: +	Framebuffer(Card& card, uint32_t width, uint32_t height, const char* fourcc); +	virtual ~Framebuffer(); + +	void print_short() const; + +	uint32_t width() const { return m_width; } +	uint32_t height() const { return m_height; } +	uint32_t format() const { return m_format; } + +	uint8_t* map(unsigned plane) const { return m_planes[plane].map; } +	uint32_t stride(unsigned plane) const { return m_planes[plane].stride; } +	uint32_t size(unsigned plane) const { return m_planes[plane].size; } + +	void clear(); + +private: +	struct FramebufferPlane { +		uint32_t handle; +		uint32_t size; +		uint32_t stride; +		uint8_t *map; +	}; + +	void Create(uint32_t width, uint32_t height, uint32_t format); +	void Destroy(); + +	unsigned m_num_planes; +	struct FramebufferPlane m_planes[4]; + +	uint32_t m_width; +	uint32_t m_height; +	uint32_t m_format; +}; +} diff --git a/libkms++/helpers.cpp b/libkms++/helpers.cpp new file mode 100644 index 0000000..7746bde --- /dev/null +++ b/libkms++/helpers.cpp @@ -0,0 +1,23 @@ + +#include "connector.h" +#include "helpers.h" +#include <cstring> + +namespace kms +{ +Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode) +{ +	// XXX these are the same at the moment +	Videomode mode; +	memcpy(&mode, &drmmode, sizeof(mode)); +	return mode; +} + +drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode) +{ +	// XXX these are the same at the moment +	drmModeModeInfo drmmode; +	memcpy(&drmmode, &mode, sizeof(drmmode)); +	return drmmode; +} +} diff --git a/libkms++/helpers.h b/libkms++/helpers.h new file mode 100644 index 0000000..ac640e8 --- /dev/null +++ b/libkms++/helpers.h @@ -0,0 +1,12 @@ +#pragma once + +#include <xf86drm.h> +#include <xf86drmMode.h> + +namespace kms +{ +class Videomode; + +Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode); +drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode); +} diff --git a/libkms++/kms++.h b/libkms++/kms++.h new file mode 100644 index 0000000..f9808ed --- /dev/null +++ b/libkms++/kms++.h @@ -0,0 +1,10 @@ +#pragma once + +#include "atomicreq.h" +#include "card.h" +#include "connector.h" +#include "crtc.h" +#include "encoder.h" +#include "framebuffer.h" +#include "plane.h" +#include "property.h" diff --git a/libkms++/plane.cpp b/libkms++/plane.cpp new file mode 100644 index 0000000..afb9c78 --- /dev/null +++ b/libkms++/plane.cpp @@ -0,0 +1,64 @@ +#include <stdio.h> +#include <iostream> +#include <unistd.h> +#include <fcntl.h> +#include <cassert> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "kms++.h" + +using namespace std; + +namespace kms +{ + +struct PlanePriv +{ +	drmModePlanePtr drm_plane; +}; + +Plane::Plane(Card &card, uint32_t id) +	:DrmObject(card, id, DRM_MODE_OBJECT_PLANE) +{ +	m_priv = new PlanePriv(); +	m_priv->drm_plane = drmModeGetPlane(this->card().fd(), this->id()); +	assert(m_priv->drm_plane); +} + +Plane::~Plane() +{ +	drmModeFreePlane(m_priv->drm_plane); +	delete m_priv; +} + +void Plane::print_short() const +{ +	auto p = m_priv->drm_plane; + +	printf("Plane %d, %d modes, %d,%d -> %dx%d\n", id(), +	       p->count_formats, +	       p->crtc_x, p->crtc_y, p->x, p->y); + +	printf("\t"); +	for (unsigned i = 0; i < p->count_formats; ++i) { +		uint32_t f = p->formats[i]; +		printf("%c%c%c%c ", +		       (f >> 0) & 0xff, +		       (f >> 8) & 0xff, +		       (f >> 16) & 0xff, +		       (f >> 24) & 0xff); +	} +	printf("\n"); +} + +bool Plane::supports_crtc(Crtc* crtc) const +{ +	return m_priv->drm_plane->possible_crtcs & (1 << crtc->idx()); +} + +PlaneType Plane::plane_type() const +{ +	return (PlaneType)get_prop_value("type"); +} +} diff --git a/libkms++/plane.h b/libkms++/plane.h new file mode 100644 index 0000000..890a28f --- /dev/null +++ b/libkms++/plane.h @@ -0,0 +1,32 @@ +#pragma once + +#include "drmobject.h" + +namespace kms +{ + +enum class PlaneType +{ +	Overlay = 0, +	Primary = 1, +	Cursor = 2, +}; + +struct PlanePriv; + +class Plane : public DrmObject +{ +public: +	Plane(Card& card, uint32_t id); +	~Plane(); + +	void print_short() const; + +	bool supports_crtc(Crtc* crtc) const; + +	PlaneType plane_type() const; + +private: +	PlanePriv* m_priv; +}; +} diff --git a/libkms++/property.cpp b/libkms++/property.cpp new file mode 100644 index 0000000..af6a0ae --- /dev/null +++ b/libkms++/property.cpp @@ -0,0 +1,36 @@ +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "kms++.h" + +namespace kms +{ + +struct PropertyPriv +{ +	drmModePropertyPtr drm_prop; +}; + +Property::Property(Card& card, uint32_t id) +	: DrmObject(card, id, DRM_MODE_OBJECT_PROPERTY) +{ +	m_priv = new PropertyPriv(); +	m_priv->drm_prop = drmModeGetProperty(card.fd(), id); +} + +Property::~Property() +{ +	drmModeFreeProperty(m_priv->drm_prop); +	delete m_priv; +} + +void Property::print_short() const +{ +	printf("Property %d, %s\n", id(), name()); +} + +const char *Property::name() const +{ +	return m_priv->drm_prop->name; +} +} diff --git a/libkms++/property.h b/libkms++/property.h new file mode 100644 index 0000000..d5306d0 --- /dev/null +++ b/libkms++/property.h @@ -0,0 +1,23 @@ +#pragma once + +#include "drmobject.h" + +namespace kms +{ + +struct PropertyPriv; + +class Property : public DrmObject +{ +public: +	Property(Card& card, uint32_t id); +	~Property(); + +	void print_short() const; + +	const char *name() const; + +private: +	PropertyPriv* m_priv; +}; +} diff --git a/libkms++/utils/color.cpp b/libkms++/utils/color.cpp new file mode 100644 index 0000000..b5b9001 --- /dev/null +++ b/libkms++/utils/color.cpp @@ -0,0 +1,67 @@ +#include "color.h" + +namespace kms +{ +RGB::RGB() +{ +	r = g = b = a = 0; +} + +RGB::RGB(uint8_t r, uint8_t g, uint8_t b) +{ +	this->r = r; +	this->g = g; +	this->b = b; +	this->a = 0; +} + +uint16_t RGB::rgb565() const +{ +	uint16_t r = (this->r >> 3) << 11; +	uint16_t g = (this->g >> 2) << 5; +	uint16_t b = (this->b >> 3) << 0; +	return r | g | b; +} + +YUV RGB::yuv() const +{ +	return YUV(*this); +} + + +YUV::YUV() +{ +	y = u = v = a = 0; +} + +YUV::YUV(uint8_t y, uint8_t u, uint8_t v) +{ +	this->y = y; +	this->u = u; +	this->v = v; +	this->a = 0; +} + +static inline uint8_t MAKE_YUV_601_Y(uint8_t r, uint8_t g, uint8_t b) +{ +	return (((66 * r + 129 * g +  25 * b + 128) >> 8) + 16); +} + +static inline uint8_t MAKE_YUV_601_U(uint8_t r, uint8_t g, uint8_t b) +{ +	return (((-38 * r -  74 * g + 112 * b + 128) >> 8) + 128); +} + +static inline uint8_t MAKE_YUV_601_V(uint8_t r, uint8_t g, uint8_t b) +{ +	return (((112 * r -  94 * g -  18 * b + 128) >> 8) + 128); +} + +YUV::YUV(const RGB& rgb) +{ +	this->y = MAKE_YUV_601_Y(rgb.r, rgb.g, rgb.b); +	this->u = MAKE_YUV_601_U(rgb.r, rgb.g, rgb.b); +	this->v = MAKE_YUV_601_V(rgb.r, rgb.g, rgb.b); +	this->a = rgb.a; +} +} diff --git a/libkms++/utils/color.h b/libkms++/utils/color.h new file mode 100644 index 0000000..1db47e8 --- /dev/null +++ b/libkms++/utils/color.h @@ -0,0 +1,48 @@ +#pragma once + +#include <cstdint> + +namespace kms +{ +struct YUV; + +struct RGB +{ +	RGB(); +	RGB(uint8_t r, uint8_t g, uint8_t b); + +	uint16_t rgb565() const; +	YUV yuv() const; + +	union { +		struct +		{ +			uint8_t b; +			uint8_t g; +			uint8_t r; +			uint8_t a; +		}; + +		uint32_t raw; +	}; +}; + +struct YUV +{ +	YUV(); +	YUV(uint8_t y, uint8_t u, uint8_t v); +	YUV(const RGB& rgb); + +	union { +		struct +		{ +			uint8_t v; +			uint8_t u; +			uint8_t y; +			uint8_t a; +		}; + +		uint32_t raw; +	}; +}; +} diff --git a/libkms++/utils/conv.cpp b/libkms++/utils/conv.cpp new file mode 100644 index 0000000..d439253 --- /dev/null +++ b/libkms++/utils/conv.cpp @@ -0,0 +1,138 @@ +#include <drm_fourcc.h> +#include <stdexcept> + +#include "framebuffer.h" +#include "color.h" +#include "conv.h" + +namespace kms +{ +static RGB read_rgb(const Framebuffer& fb, int x, int y) +{ +	uint32_t *pc = (uint32_t *)(fb.map(0) + fb.stride(0) * y); + +	uint32_t c = pc[x]; + +	return RGB((c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); +} + +static YUV read_rgb_as_yuv(const Framebuffer& fb, int x, int y) +{ +	RGB rgb = read_rgb(fb, x, y); +	return YUV(rgb); +} + +static void fb_rgb_to_packed_yuv(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ +	unsigned w = src_fb.width(); +	unsigned h = src_fb.height(); + +	uint8_t *dst = dst_fb.map(0); + +	for (unsigned y = 0; y < h; ++y) { +		for (unsigned x = 0; x < w; x += 2) { +			YUV yuv1 = read_rgb_as_yuv(src_fb, x + 0, y); +			YUV yuv2 = read_rgb_as_yuv(src_fb, x + 1, y); + +			switch (dst_fb.format()) { +			case DRM_FORMAT_UYVY: +				dst[x * 2 + 0] = (yuv1.u + yuv2.u) / 2; +				dst[x * 2 + 1] = yuv1.y; +				dst[x * 2 + 2] = (yuv1.v + yuv2.v) / 2; +				dst[x * 2 + 3] = yuv2.y; +				break; +			case DRM_FORMAT_YUYV: +				dst[x * 2 + 0] = yuv1.y; +				dst[x * 2 + 1] = (yuv1.u + yuv2.u) / 2; +				dst[x * 2 + 2] = yuv2.y; +				dst[x * 2 + 3] = (yuv1.v + yuv2.v) / 2; +				break; + +			default: +				throw std::invalid_argument("fo"); +			} +		} + +		dst += dst_fb.stride(0); +	} +} + +static void fb_rgb_to_semiplanar_yuv(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ +	unsigned w = src_fb.width(); +	unsigned h = src_fb.height(); + +	uint8_t *dst_y = dst_fb.map(0); +	uint8_t *dst_uv = dst_fb.map(1); + +	for (unsigned y = 0; y < h; ++y) { +		for (unsigned x = 0; x < w; ++x) { +			YUV yuv = read_rgb_as_yuv(src_fb, x, y); +			dst_y[x] = yuv.y; +		} + +		dst_y += dst_fb.stride(0); +	} + +	for (unsigned y = 0; y < h; y += 2) { +		for (unsigned x = 0; x < w; x += 2) { +			YUV yuv00 = read_rgb_as_yuv(src_fb, x + 0, y + 0); +			YUV yuv01 = read_rgb_as_yuv(src_fb, x + 1, y + 0); +			YUV yuv10 = read_rgb_as_yuv(src_fb, x + 0, y + 1); +			YUV yuv11 = read_rgb_as_yuv(src_fb, x + 1, y + 1); + +			unsigned u = (yuv00.u + yuv01.u + yuv10.u + yuv11.u) / 4; +			unsigned v = (yuv00.v + yuv01.v + yuv10.v + yuv11.v) / 4; + +			dst_uv[x + 0] = u; +			dst_uv[x + 1] = v; +		} + +		dst_uv += dst_fb.stride(1); +	} +} + +static void fb_rgb_to_rgb565(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ +	unsigned w = src_fb.width(); +	unsigned h = src_fb.height(); + +	uint8_t *dst = dst_fb.map(0); + +	for (unsigned y = 0; y < h; ++y) { +		for (unsigned x = 0; x < w; ++x) { +			RGB rgb = read_rgb(src_fb, x, y); + +			unsigned r = rgb.r * 32 / 256; +			unsigned g = rgb.g * 64 / 256; +			unsigned b = rgb.b * 32 / 256; + +			((uint16_t *)dst)[x] = (r << 11) | (g << 5) | (b << 0); +		} + +		dst += dst_fb.stride(0); +	} +} + +void color_convert(Framebuffer& dst, const Framebuffer &src) +{ +	switch (dst.format()) { +	case DRM_FORMAT_NV12: +	case DRM_FORMAT_NV21: +		fb_rgb_to_semiplanar_yuv(dst, src); +		break; + +	case DRM_FORMAT_YUYV: +	case DRM_FORMAT_UYVY: +		fb_rgb_to_packed_yuv(dst, src); +		break; + +	case DRM_FORMAT_RGB565: +		fb_rgb_to_rgb565(dst, src); +		break; + +	default: +		throw std::invalid_argument("fo"); +	} +} +} diff --git a/libkms++/utils/conv.h b/libkms++/utils/conv.h new file mode 100644 index 0000000..d1b306a --- /dev/null +++ b/libkms++/utils/conv.h @@ -0,0 +1,8 @@ +#pragma once + +namespace kms +{ +class Framebuffer; + +void color_convert(Framebuffer& dst, const Framebuffer &src); +} diff --git a/libkms++/utils/testpat.cpp b/libkms++/utils/testpat.cpp new file mode 100644 index 0000000..cd35e0e --- /dev/null +++ b/libkms++/utils/testpat.cpp @@ -0,0 +1,162 @@ + +#include <chrono> +#include <cstring> +#include <cassert> + +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <drm_fourcc.h> +#include <drm.h> +#include <drm_mode.h> + +#include "card.h" +#include "framebuffer.h" +#include "testpat.h" +#include "color.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +namespace kms +{ +static void draw_pixel(Framebuffer& buf, unsigned x, unsigned y, RGB color) +{ +	static RGB c1; + +	switch (buf.format()) { +	case DRM_FORMAT_XRGB8888: +	{ +		uint32_t *p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4); +		*p = color.raw; +		break; +	} +	case DRM_FORMAT_RGB565: +	{ +		uint16_t *p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2); +		*p = color.rgb565(); +		break; +	} +	case DRM_FORMAT_UYVY: +	{ +		if ((x & 1) == 0) { +			c1 = color; +			return; +		} + +		uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2); + +		YUV yuv1 = c1.yuv(); +		YUV yuv2 = color.yuv(); + +		p[0] = (yuv1.u + yuv2.u) / 2; +		p[1] = yuv1.y; +		p[2] = (yuv1.v + yuv2.v) / 2; +		p[3] = yuv2.y; +		break; +	} +	case DRM_FORMAT_YUYV: +	{ +		if ((x & 1) == 0) { +			c1 = color; +			return; +		} + +		uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2); + +		YUV yuv1 = c1.yuv(); +		YUV yuv2 = color.yuv(); + +		p[0] = yuv1.y; +		p[1] = (yuv1.u + yuv2.u) / 2; +		p[2] = yuv2.y; +		p[3] = (yuv1.v + yuv2.v) / 2; +		break; +	} +	} +} + +static void draw_rgb_test_pattern(Framebuffer& fb) +{ +	unsigned x, y; +	unsigned w = fb.width(); +	unsigned h = fb.height(); + +	const unsigned mw = 20; + +	const unsigned xm1 = mw; +	const unsigned xm2 = w - mw - 1; +	const unsigned ym1 = mw; +	const unsigned ym2 = h - mw - 1; + +	for (y = 0; y < h; y++) { +		for (x = 0; x < w; x++) { +			// white margin lines +			if (x == xm1 || x == xm2 || y == ym1 || y == ym2) +				draw_pixel(fb, x, y, RGB(255, 255, 255)); +			// white box outlines to corners +			else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2)) +				draw_pixel(fb, x, y, RGB(255, 255, 255)); +			// white box outlines to corners +			else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2)) +				draw_pixel(fb, x, y, RGB(255, 255, 255)); +			// blue bar on the left +			else if (x < xm1 && (y > ym1 && y < ym2)) +				draw_pixel(fb, x, y, RGB(0, 0, 255)); +			// blue bar on the top +			else if (y < ym1 && (x > xm1 && x < xm2)) +				draw_pixel(fb, x, y, RGB(0, 0, 255)); +			// red bar on the right +			else if (x > xm2 && (y > ym1 && y < ym2)) +				draw_pixel(fb, x, y, RGB(255, 0, 0)); +			// red bar on the bottom +			else if (y > ym2 && (x > xm1 && x < xm2)) +				draw_pixel(fb, x, y, RGB(255, 0, 0)); +			// inside the margins +			else if (x > xm1 && x < xm2 && y > ym1 && y < ym2) { +				// diagonal line +				if (x == y || w - x == h - y) +					draw_pixel(fb, x, y, RGB(255, 255, 255)); +				// diagonal line +				else if (w - x == y || x == h - y) +					draw_pixel(fb, x, y, RGB(255, 255, 255)); +				else { +					int t = (x - xm1 - 1) * 3 / (xm2 - xm1 - 1); +					unsigned r = 0, g = 0, b = 0; + +					unsigned c = (y - ym1 - 1) % 256; + +					switch (t) { +					case 0: +						r = c; +						break; +					case 1: +						g = c; +						break; +					case 2: +						b = c; +						break; +					} + +					draw_pixel(fb, x, y, RGB(r, g, b)); +				} +				// black corners +			} else { +				draw_pixel(fb, x, y, RGB(0, 0, 0)); +			} +		} +	} +} + +void draw_test_pattern(Framebuffer& fb) +{ +	using namespace std::chrono; + +	auto t1 = high_resolution_clock::now(); + +	draw_rgb_test_pattern(fb); + +	auto t2 = high_resolution_clock::now(); +	auto time_span = duration_cast<microseconds>(t2 - t1); + +	printf("draw took %lu us\n", time_span.count()); +} +} diff --git a/libkms++/utils/testpat.h b/libkms++/utils/testpat.h new file mode 100644 index 0000000..b60271a --- /dev/null +++ b/libkms++/utils/testpat.h @@ -0,0 +1,6 @@ +#pragma once + +namespace kms +{ +void draw_test_pattern(Framebuffer& fb); +} | 
