main intel.c
  1/* wld: intel.c
  2 *
  3 * Copyright (c) 2013, 2014 Michael Forney
  4 *
  5 * Permission is hereby granted, free of charge, to any person obtaining a copy
  6 * of this software and associated documentation files (the "Software"), to deal
  7 * in the Software without restriction, including without limitation the rights
  8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 * copies of the Software, and to permit persons to whom the Software is
 10 * furnished to do so, subject to the following conditions:
 11 *
 12 * The above copyright notice and this permission notice shall be included in
 13 * all copies or substantial portions of the Software.
 14 *
 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21 * SOFTWARE.
 22 */
 23
 24#include "drm-private.h"
 25#include "drm.h"
 26#include "intel/batch.h"
 27#include "intel/blt.h"
 28#include "wld-private.h"
 29
 30#include <i915_drm.h>
 31#include <intel_bufmgr.h>
 32#include <string.h>
 33#include <unistd.h>
 34
 35struct intel_context {
 36	struct wld_context base;
 37	drm_intel_bufmgr *bufmgr;
 38};
 39
 40struct intel_renderer {
 41	struct wld_renderer base;
 42	struct intel_batch batch;
 43	struct intel_buffer *target;
 44};
 45
 46struct intel_buffer {
 47	struct buffer base;
 48	struct wld_exporter exporter;
 49	drm_intel_bo *bo;
 50};
 51
 52#include "interface/buffer.h"
 53#include "interface/context.h"
 54#include "interface/renderer.h"
 55#define DRM_DRIVER_NAME intel
 56#include "interface/drm.h"
 57IMPL(intel_context, wld_context)
 58IMPL(intel_renderer, wld_renderer)
 59IMPL(intel_buffer, wld_buffer)
 60
 61static void
 62pack_gray_row_to_mono(uint8_t *dst, const uint8_t *src, uint32_t width)
 63{
 64	uint32_t x, bytes_per_row;
 65
 66	bytes_per_row = (width + 7) / 8;
 67	memset(dst, 0, bytes_per_row);
 68
 69	for (x = 0; x < width; ++x) {
 70		if (src[x] >= 128)
 71			dst[x / 8] |= 0x80 >> (x & 7);
 72	}
 73}
 74
 75/**** DRM driver ****/
 76bool
 77driver_device_supported(uint32_t vendor_id, uint32_t device_id)
 78{
 79	return vendor_id == 0x8086;
 80}
 81
 82struct wld_context *
 83driver_create_context(int drm_fd)
 84{
 85	struct intel_context *context;
 86
 87	context = malloc(sizeof *context);
 88
 89	if (!context)
 90		goto error0;
 91
 92	context_initialize(&context->base, &wld_context_impl);
 93	context->bufmgr = drm_intel_bufmgr_gem_init(drm_fd, INTEL_BATCH_SIZE);
 94
 95	if (!context->bufmgr)
 96		goto error1;
 97
 98	return &context->base;
 99
100error1:
101	free(context);
102error0:
103	return NULL;
104}
105
106/**** Context ****/
107struct wld_renderer *
108context_create_renderer(struct wld_context *base)
109{
110	struct intel_context *context = intel_context(base);
111	struct intel_renderer *renderer;
112
113	if (!(renderer = malloc(sizeof *renderer)))
114		goto error0;
115
116	if (!(intel_batch_initialize(&renderer->batch, context->bufmgr)))
117		goto error1;
118
119	renderer_initialize(&renderer->base, &wld_renderer_impl);
120
121	return &renderer->base;
122
123error1:
124	free(renderer);
125error0:
126	return NULL;
127}
128
129static bool export(struct wld_exporter *exporter, struct wld_buffer *base,
130                   uint32_t type, union wld_object *object)
131{
132	struct intel_buffer *buffer = intel_buffer(base);
133
134	switch (type) {
135	case WLD_DRM_OBJECT_HANDLE:
136		object->u32 = buffer->bo->handle;
137		return true;
138	case WLD_DRM_OBJECT_PRIME_FD:
139		if (drm_intel_bo_gem_export_to_prime(buffer->bo, &object->i) != 0)
140			return false;
141		return true;
142	default:
143		return false;
144	}
145}
146
147static struct buffer *
148new_buffer(uint32_t width, uint32_t height,
149           uint32_t format, uint32_t pitch,
150           drm_intel_bo *bo)
151{
152	struct intel_buffer *buffer;
153
154	if (!(buffer = malloc(sizeof *buffer)))
155		return NULL;
156
157	buffer_initialize(&buffer->base, &wld_buffer_impl,
158	                  width, height, format, pitch);
159	buffer->bo = bo;
160	buffer->exporter.export = &export;
161	wld_buffer_add_exporter(&buffer->base.base, &buffer->exporter);
162
163	return &buffer->base;
164}
165
166struct buffer *
167context_create_buffer(struct wld_context *base,
168                      uint32_t width, uint32_t height,
169                      uint32_t format, uint32_t flags)
170{
171	struct intel_context *context = intel_context(base);
172	struct buffer *buffer;
173	drm_intel_bo *bo;
174	uint32_t tiling_mode = width >= 128 && !(flags & WLD_FLAG_CURSOR) ? I915_TILING_X : I915_TILING_NONE;
175	unsigned long pitch;
176
177	bo = drm_intel_bo_alloc_tiled(context->bufmgr, "buffer", width, height, 4,
178	                              &tiling_mode, &pitch, 0);
179
180	if (!bo)
181		goto error0;
182
183	if (!(buffer = new_buffer(width, height, format, pitch, bo)))
184		goto error1;
185
186	return buffer;
187
188error1:
189	drm_intel_bo_unreference(bo);
190error0:
191	return NULL;
192}
193
194struct buffer *
195context_import_buffer(struct wld_context *base,
196                      uint32_t type, union wld_object object,
197                      uint32_t width, uint32_t height,
198                      uint32_t format, uint32_t pitch)
199{
200	struct intel_context *context = intel_context(base);
201	struct buffer *buffer;
202	drm_intel_bo *bo;
203
204	switch (type) {
205	case WLD_DRM_OBJECT_PRIME_FD: {
206		uint32_t size = width * height * format_bytes_per_pixel(format);
207		bo = drm_intel_bo_gem_create_from_prime(context->bufmgr,
208		                                        object.i, size);
209		break;
210	}
211	default:
212		bo = NULL;
213	};
214
215	if (!bo)
216		goto error0;
217
218	if (!(buffer = new_buffer(width, height, format, pitch, bo)))
219		goto error1;
220
221	return buffer;
222
223error1:
224	drm_intel_bo_unreference(bo);
225error0:
226	return NULL;
227}
228
229void
230context_destroy(struct wld_context *base)
231{
232	struct intel_context *context = intel_context(base);
233
234	drm_intel_bufmgr_destroy(context->bufmgr);
235	free(context);
236}
237
238/**** Renderer ****/
239uint32_t
240renderer_capabilities(struct wld_renderer *renderer, struct buffer *buffer)
241{
242	if (buffer->base.impl == &wld_buffer_impl)
243		return WLD_CAPABILITY_READ | WLD_CAPABILITY_WRITE;
244
245	return 0;
246}
247
248bool
249renderer_set_target(struct wld_renderer *base, struct buffer *buffer)
250{
251	struct intel_renderer *renderer = intel_renderer(base);
252
253	if (buffer && buffer->base.impl != &wld_buffer_impl)
254		return false;
255
256	renderer->target = buffer ? intel_buffer(&buffer->base) : NULL;
257
258	return true;
259}
260
261void
262renderer_fill_rectangle(struct wld_renderer *base, uint32_t color,
263                        int32_t x, int32_t y,
264                        uint32_t width, uint32_t height)
265{
266	struct intel_renderer *renderer = intel_renderer(base);
267	struct intel_buffer *dst = renderer->target;
268
269	xy_color_blt(&renderer->batch, dst->bo, dst->base.base.pitch,
270	             x, y, x + width, y + height, color);
271}
272
273void
274renderer_copy_rectangle(struct wld_renderer *base,
275                        struct buffer *buffer_base,
276                        int32_t dst_x, int32_t dst_y,
277                        int32_t src_x, int32_t src_y,
278                        uint32_t width, uint32_t height)
279{
280	struct intel_renderer *renderer = intel_renderer(base);
281
282	if (buffer_base->base.impl != &wld_buffer_impl)
283		return;
284
285	struct intel_buffer *src = intel_buffer(&buffer_base->base),
286	                    *dst = renderer->target;
287
288	xy_src_copy_blt(&renderer->batch,
289	                src->bo, src->base.base.pitch, src_x, src_y,
290	                dst->bo, dst->base.base.pitch, dst_x, dst_y, width, height);
291}
292
293void
294renderer_draw_text(struct wld_renderer *base,
295                   struct font *font, uint32_t color,
296                   int32_t x, int32_t y, const char *text,
297                   uint32_t length, struct wld_extents *extents)
298{
299	struct intel_renderer *renderer = intel_renderer(base);
300	struct intel_buffer *dst = renderer->target;
301	int ret;
302	struct glyph *glyph;
303	uint32_t row;
304	FT_UInt glyph_index;
305	uint32_t c;
306	uint8_t immediate[512];
307	uint8_t *byte;
308	int32_t origin_x = x;
309
310	xy_setup_blt(&renderer->batch, true, BLT_RASTER_OPERATION_SRC,
311	             0, color, dst->bo, dst->base.base.pitch);
312
313	if (length == -1)
314		length = strlen(text);
315
316	while ((ret = FcUtf8ToUcs4((FcChar8 *)text, &c, length)) > 0 && c != '\0') {
317		text += ret;
318		length -= ret;
319		glyph_index = FT_Get_Char_Index(font->face, c);
320
321		if (!font_ensure_glyph(font, glyph_index))
322			continue;
323
324		glyph = font->glyphs[glyph_index];
325
326		if (glyph->bitmap.width == 0 || glyph->bitmap.rows == 0)
327			goto advance;
328
329		byte = immediate;
330
331		/* XY_TEXT_IMMEDIATE requires a pitch with no extra bytes */
332		for (row = 0; row < glyph->bitmap.rows; ++row) {
333			const uint8_t *src = glyph->bitmap.buffer +
334			                     (row * glyph->bitmap.pitch);
335			uint32_t bytes_per_row = (glyph->bitmap.width + 7) / 8;
336
337			if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
338				memcpy(byte, src, bytes_per_row);
339			} else {
340				pack_gray_row_to_mono(byte, src, glyph->bitmap.width);
341			}
342			byte += (glyph->bitmap.width + 7) / 8;
343		}
344
345	retry:
346		ret = xy_text_immediate_blt(&renderer->batch, dst->bo,
347		                            origin_x + glyph->x, y + glyph->y,
348		                            origin_x + glyph->x + glyph->bitmap.width,
349		                            y + glyph->y + glyph->bitmap.rows,
350		                            (byte - immediate + 3) / 4,
351		                            (uint32_t *)immediate);
352
353		if (ret == INTEL_BATCH_NO_SPACE) {
354			intel_batch_flush(&renderer->batch);
355			xy_setup_blt(&renderer->batch, true, BLT_RASTER_OPERATION_SRC,
356			             0, color, dst->bo, dst->base.base.pitch);
357			goto retry;
358		}
359
360	advance:
361		origin_x += glyph->advance;
362	}
363
364	if (extents)
365		extents->advance = origin_x - x;
366}
367
368void
369renderer_flush(struct wld_renderer *base)
370{
371	struct intel_renderer *renderer = intel_renderer(base);
372
373	intel_batch_flush(&renderer->batch);
374}
375
376void
377renderer_destroy(struct wld_renderer *base)
378{
379	struct intel_renderer *renderer = intel_renderer(base);
380
381	intel_batch_finalize(&renderer->batch);
382	free(renderer);
383}
384
385/**** Buffer ****/
386bool
387buffer_map(struct buffer *base)
388{
389	struct intel_buffer *buffer = intel_buffer(&base->base);
390
391	if (drm_intel_gem_bo_map_gtt(buffer->bo) != 0)
392		return false;
393
394	buffer->base.base.map = buffer->bo->virtual;
395
396	return true;
397}
398
399bool
400buffer_unmap(struct buffer *base)
401{
402	struct intel_buffer *buffer = intel_buffer(&base->base);
403
404	if (drm_intel_gem_bo_unmap_gtt(buffer->bo) != 0)
405		return false;
406
407	buffer->base.base.map = NULL;
408
409	return true;
410}
411
412void
413buffer_destroy(struct buffer *base)
414{
415	struct intel_buffer *buffer = intel_buffer(&base->base);
416
417	drm_intel_bo_unreference(buffer->bo);
418	free(buffer);
419}