summaryrefslogtreecommitdiff
path: root/kms++util/src/drawing.cpp
blob: f8cc03fb56413a17469f25134f2503cb10e77a3c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

#include <kms++/kms++.h>
#include <kms++util/kms++util.h>

using namespace std;

namespace kms
{
void draw_rgb_pixel(IMappedFramebuffer& buf, unsigned x, unsigned y, RGB color)
{
	switch (buf.format()) {
	case PixelFormat::XRGB8888:
	case PixelFormat::ARGB8888:
	{
		uint32_t *p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
		*p = color.argb8888();
		break;
	}
	case PixelFormat::XBGR8888:
	case PixelFormat::ABGR8888:
	{
		uint32_t *p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
		*p = color.abgr8888();
		break;
	}
	case PixelFormat::RGB888:
	{
		uint8_t *p = buf.map(0) + buf.stride(0) * y + x * 3;
		p[0] = color.b;
		p[1] = color.g;
		p[2] = color.r;
		break;
	}
	case PixelFormat::BGR888:
	{
		uint8_t *p = buf.map(0) + buf.stride(0) * y + x * 3;
		p[0] = color.r;
		p[1] = color.g;
		p[2] = color.b;
		break;
	}
	case PixelFormat::RGB565:
	{
		uint16_t *p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
		*p = color.rgb565();
		break;
	}
	case PixelFormat::BGR565:
	{
		uint16_t *p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
		*p = color.bgr565();
		break;
	}
	default:
		throw std::invalid_argument("invalid pixelformat");
	}
}

void draw_yuv422_macropixel(IMappedFramebuffer& buf, unsigned x, unsigned y, YUV yuv1, YUV yuv2)
{
	ASSERT((x & 1) == 0);

	uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2);

	uint8_t y0 = yuv1.y;
	uint8_t y1 = yuv2.y;
	uint8_t u = (yuv1.u + yuv2.u) / 2;
	uint8_t v = (yuv1.v + yuv2.v) / 2;

	switch (buf.format()) {
	case PixelFormat::UYVY:
		p[0] = u;
		p[1] = y0;
		p[2] = v;
		p[3] = y1;
		break;

	case PixelFormat::YUYV:
		p[0] = y0;
		p[1] = u;
		p[2] = y1;
		p[3] = v;
		break;

	case PixelFormat::YVYU:
		p[0] = y0;
		p[1] = v;
		p[2] = y1;
		p[3] = u;
		break;

	case PixelFormat::VYUY:
		p[0] = v;
		p[1] = y0;
		p[2] = u;
		p[3] = y1;
		break;

	default:
		throw std::invalid_argument("invalid pixelformat");
	}
}

void draw_yuv420_macropixel(IMappedFramebuffer& buf, unsigned x, unsigned y,
			    YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4)
{
	ASSERT((x & 1) == 0);
	ASSERT((y & 1) == 0);

	uint8_t *py1 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 0) + x);
	uint8_t *py2 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 1) + x);

	uint8_t *puv = (uint8_t*)(buf.map(1) + buf.stride(1) * (y / 2) + x);

	uint8_t y0 = yuv1.y;
	uint8_t y1 = yuv2.y;
	uint8_t y2 = yuv3.y;
	uint8_t y3 = yuv4.y;
	uint8_t u = (yuv1.u + yuv2.u + yuv3.u + yuv4.u) / 4;
	uint8_t v = (yuv1.v + yuv2.v + yuv3.v + yuv4.v) / 4;

	switch (buf.format()) {
	case PixelFormat::NV12:
		py1[0] = y0;
		py1[1] = y1;
		py2[0] = y2;
		py2[1] = y3;
		puv[0] = u;
		puv[1] = v;
		break;

	case PixelFormat::NV21:
		py1[0] = y0;
		py1[1] = y1;
		py2[0] = y2;
		py2[1] = y3;
		puv[0] = v;
		puv[1] = u;
		break;

	default:
		throw std::invalid_argument("invalid pixelformat");
	}
}

