1 /**************************************************************************
3 * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 **************************************************************************/
28 #include "intel_batchbuffer.h"
29 #include "intel_ioctl.h"
31 /* Relocations in kernel space:
32 * - pass dma buffer seperately
33 * - memory manager knows how to patch
34 * - pass list of dependent buffers
35 * - pass relocation list
38 * - get back an offset for buffer to fire
39 * - memory manager knows how to fire buffer
41 * Really want the buffer to be AGP and pinned.
45 /* Cliprect fence: The highest fence protecting a dma buffer
46 * containing explicit cliprect information. Like the old drawable
47 * lock but irq-driven. X server must wait for this fence to expire
48 * before changing cliprects [and then doing sw rendering?]. For
49 * other dma buffers, the scheduler will grab current cliprect info
50 * and mix into buffer. X server must hold the lock while changing
51 * cliprects??? Make per-drawable. Need cliprects in shared memory
52 * -- beats storing them with every cmd buffer in the queue.
54 * ==> X server must wait for this fence to expire before touching the
55 * framebuffer with new cliprects.
57 * ==> Cliprect-dependent buffers associated with a
58 * cliprect-timestamp. All of the buffers associated with a timestamp
59 * must go to hardware before any buffer with a newer timestamp.
61 * ==> Dma should be queued per-drawable for correct X/GL
62 * synchronization. Or can fences be used for this?
64 * Applies to: Blit operations, metaops, X server operations -- X
65 * server automatically waits on its own dma to complete before
66 * modifying cliprects ???
70 intel_dump_batchbuffer(GLuint offset
, GLuint
* ptr
, GLuint count
)
73 fprintf(stderr
, "\n\n\nSTART BATCH (%d dwords):\n", count
/ 4);
74 for (i
= 0; i
< count
/ 4; i
+= 4)
75 fprintf(stderr
, "0x%x:\t0x%08x 0x%08x 0x%08x 0x%08x\n",
76 offset
+ i
* 4, ptr
[i
], ptr
[i
+ 1], ptr
[i
+ 2], ptr
[i
+ 3]);
77 fprintf(stderr
, "END BATCH\n\n\n");
81 intel_batchbuffer_reset(struct intel_batchbuffer
*batch
)
87 * Get a new, free batchbuffer.
90 batch
->size
= batch
->intel
->intelScreen
->maxBatchSize
;
91 driBOData(batch
->buffer
, batch
->size
, NULL
, 0);
93 driBOResetList(&batch
->list
);
96 * Unreference buffers previously on the relocation list.
99 for (i
= 0; i
< batch
->nr_relocs
; i
++) {
100 struct buffer_reloc
*r
= &batch
->reloc
[i
];
101 driBOUnReference(r
->buf
);
104 batch
->list_count
= 0;
105 batch
->nr_relocs
= 0;
109 * We don't refcount the batchbuffer itself since we can't destroy it
110 * while it's on the list.
114 driBOAddListItem(&batch
->list
, batch
->buffer
,
115 DRM_BO_FLAG_MEM_TT
| DRM_BO_FLAG_EXE
,
116 DRM_BO_MASK_MEM
| DRM_BO_FLAG_EXE
);
119 batch
->map
= driBOMap(batch
->buffer
, DRM_BO_FLAG_WRITE
, 0);
120 batch
->ptr
= batch
->map
;
123 /*======================================================================
126 struct intel_batchbuffer
*
127 intel_batchbuffer_alloc(struct intel_context
*intel
)
129 struct intel_batchbuffer
*batch
= calloc(sizeof(*batch
), 1);
131 batch
->intel
= intel
;
133 driGenBuffers(intel
->intelScreen
->batchPool
, "batchbuffer", 1,
134 &batch
->buffer
, 4096,
135 DRM_BO_FLAG_MEM_TT
| DRM_BO_FLAG_EXE
, 0);
136 batch
->last_fence
= NULL
;
137 driBOCreateList(20, &batch
->list
);
138 intel_batchbuffer_reset(batch
);
143 intel_batchbuffer_free(struct intel_batchbuffer
*batch
)
145 if (batch
->last_fence
) {
146 driFenceFinish(batch
->last_fence
,
147 DRM_FENCE_TYPE_EXE
| DRM_I915_FENCE_TYPE_RW
, GL_FALSE
);
148 driFenceUnReference(batch
->last_fence
);
149 batch
->last_fence
= NULL
;
152 driBOUnmap(batch
->buffer
);
155 driBOUnReference(batch
->buffer
);
156 batch
->buffer
= NULL
;
160 /* TODO: Push this whole function into bufmgr.
163 do_flush_locked(struct intel_batchbuffer
*batch
,
165 GLboolean ignore_cliprects
, GLboolean allow_unlock
)
169 struct intel_context
*intel
= batch
->intel
;
171 struct _DriFenceObject
*fo
;
173 driBOValidateList(batch
->intel
->driFd
, &batch
->list
);
175 /* Apply the relocations. This nasty map indicates to me that the
176 * whole task should be done internally by the memory manager, and
177 * that dma buffers probably need to be pinned within agp space.
179 ptr
= (GLuint
*) driBOMap(batch
->buffer
, DRM_BO_FLAG_WRITE
,
180 DRM_BO_HINT_ALLOW_UNFENCED_MAP
);
183 for (i
= 0; i
< batch
->nr_relocs
; i
++) {
184 struct buffer_reloc
*r
= &batch
->reloc
[i
];
186 ptr
[r
->offset
/ 4] = driBOOffset(r
->buf
) + r
->delta
;
189 if (INTEL_DEBUG
& DEBUG_BATCH
)
190 intel_dump_batchbuffer(0, ptr
, used
);
192 driBOUnmap(batch
->buffer
);
195 /* Throw away non-effective packets. Won't work once we have
196 * hardware contexts which would preserve statechanges beyond a
200 if (!(intel
->numClipRects
== 0 && !ignore_cliprects
)) {
201 intel_batch_ioctl(batch
->intel
,
202 driBOOffset(batch
->buffer
),
203 used
, ignore_cliprects
, allow_unlock
);
208 * Kernel fencing. The flags tells the kernel that we've
209 * programmed an MI_FLUSH.
212 fenceFlags
= DRM_I915_FENCE_FLAG_FLUSHED
;
213 fo
= driFenceBuffers(batch
->intel
->driFd
,
214 "Batch fence", fenceFlags
);
217 * User space fencing.
220 driBOFence(batch
->buffer
, fo
);
222 if (driFenceType(fo
) == DRM_FENCE_TYPE_EXE
) {
225 * Oops. We only validated a batch buffer. This means we
226 * didn't do any proper rendering. Discard this fence object.
229 driFenceUnReference(fo
);
231 driFenceUnReference(batch
->last_fence
);
232 batch
->last_fence
= fo
;
233 for (i
= 0; i
< batch
->nr_relocs
; i
++) {
234 struct buffer_reloc
*r
= &batch
->reloc
[i
];
235 driBOFence(r
->buf
, fo
);
239 if (intel
->numClipRects
== 0 && !ignore_cliprects
) {
241 UNLOCK_HARDWARE(intel
);
243 LOCK_HARDWARE(intel
);
245 intel
->vtbl
.lost_hardware(intel
);
250 struct _DriFenceObject
*
251 intel_batchbuffer_flush(struct intel_batchbuffer
*batch
)
253 struct intel_context
*intel
= batch
->intel
;
254 GLuint used
= batch
->ptr
- batch
->map
;
255 GLboolean was_locked
= intel
->locked
;
258 return batch
->last_fence
;
260 /* Add the MI_BATCH_BUFFER_END. Always add an MI_FLUSH - this is a
261 * performance drain that we would like to avoid.
264 ((int *) batch
->ptr
)[0] = intel
->vtbl
.flush_cmd();
265 ((int *) batch
->ptr
)[1] = 0;
266 ((int *) batch
->ptr
)[2] = MI_BATCH_BUFFER_END
;
270 ((int *) batch
->ptr
)[0] = intel
->vtbl
.flush_cmd();
271 ((int *) batch
->ptr
)[1] = MI_BATCH_BUFFER_END
;
275 driBOUnmap(batch
->buffer
);
279 /* TODO: Just pass the relocation list and dma buffer up to the
283 LOCK_HARDWARE(intel
);
285 do_flush_locked(batch
, used
, !(batch
->flags
& INTEL_BATCH_CLIPRECTS
),
289 UNLOCK_HARDWARE(intel
);
293 intel_batchbuffer_reset(batch
);
294 return batch
->last_fence
;
298 intel_batchbuffer_finish(struct intel_batchbuffer
*batch
)
300 struct _DriFenceObject
*fence
= intel_batchbuffer_flush(batch
);
301 driFenceReference(fence
);
302 driFenceFinish(fence
, 3, GL_FALSE
);
303 driFenceUnReference(fence
);
307 /* This is the only way buffers get added to the validate list.
310 intel_batchbuffer_emit_reloc(struct intel_batchbuffer
*batch
,
311 struct _DriBufferObject
*buffer
,
312 GLuint flags
, GLuint mask
, GLuint delta
)
314 assert(batch
->nr_relocs
< MAX_RELOCS
);
316 driBOAddListItem(&batch
->list
, buffer
, flags
, mask
);
319 struct buffer_reloc
*r
= &batch
->reloc
[batch
->nr_relocs
++];
320 driBOReference(buffer
);
322 r
->offset
= batch
->ptr
- batch
->map
;
333 intel_batchbuffer_data(struct intel_batchbuffer
*batch
,
334 const void *data
, GLuint bytes
, GLuint flags
)
336 assert((bytes
& 3) == 0);
337 intel_batchbuffer_require_space(batch
, bytes
, flags
);
338 __memcpy(batch
->ptr
, data
, bytes
);