+ /* We must update constants even on "just" fragprog changes, because
+ * we don't check whether the current constant buffer matches the latest
+ * one bound to this fragment program.
+ * Doing such a check would likely be a pessimization.
+ */
+ if ((nvfx->hw_fragprog != fp) || (nvfx->dirty & (NVFX_NEW_FRAGPROG | NVFX_NEW_FRAGCONST))) {
+ int offset;
+ uint32_t* fpmap;
+
+update:
+ ++fp->bo_prog_idx;
+ if(fp->bo_prog_idx >= fp->progs_per_bo)
+ {
+ if(fp->fpbo && !nouveau_bo_busy(fp->fpbo->next->bo, NOUVEAU_BO_WR))
+ {
+ fp->fpbo = fp->fpbo->next;
+ }
+ else
+ {
+ struct nvfx_fragment_program_bo* fpbo = os_malloc_aligned(sizeof(struct nvfx_fragment_program) + (fp->prog_size + 8) * fp->progs_per_bo, 16);
+ uint8_t* map;
+ uint8_t* buf;
+
+ fpbo->slots = (unsigned char*)&fpbo->insn[(fp->prog_size) * fp->progs_per_bo];
+ memset(fpbo->slots, 0, 8 * fp->progs_per_bo);
+ if(fp->fpbo)
+ {
+ fpbo->next = fp->fpbo->next;
+ fp->fpbo->next = fpbo;
+ }
+ else
+ fpbo->next = fpbo;
+ fp->fpbo = fpbo;
+ fpbo->bo = 0;
+ fp->progs += fp->progs_per_bo;
+ fp->progs_left_with_obsolete_slot_assignments += fp->progs_per_bo;
+ nouveau_bo_new(nvfx->screen->base.device, NOUVEAU_BO_VRAM | NOUVEAU_BO_MAP, 64, fp->prog_size * fp->progs_per_bo, &fpbo->bo);
+ nouveau_bo_map(fpbo->bo, NOUVEAU_BO_NOSYNC);
+
+ map = fpbo->bo->map;
+ buf = (uint8_t*)fpbo->insn;
+ for(unsigned i = 0; i < fp->progs_per_bo; ++i)
+ {
+ memcpy(buf, fp->insn, fp->insn_len * 4);
+ nvfx_fp_memcpy(map, fp->insn, fp->insn_len * 4);
+ map += fp->prog_size;
+ buf += fp->prog_size;
+ }
+ }
+ fp->bo_prog_idx = 0;
+ }
+
+ offset = fp->bo_prog_idx * fp->prog_size;
+ fpmap = (uint32_t*)((char*)fp->fpbo->bo->map + offset);
+
+ if(nvfx->constbuf[PIPE_SHADER_FRAGMENT]) {
+ struct pipe_resource* constbuf = nvfx->constbuf[PIPE_SHADER_FRAGMENT];
+ uint32_t* map = (uint32_t*)nvfx_buffer(constbuf)->data;
+ uint32_t* fpmap = (uint32_t*)((char*)fp->fpbo->bo->map + offset);
+ uint32_t* buf = (uint32_t*)((char*)fp->fpbo->insn + offset);
+ int i;
+ for (i = 0; i < fp->nr_consts; ++i) {
+ unsigned off = fp->consts[i].offset;
+ unsigned idx = fp->consts[i].index * 4;
+
+ /* TODO: is checking a good idea? */
+ if(memcmp(&buf[off], &map[idx], 4 * sizeof(uint32_t))) {
+ memcpy(&buf[off], &map[idx], 4 * sizeof(uint32_t));
+ nvfx_fp_memcpy(&fpmap[off], &map[idx], 4 * sizeof(uint32_t));
+ }
+ }
+ }
+
+ /* we only do this if we aren't sure that all program versions have the
+ * current slot assignments, otherwise we just update constants for speed
+ */
+ if(fp->progs_left_with_obsolete_slot_assignments) {
+ unsigned char* fpbo_slots = &fp->fpbo->slots[fp->bo_prog_idx * 8];
+ /* also relocate sprite coord slot, if any */
+ for(unsigned i = 0; i <= fp->num_slots; ++i) {
+ unsigned value = fp->slot_to_fp_input[i];;
+ if(value != fpbo_slots[i]) {
+ unsigned* p;
+ unsigned* begin = (unsigned*)fp->slot_relocations[i].data;
+ unsigned* end = (unsigned*)((char*)fp->slot_relocations[i].data + fp->slot_relocations[i].size);
+ //printf("fp %p reloc slot %u/%u: %u -> %u\n", fp, i, fp->num_slots, fpbo_slots[i], value);
+ if(value == 0)
+ {
+ /* was relocated to an input, switch type to temporary */
+ for(p = begin; p != end; ++p) {
+ unsigned off = *p;
+ unsigned dw = fp->insn[off];
+ dw &=~ NVFX_FP_REG_TYPE_MASK;
+ //printf("reloc_tmp at %x\n", off);
+ nvfx_fp_memcpy(&fpmap[off], &dw, sizeof(dw));
+ }
+ } else {
+ if(!fpbo_slots[i])
+ {
+ /* was relocated to a temporary, switch type to input */
+ for(p= begin; p != end; ++p) {
+ unsigned off = *p;
+ unsigned dw = fp->insn[off];
+ //printf("reloc_in at %x\n", off);
+ dw |= NVFX_FP_REG_TYPE_INPUT << NVFX_FP_REG_TYPE_SHIFT;
+ nvfx_fp_memcpy(&fpmap[off], &dw, sizeof(dw));
+ }
+ }
+
+ /* set the correct input index */
+ for(p = begin; p != end; ++p) {
+ unsigned off = *p & ~3;
+ unsigned dw = fp->insn[off];
+ //printf("reloc&~3 at %x\n", off);
+ dw = (dw & ~NVFX_FP_OP_INPUT_SRC_MASK) | (value << NVFX_FP_OP_INPUT_SRC_SHIFT);
+ nvfx_fp_memcpy(&fpmap[off], &dw, sizeof(dw));
+ }
+ }
+ fpbo_slots[i] = value;
+ }
+ }
+ --fp->progs_left_with_obsolete_slot_assignments;
+ }
+
+ nvfx->hw_fragprog = fp;
+
+ MARK_RING(chan, 8, 1);
+ BEGIN_RING(chan, eng3d, NV30_3D_FP_ACTIVE_PROGRAM, 1);
+ OUT_RELOC(chan, fp->fpbo->bo, offset, NOUVEAU_BO_VRAM |
+ NOUVEAU_BO_GART | NOUVEAU_BO_RD | NOUVEAU_BO_LOW |
+ NOUVEAU_BO_OR, NV30_3D_FP_ACTIVE_PROGRAM_DMA0,
+ NV30_3D_FP_ACTIVE_PROGRAM_DMA1);
+ BEGIN_RING(chan, eng3d, NV30_3D_FP_CONTROL, 1);
+ OUT_RING(chan, fp->fp_control);
+ if(!nvfx->is_nv4x) {
+ BEGIN_RING(chan, eng3d, NV30_3D_FP_REG_CONTROL, 1);
+ OUT_RING(chan, (1<<16)|0x4);
+ BEGIN_RING(chan, eng3d, NV30_3D_TEX_UNITS_ENABLE, 1);
+ OUT_RING(chan, fp->samplers);
+ }