2 |
- |
1 |
/* Copyright (C) 1998, 1999 Aladdin Enterprises. All rights reserved.
|
|
|
2 |
|
|
|
3 |
This software is provided AS-IS with no warranty, either express or
|
|
|
4 |
implied.
|
|
|
5 |
|
|
|
6 |
This software is distributed under license and may not be copied,
|
|
|
7 |
modified or distributed except as expressly authorized under the terms
|
|
|
8 |
of the license contained in the file LICENSE in this distribution.
|
|
|
9 |
|
|
|
10 |
For more information about licensing, please refer to
|
|
|
11 |
http://www.ghostscript.com/licensing/. For information on
|
|
|
12 |
commercial licensing, go to http://www.artifex.com/licensing/ or
|
|
|
13 |
contact Artifex Software, Inc., 101 Lucas Valley Road #110,
|
|
|
14 |
San Rafael, CA 94903, U.S.A., +1(415)492-9861.
|
|
|
15 |
*/
|
|
|
16 |
|
|
|
17 |
/* $Id: gdevprna.c,v 1.6 2004/08/04 19:36:12 stefan Exp $ */
|
|
|
18 |
/* Generic asynchronous printer driver support */
|
|
|
19 |
|
|
|
20 |
/* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
|
|
|
21 |
/* Revised 8/7/98 by L. Peter Deutsch (ghost@aladdin.com) for */
|
|
|
22 |
/* memory manager changes */
|
|
|
23 |
/* 12/1/98 soho@crl.com - Removed unnecessary flush & reopen in */
|
|
|
24 |
/* gdev_prn_async_write_get_hardware_params */
|
|
|
25 |
#include "gdevprna.h"
|
|
|
26 |
#include "gsalloc.h"
|
|
|
27 |
#include "gsdevice.h"
|
|
|
28 |
#include "gsmemlok.h"
|
|
|
29 |
#include "gsmemret.h"
|
|
|
30 |
#include "gsnogc.h"
|
|
|
31 |
#include "gxcldev.h"
|
|
|
32 |
#include "gxclpath.h"
|
|
|
33 |
#include "gxpageq.h"
|
|
|
34 |
#include "gzht.h" /* for gx_ht_cache_default_bits */
|
|
|
35 |
|
|
|
36 |
/* ----------------- Constants ----------------------- */
|
|
|
37 |
/*
|
|
|
38 |
* Fixed overhead # bytes to run renderer in (+ driver-spec'd variable bytes):
|
|
|
39 |
* empirical & still very subject to change.
|
|
|
40 |
*/
|
|
|
41 |
#define RendererAllocationOverheadBytes 503000 /* minimum is 503,000 as of 4/26/99 */
|
|
|
42 |
|
|
|
43 |
#ifdef DEBUG
|
|
|
44 |
/* 196000 is pretty much the minimum, given 16K phys memfile blocks */
|
|
|
45 |
/*# define DebugBandlistMemorySize 196000*/ /* comment out to disable fixed (debug) bandlist size */
|
|
|
46 |
#endif /* defined(DEBUG) */
|
|
|
47 |
|
|
|
48 |
/* ---------------- Standard device procedures ---------------- */
|
|
|
49 |
private dev_proc_close_device(gdev_prn_async_write_close_device);
|
|
|
50 |
private dev_proc_output_page(gdev_prn_async_write_output_page);
|
|
|
51 |
private dev_proc_put_params(gdev_prn_async_write_put_params);
|
|
|
52 |
private dev_proc_get_hardware_params(gdev_prn_async_write_get_hardware_params);
|
|
|
53 |
private dev_proc_put_params(gdev_prn_async_render_put_params);
|
|
|
54 |
|
|
|
55 |
/* ---------------- Forward Declarations ---------------------- */
|
|
|
56 |
private void gdev_prn_dealloc(gx_device_printer *);
|
|
|
57 |
private proc_free_up_bandlist_memory(gdev_prn_async_write_free_up_bandlist_memory);
|
|
|
58 |
private int flush_page(gx_device_printer *, bool);
|
|
|
59 |
private int reopen_clist_after_flush(gx_device_printer *);
|
|
|
60 |
private void reinit_printer_into_printera(gx_device_printer * const);
|
|
|
61 |
private int alloc_bandlist_memory(gs_memory_t **, gs_memory_t *);
|
|
|
62 |
private void free_bandlist_memory(gs_memory_t *);
|
|
|
63 |
private int alloc_render_memory(gs_memory_t **, gs_memory_t *, long);
|
|
|
64 |
private void free_render_memory(gs_memory_t *);
|
|
|
65 |
private gs_memory_recover_status_t
|
|
|
66 |
prna_mem_recover(gs_memory_retrying_t *rmem, void *proc_data);
|
|
|
67 |
|
|
|
68 |
/* ------ Open/close ------ */
|
|
|
69 |
|
|
|
70 |
/*
|
|
|
71 |
* Open this printer device in ASYNC (overlapped) mode.
|
|
|
72 |
* This routine must always called by the concrete device's xx_open routine
|
|
|
73 |
* in lieu of gdev_prn_open.
|
|
|
74 |
*/
|
|
|
75 |
int
|
|
|
76 |
gdev_prn_async_write_open(gx_device_printer * pwdev, int max_raster,
|
|
|
77 |
int min_band_height, int max_src_image_row)
|
|
|
78 |
{
|
|
|
79 |
gx_device *const pdev = (gx_device *) pwdev;
|
|
|
80 |
int code;
|
|
|
81 |
bool writer_is_open = false;
|
|
|
82 |
gx_device_clist_writer *const pcwdev =
|
|
|
83 |
&((gx_device_clist *) pwdev)->writer;
|
|
|
84 |
gx_device_clist_reader *pcrdev = 0;
|
|
|
85 |
gx_device_printer *prdev = 0;
|
|
|
86 |
gs_memory_t *render_memory = 0; /* renderer's mem allocator */
|
|
|
87 |
|
|
|
88 |
pwdev->page_queue = 0;
|
|
|
89 |
pwdev->bandlist_memory = 0;
|
|
|
90 |
pwdev->async_renderer = 0;
|
|
|
91 |
|
|
|
92 |
/* allocate & init render memory */
|
|
|
93 |
/* The big memory consumers are: */
|
|
|
94 |
/* - the buffer used to read images from the command list */
|
|
|
95 |
/* - buffer used by gx_real_default_strip_copy_rop() */
|
|
|
96 |
/* - line pointer tables for memory devices used in plane extraction */
|
|
|
97 |
/* - the halftone cache */
|
|
|
98 |
/* - the band rendering buffer */
|
|
|
99 |
/* The * 2's in the next statement are a ****** HACK ****** to deal with */
|
|
|
100 |
/* sandbars in the memory manager. */
|
|
|
101 |
if ((code = alloc_render_memory(&render_memory,
|
|
|
102 |
pwdev->memory->non_gc_memory, RendererAllocationOverheadBytes + max_raster
|
|
|
103 |
/* the first * 2 is not a hack */
|
|
|
104 |
+ (max_raster + sizeof(void *) * 2) * min_band_height
|
|
|
105 |
+ max_src_image_row + gx_ht_cache_default_bits() * 2)) < 0)
|
|
|
106 |
goto open_err;
|
|
|
107 |
|
|
|
108 |
/* Alloc & init bandlist allocators */
|
|
|
109 |
/* Bandlist mem is threadsafe & common to rdr/wtr, so it's used */
|
|
|
110 |
/* for page queue & cmd list buffers. */
|
|
|
111 |
if ((code = alloc_bandlist_memory
|
|
|
112 |
(&pwdev->bandlist_memory, pwdev->memory->non_gc_memory)) < 0)
|
|
|
113 |
goto open_err;
|
|
|
114 |
|
|
|
115 |
/* Dictate banding parameters for both renderer & writer */
|
|
|
116 |
/* Protect from user change, since user changing these won't be */
|
|
|
117 |
/* detected, ergo the necessary close/reallocate/open wouldn't happen. */
|
|
|
118 |
pwdev->space_params.banding_type = BandingAlways;
|
|
|
119 |
pwdev->space_params.params_are_read_only = true;
|
|
|
120 |
|
|
|
121 |
/* Make a copy of device for use as rendering device b4 opening writer */
|
|
|
122 |
code = gs_copydevice((gx_device **) & prdev, pdev, render_memory);
|
|
|
123 |
pcrdev = &((gx_device_clist *) prdev)->reader;
|
|
|
124 |
if (code < 0)
|
|
|
125 |
goto open_err;
|
|
|
126 |
|
|
|
127 |
/* -------------- Open cmd list WRITER instance of device ------- */
|
|
|
128 |
/* --------------------------------------------------------------- */
|
|
|
129 |
/* This is wrong, because it causes the same thing in the renderer */
|
|
|
130 |
pwdev->OpenOutputFile = 0; /* Don't open output file in writer */
|
|
|
131 |
|
|
|
132 |
/* Hack: set this vector to tell gdev_prn_open to allocate for async rendering */
|
|
|
133 |
pwdev->free_up_bandlist_memory = &gdev_prn_async_write_free_up_bandlist_memory;
|
|
|
134 |
|
|
|
135 |
/* prevent clist writer from queuing path graphics & force it to split images */
|
|
|
136 |
pwdev->clist_disable_mask |= clist_disable_fill_path |
|
|
|
137 |
clist_disable_stroke_path | clist_disable_complex_clip |
|
|
|
138 |
clist_disable_nonrect_hl_image | clist_disable_pass_thru_params;
|
|
|
139 |
|
|
|
140 |
if ((code = gdev_prn_open(pdev)) >= 0) {
|
|
|
141 |
writer_is_open = true;
|
|
|
142 |
|
|
|
143 |
/* set up constant async-specific fields in device */
|
|
|
144 |
reinit_printer_into_printera(pwdev);
|
|
|
145 |
|
|
|
146 |
/* keep ptr to renderer device */
|
|
|
147 |
pwdev->async_renderer = prdev;
|
|
|
148 |
|
|
|
149 |
/* Allocate the page queue, then initialize it */
|
|
|
150 |
/* Use bandlist memory since it's shared between rdr & wtr */
|
|
|
151 |
if ((pwdev->page_queue = gx_page_queue_alloc(pwdev->bandlist_memory)) == 0)
|
|
|
152 |
code = gs_note_error(gs_error_VMerror);
|
|
|
153 |
else
|
|
|
154 |
/* Allocate from clist allocator since it is thread-safe */
|
|
|
155 |
code = gx_page_queue_init(pwdev->page_queue, pwdev->bandlist_memory);
|
|
|
156 |
}
|
|
|
157 |
/* ------------ Open cmd list RENDERER instance of device ------- */
|
|
|
158 |
/* --------------------------------------------------------------- */
|
|
|
159 |
if (code >= 0) {
|
|
|
160 |
gx_semaphore_t *open_semaphore;
|
|
|
161 |
|
|
|
162 |
/* Force writer's actual band params into reader's requested params */
|
|
|
163 |
prdev->space_params.band = pcwdev->page_info.band_params;
|
|
|
164 |
|
|
|
165 |
/* copydevice has already set up prdev->memory = render_memory */
|
|
|
166 |
/* prdev->bandlist_memory = pwdev->bandlist_memory; */
|
|
|
167 |
prdev->buffer_memory = prdev->memory;
|
|
|
168 |
|
|
|
169 |
/* enable renderer to accept changes to params computed by writer */
|
|
|
170 |
prdev->space_params.params_are_read_only = false;
|
|
|
171 |
|
|
|
172 |
/* page queue is common to both devices */
|
|
|
173 |
prdev->page_queue = pwdev->page_queue;
|
|
|
174 |
|
|
|
175 |
/* Start renderer thread & wait for its successful open of device */
|
|
|
176 |
if (!(open_semaphore = gx_semaphore_alloc(prdev->memory)))
|
|
|
177 |
code = gs_note_error(gs_error_VMerror);
|
|
|
178 |
else {
|
|
|
179 |
gdev_prn_start_render_params thread_params;
|
|
|
180 |
|
|
|
181 |
thread_params.writer_device = pwdev;
|
|
|
182 |
thread_params.open_semaphore = open_semaphore;
|
|
|
183 |
thread_params.open_code = 0;
|
|
|
184 |
code = (*pwdev->printer_procs.start_render_thread)
|
|
|
185 |
(&thread_params);
|
|
|
186 |
if (code >= 0)
|
|
|
187 |
gx_semaphore_wait(open_semaphore);
|
|
|
188 |
code = thread_params.open_code;
|
|
|
189 |
gx_semaphore_free(open_semaphore);
|
|
|
190 |
}
|
|
|
191 |
}
|
|
|
192 |
/* ----- Set the recovery procedure for the mem allocator ----- */
|
|
|
193 |
if (code >= 0) {
|
|
|
194 |
gs_memory_retrying_set_recover(
|
|
|
195 |
(gs_memory_retrying_t *)pwdev->memory->non_gc_memory,
|
|
|
196 |
prna_mem_recover,
|
|
|
197 |
(void *)pcwdev
|
|
|
198 |
);
|
|
|
199 |
}
|
|
|
200 |
/* --------------------- Wrap up --------------------------------- */
|
|
|
201 |
/* --------------------------------------------------------------- */
|
|
|
202 |
if (code < 0) {
|
|
|
203 |
open_err:
|
|
|
204 |
/* error mop-up */
|
|
|
205 |
if (render_memory && !prdev)
|
|
|
206 |
free_render_memory(render_memory);
|
|
|
207 |
|
|
|
208 |
gdev_prn_dealloc(pwdev);
|
|
|
209 |
if (writer_is_open) {
|
|
|
210 |
gdev_prn_close(pdev);
|
|
|
211 |
pwdev->free_up_bandlist_memory = 0;
|
|
|
212 |
}
|
|
|
213 |
}
|
|
|
214 |
return code;
|
|
|
215 |
}
|
|
|
216 |
|
|
|
217 |
/* This procedure is called from within the memory allocator when regular */
|
|
|
218 |
/* malloc's fail -- this procedure tries to free up pages from the queue */
|
|
|
219 |
/* and returns a status code indicating whether any more can be freed. */
|
|
|
220 |
private gs_memory_recover_status_t
|
|
|
221 |
prna_mem_recover(gs_memory_retrying_t *rmem, void *proc_data)
|
|
|
222 |
{
|
|
|
223 |
int pages_remain = 0;
|
|
|
224 |
gx_device_clist_writer *cldev = proc_data;
|
|
|
225 |
|
|
|
226 |
if (cldev->free_up_bandlist_memory != NULL)
|
|
|
227 |
pages_remain =
|
|
|
228 |
(*cldev->free_up_bandlist_memory)( (gx_device *)cldev, false );
|
|
|
229 |
return (pages_remain > 0) ? RECOVER_STATUS_RETRY_OK : RECOVER_STATUS_NO_RETRY;
|
|
|
230 |
}
|
|
|
231 |
|
|
|
232 |
/* (Re)set printer device fields which get trampled by gdevprn_open & put_params */
|
|
|
233 |
private void
|
|
|
234 |
reinit_printer_into_printera(
|
|
|
235 |
gx_device_printer * const pdev /* printer to convert */
|
|
|
236 |
)
|
|
|
237 |
{
|
|
|
238 |
/* Change some of the procedure vector to point at async procedures */
|
|
|
239 |
/* Originals were already saved by gdev_prn_open */
|
|
|
240 |
if (dev_proc(pdev, close_device) == gdev_prn_close)
|
|
|
241 |
set_dev_proc(pdev, close_device, gdev_prn_async_write_close_device);
|
|
|
242 |
set_dev_proc(pdev, output_page, gdev_prn_async_write_output_page);
|
|
|
243 |
set_dev_proc(pdev, put_params, gdev_prn_async_write_put_params);
|
|
|
244 |
set_dev_proc(pdev, get_xfont_procs, gx_default_get_xfont_procs);
|
|
|
245 |
set_dev_proc(pdev, get_xfont_device, gx_default_get_xfont_device);
|
|
|
246 |
set_dev_proc(pdev, get_hardware_params, gdev_prn_async_write_get_hardware_params);
|
|
|
247 |
|
|
|
248 |
/* clist writer calls this if it runs out of memory & wants to retry */
|
|
|
249 |
pdev->free_up_bandlist_memory = &gdev_prn_async_write_free_up_bandlist_memory;
|
|
|
250 |
}
|
|
|
251 |
|
|
|
252 |
/* Generic closing for the writer device. */
|
|
|
253 |
private int
|
|
|
254 |
gdev_prn_async_write_close_device(gx_device * pdev)
|
|
|
255 |
{
|
|
|
256 |
gx_device_printer *const pwdev = (gx_device_printer *) pdev;
|
|
|
257 |
|
|
|
258 |
/* Signal render thread to close & terminate when done */
|
|
|
259 |
gx_page_queue_add_page(pwdev->page_queue,
|
|
|
260 |
GX_PAGE_QUEUE_ACTION_TERMINATE, 0, 0);
|
|
|
261 |
|
|
|
262 |
/* Wait for renderer to finish all pages & terminate req */
|
|
|
263 |
gx_page_queue_wait_until_empty(pwdev->page_queue);
|
|
|
264 |
|
|
|
265 |
/* Cascade down to original close rtn */
|
|
|
266 |
gdev_prn_close(pdev);
|
|
|
267 |
pwdev->free_up_bandlist_memory = 0;
|
|
|
268 |
|
|
|
269 |
/* Deallocte dynamic stuff */
|
|
|
270 |
gdev_prn_dealloc(pwdev);
|
|
|
271 |
return 0;
|
|
|
272 |
}
|
|
|
273 |
|
|
|
274 |
/* Deallocte dynamic memory attached to device. Aware of possible imcomplete open */
|
|
|
275 |
private void
|
|
|
276 |
gdev_prn_dealloc(gx_device_printer * pwdev)
|
|
|
277 |
{
|
|
|
278 |
gx_device_printer *const prdev = pwdev->async_renderer;
|
|
|
279 |
|
|
|
280 |
/* Delete renderer device & its memory allocator */
|
|
|
281 |
if (prdev) {
|
|
|
282 |
gs_memory_t *render_alloc = prdev->memory;
|
|
|
283 |
|
|
|
284 |
gs_free_object(render_alloc, prdev, "gdev_prn_dealloc");
|
|
|
285 |
free_render_memory(render_alloc);
|
|
|
286 |
}
|
|
|
287 |
/* Free page queue */
|
|
|
288 |
if (pwdev->page_queue) {
|
|
|
289 |
gx_page_queue_dnit(pwdev->page_queue);
|
|
|
290 |
gs_free_object(pwdev->bandlist_memory, pwdev->page_queue,
|
|
|
291 |
"gdev_prn_dealloc");
|
|
|
292 |
pwdev->page_queue = 0;
|
|
|
293 |
}
|
|
|
294 |
/* Free memory bandlist allocators */
|
|
|
295 |
if (pwdev->bandlist_memory)
|
|
|
296 |
free_bandlist_memory(pwdev->bandlist_memory);
|
|
|
297 |
}
|
|
|
298 |
|
|
|
299 |
/* Open the render portion of a printer device in ASYNC (overlapped) mode.
|
|
|
300 |
|
|
|
301 |
* This routine is always called by concrete device's xx_open_render_device
|
|
|
302 |
* in lieu of gdev_prn_open.
|
|
|
303 |
*/
|
|
|
304 |
int
|
|
|
305 |
gdev_prn_async_render_open(gx_device_printer * prdev)
|
|
|
306 |
{
|
|
|
307 |
gx_device *const pdev = (gx_device *) prdev;
|
|
|
308 |
|
|
|
309 |
prdev->is_async_renderer = true;
|
|
|
310 |
return gdev_prn_open(pdev);
|
|
|
311 |
}
|
|
|
312 |
|
|
|
313 |
/* Generic closing for the rendering device. */
|
|
|
314 |
int
|
|
|
315 |
gdev_prn_async_render_close_device(gx_device_printer * prdev)
|
|
|
316 |
{
|
|
|
317 |
gx_device *const pdev = (gx_device *) prdev;
|
|
|
318 |
|
|
|
319 |
return gdev_prn_close(pdev);
|
|
|
320 |
}
|
|
|
321 |
|
|
|
322 |
/* (Re)set renderer device fields which get trampled by gdevprn_open & put_params */
|
|
|
323 |
private void
|
|
|
324 |
reinit_printer_into_renderer(
|
|
|
325 |
gx_device_printer * const pdev /* printer to convert */
|
|
|
326 |
)
|
|
|
327 |
{
|
|
|
328 |
set_dev_proc(pdev, put_params, gdev_prn_async_render_put_params);
|
|
|
329 |
}
|
|
|
330 |
|
|
|
331 |
/* ---------- Start rasterizer thread ------------ */
|
|
|
332 |
/*
|
|
|
333 |
* Must be called by async device driver implementation (see gdevprna.h
|
|
|
334 |
* under "Synchronizing the Instances"). This is the rendering loop, which
|
|
|
335 |
* requires its own thread for as long as the device is open. This proc only
|
|
|
336 |
* returns after the device is closed, or if the open failed. NB that an
|
|
|
337 |
* open error leaves things in a state where the writer thread will not be
|
|
|
338 |
* able to close since it's expecting the renderer to acknowledge its
|
|
|
339 |
* requests before the writer can close. Ergo, if this routine fails you'll
|
|
|
340 |
* crash unless the caller fixes the problem & successfully retries this.
|
|
|
341 |
*/
|
|
|
342 |
int /* rets 0 ok, -ve error code if open failed */
|
|
|
343 |
gdev_prn_async_render_thread(
|
|
|
344 |
gdev_prn_start_render_params * params
|
|
|
345 |
)
|
|
|
346 |
{
|
|
|
347 |
gx_device_printer *const pwdev = params->writer_device;
|
|
|
348 |
gx_device_printer *const prdev = pwdev->async_renderer;
|
|
|
349 |
gx_page_queue_entry_t *entry;
|
|
|
350 |
int code;
|
|
|
351 |
|
|
|
352 |
/* Open device, but don't use default if user didn't override */
|
|
|
353 |
if (prdev->printer_procs.open_render_device ==
|
|
|
354 |
gx_default_open_render_device)
|
|
|
355 |
code = gdev_prn_async_render_open(prdev);
|
|
|
356 |
else
|
|
|
357 |
code = (*prdev->printer_procs.open_render_device) (prdev);
|
|
|
358 |
reinit_printer_into_renderer(prdev);
|
|
|
359 |
|
|
|
360 |
/* The cmd list logic assumes reader's & writer's tile caches are same size */
|
|
|
361 |
if (code >= 0 &&
|
|
|
362 |
((gx_device_clist *) pwdev)->writer.page_tile_cache_size !=
|
|
|
363 |
((gx_device_clist *) prdev)->writer.page_tile_cache_size) {
|
|
|
364 |
gdev_prn_async_render_close_device(prdev);
|
|
|
365 |
code = gs_note_error(gs_error_VMerror);
|
|
|
366 |
}
|
|
|
367 |
params->open_code = code;
|
|
|
368 |
gx_semaphore_signal(params->open_semaphore);
|
|
|
369 |
if (code < 0)
|
|
|
370 |
return code;
|
|
|
371 |
|
|
|
372 |
/* fake open, since not called by gs_opendevice */
|
|
|
373 |
prdev->is_open = true;
|
|
|
374 |
|
|
|
375 |
/* Successful open */
|
|
|
376 |
while ((entry = gx_page_queue_start_dequeue(prdev->page_queue))
|
|
|
377 |
&& entry->action != GX_PAGE_QUEUE_ACTION_TERMINATE) {
|
|
|
378 |
/* Force printer open again if it mysteriously closed. */
|
|
|
379 |
/* This shouldn't ever happen, but... */
|
|
|
380 |
if (!prdev->is_open) {
|
|
|
381 |
if (prdev->printer_procs.open_render_device ==
|
|
|
382 |
gx_default_open_render_device)
|
|
|
383 |
code = gdev_prn_async_render_open(prdev);
|
|
|
384 |
else
|
|
|
385 |
code = (*prdev->printer_procs.open_render_device) (prdev);
|
|
|
386 |
reinit_printer_into_renderer(prdev);
|
|
|
387 |
|
|
|
388 |
if (code >= 0) {
|
|
|
389 |
prdev->is_open = true;
|
|
|
390 |
gdev_prn_output_page((gx_device *) prdev, 0, true);
|
|
|
391 |
}
|
|
|
392 |
}
|
|
|
393 |
if (prdev->is_open) {
|
|
|
394 |
/* Force retrieved entry onto render device */
|
|
|
395 |
((gx_device_clist *) prdev)->common.page_info = entry->page_info;
|
|
|
396 |
|
|
|
397 |
/* Set up device geometry */
|
|
|
398 |
if (clist_setup_params((gx_device *) prdev) >= 0)
|
|
|
399 |
/* Go this again, since setup_params may have trashed it */
|
|
|
400 |
((gx_device_clist *) prdev)->common.page_info = entry->page_info;
|
|
|
401 |
|
|
|
402 |
/* Call appropriate renderer routine to deal w/buffer */
|
|
|
403 |
/* Ignore status, since we don't know how to deal w/errors! */
|
|
|
404 |
switch (entry->action) {
|
|
|
405 |
|
|
|
406 |
case GX_PAGE_QUEUE_ACTION_FULL_PAGE:
|
|
|
407 |
(*dev_proc(prdev, output_page))((gx_device *) prdev,
|
|
|
408 |
entry->num_copies, true);
|
|
|
409 |
break;
|
|
|
410 |
|
|
|
411 |
case GX_PAGE_QUEUE_ACTION_PARTIAL_PAGE:
|
|
|
412 |
case GX_PAGE_QUEUE_ACTION_COPY_PAGE:
|
|
|
413 |
(*dev_proc(prdev, output_page))((gx_device *) prdev,
|
|
|
414 |
entry->num_copies, false);
|
|
|
415 |
break;
|
|
|
416 |
}
|
|
|
417 |
/*
|
|
|
418 |
* gx_page_queue_finish_dequeue will close and free the band
|
|
|
419 |
* list files, so we don't need to call clist_close_output_file.
|
|
|
420 |
*/
|
|
|
421 |
}
|
|
|
422 |
/* Finalize dequeue & free retrieved queue entry */
|
|
|
423 |
gx_page_queue_finish_dequeue(entry);
|
|
|
424 |
}
|
|
|
425 |
|
|
|
426 |
/* Close device, but don't use default if user hasn't overriden. */
|
|
|
427 |
/* Ignore status, since returning bad status means open failed */
|
|
|
428 |
if (prdev->printer_procs.close_render_device ==
|
|
|
429 |
gx_default_close_render_device)
|
|
|
430 |
gdev_prn_async_render_close_device(prdev);
|
|
|
431 |
else
|
|
|
432 |
(*prdev->printer_procs.close_render_device)(prdev);
|
|
|
433 |
|
|
|
434 |
/* undo fake open, since not called by gs_closedevice */
|
|
|
435 |
prdev->is_open = false;
|
|
|
436 |
|
|
|
437 |
/* Now that device is closed, acknowledge gx_page_queue_terminate */
|
|
|
438 |
gx_page_queue_finish_dequeue(entry);
|
|
|
439 |
|
|
|
440 |
return 0;
|
|
|
441 |
}
|
|
|
442 |
|
|
|
443 |
/* ------ Get/put parameters ------ */
|
|
|
444 |
|
|
|
445 |
/* Put parameters. */
|
|
|
446 |
private int
|
|
|
447 |
gdev_prn_async_write_put_params(gx_device * pdev, gs_param_list * plist)
|
|
|
448 |
{
|
|
|
449 |
gx_device_clist_writer *const pclwdev =
|
|
|
450 |
&((gx_device_clist *) pdev)->writer;
|
|
|
451 |
gx_device_printer *const pwdev = (gx_device_printer *) pdev;
|
|
|
452 |
gdev_prn_space_params save_sp = pwdev->space_params;
|
|
|
453 |
int save_height = pwdev->height;
|
|
|
454 |
int save_width = pwdev->width;
|
|
|
455 |
int code, ecode;
|
|
|
456 |
|
|
|
457 |
if (!pwdev->is_open)
|
|
|
458 |
return (*pwdev->orig_procs.put_params) (pdev, plist);
|
|
|
459 |
|
|
|
460 |
/*
|
|
|
461 |
* First, cascade to real device's put_params.
|
|
|
462 |
* If that put_params made any changes that require re-opening
|
|
|
463 |
* the device, just flush the page; the parameter block at the
|
|
|
464 |
* head of the next page will reflect the changes just made.
|
|
|
465 |
* If the put_params requires no re-open, just slip it into the
|
|
|
466 |
* stream in the command buffer. This way, the
|
|
|
467 |
* writer device should parallel the renderer status at the same point
|
|
|
468 |
* in their respective executions.
|
|
|
469 |
*
|
|
|
470 |
* NB. that all this works only because we take the position that
|
|
|
471 |
* put_params can make no change that actually affects hardware's state
|
|
|
472 |
* before the final output_page on the RASTERIZER.
|
|
|
473 |
*/
|
|
|
474 |
/* Call original procedure, but "closed" to prevent closing device */
|
|
|
475 |
pwdev->is_open = false; /* prevent put_params from closing device */
|
|
|
476 |
code = (*pwdev->orig_procs.put_params) (pdev, plist);
|
|
|
477 |
pwdev->is_open = true;
|
|
|
478 |
pwdev->OpenOutputFile = 0; /* This is wrong, because it causes the same thing in the renderer */
|
|
|
479 |
|
|
|
480 |
/* Flush device or emit to command list, depending if device changed geometry */
|
|
|
481 |
if (memcmp(&pwdev->space_params, &save_sp, sizeof(save_sp)) != 0 ||
|
|
|
482 |
pwdev->width != save_width || pwdev->height != save_height
|
|
|
483 |
) {
|
|
|
484 |
int pageq_remaining;
|
|
|
485 |
int new_width = pwdev->width;
|
|
|
486 |
int new_height = pwdev->height;
|
|
|
487 |
gdev_prn_space_params new_sp = pwdev->space_params;
|
|
|
488 |
|
|
|
489 |
/* Need to start a new page, reallocate clist memory */
|
|
|
490 |
pwdev->width = save_width;
|
|
|
491 |
pwdev->height = save_height;
|
|
|
492 |
pwdev->space_params = save_sp;
|
|
|
493 |
|
|
|
494 |
/* First, get rid of any pending partial pages */
|
|
|
495 |
code = flush_page(pwdev, false);
|
|
|
496 |
|
|
|
497 |
/* Free and reallocate the printer memory. */
|
|
|
498 |
pageq_remaining = 1; /* assume there are pages left in queue */
|
|
|
499 |
do {
|
|
|
500 |
ecode =
|
|
|
501 |
gdev_prn_reallocate_memory(pdev,
|
|
|
502 |
&new_sp, new_width, new_height);
|
|
|
503 |
if (ecode >= 0)
|
|
|
504 |
break; /* managed to recover enough memory */
|
|
|
505 |
if (!pdev->is_open) {
|
|
|
506 |
/* Disaster! Device was forced closed, which async drivers */
|
|
|
507 |
/* aren't suppsed to do. */
|
|
|
508 |
gdev_prn_async_write_close_device(pdev);
|
|
|
509 |
return ecode; /* caller 'spozed to know could be closed now */
|
|
|
510 |
}
|
|
|
511 |
pclwdev->error_is_retryable = (ecode == gs_error_VMerror);
|
|
|
512 |
}
|
|
|
513 |
while (pageq_remaining >= 1 &&
|
|
|
514 |
(pageq_remaining = ecode =
|
|
|
515 |
clist_VMerror_recover(pclwdev, ecode)) >= 0);
|
|
|
516 |
if (ecode < 0) {
|
|
|
517 |
gdev_prn_free_memory(pdev);
|
|
|
518 |
pclwdev->is_open = false;
|
|
|
519 |
code = ecode;
|
|
|
520 |
}
|
|
|
521 |
} else if (code >= 0) {
|
|
|
522 |
do
|
|
|
523 |
if ((ecode = cmd_put_params(pclwdev, plist)) >= 0)
|
|
|
524 |
break;
|
|
|
525 |
while ((ecode = clist_VMerror_recover(pclwdev, ecode)) >= 0);
|
|
|
526 |
if (ecode < 0 && pclwdev->error_is_retryable &&
|
|
|
527 |
pclwdev->driver_call_nesting == 0
|
|
|
528 |
)
|
|
|
529 |
ecode = clist_VMerror_recover_flush(pclwdev, ecode);
|
|
|
530 |
if (ecode < 0)
|
|
|
531 |
code = ecode;
|
|
|
532 |
}
|
|
|
533 |
/* Reset fields that got trashed by gdev_prn_put_params and/or gdev_prn_open */
|
|
|
534 |
reinit_printer_into_printera(pwdev);
|
|
|
535 |
|
|
|
536 |
return code;
|
|
|
537 |
}
|
|
|
538 |
|
|
|
539 |
/* Get hardware-detected params. Drain page queue, then call renderer version */
|
|
|
540 |
private int
|
|
|
541 |
gdev_prn_async_write_get_hardware_params(gx_device * pdev, gs_param_list * plist)
|
|
|
542 |
{
|
|
|
543 |
gx_device_printer *const pwdev = (gx_device_printer *) pdev;
|
|
|
544 |
gx_device_printer *const prdev = pwdev->async_renderer;
|
|
|
545 |
|
|
|
546 |
if (!pwdev->is_open || !prdev)
|
|
|
547 |
/* if not open, just use device's get hw params */
|
|
|
548 |
return (dev_proc(pwdev, get_hardware_params))(pdev, plist);
|
|
|
549 |
else {
|
|
|
550 |
/* wait for empty pipeline */
|
|
|
551 |
gx_page_queue_wait_until_empty(pwdev->page_queue);
|
|
|
552 |
|
|
|
553 |
/* get reader's h/w params, now that writer & reader are sync'ed */
|
|
|
554 |
return (dev_proc(prdev, get_hardware_params))
|
|
|
555 |
((gx_device *) prdev, plist);
|
|
|
556 |
}
|
|
|
557 |
}
|
|
|
558 |
|
|
|
559 |
/* Put parameters on RENDERER. */
|
|
|
560 |
private int /* returns -ve err code only if FATAL error (can't keep rendering) */
|
|
|
561 |
gdev_prn_async_render_put_params(gx_device * pdev, gs_param_list * plist)
|
|
|
562 |
{
|
|
|
563 |
gx_device_printer *const prdev = (gx_device_printer *) pdev;
|
|
|
564 |
bool save_is_open = prdev->is_open;
|
|
|
565 |
|
|
|
566 |
/* put_parms from clist are guaranteed to never re-init device */
|
|
|
567 |
/* They're also pretty much guaranteed to always succeed */
|
|
|
568 |
(*prdev->orig_procs.put_params) (pdev, plist);
|
|
|
569 |
|
|
|
570 |
/* If device closed itself, try to open & clear it */
|
|
|
571 |
if (!prdev->is_open && save_is_open) {
|
|
|
572 |
int code;
|
|
|
573 |
|
|
|
574 |
if (prdev->printer_procs.open_render_device ==
|
|
|
575 |
gx_default_open_render_device)
|
|
|
576 |
code = gdev_prn_async_render_open(prdev);
|
|
|
577 |
else
|
|
|
578 |
code = (*prdev->printer_procs.open_render_device) (prdev);
|
|
|
579 |
reinit_printer_into_renderer(prdev);
|
|
|
580 |
if (code >= 0)
|
|
|
581 |
/****** CLEAR PAGE SOMEHOW ******/;
|
|
|
582 |
else
|
|
|
583 |
return code; /* this'll cause clist to stop processing this band! */
|
|
|
584 |
}
|
|
|
585 |
return 0; /* return this unless FATAL status */
|
|
|
586 |
}
|
|
|
587 |
|
|
|
588 |
|
|
|
589 |
/* ------ Others ------ */
|
|
|
590 |
|
|
|
591 |
/* Output page causes file to get added to page queue for later rasterizing */
|
|
|
592 |
private int
|
|
|
593 |
gdev_prn_async_write_output_page(gx_device * pdev, int num_copies, int flush)
|
|
|
594 |
{
|
|
|
595 |
gx_device_printer *const pwdev = (gx_device_printer *) pdev;
|
|
|
596 |
gx_device_clist_writer *const pcwdev =
|
|
|
597 |
&((gx_device_clist *) pdev)->writer;
|
|
|
598 |
int flush_code;
|
|
|
599 |
int add_code;
|
|
|
600 |
int open_code;
|
|
|
601 |
int one_last_time = 1;
|
|
|
602 |
|
|
|
603 |
/* do NOT close files before sending to page queue */
|
|
|
604 |
flush_code = clist_end_page(pcwdev);
|
|
|
605 |
add_code = gx_page_queue_add_page(pwdev->page_queue,
|
|
|
606 |
(flush ? GX_PAGE_QUEUE_ACTION_FULL_PAGE :
|
|
|
607 |
GX_PAGE_QUEUE_ACTION_COPY_PAGE),
|
|
|
608 |
&pcwdev->page_info, num_copies);
|
|
|
609 |
if (flush && (flush_code >= 0) && (add_code >= 0)) {
|
|
|
610 |
/* This page is finished */
|
|
|
611 |
gx_finish_output_page(pdev, num_copies, flush);
|
|
|
612 |
}
|
|
|
613 |
|
|
|
614 |
/* Open new band files to take the place of ones added to page queue */
|
|
|
615 |
while ((open_code = (*gs_clist_device_procs.open_device)
|
|
|
616 |
((gx_device *) pdev)) == gs_error_VMerror) {
|
|
|
617 |
/* Open failed, try after a page gets rendered */
|
|
|
618 |
if (!gx_page_queue_wait_one_page(pwdev->page_queue)
|
|
|
619 |
&& one_last_time-- <= 0)
|
|
|
620 |
break;
|
|
|
621 |
}
|
|
|
622 |
|
|
|
623 |
return
|
|
|
624 |
(flush_code < 0 ? flush_code : open_code < 0 ? open_code :
|
|
|
625 |
add_code < 0 ? add_code : 0);
|
|
|
626 |
}
|
|
|
627 |
|
|
|
628 |
/* Free bandlist memory waits until the rasterizer runs enough to free some mem */
|
|
|
629 |
private int /* -ve err, 0 if no pages remain to rasterize, 1 if more pages to go */
|
|
|
630 |
gdev_prn_async_write_free_up_bandlist_memory(gx_device * pdev, bool flush_current)
|
|
|
631 |
{
|
|
|
632 |
gx_device_printer *const pwdev = (gx_device_printer *) pdev;
|
|
|
633 |
|
|
|
634 |
if (flush_current) {
|
|
|
635 |
int code = flush_page(pwdev, true);
|
|
|
636 |
|
|
|
637 |
if (code < 0)
|
|
|
638 |
return code;
|
|
|
639 |
}
|
|
|
640 |
return gx_page_queue_wait_one_page(pwdev->page_queue);
|
|
|
641 |
}
|
|
|
642 |
|
|
|
643 |
/* -------- Utility Routines --------- */
|
|
|
644 |
|
|
|
645 |
/* Flush out any partial pages accumulated in device */
|
|
|
646 |
/* LEAVE DEVICE in a state where it must be re-opened/re-init'd */
|
|
|
647 |
private int /* ret 0 ok no flush, -ve error code */
|
|
|
648 |
flush_page(
|
|
|
649 |
gx_device_printer * pwdev, /* async writer device to flush */
|
|
|
650 |
bool partial /* true if only partial page */
|
|
|
651 |
)
|
|
|
652 |
{
|
|
|
653 |
gx_device_clist *const pcldev = (gx_device_clist *) pwdev;
|
|
|
654 |
gx_device_clist_writer *const pcwdev = &pcldev->writer;
|
|
|
655 |
int flush_code = 0;
|
|
|
656 |
int add_code = 0;
|
|
|
657 |
|
|
|
658 |
/* do NOT close files before sending to page queue */
|
|
|
659 |
flush_code = clist_end_page(pcwdev);
|
|
|
660 |
add_code = gx_page_queue_add_page(pwdev->page_queue,
|
|
|
661 |
(partial ? GX_PAGE_QUEUE_ACTION_PARTIAL_PAGE :
|
|
|
662 |
GX_PAGE_QUEUE_ACTION_FULL_PAGE),
|
|
|
663 |
&pcwdev->page_info, 0);
|
|
|
664 |
|
|
|
665 |
/* Device no longer has BANDFILES, so it must be re-init'd by caller */
|
|
|
666 |
pcwdev->page_info.bfile = pcwdev->page_info.cfile = 0;
|
|
|
667 |
|
|
|
668 |
/* return the worst of the status. */
|
|
|
669 |
if (flush_code < 0)
|
|
|
670 |
return flush_code;
|
|
|
671 |
return add_code;
|
|
|
672 |
}
|
|
|
673 |
|
|
|
674 |
/* Flush any pending partial pages, re-open device */
|
|
|
675 |
private int
|
|
|
676 |
reopen_clist_after_flush(
|
|
|
677 |
gx_device_printer * pwdev /* async writer device to flush */
|
|
|
678 |
)
|
|
|
679 |
{
|
|
|
680 |
int open_code;
|
|
|
681 |
int one_last_time = 1;
|
|
|
682 |
|
|
|
683 |
/* Open new band files to take the place of ones added to page queue */
|
|
|
684 |
while ((open_code = (*gs_clist_device_procs.open_device)
|
|
|
685 |
((gx_device *) pwdev)) == gs_error_VMerror) {
|
|
|
686 |
/* Open failed, try after a page gets rendered */
|
|
|
687 |
if (!gx_page_queue_wait_one_page(pwdev->page_queue)
|
|
|
688 |
&& one_last_time-- <= 0)
|
|
|
689 |
break;
|
|
|
690 |
}
|
|
|
691 |
return open_code;
|
|
|
692 |
}
|
|
|
693 |
|
|
|
694 |
/*
|
|
|
695 |
* The bandlist does allocations on the writer's thread & deallocations on
|
|
|
696 |
* the reader's thread, so it needs to have mutual exclusion from itself, as
|
|
|
697 |
* well as from other memory allocators since the reader can run at the same
|
|
|
698 |
* time as the interpreter. The bandlist allocator therefore consists of
|
|
|
699 |
* a monitor-locking wrapper around either a direct heap allocator or (for
|
|
|
700 |
* testing) a fixed-limit allocator.
|
|
|
701 |
*/
|
|
|
702 |
|
|
|
703 |
/* Create a bandlist allocator. */
|
|
|
704 |
private int
|
|
|
705 |
alloc_bandlist_memory(gs_memory_t ** final_allocator,
|
|
|
706 |
gs_memory_t * base_allocator)
|
|
|
707 |
{
|
|
|
708 |
gs_memory_t *data_allocator = 0;
|
|
|
709 |
gs_memory_locked_t *locked_allocator = 0;
|
|
|
710 |
int code = 0;
|
|
|
711 |
|
|
|
712 |
#if defined(DEBUG) && defined(DebugBandlistMemorySize)
|
|
|
713 |
code = alloc_render_memory(&data_allocator, base_allocator,
|
|
|
714 |
DebugBandlistMemorySize);
|
|
|
715 |
if (code < 0)
|
|
|
716 |
return code;
|
|
|
717 |
#else
|
|
|
718 |
data_allocator = (gs_memory_t *)gs_malloc_memory_init();
|
|
|
719 |
if (!data_allocator)
|
|
|
720 |
return_error(gs_error_VMerror);
|
|
|
721 |
#endif
|
|
|
722 |
locked_allocator = (gs_memory_locked_t *)
|
|
|
723 |
gs_alloc_bytes_immovable(data_allocator, sizeof(gs_memory_locked_t),
|
|
|
724 |
"alloc_bandlist_memory(locked allocator)");
|
|
|
725 |
if (!locked_allocator)
|
|
|
726 |
goto alloc_err;
|
|
|
727 |
code = gs_memory_locked_init(locked_allocator, data_allocator);
|
|
|
728 |
if (code < 0)
|
|
|
729 |
goto alloc_err;
|
|
|
730 |
*final_allocator = (gs_memory_t *)locked_allocator;
|
|
|
731 |
return 0;
|
|
|
732 |
alloc_err:
|
|
|
733 |
if (locked_allocator)
|
|
|
734 |
free_bandlist_memory((gs_memory_t *)locked_allocator);
|
|
|
735 |
else if (data_allocator)
|
|
|
736 |
gs_memory_free_all(data_allocator, FREE_ALL_EVERYTHING,
|
|
|
737 |
"alloc_bandlist_memory(data allocator)");
|
|
|
738 |
return (code < 0 ? code : gs_note_error(gs_error_VMerror));
|
|
|
739 |
}
|
|
|
740 |
|
|
|
741 |
/* Free a bandlist allocator. */
|
|
|
742 |
private void
|
|
|
743 |
free_bandlist_memory(gs_memory_t *bandlist_allocator)
|
|
|
744 |
{
|
|
|
745 |
gs_memory_locked_t *const lmem = (gs_memory_locked_t *)bandlist_allocator;
|
|
|
746 |
gs_memory_t *data_mem = gs_memory_locked_target(lmem);
|
|
|
747 |
|
|
|
748 |
gs_memory_free_all(bandlist_allocator,
|
|
|
749 |
FREE_ALL_STRUCTURES | FREE_ALL_ALLOCATOR,
|
|
|
750 |
"free_bandlist_memory(locked allocator)");
|
|
|
751 |
if (data_mem)
|
|
|
752 |
gs_memory_free_all(data_mem, FREE_ALL_EVERYTHING,
|
|
|
753 |
"free_bandlist_memory(data allocator)");
|
|
|
754 |
}
|
|
|
755 |
|
|
|
756 |
/* Create an allocator with a fixed memory limit. */
|
|
|
757 |
private int
|
|
|
758 |
alloc_render_memory(gs_memory_t **final_allocator,
|
|
|
759 |
gs_memory_t *base_allocator, long space)
|
|
|
760 |
{
|
|
|
761 |
gs_ref_memory_t *rmem =
|
|
|
762 |
ialloc_alloc_state((gs_memory_t *)base_allocator, space);
|
|
|
763 |
vm_spaces spaces;
|
|
|
764 |
int i, code;
|
|
|
765 |
|
|
|
766 |
if (rmem == 0)
|
|
|
767 |
return_error(gs_error_VMerror);
|
|
|
768 |
code = ialloc_add_chunk(rmem, space, "alloc_render_memory");
|
|
|
769 |
if (code < 0) {
|
|
|
770 |
gs_memory_free_all((gs_memory_t *)rmem, FREE_ALL_EVERYTHING,
|
|
|
771 |
"alloc_render_memory");
|
|
|
772 |
return code;
|
|
|
773 |
}
|
|
|
774 |
*final_allocator = (gs_memory_t *)rmem;
|
|
|
775 |
|
|
|
776 |
/* Call the reclaim procedure to delete the string marking tables */
|
|
|
777 |
/* Only need this once since no other chunks will ever exist */
|
|
|
778 |
|
|
|
779 |
for ( i = 0; i < countof(spaces_indexed); ++i )
|
|
|
780 |
spaces_indexed[i] = 0;
|
|
|
781 |
space_local = space_global = (gs_ref_memory_t *)rmem;
|
|
|
782 |
spaces.vm_reclaim = gs_nogc_reclaim; /* no real GC on this chunk */
|
|
|
783 |
GS_RECLAIM(&spaces, false);
|
|
|
784 |
|
|
|
785 |
return 0;
|
|
|
786 |
}
|
|
|
787 |
|
|
|
788 |
/* Free an allocator with a fixed memory limit. */
|
|
|
789 |
private void
|
|
|
790 |
free_render_memory(gs_memory_t *render_allocator)
|
|
|
791 |
{
|
|
|
792 |
if (render_allocator)
|
|
|
793 |
gs_memory_free_all(render_allocator, FREE_ALL_EVERYTHING,
|
|
|
794 |
"free_render_memory");
|
|
|
795 |
}
|