Ejemplo n.º 1
0
/* Note that we treat this as "texture" for RasterOp. */
static int
tile_pattern_clist(const tile_fill_state_t * ptfs,
                  int x, int y, int w, int h)
{
    gx_color_tile *ptile = ptfs->pdevc->colors.pattern.p_tile;
    gx_device_clist *cdev = ptile->cdev;
    gx_device_clist_reader *crdev = (gx_device_clist_reader *)cdev;
    gx_device *dev = ptfs->orig_dev;
    int code;

    crdev->offset_map = NULL;
    crdev->page_info.io_procs->rewind(crdev->page_info.bfile, false, NULL);
    crdev->page_info.io_procs->rewind(crdev->page_info.cfile, false, NULL);
     /* Check for and get ICC profile table */
    if (crdev->icc_table == NULL)
        code = clist_read_icctable(crdev);
    /* Also allocate the icc cache for the clist reader */
    if ( crdev->icc_cache_cl == NULL )
        crdev->icc_cache_cl = gsicc_cache_new(crdev->memory);
    if_debug0('L', "Pattern clist playback begin\n");
    code = clist_playback_file_bands(playback_action_render,
                crdev, &crdev->page_info, dev, 0, 0, ptfs->xoff - x, ptfs->yoff - y);
    if_debug0('L', "Pattern clist playback end\n");
    return code;
}
Ejemplo n.º 2
0
static int
gx_output_saved_page(gx_device_printer *pdev, gx_saved_page *page)
{
    int code;
    /* Note that banding_type is NOT a device parameter handled in the paramlist */
    gdev_banding_type save_banding_type = pdev->space_params.banding_type;
    gx_device_clist_reader *crdev = (gx_device_clist_reader *)pdev;

    pdev->space_params.banding_type = BandingAlways;

    if ((code = gx_saved_page_load(pdev, page)) < 0)
        goto out;

    /* don't want the band files touched after printing */
    crdev->do_not_open_or_close_bandfiles = true;

    /* load the color_usage_array */
    if ((code = clist_read_color_usage_array(crdev)) < 0)
        goto out;
    if ((code = clist_read_icctable(crdev)) < 0)
        goto out;
    code = (crdev->icc_cache_cl = gsicc_cache_new(crdev->memory)) == NULL ?
           gs_error_VMerror : code;
    if (code < 0)
        goto out;

    /* After setting params, make sure bg_printing is off */
    pdev->bg_print_requested = false;

    /* Note: we never flush pages allowing for re-printing from the list */
    /* data (files) will be deleted when the list is flushed or freed.   */
    code = (*dev_proc(pdev, output_page)) ((gx_device *) pdev,
               (pdev->IgnoreNumCopies || pdev->NumCopies_set <= 0) ? 1 : pdev->NumCopies, false);

    clist_free_icc_table(crdev->icc_table, crdev->memory);
    crdev->icc_table = NULL;
    rc_decrement(crdev->icc_cache_cl,"clist_finish_page");
    crdev->icc_cache_cl = NULL;

    /* Close the clist files */
    code = crdev->page_info.io_procs->fclose(crdev->page_info.cfile, crdev->page_info.cfname, false);
    if (code >= 0) {
        crdev->page_info.cfile = NULL;
        code = crdev->page_info.io_procs->fclose(crdev->page_info.bfile, crdev->page_info.bfname, false);
    }
    if (code < 0)
        goto out;
    crdev->page_info.bfile = NULL;

out:
    pdev->space_params.banding_type = save_banding_type;
    return code;
}
Ejemplo n.º 3
0
/* Exported for use by background printing.                             */
gx_device *
setup_device_and_mem_for_thread(gs_memory_t *chunk_base_mem, gx_device *dev, bool bg_print, gsicc_link_cache_t **cachep)
{
    int i, code;
    char fmode[4];
    gs_memory_t *thread_mem;
    gx_device_clist *cldev = (gx_device_clist *)dev;
    gx_device_printer *pdev = (gx_device_printer *)dev;
    gx_device_clist_common *cdev = (gx_device_clist_common *)cldev;
    gx_device *ndev;
    gx_device_clist *ncldev;
    gx_device_clist_common *ncdev;
    gx_device_printer *npdev;
    gx_device *protodev;
    gs_c_param_list paramlist;
    gs_devn_params *pclist_devn_params;

    /* Every thread will have a 'chunk allocator' to reduce the interaction
     * with the 'base' allocator which has 'mutex' (locking) protection.
     * This improves performance of the threads.
     */
    if ((code = gs_memory_chunk_wrap(&(thread_mem), chunk_base_mem )) < 0) {
        emprintf1(dev->memory, "chunk_wrap returned error code: %d\n", code);
        return NULL;
    }
    /* Find the prototype for this device (needed so we can copy from it) */
    for (i=0; (protodev = (gx_device *)gs_getdevice(i)) != NULL; i++)
        if (strcmp(protodev->dname, dev->dname) == 0)
            break;

    /* Clone the device from the prototype device */
    if (protodev == NULL ||
        (code = gs_copydevice((gx_device **) &ndev, protodev, thread_mem)) < 0) {
        gs_memory_chunk_release(thread_mem);
        return NULL;
    }
    ncldev = (gx_device_clist *)ndev;
    ncdev = (gx_device_clist_common *)ndev;
    npdev = (gx_device_printer *)ndev;
    gx_device_fill_in_procs(ndev);
    ((gx_device_printer *)ncdev)->buffer_memory =
        ncdev->memory =
            ncdev->bandlist_memory =
               thread_mem;
    ndev->PageCount = dev->PageCount;       /* copy to prevent mismatch error */
    npdev->file = pdev->file;               /* For background printing when doing N copies with %d */
    strcpy((npdev->fname), (pdev->fname));
    ndev->color_info = dev->color_info;     /* copy before putdeviceparams */
    ndev->pad = dev->pad;
    ndev->log2_align_mod = dev->log2_align_mod;
    ndev->is_planar = dev->is_planar;
#if CMM_THREAD_SAFE
        ndev->icc_struct = dev->icc_struct;  /* Set before put params */
        rc_increment(ndev->icc_struct);
#endif
    /* get the current device parameters to put into the cloned device */
    gs_c_param_list_write(&paramlist, dev->memory);
    if ((code = gs_getdeviceparams(dev, (gs_param_list *)&paramlist)) < 0) {
        emprintf1(dev->memory,
                  "Error getting device params, code=%d. Rendering threads not started.\n",
                  code);
        goto out_cleanup;
    }
    gs_c_param_list_read(&paramlist);
    if ((code = gs_putdeviceparams(ndev, (gs_param_list *)&paramlist)) < 0)
        goto out_cleanup;
    gs_c_param_list_release(&paramlist);

    /* In the case of a separation device, we need to make sure we get the
       devn params copied over */
    pclist_devn_params = dev_proc(dev, ret_devn_params)(dev);
    if (pclist_devn_params != NULL) {
        code = devn_copy_params(dev, ndev);
        if (code < 0) {
#ifdef DEBUG /* suppress a warning on a release build */
            gs_note_error(gs_error_VMerror);
#endif
            goto out_cleanup;
        }
    }
    /* Also make sure supports_devn is set correctly */
    ndev->icc_struct->supports_devn = cdev->icc_struct->supports_devn;
    ncdev->page_uses_transparency = cdev->page_uses_transparency;
    if_debug3m(gs_debug_flag_icc, cdev->memory,
               "[icc] MT clist device = 0x%p profile = 0x%p handle = 0x%p\n",
               ncdev,
               ncdev->icc_struct->device_profile[0],
               ncdev->icc_struct->device_profile[0]->profile_handle);
    /* If the device is_planar, then set the flag in the new_device and the procs */
    if ((ncdev->is_planar = cdev->is_planar))
        gdev_prn_set_procs_planar(ndev);

    /* gdev_prn_allocate_memory sets the clist for writing, creating new files.
     * We need  to unlink those files and open the main thread's files, then
     * reset the clist state for reading/rendering
     */
    if ((code = gdev_prn_allocate_memory(ndev, NULL, ndev->width, ndev->height)) < 0)
        goto out_cleanup;

    /* close and unlink the temp files just created */
    ncdev->page_info.io_procs->fclose(ncdev->page_info.cfile, ncdev->page_info.cfname, true);
    ncdev->page_info.io_procs->fclose(ncdev->page_info.bfile, ncdev->page_info.bfname, true);
    ncdev->page_info.cfile = ncdev->page_info.bfile = NULL;

    /* open the main thread's files for this thread */
    strcpy(fmode, "r");                 /* read access for threads */
    strncat(fmode, gp_fmode_binary_suffix, 1);
    if ((code=cdev->page_info.io_procs->fopen(cdev->page_info.cfname, fmode, &ncdev->page_info.cfile,
                        thread_mem, thread_mem, true)) < 0 ||
         (code=cdev->page_info.io_procs->fopen(cdev->page_info.bfname, fmode, &ncdev->page_info.bfile,
                        thread_mem, thread_mem, false)) < 0)
        goto out_cleanup;

    strcpy((ncdev->page_info.cfname), (cdev->page_info.cfname));
    strcpy((ncdev->page_info.bfname), (cdev->page_info.bfname));
    clist_render_init(ncldev);      /* Initialize clist device for reading */
    ncdev->page_info.bfile_end_pos = cdev->page_info.bfile_end_pos;

    /* The threads are maintained until clist_finish_page.  At which
       point, the threads are torn down, the master clist reader device
       is changed to writer, and the icc_table and the icc_cache_cl freed */
#if CMM_THREAD_SAFE
    /* safe to share the link cache */
    ncdev->icc_cache_cl = cdev->icc_cache_cl;
    rc_increment(cdev->icc_cache_cl, "setup_render_thread");
#else
    /* each thread needs its own link cache */
    if (cachep != NULL) {
        if (*cachep == NULL) {
            /* We don't have one cached that we can reuse, so make one. */
            if ((*cachep = gsicc_cache_new(thread_mem->thread_safe_memory)) == NULL)
                goto out_cleanup;
        }
        rc_increment(*cachep);
        ncdev->icc_cache_cl = *cachep;
    } else if ((ncdev->icc_cache_cl = gsicc_cache_new(thread_mem)) == NULL)
        goto out_cleanup;
#endif
    if (bg_print) {
        gx_device_clist_reader *ncrdev = (gx_device_clist_reader *)ncdev;

        if (cdev->icc_table != NULL) {
            /* This is a background printing thread, so it cannot share the icc_table  */
            /* since this probably was created with a GC'ed allocator and the bg_print */
            /* thread can't deal with the relocation. Free the cdev->icc_table and get */
            /* a new one from the clist.                                               */
            clist_free_icc_table(cdev->icc_table, cdev->memory);
            cdev->icc_table = NULL;
            if ((code = clist_read_icctable((gx_device_clist_reader *)ncdev)) < 0)
                goto out_cleanup;
        }
        /* Similarly for the color_usage_array, when the foreground device switches to */
        /* writer mode, the foreground's array will be freed.                          */
        if ((code = clist_read_color_usage_array(ncrdev)) < 0)
            goto out_cleanup;
    } else {
    /* Use the same profile table and color usage array in each thread */
        ncdev->icc_table = cdev->icc_table;		/* OK for multiple rendering threads */
        ((gx_device_clist_reader *)ncdev)->color_usage_array =
                ((gx_device_clist_reader *)cdev)->color_usage_array;
    }
    /* Needed for case when the target has cielab profile and pdf14 device
       has a RGB profile stored in the profile list of the clist */
    ncdev->trans_dev_icc_hash = cdev->trans_dev_icc_hash;

    /* success */
    return ndev;

out_cleanup:
    /* Close the file handles, but don't delete (unlink) the files */
    if (ncdev->page_info.bfile != NULL)
        ncdev->page_info.io_procs->fclose(ncdev->page_info.bfile, ncdev->page_info.bfname, false);
    if (ncdev->page_info.cfile != NULL)
        ncdev->page_info.io_procs->fclose(ncdev->page_info.cfile, ncdev->page_info.cfname, false);
    ncdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */

    if (ndev != NULL) {
        gdev_prn_free_memory(ndev);
        gs_free_object(thread_mem, ndev, "setup_device_and_mem_for_thread");
    }
    gs_memory_chunk_release(thread_mem);
    return NULL;
}
Ejemplo n.º 4
0
/* Set up and start the render threads */
static int
clist_setup_render_threads(gx_device *dev, int y)
{
    gx_device_printer *pdev = (gx_device_printer *)dev;
    gx_device_clist *cldev = (gx_device_clist *)dev;
    gx_device_clist_common *cdev = (gx_device_clist_common *)cldev;
    gx_device_clist_reader *crdev = &cldev->reader;
    gs_memory_t *mem = cdev->bandlist_memory;
    gs_memory_t *chunk_base_mem = mem->thread_safe_memory;
    gs_memory_status_t mem_status;
    gx_device *protodev;
    gs_c_param_list paramlist;
    int i, code, band;
    int band_count = cdev->nbands;
    char fmode[4];
    gs_devn_params *pclist_devn_params;
    crdev->num_render_threads = pdev->num_render_threads_requested;

    if(gs_debug[':'] != 0)
        dprintf1("%% %d rendering threads requested.\n", pdev->num_render_threads_requested);

    if (crdev->num_render_threads > band_count)
        crdev->num_render_threads = band_count; /* don't bother starting more threads than bands */

    /* Allocate and initialize an array of thread control structures */
    crdev->render_threads = (clist_render_thread_control_t *)
              gs_alloc_byte_array(mem, crdev->num_render_threads,
              sizeof(clist_render_thread_control_t), "clist_setup_render_threads" );
    /* fallback to non-threaded if allocation fails */
    if (crdev->render_threads == NULL) {
        emprintf(mem, " VMerror prevented threads from starting.\n");
        return_error(gs_error_VMerror);
    }

    memset(crdev->render_threads, 0, crdev->num_render_threads *
            sizeof(clist_render_thread_control_t));
    crdev->main_thread_data = cdev->data;               /* save data area */
    /* Based on the line number requested, decide the order of band rendering */
    /* Almost all devices go in increasing line order (except the bmp* devices ) */
    crdev->thread_lookahead_direction = (y < (cdev->height - 1)) ? 1 : -1;
    band = y / crdev->page_info.band_params.BandHeight;

    /* Close the files so we can open them in multiple threads */
    if ((code = cdev->page_info.io_procs->fclose(cdev->page_cfile, cdev->page_cfname, false)) < 0 ||
        (code = cdev->page_info.io_procs->fclose(cdev->page_bfile, cdev->page_bfname, false)) < 0) {
        gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads");
        crdev->render_threads = NULL;
        emprintf(mem, "Closing clist files prevented threads from starting.\n");
        return_error(gs_error_unknownerror); /* shouldn't happen */
    }
    cdev->page_cfile = cdev->page_bfile = NULL;
    strcpy(fmode, "r");                 /* read access for threads */
    strncat(fmode, gp_fmode_binary_suffix, 1);
    /* Find the prototype for this device (needed so we can copy from it) */
    for (i=0; (protodev = (gx_device *)gs_getdevice(i)) != NULL; i++)
        if (strcmp(protodev->dname, dev->dname) == 0)
            break;
    if (protodev == NULL) {
        emprintf(mem,
                 "Could not find prototype device. Rendering threads not started.\n");
        return gs_error_rangecheck;
    }

    gs_c_param_list_write(&paramlist, mem);
    if ((code = gs_getdeviceparams(dev, (gs_param_list *)&paramlist)) < 0) {
        emprintf1(mem,
                  "Error getting device params, code=%d. Rendering threads not started.\n",
                  code);
        return code;
    }

    /* If the 'mem' is not thread safe, we need to wrap it in a locking memory */
    gs_memory_status(chunk_base_mem, &mem_status);
    if (mem_status.is_thread_safe == false) {
            return_error(gs_error_VMerror);
    }

    /* Loop creating the devices and semaphores for each thread, then start them */
    for (i=0; (i < crdev->num_render_threads) && (band >= 0) && (band < band_count);
            i++, band += crdev->thread_lookahead_direction) {
        gx_device *ndev;
        gx_device_clist *ncldev;
        gx_device_clist_common *ncdev;
        clist_render_thread_control_t *thread = &(crdev->render_threads[i]);

        /* Every thread will have a 'chunk allocator' to reduce the interaction
         * with the 'base' allocator which has 'mutex' (locking) protection.
         * This improves performance of the threads.
         */
        if ((code = gs_memory_chunk_wrap(&(thread->memory), chunk_base_mem )) < 0) {
            emprintf1(mem, "chunk_wrap returned error code: %d\n", code);
            break;
        }

        thread->band = -1;              /* a value that won't match any valid band */
        if ((code = gs_copydevice((gx_device **) &ndev, protodev, thread->memory)) < 0) {
            code = 0;           /* even though we failed, no cleanup needed */
            break;
        }
        ncldev = (gx_device_clist *)ndev;
        ncdev = (gx_device_clist_common *)ndev;
        gx_device_fill_in_procs(ndev);
        ((gx_device_printer *)ncdev)->buffer_memory = ncdev->memory =
                ncdev->bandlist_memory = thread->memory;
        gs_c_param_list_read(&paramlist);
        ndev->PageCount = dev->PageCount;       /* copy to prevent mismatch error */
#if CMM_THREAD_SAFE
        ndev->icc_struct = dev->icc_struct;  /* Set before put params */
        rc_increment(ndev->icc_struct);
#endif
        if ((code = gs_putdeviceparams(ndev, (gs_param_list *)&paramlist)) < 0)
            break;
        /* In the case of a separation device, we need to make sure we get the
           devn params copied over */
        pclist_devn_params = dev_proc(dev, ret_devn_params)(dev);
        if (pclist_devn_params != NULL) {
            code = devn_copy_params(dev, (gx_device*) ncdev);
            if (code < 0) return_error(gs_error_VMerror);
        }
        ncdev->page_uses_transparency = cdev->page_uses_transparency;
        if_debug3('{',"[{]MT clist device = 0x%x profile = 0x%x handle = 0x%x\n", 
                  ncdev,
                  ncdev->icc_struct->device_profile[0],
                  ncdev->icc_struct->device_profile[0]->profile_handle);
        /* gdev_prn_allocate_memory sets the clist for writing, creating new files.
         * We need  to unlink those files and open the main thread's files, then
         * reset the clist state for reading/rendering
         */
        if ((code = gdev_prn_allocate_memory(ndev, NULL, ndev->width, ndev->height)) < 0)
            break;
        /* Needed for case when the target has cielab profile and pdf14 device
           has a RGB profile stored in the profile list of the clist */
        ncdev->trans_dev_icc_hash = cdev->trans_dev_icc_hash;
        thread->cdev = ndev;
        /* close and unlink the temp files just created */
        cdev->page_info.io_procs->fclose(ncdev->page_cfile, ncdev->page_cfname, true);
        cdev->page_info.io_procs->fclose(ncdev->page_bfile, ncdev->page_bfname, true);
        /* open the main thread's files for this thread */
        if ((code=cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &ncdev->page_cfile,
                            thread->memory, thread->memory, true)) < 0 ||
             (code=cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &ncdev->page_bfile,
                            thread->memory, thread->memory, false)) < 0)
            break;
        clist_render_init(ncldev);      /* Initialize clist device for reading */
        ncdev->page_bfile_end_pos = cdev->page_bfile_end_pos;
        /* Use the same link cache in each thread and the same profile table.
           The threads are maintained until clist_finish_page.  At which
           point, the threads are torn down and the master clist reader device
           is destroyed along with the icc_table and the icc_cache_cl */