void draw_rect(IMappedFramebuffer &fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, RGB color)
{
	for (unsigned i = x; i < x + w; ++i) {
		for (unsigned j = y; j < y + h; ++j) {
			draw_rgb_pixel(fb, i, j, color);
		}
	}
}

static bool get_char_pixel(char c, uint32_t x, uint32_t y)
{
#include "font_8x8.h"

	uint8_t bits = fontdata_8x8[8 * c + y];
	bool bit = (bits >> (7 - x)) & 1;

	return bit;
}

static void draw_char(IMappedFramebuffer& buf, uint32_t xpos, uint32_t ypos, char c, RGB color)
{
	unsigned x, y;
	YUV yuvcolor = color.yuv();

	switch (buf.format()) {
	case PixelFormat::XRGB8888:
	case PixelFormat::XBGR8888:
	case PixelFormat::ARGB8888:
	case PixelFormat::ABGR8888:
	case PixelFormat::RGB888:
	case PixelFormat::BGR888:
	case PixelFormat::RGB565:
	case PixelFormat::BGR565:
		for (y = 0; y < 8; y++) {
			for (x = 0; x < 8; x++) {
				bool b = get_char_pixel(c, x, y);

				draw_rgb_pixel(buf, xpos + x, ypos + y, b ? color : RGB());
			}
		}
		break;

	case PixelFormat::UYVY:
	case PixelFormat::YUYV:
	case PixelFormat::YVYU:
	case PixelFormat::VYUY:
		for (y = 0; y < 8; y++) {
			for (x = 0; x < 8; x += 2) {
				bool b0 = get_char_pixel(c, x, y);
				bool b1 = get_char_pixel(c, x + 1, y);

				draw_yuv422_macropixel(buf, xpos + x, ypos + y,
						       b0 ? yuvcolor : YUV(), b1 ? yuvcolor : YUV());
			}
		}
		break;

	case PixelFormat::NV12:
	case PixelFormat::NV21:
		for (y = 0; y < 8; y += 2) {
			for (x = 0; x < 8; x += 2) {
				bool b00 = get_char_pixel(c, x, y);
				bool b10 = get_char_pixel(c, x + 1, y);
				bool b01 = get_char_pixel(c, x, y + 1);
				bool b11 = get_char_pixel(c, x + 1, y + 1);

				draw_yuv420_macropixel(buf, xpos + x, ypos + y,
						       b00 ? yuvcolor : YUV(), b10 ? yuvcolor : YUV(),
						       b01 ? yuvcolor : YUV(), b11 ? yuvcolor : YUV());
			}
		}
		break;
	default:
		throw std::invalid_argument("unknown pixelformat");
	}
}

