Exemple #1
0
static void r300_emit_query_end_frag_pipes(struct r300_context *r300,
                                           struct r300_query *query)
{
    struct r300_capabilities* caps = &r300->screen->caps;
    uint32_t gb_pipes = r300->screen->info.r300_num_gb_pipes;
    CS_LOCALS(r300);

    assert(gb_pipes);

    BEGIN_CS(6 * gb_pipes + 2);
    /* I'm not so sure I like this switch, but it's hard to be elegant
     * when there's so many special cases...
     *
     * So here's the basic idea. For each pipe, enable writes to it only,
     * then put out the relocation for ZPASS_ADDR, taking into account a
     * 4-byte offset for each pipe. RV380 and older are special; they have
     * only two pipes, and the second pipe's enable is on bit 3, not bit 1,
     * so there's a chipset cap for that. */
    switch (gb_pipes) {
        case 4:
            /* pipe 3 only */
            OUT_CS_REG(R300_SU_REG_DEST, 1 << 3);
            OUT_CS_REG(R300_ZB_ZPASS_ADDR, (query->num_results + 3) * 4);
            OUT_CS_RELOC(r300->query_current);
        case 3:
            /* pipe 2 only */
            OUT_CS_REG(R300_SU_REG_DEST, 1 << 2);
            OUT_CS_REG(R300_ZB_ZPASS_ADDR, (query->num_results + 2) * 4);
            OUT_CS_RELOC(r300->query_current);
        case 2:
            /* pipe 1 only */
            /* As mentioned above, accommodate RV380 and older. */
            OUT_CS_REG(R300_SU_REG_DEST,
                    1 << (caps->high_second_pipe ? 3 : 1));
            OUT_CS_REG(R300_ZB_ZPASS_ADDR, (query->num_results + 1) * 4);
            OUT_CS_RELOC(r300->query_current);
        case 1:
            /* pipe 0 only */
            OUT_CS_REG(R300_SU_REG_DEST, 1 << 0);
            OUT_CS_REG(R300_ZB_ZPASS_ADDR, (query->num_results + 0) * 4);
            OUT_CS_RELOC(r300->query_current);
            break;
        default:
            fprintf(stderr, "r300: Implementation error: Chipset reports %d"
                    " pixel pipes!\n", gb_pipes);
            abort();
    }

    /* And, finally, reset it to normal... */
    OUT_CS_REG(R300_SU_REG_DEST, 0xF);
    END_CS;
}
Exemple #2
0
static void rv530_emit_query_end_double_z(struct r300_context *r300,
                                          struct r300_query *query)
{
    CS_LOCALS(r300);

    BEGIN_CS(14);
    OUT_CS_REG(RV530_FG_ZBREG_DEST, RV530_FG_ZBREG_DEST_PIPE_SELECT_0);
    OUT_CS_REG(R300_ZB_ZPASS_ADDR, (query->num_results + 0) * 4);
    OUT_CS_RELOC(r300->query_current);
    OUT_CS_REG(RV530_FG_ZBREG_DEST, RV530_FG_ZBREG_DEST_PIPE_SELECT_1);
    OUT_CS_REG(R300_ZB_ZPASS_ADDR, (query->num_results + 1) * 4);
    OUT_CS_RELOC(r300->query_current);
    OUT_CS_REG(RV530_FG_ZBREG_DEST, RV530_FG_ZBREG_DEST_PIPE_SELECT_ALL);
    END_CS;
}
Exemple #3
0
static void r300_end_query(struct pipe_context* pipe,
                           struct pipe_query* query)
{
    struct r300_context* r300 = r300_context(pipe);
    struct r300_query* q = (struct r300_query*)query;
    CS_LOCALS(r300);