#if CMM_THREAD_SAFE
        ncdev->icc_cache_cl = cdev->icc_cache_cl;
#else
        ncdev->icc_cache_cl = gsicc_cache_new(crdev->memory);
#endif
        ncdev->icc_table = cdev->icc_table;
        /* create the buf device for this thread, and allocate the semaphores */
        if ((code = gdev_create_buf_device(cdev->buf_procs.create_buf_device,
                                &(thread->bdev), cdev->target,
                                band*crdev->page_band_height, NULL,
                                thread->memory, clist_get_band_complexity(dev,y)) < 0))
            break;
        if ((thread->sema_this = gx_semaphore_alloc(thread->memory)) == NULL ||
            (thread->sema_group = gx_semaphore_alloc(thread->memory)) == NULL) {
            code = gs_error_VMerror;
            break;
        }
        /* Start thread 'i' to do band */
        if ((code = clist_start_render_thread(dev, i, band)) < 0)
            break;
    }
    gs_c_param_list_release(&paramlist);
    /* If the code < 0, the last thread creation failed -- clean it up */
    if (code < 0) {
        /* the following relies on 'free' ignoring NULL pointers */
        gx_semaphore_free(crdev->render_threads[i].sema_group);
        gx_semaphore_free(crdev->render_threads[i].sema_this);
        if (crdev->render_threads[i].bdev != NULL)
            cdev->buf_procs.destroy_buf_device(crdev->render_threads[i].bdev);
        if (crdev->render_threads[i].cdev != NULL) {
            gx_device_clist_common *thread_cdev = (gx_device_clist_common *)crdev->render_threads[i].cdev;

            /* Close the file handles, but don't delete (unlink) the files */
            thread_cdev->page_info.io_procs->fclose(thread_cdev->page_bfile, thread_cdev->page_bfname, false);
            thread_cdev->page_info.io_procs->fclose(thread_cdev->page_cfile, thread_cdev->page_cfname, false);
            thread_cdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */

            gdev_prn_free_memory((gx_device *)thread_cdev);
            gs_free_object(crdev->render_threads[i].memory, thread_cdev,
            "clist_setup_render_threads");
        }
        if (crdev->render_threads[i].memory != NULL)
            gs_memory_chunk_release(crdev->render_threads[i].memory);
    }
    /* If we weren't able to create at least one thread, punt   */
    /* Although a single thread isn't any more efficient, the   */
    /* machinery still works, so that's OK.                     */
    if (i == 0) {
        if (crdev->render_threads[0].memory != NULL) {
            gs_memory_chunk_release(crdev->render_threads[0].memory);
            /* free up the locking wrapper if we allocated one */
            if (chunk_base_mem != mem) {
                gs_memory_locked_release((gs_memory_locked_t *)chunk_base_mem);
                gs_free_object(mem, chunk_base_mem, "clist_setup_render_threads(locked allocator)");
            }
        }
        gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads");
        crdev->render_threads = NULL;
        /* restore the file pointers */
        if (cdev->page_cfile == NULL) {
            char fmode[4];

            strcpy(fmode, "a+");        /* file already exists and we want to re-use it */
            strncat(fmode, gp_fmode_binary_suffix, 1);
            cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &cdev->page_cfile,
                                mem, cdev->bandlist_memory, true);
            cdev->page_info.io_procs->fseek(cdev->page_cfile, 0, SEEK_SET, cdev->page_cfname);
            cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &cdev->page_bfile,
                                mem, cdev->bandlist_memory, false);
            cdev->page_info.io_procs->fseek(cdev->page_bfile, 0, SEEK_SET, cdev->page_bfname);
        }
        emprintf1(mem, "Rendering threads not started, code=%d.\n", code);
        return_error(code);
    }
    crdev->num_render_threads = i;
    crdev->curr_render_thread = 0;

    if(gs_debug[':'] != 0)
        dprintf1("%% Using %d rendering threads\n", i);

    return 0;
}