void draw_text(IMappedFramebuffer& buf, uint32_t x, uint32_t y, const string& str, RGB color)
{
	for(unsigned i = 0; i < str.size(); i++)
		draw_char(buf, (x + 8 * i), y, str[i], color);
}

}
n> * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /* * Authors: * Ben Skeggs <darktama@iinet.net.au> */ #include "drmP.h" #include "drm.h" #include "nouveau_drv.h" #include "nouveau_drm.h" /* NVidia uses context objects to drive drawing operations. Context objects can be selected into 8 subchannels in the FIFO, and then used via DMA command buffers. A context object is referenced by a user defined handle (CARD32). The HW looks up graphics objects in a hash table in the instance RAM. An entry in the hash table consists of 2 CARD32. The first CARD32 contains the handle, the second one a bitfield, that contains the address of the object in instance RAM. The format of the second CARD32 seems to be: NV4 to NV30: 15: 0 instance_addr >> 4 17:16 engine (here uses 1 = graphics) 28:24 channel id (here uses 0) 31 valid (use 1) NV40: 15: 0 instance_addr >> 4 (maybe 19-0) 21:20 engine (here uses 1 = graphics) I'm unsure about the other bits, but using 0 seems to work. The key into the hash table depends on the object handle and channel id and is given as: */ static uint32_t nouveau_ramht_hash_handle(struct drm_device *dev, int channel, uint32_t handle) { struct drm_nouveau_private *dev_priv=dev->dev_private; uint32_t hash = 0; int i; DRM_DEBUG("ch%d handle=0x%08x\n", channel, handle); for (i=32;i>0;i-=dev_priv->ramht_bits) { hash ^= (handle & ((1 << dev_priv->ramht_bits) - 1)); handle >>= dev_priv->ramht_bits; } if (dev_priv->card_type < NV_50) hash ^= channel << (dev_priv->ramht_bits - 4); hash <<= 3; DRM_DEBUG("hash=0x%08x\n", hash); return hash; } static int nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht, uint32_t offset) { struct drm_nouveau_private *dev_priv=dev->dev_private; uint32_t ctx = INSTANCE_RD(ramht, (offset + 4)/4); if (dev_priv->card_type < NV_40) return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0); return (ctx != 0); } static int nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) { struct drm_nouveau_private *dev_priv=dev->dev_private; struct nouveau_channel *chan = dev_priv->fifos[ref->channel]; struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL; struct nouveau_gpuobj *gpuobj = ref->gpuobj; uint32_t ctx, co, ho; if (!ramht) { DRM_ERROR("No hash table!\n"); return -EINVAL; } if (dev_priv->card_type < NV_40) { ctx = NV_RAMHT_CONTEXT_VALID | (ref->instance >> 4) | (ref->channel << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) | (gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT); } else if (dev_priv->card_type < NV_50) { ctx = (ref->instance >> 4) | (ref->channel << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) | (gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT); } else { ctx = (ref->instance >> 4) | (gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT); } co = ho = nouveau_ramht_hash_handle(dev, ref->channel, ref->handle); do { if (!nouveau_ramht_entry_valid(dev, ramht, co)) { DRM_DEBUG("insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n", ref->channel, co, ref->handle, ctx); INSTANCE_WR(ramht, (co + 0)/4, ref->handle); INSTANCE_WR(ramht, (co + 4)/4, ctx); list_add_tail(&ref->list, &chan->ramht_refs); return 0; } DRM_DEBUG("collision ch%d 0x%08x: h=0x%08x\n", ref->channel, co, INSTANCE_RD(ramht, co/4)); co += 8; if (co >= dev_priv->ramht_size) { DRM_INFO("no space left after collision\n"); co = 0; /* exit as it seems to cause crash with nouveau_demo and * 0xdead0001 object */ break; } } while (co != ho); DRM_ERROR("RAMHT space exhausted. ch=%d\n", ref->channel); return -ENOMEM; } static void nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = dev_priv->fifos[ref->channel]; struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL; uint32_t co, ho; if (!ramht) { DRM_ERROR("No hash table!\n"); return; } co = ho = nouveau_ramht_hash_handle(dev, ref->channel, ref->handle); do { if (nouveau_ramht_entry_valid(dev, ramht, co) && (ref->handle == INSTANCE_RD(ramht, (co/4)))) { DRM_DEBUG("remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n", ref->channel, co, ref->handle, INSTANCE_RD(ramht, (co + 4))); INSTANCE_WR(ramht, (co + 0)/4, 0x00000000); INSTANCE_WR(ramht, (co + 4)/4, 0x00000000); list_del(&ref->list); return; } co += 8; if (co >= dev_priv->ramht_size) co = 0; } while (co != ho); DRM_ERROR("RAMHT entry not found. ch=%d, handle=0x%08x\n", ref->channel, ref->handle); } int nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, int size, int align, uint32_t flags, struct nouveau_gpuobj **gpuobj_ret) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_engine *engine = &dev_priv->Engine; struct nouveau_gpuobj *gpuobj; struct mem_block *pramin = NULL; int ret; DRM_DEBUG("ch%d size=%d align=%d flags=0x%08x\n", chan ? chan->id : -1, size, align, flags); if (!dev_priv || !gpuobj_ret || *gpuobj_ret != NULL) return -EINVAL; gpuobj = drm_calloc(1, sizeof(*gpuobj), DRM_MEM_DRIVER); if (!gpuobj) return -ENOMEM; DRM_DEBUG("gpuobj %p\n", gpuobj); gpuobj->flags = flags; gpuobj->im_channel = chan ? chan->id : -1; list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); /* Choose between global instmem heap, and per-channel private * instmem heap. On <NV50 allow requests for private instmem * to be satisfied from global heap if no per-channel area * available. */ if (chan) { if (chan->ramin_heap) { DRM_DEBUG("private heap\n"); pramin = chan->ramin_heap; } else if (dev_priv->card_type < NV_50) { DRM_DEBUG("global heap fallback\n"); pramin = dev_priv->ramin_heap; } } else { DRM_DEBUG("global heap\n"); pramin = dev_priv->ramin_heap; } if (!pramin) { DRM_ERROR("No PRAMIN heap!\n"); return -EINVAL; } if (!chan && (ret = engine->instmem.populate(dev, gpuobj, &size))) { nouveau_gpuobj_del(dev, &gpuobj); return ret; } /* Allocate a chunk of the PRAMIN aperture */ gpuobj->im_pramin = nouveau_mem_alloc_block(pramin, size, drm_order(align), (struct drm_file *)-2); if (!gpuobj->im_pramin) { nouveau_gpuobj_del(dev, &gpuobj); return -ENOMEM; } gpuobj->im_pramin->flags = NOUVEAU_MEM_INSTANCE; if (!chan && (ret = engine->instmem.bind(dev, gpuobj))) { nouveau_gpuobj_del(dev, &gpuobj); return ret; } if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) { int i; for (i = 0; i < gpuobj->im_pramin->size; i += 4) INSTANCE_WR(gpuobj, i/4, 0); } *gpuobj_ret = gpuobj; return 0; } int nouveau_gpuobj_early_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; DRM_DEBUG("\n"); INIT_LIST_HEAD(&dev_priv->gpuobj_list); return 0; } int nouveau_gpuobj_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; int ret; DRM_DEBUG("\n"); if (dev_priv->card_type < NV_50) { if ((ret = nouveau_gpuobj_new_fake(dev, dev_priv->ramht_offset, ~0, dev_priv->ramht_size, NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ALLOW_NO_REFS, &dev_priv->ramht, NULL))) return ret; } return 0; } void nouveau_gpuobj_takedown(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; DRM_DEBUG("\n"); nouveau_gpuobj_del(dev, &dev_priv->ramht); } void nouveau_gpuobj_late_takedown(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *gpuobj = NULL; struct list_head *entry, *tmp; DRM_DEBUG("\n"); list_for_each_safe(entry, tmp, &dev_priv->gpuobj_list) { gpuobj = list_entry(entry, struct nouveau_gpuobj, list); DRM_ERROR("gpuobj %p still exists at takedown, refs=%d\n", gpuobj, gpuobj->refcount); gpuobj->refcount = 0; nouveau_gpuobj_del(dev, &gpuobj); } } int nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_engine *engine = &dev_priv->Engine; struct nouveau_gpuobj *gpuobj; DRM_DEBUG("gpuobj %p\n", pgpuobj ? *pgpuobj : NULL); if (!dev_priv || !pgpuobj || !(*pgpuobj)) return -EINVAL; gpuobj = *pgpuobj; if (gpuobj->refcount != 0) { DRM_ERROR("gpuobj refcount is %d\n", gpuobj->refcount); return -EINVAL; } if (gpuobj->dtor) gpuobj->dtor(dev, gpuobj); if (gpuobj->im_backing) { if (gpuobj->flags & NVOBJ_FLAG_FAKE) drm_free(gpuobj->im_backing, sizeof(*gpuobj->im_backing), DRM_MEM_DRIVER); else engine->instmem.clear(dev, gpuobj);