    BEGIN_CS(4);
    OUT_CS_REG_SEQ(R300_ZB_ZPASS_ADDR, 1);
    OUT_CS_RELOC(q->buf, 0, 0, RADEON_GEM_DOMAIN_GTT, 0);
    END_CS;
}
static void r300_emit_draw_elements(struct r300_context *r300,
                                    struct pipe_buffer* indexBuffer,
                                    unsigned indexSize,
                                    unsigned minIndex,
                                    unsigned maxIndex,
                                    unsigned mode,
                                    unsigned start,
                                    unsigned count)
{
    uint32_t count_dwords;
    uint32_t offset_dwords = indexSize * start / sizeof(uint32_t);
    CS_LOCALS(r300);

    /* XXX most of these are stupid */
    assert(indexSize == 4 || indexSize == 2);
    assert((start * indexSize)  % 4 == 0);
    assert(offset_dwords == 0);

    BEGIN_CS(10);
    OUT_CS_REG(R300_VAP_VF_MAX_VTX_INDX, maxIndex);
    OUT_CS_PKT3(R300_PACKET3_3D_DRAW_INDX_2, 0);
    if (indexSize == 4) {
        count_dwords = count + start;
        OUT_CS(R300_VAP_VF_CNTL__PRIM_WALK_INDICES | (count << 16) |
               R300_VAP_VF_CNTL__INDEX_SIZE_32bit |
               r300_translate_primitive(mode));
    } else {
        count_dwords = (count + start + 1) / 2;
        OUT_CS(R300_VAP_VF_CNTL__PRIM_WALK_INDICES | (count << 16) |
               r300_translate_primitive(mode));
    }

    /* INDX_BUFFER is a truly special packet3.
     * Unlike most other packet3, where the offset is after the count,
     * the order is reversed, so the relocation ends up carrying the
     * size of the indexbuf instead of the offset.
     *
     * XXX Fix offset
     */
    OUT_CS_PKT3(R300_PACKET3_INDX_BUFFER, 2);
    OUT_CS(R300_INDX_BUFFER_ONE_REG_WR | (R300_VAP_PORT_IDX0 >> 2) |
           (0 << R300_INDX_BUFFER_SKIP_SHIFT));
    OUT_CS(offset_dwords);
    OUT_CS_RELOC(indexBuffer, count_dwords,
        RADEON_GEM_DOMAIN_GTT, 0, 0);

    END_CS;
}
Exemple #5
0
static void r300_render_draw_elements(struct vbuf_render* render,
                                      const ushort* indices,
                                      uint count)
{
    struct r300_render* r300render = r300_render(render);
    struct r300_context* r300 = r300render->r300;
    unsigned max_index = (r300->vbo->size - r300->draw_vbo_offset) /
                         (r300render->r300->vertex_info.size * 4) - 1;
    struct pipe_resource *index_buffer = NULL;
    unsigned index_buffer_offset;

    CS_LOCALS(r300);
    DBG(r300, DBG_DRAW, "r300: render_draw_elements (count: %d)\n", count);

    u_upload_data(r300->uploader, 0, count * 2, indices,
                  &index_buffer_offset, &index_buffer);
    if (!index_buffer) {
        return;
    }

    if (!r300_prepare_for_rendering(r300,
                                    PREP_EMIT_STATES |
                                    PREP_EMIT_VARRAYS_SWTCL | PREP_INDEXED,
                                    index_buffer, 12, 0, 0, -1)) {
        pipe_resource_reference(&index_buffer, NULL);
        return;
    }

    BEGIN_CS(12);
    OUT_CS_REG(R300_GA_COLOR_CONTROL,
               r300_provoking_vertex_fixes(r300, r300render->prim));
    OUT_CS_REG(R300_VAP_VF_MAX_VTX_INDX, max_index);

    OUT_CS_PKT3(R300_PACKET3_3D_DRAW_INDX_2, 0);
    OUT_CS(R300_VAP_VF_CNTL__PRIM_WALK_INDICES | (count << 16) |
           r300render->hwprim);

    OUT_CS_PKT3(R300_PACKET3_INDX_BUFFER, 2);
    OUT_CS(R300_INDX_BUFFER_ONE_REG_WR | (R300_VAP_PORT_IDX0 >> 2));
    OUT_CS(index_buffer_offset);
    OUT_CS((count + 1) / 2);
    OUT_CS_RELOC(r300_resource(index_buffer));
    END_CS;

    pipe_resource_reference(&index_buffer, NULL);
}
Exemple #6
0
void r300_emit_aa_state(struct r300_context *r300, unsigned size, void *state)
{
    struct r300_aa_state *aa = (struct r300_aa_state*)state;
    CS_LOCALS(r300);

    BEGIN_CS(size);
    OUT_CS_REG(R300_GB_AA_CONFIG, aa->aa_config);

    if (aa->dest) {
        OUT_CS_REG(R300_RB3D_AARESOLVE_OFFSET, aa->dest->offset);
        OUT_CS_RELOC(aa->dest);
        OUT_CS_REG(R300_RB3D_AARESOLVE_PITCH, aa->dest->pitch);
    }

    OUT_CS_REG(R300_RB3D_AARESOLVE_CTL, aa->aaresolve_ctl);
    END_CS;
}
Exemple #7
0
void r300_emit_aa_state(struct r300_context *r300, unsigned size, void *state)
{
    struct r300_aa_state *aa = (struct r300_aa_state*)state;
    CS_LOCALS(r300);

    BEGIN_CS(size);
    OUT_CS_REG(R300_GB_AA_CONFIG, aa->aa_config);

    if (aa->dest) {
        OUT_CS_REG_SEQ(R300_RB3D_AARESOLVE_OFFSET, 3);
        OUT_CS(aa->dest->offset);
        OUT_CS(aa->dest->pitch & R300_RB3D_AARESOLVE_PITCH_MASK);
        OUT_CS(R300_RB3D_AARESOLVE_CTL_AARESOLVE_MODE_RESOLVE |
               R300_RB3D_AARESOLVE_CTL_AARESOLVE_ALPHA_AVERAGE);
        OUT_CS_RELOC(aa->dest);
    } else {
        OUT_CS_REG(R300_RB3D_AARESOLVE_CTL, 0);
    }

    END_CS;
}
Exemple #8
0
void r300_emit_textures_state(struct r300_context *r300,
                              unsigned size, void *state)
{
    struct r300_textures_state *allstate = (struct r300_textures_state*)state;
    struct r300_texture_sampler_state *texstate;
    struct r300_resource *tex;
    unsigned i;
    boolean has_us_format = r300->screen->caps.has_us_format;
    CS_LOCALS(r300);

    BEGIN_CS(size);
    OUT_CS_REG(R300_TX_ENABLE, allstate->tx_enable);

    for (i = 0; i < allstate->count; i++) {
        if ((1 << i) & allstate->tx_enable) {
            texstate = &allstate->regs[i];
            tex = r300_resource(allstate->sampler_views[i]->base.texture);

            OUT_CS_REG(R300_TX_FILTER0_0 + (i * 4), texstate->filter0);
            OUT_CS_REG(R300_TX_FILTER1_0 + (i * 4), texstate->filter1);
            OUT_CS_REG(R300_TX_BORDER_COLOR_0 + (i * 4),
                       texstate->border_color);

            OUT_CS_REG(R300_TX_FORMAT0_0 + (i * 4), texstate->format.format0);
            OUT_CS_REG(R300_TX_FORMAT1_0 + (i * 4), texstate->format.format1);
            OUT_CS_REG(R300_TX_FORMAT2_0 + (i * 4), texstate->format.format2);

            OUT_CS_REG(R300_TX_OFFSET_0 + (i * 4), texstate->format.tile_config);
            OUT_CS_RELOC(tex);

            if (has_us_format) {
                OUT_CS_REG(R500_US_FORMAT0_0 + (i * 4),
                           texstate->format.us_format0);
            }
        }
    }
    END_CS;
}
Exemple #9
0
void r300_emit_vertex_arrays_swtcl(struct r300_context *r300, boolean indexed)
{
    CS_LOCALS(r300);

    DBG(r300, DBG_SWTCL, "r300: Preparing vertex buffer %p for render, "
            "vertex size %d\n", r300->vbo,
            r300->vertex_info.size);
    /* Set the pointer to our vertex buffer. The emitted values are this:
     * PACKET3 [3D_LOAD_VBPNTR]
     * COUNT   [1]
     * FORMAT  [size | stride << 8]
     * OFFSET  [offset into BO]
     * VBPNTR  [relocated BO]
     */
    BEGIN_CS(7);
    OUT_CS_PKT3(R300_PACKET3_3D_LOAD_VBPNTR, 3);
    OUT_CS(1 | (!indexed ? R300_VC_FORCE_PREFETCH : 0));
    OUT_CS(r300->vertex_info.size |
            (r300->vertex_info.size << 8));
    OUT_CS(r300->draw_vbo_offset);
    OUT_CS(0);
    OUT_CS_RELOC(r300_resource(r300->vbo));
    END_CS;
}
Exemple #10
0
static void r300_emit_draw_elements(struct r300_context *r300,
                                    struct pipe_resource* indexBuffer,
                                    unsigned indexSize,
                                    unsigned max_index,
                                    unsigned mode,
                                    unsigned start,
                                    unsigned count,
                                    uint16_t *imm_indices3)
{
    uint32_t count_dwords, offset_dwords;
    boolean alt_num_verts = count > 65535;
    CS_LOCALS(r300);

    if (count >= (1 << 24)) {
        fprintf(stderr, "r300: Got a huge number of vertices: %i, "
                "refusing to render (max_index: %i).\n", count, max_index);
        return;
    }

    DBG(r300, DBG_DRAW, "r300: Indexbuf of %u indices, max %u\n",
        count, max_index);

    r300_emit_draw_init(r300, mode, max_index);

    /* If start is odd, render the first triangle with indices embedded
     * in the command stream. This will increase start by 3 and make it
     * even. We can then proceed without a fallback. */
    if (indexSize == 2 && (start & 1) &&
        mode == PIPE_PRIM_TRIANGLES) {
        BEGIN_CS(4);
        OUT_CS_PKT3(R300_PACKET3_3D_DRAW_INDX_2, 2);
        OUT_CS(R300_VAP_VF_CNTL__PRIM_WALK_INDICES | (3 << 16) |
               R300_VAP_VF_CNTL__PRIM_TRIANGLES);
        OUT_CS(imm_indices3[1] << 16 | imm_indices3[0]);
        OUT_CS(imm_indices3[2]);
        END_CS;

        start += 3;
        count -= 3;
        if (!count)
           return;
    }

    offset_dwords = indexSize * start / sizeof(uint32_t);

    BEGIN_CS(8 + (alt_num_verts ? 2 : 0));
    if (alt_num_verts) {
        OUT_CS_REG(R500_VAP_ALT_NUM_VERTICES, count);
    }
    OUT_CS_PKT3(R300_PACKET3_3D_DRAW_INDX_2, 0);
    if (indexSize == 4) {
        count_dwords = count;
        OUT_CS(R300_VAP_VF_CNTL__PRIM_WALK_INDICES | (count << 16) |
               R300_VAP_VF_CNTL__INDEX_SIZE_32bit |
               r300_translate_primitive(mode) |
               (alt_num_verts ? R500_VAP_VF_CNTL__USE_ALT_NUM_VERTS : 0));
    } else {
        count_dwords = (count + 1) / 2;
        OUT_CS(R300_VAP_VF_CNTL__PRIM_WALK_INDICES | (count << 16) |
               r300_translate_primitive(mode) |
               (alt_num_verts ? R500_VAP_VF_CNTL__USE_ALT_NUM_VERTS : 0));
    }

    OUT_CS_PKT3(R300_PACKET3_INDX_BUFFER, 2);
    OUT_CS(R300_INDX_BUFFER_ONE_REG_WR | (R300_VAP_PORT_IDX0 >> 2) |
           (0 << R300_INDX_BUFFER_SKIP_SHIFT));
    OUT_CS(offset_dwords << 2);
    OUT_CS(count_dwords);
    OUT_CS_RELOC(r300_resource(indexBuffer));
    END_CS;
}
Exemple #11
0
void r300_emit_vertex_arrays(struct r300_context* r300, int offset,
                             boolean indexed, int instance_id)
{
    struct pipe_vertex_buffer *vbuf = r300->vertex_buffer;
    struct pipe_vertex_element *velem = r300->velems->velem;
    struct r300_resource *buf;
    int i;
    unsigned vertex_array_count = r300->velems->count;
    unsigned packet_size = (vertex_array_count * 3 + 1) / 2;
    struct pipe_vertex_buffer *vb1, *vb2;
    unsigned *hw_format_size = r300->velems->format_size;
    unsigned size1, size2, offset1, offset2, stride1, stride2;
    CS_LOCALS(r300);

    BEGIN_CS(2 + packet_size + vertex_array_count * 2);
    OUT_CS_PKT3(R300_PACKET3_3D_LOAD_VBPNTR, packet_size);
    OUT_CS(vertex_array_count | (!indexed ? R300_VC_FORCE_PREFETCH : 0));

    if (instance_id == -1) {
        /* Non-instanced arrays. This ignores instance_divisor and instance_id. */
        for (i = 0; i < vertex_array_count - 1; i += 2) {
            vb1 = &vbuf[velem[i].vertex_buffer_index];
            vb2 = &vbuf[velem[i+1].vertex_buffer_index];
            size1 = hw_format_size[i];
            size2 = hw_format_size[i+1];

            OUT_CS(R300_VBPNTR_SIZE0(size1) | R300_VBPNTR_STRIDE0(vb1->stride) |
                   R300_VBPNTR_SIZE1(size2) | R300_VBPNTR_STRIDE1(vb2->stride));
            OUT_CS(vb1->buffer_offset + velem[i].src_offset   + offset * vb1->stride);
            OUT_CS(vb2->buffer_offset + velem[i+1].src_offset + offset * vb2->stride);
        }

        if (vertex_array_count & 1) {
            vb1 = &vbuf[velem[i].vertex_buffer_index];
            size1 = hw_format_size[i];

            OUT_CS(R300_VBPNTR_SIZE0(size1) | R300_VBPNTR_STRIDE0(vb1->stride));
            OUT_CS(vb1->buffer_offset + velem[i].src_offset + offset * vb1->stride);
        }

        for (i = 0; i < vertex_array_count; i++) {
            buf = r300_resource(vbuf[velem[i].vertex_buffer_index].buffer);
            OUT_CS_RELOC(buf);
        }
    } else {
        /* Instanced arrays. */
        for (i = 0; i < vertex_array_count - 1; i += 2) {
            vb1 = &vbuf[velem[i].vertex_buffer_index];
            vb2 = &vbuf[velem[i+1].vertex_buffer_index];
            size1 = hw_format_size[i];
            size2 = hw_format_size[i+1];

            if (velem[i].instance_divisor) {
                stride1 = 0;
                offset1 = vb1->buffer_offset + velem[i].src_offset +
                          (instance_id / velem[i].instance_divisor) * vb1->stride;
            } else {
                stride1 = vb1->stride;
                offset1 = vb1->buffer_offset + velem[i].src_offset + offset * vb1->stride;
            }
            if (velem[i+1].instance_divisor) {
                stride2 = 0;
                offset2 = vb2->buffer_offset + velem[i+1].src_offset +
                          (instance_id / velem[i+1].instance_divisor) * vb2->stride;
            } else {
                stride2 = vb2->stride;
                offset2 = vb2->buffer_offset + velem[i+1].src_offset + offset * vb2->stride;
            }

            OUT_CS(R300_VBPNTR_SIZE0(size1) | R300_VBPNTR_STRIDE0(stride1) |
                   R300_VBPNTR_SIZE1(size2) | R300_VBPNTR_STRIDE1(stride2));
            OUT_CS(offset1);
            OUT_CS(offset2);
        }

        if (vertex_array_count & 1) {
            vb1 = &vbuf[velem[i].vertex_buffer_index];
            size1 = hw_format_size[i];

            if (velem[i].instance_divisor) {
                stride1 = 0;
                offset1 = vb1->buffer_offset + velem[i].src_offset +
                          (instance_id / velem[i].instance_divisor) * vb1->stride;
            } else {
                stride1 = vb1->stride;
                offset1 = vb1->buffer_offset + velem[i].src_offset + offset * vb1->stride;
            }

            OUT_CS(R300_VBPNTR_SIZE0(size1) | R300_VBPNTR_STRIDE0(stride1));
            OUT_CS(offset1);
        }

        for (i = 0; i < vertex_array_count; i++) {
            buf = r300_resource(vbuf[velem[i].vertex_buffer_index].buffer);
            OUT_CS_RELOC(buf);
        }
    }
    END_CS;
}
Exemple #12
0
void r300_emit_fb_state(struct r300_context* r300, unsigned size, void* state)
{
    struct pipe_framebuffer_state* fb = (struct pipe_framebuffer_state*)state;
    struct r300_surface* surf;
    unsigned i;
    uint32_t rb3d_cctl = 0;

    CS_LOCALS(r300);

    BEGIN_CS(size);

    if (r300->screen->caps.is_r500) {
        rb3d_cctl = R300_RB3D_CCTL_INDEPENDENT_COLORFORMAT_ENABLE_ENABLE;
    }
    /* NUM_MULTIWRITES replicates COLOR[0] to all colorbuffers. */
    if (fb->nr_cbufs && r300->fb_multiwrite) {
        rb3d_cctl |= R300_RB3D_CCTL_NUM_MULTIWRITES(fb->nr_cbufs);
    }
    if (r300->cmask_in_use) {
        rb3d_cctl |= R300_RB3D_CCTL_AA_COMPRESSION_ENABLE |
                     R300_RB3D_CCTL_CMASK_ENABLE;
    }

    OUT_CS_REG(R300_RB3D_CCTL, rb3d_cctl);

    /* Set up colorbuffers. */
    for (i = 0; i < fb->nr_cbufs; i++) {
        surf = r300_surface(r300_get_nonnull_cb(fb, i));

        OUT_CS_REG(R300_RB3D_COLOROFFSET0 + (4 * i), surf->offset);
        OUT_CS_RELOC(surf);

        OUT_CS_REG(R300_RB3D_COLORPITCH0 + (4 * i), surf->pitch);
        OUT_CS_RELOC(surf);

        if (r300->cmask_in_use && i == 0) {
            OUT_CS_REG(R300_RB3D_CMASK_OFFSET0, 0);
            OUT_CS_REG(R300_RB3D_CMASK_PITCH0, surf->pitch_cmask);
            OUT_CS_REG(R300_RB3D_COLOR_CLEAR_VALUE, r300->color_clear_value);
            if (r300->screen->caps.is_r500 && r300->screen->info.drm_minor >= 29) {
                OUT_CS_REG_SEQ(R500_RB3D_COLOR_CLEAR_VALUE_AR, 2);
                OUT_CS(r300->color_clear_value_ar);
                OUT_CS(r300->color_clear_value_gb);
            }
        }
    }

    /* Set up the ZB part of the CBZB clear. */
    if (r300->cbzb_clear) {
        surf = r300_surface(fb->cbufs[0]);

        OUT_CS_REG(R300_ZB_FORMAT, surf->cbzb_format);

        OUT_CS_REG(R300_ZB_DEPTHOFFSET, surf->cbzb_midpoint_offset);
        OUT_CS_RELOC(surf);

        OUT_CS_REG(R300_ZB_DEPTHPITCH, surf->cbzb_pitch);
        OUT_CS_RELOC(surf);

        DBG(r300, DBG_CBZB,
            "CBZB clearing cbuf %08x %08x\n", surf->cbzb_format,
            surf->cbzb_pitch);
    }
    /* Set up a zbuffer. */
    else if (fb->zsbuf) {
        surf = r300_surface(fb->zsbuf);

        OUT_CS_REG(R300_ZB_FORMAT, surf->format);

        OUT_CS_REG(R300_ZB_DEPTHOFFSET, surf->offset);
        OUT_CS_RELOC(surf);

        OUT_CS_REG(R300_ZB_DEPTHPITCH, surf->pitch);
        OUT_CS_RELOC(surf);

        if (r300->hyperz_enabled) {
            /* HiZ RAM. */
            OUT_CS_REG(R300_ZB_HIZ_OFFSET, 0);
            OUT_CS_REG(R300_ZB_HIZ_PITCH, surf->pitch_hiz);
            /* Z Mask RAM. (compressed zbuffer) */
            OUT_CS_REG(R300_ZB_ZMASK_OFFSET, 0);
            OUT_CS_REG(R300_ZB_ZMASK_PITCH, surf->pitch_zmask);
        }
    }

    END_CS;
}
Exemple #13
0
void r300_emit_fb_state(struct r300_context* r300, unsigned size, void* state)
{
    struct pipe_framebuffer_state* fb = (struct pipe_framebuffer_state*)state;
    struct r300_surface* surf;
    unsigned i;
    uint32_t rb3d_cctl = 0;

    CS_LOCALS(r300);

    BEGIN_CS(size);

    /* NUM_MULTIWRITES replicates COLOR[0] to all colorbuffers, which is not
     * what we usually want. */
    if (r300->screen->caps.is_r500) {
        rb3d_cctl = R300_RB3D_CCTL_INDEPENDENT_COLORFORMAT_ENABLE_ENABLE;
    }
    if (fb->nr_cbufs && r300->fb_multiwrite) {
        rb3d_cctl |= R300_RB3D_CCTL_NUM_MULTIWRITES(fb->nr_cbufs);
    }

    OUT_CS_REG(R300_RB3D_CCTL, rb3d_cctl);

    /* Set up colorbuffers. */
    for (i = 0; i < fb->nr_cbufs; i++) {
        surf = r300_surface(fb->cbufs[i]);

        OUT_CS_REG(R300_RB3D_COLOROFFSET0 + (4 * i), surf->offset);
        OUT_CS_RELOC(surf);

        OUT_CS_REG(R300_RB3D_COLORPITCH0 + (4 * i), surf->pitch);
        OUT_CS_RELOC(surf);
    }

    /* Set up the ZB part of the CBZB clear. */
    if (r300->cbzb_clear) {
        surf = r300_surface(fb->cbufs[0]);

        OUT_CS_REG(R300_ZB_FORMAT, surf->cbzb_format);

        OUT_CS_REG(R300_ZB_DEPTHOFFSET, surf->cbzb_midpoint_offset);
        OUT_CS_RELOC(surf);

        OUT_CS_REG(R300_ZB_DEPTHPITCH, surf->cbzb_pitch);
        OUT_CS_RELOC(surf);

        DBG(r300, DBG_CBZB,
            "CBZB clearing cbuf %08x %08x\n", surf->cbzb_format,
            surf->cbzb_pitch);
    }
    /* Set up a zbuffer. */
    else if (fb->zsbuf) {
        surf = r300_surface(fb->zsbuf);

        OUT_CS_REG(R300_ZB_FORMAT, surf->format);

        OUT_CS_REG(R300_ZB_DEPTHOFFSET, surf->offset);
        OUT_CS_RELOC(surf);

        OUT_CS_REG(R300_ZB_DEPTHPITCH, surf->pitch);
        OUT_CS_RELOC(surf);

        if (r300->hyperz_enabled) {
            /* HiZ RAM. */
            OUT_CS_REG(R300_ZB_HIZ_OFFSET, 0);
            OUT_CS_REG(R300_ZB_HIZ_PITCH, surf->pitch_hiz);
            /* Z Mask RAM. (compressed zbuffer) */
            OUT_CS_REG(R300_ZB_ZMASK_OFFSET, 0);
            OUT_CS_REG(R300_ZB_ZMASK_PITCH, surf->pitch_zmask);
        }
    }

    END_CS;
}