blob: 786889f8b571ca3642b0d960bc2c14024ac8ab34 [file] [log] [blame]
/*
* Copyright 2010 Christoph Bumiller
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_state.h"
#include "util/u_inlines.h"
#include "nvc0_context.h"
static INLINE void
nvc0_program_update_context_state(struct nvc0_context *nvc0,
struct nvc0_program *prog, int stage)
{
struct nouveau_pushbuf *push = nvc0->base.pushbuf;
if (prog && prog->need_tls) {
const uint32_t flags = NOUVEAU_BO_VRAM | NOUVEAU_BO_RDWR;
if (!nvc0->state.tls_required)
BCTX_REFN_bo(nvc0->bufctx_3d, TLS, flags, nvc0->screen->tls);
nvc0->state.tls_required |= 1 << stage;
} else {
if (nvc0->state.tls_required == (1 << stage))
nouveau_bufctx_reset(nvc0->bufctx_3d, NVC0_BIND_TLS);
nvc0->state.tls_required &= ~(1 << stage);
}
if (prog && prog->immd_size) {
BEGIN_NVC0(push, NVC0_3D(CB_SIZE), 3);
/* NOTE: may overlap code of a different shader */
PUSH_DATA (push, align(prog->immd_size, 0x100));
PUSH_DATAh(push, nvc0->screen->text->offset + prog->immd_base);
PUSH_DATA (push, nvc0->screen->text->offset + prog->immd_base);
BEGIN_NVC0(push, NVC0_3D(CB_BIND(stage)), 1);
PUSH_DATA (push, (14 << 4) | 1);
nvc0->state.c14_bound |= 1 << stage;
} else
if (nvc0->state.c14_bound & (1 << stage)) {
BEGIN_NVC0(push, NVC0_3D(CB_BIND(stage)), 1);
PUSH_DATA (push, (14 << 4) | 0);
nvc0->state.c14_bound &= ~(1 << stage);
}
}
static INLINE boolean
nvc0_program_validate(struct nvc0_context *nvc0, struct nvc0_program *prog)
{
if (prog->mem)
return TRUE;
if (!prog->translated) {
prog->translated = nvc0_program_translate(
prog, nvc0->screen->base.device->chipset);
if (!prog->translated)
return FALSE;
}
if (likely(prog->code_size))
return nvc0_program_upload_code(nvc0, prog);
return TRUE; /* stream output info only */
}
void
nvc0_vertprog_validate(struct nvc0_context *nvc0)
{
struct nouveau_pushbuf *push = nvc0->base.pushbuf;
struct nvc0_program *vp = nvc0->vertprog;
if (!nvc0_program_validate(nvc0, vp))
return;
nvc0_program_update_context_state(nvc0, vp, 0);
BEGIN_NVC0(push, NVC0_3D(SP_SELECT(1)), 2);
PUSH_DATA (push, 0x11);
PUSH_DATA (push, vp->code_base);
BEGIN_NVC0(push, NVC0_3D(SP_GPR_ALLOC(1)), 1);
PUSH_DATA (push, vp->max_gpr);
// BEGIN_NVC0(push, NVC0_3D_(0x163c), 1);
// PUSH_DATA (push, 0);
}
void
nvc0_fragprog_validate(struct nvc0_context *nvc0)
{
struct nouveau_pushbuf *push = nvc0->base.pushbuf;
struct nvc0_program *fp = nvc0->fragprog;
if (!nvc0_program_validate(nvc0, fp))
return;
nvc0_program_update_context_state(nvc0, fp, 4);
if (fp->fp.early_z != nvc0->state.early_z_forced) {
nvc0->state.early_z_forced = fp->fp.early_z;
IMMED_NVC0(push, NVC0_3D(FORCE_EARLY_FRAGMENT_TESTS), fp->fp.early_z);
}
BEGIN_NVC0(push, NVC0_3D(SP_SELECT(5)), 2);
PUSH_DATA (push, 0x51);
PUSH_DATA (push, fp->code_base);
BEGIN_NVC0(push, NVC0_3D(SP_GPR_ALLOC(5)), 1);
PUSH_DATA (push, fp->max_gpr);
BEGIN_NVC0(push, SUBC_3D(0x0360), 2);
PUSH_DATA (push, 0x20164010);
PUSH_DATA (push, 0x20);
BEGIN_NVC0(push, NVC0_3D(ZCULL_TEST_MASK), 1);
PUSH_DATA (push, fp->flags[0]);
}
void
nvc0_tctlprog_validate(struct nvc0_context *nvc0)
{
struct nouveau_pushbuf *push = nvc0->base.pushbuf;
struct nvc0_program *tp = nvc0->tctlprog;
if (tp && nvc0_program_validate(nvc0, tp)) {
if (tp->tp.tess_mode != ~0) {
BEGIN_NVC0(push, NVC0_3D(TESS_MODE), 1);
PUSH_DATA (push, tp->tp.tess_mode);
}
BEGIN_NVC0(push, NVC0_3D(SP_SELECT(2)), 2);
PUSH_DATA (push, 0x21);
PUSH_DATA (push, tp->code_base);
BEGIN_NVC0(push, NVC0_3D(SP_GPR_ALLOC(2)), 1);
PUSH_DATA (push, tp->max_gpr);
if (tp->tp.input_patch_size <= 32)
IMMED_NVC0(push, NVC0_3D(PATCH_VERTICES), tp->tp.input_patch_size);
} else {
BEGIN_NVC0(push, NVC0_3D(SP_SELECT(2)), 1);
PUSH_DATA (push, 0x20);
}
nvc0_program_update_context_state(nvc0, tp, 1);
}
void
nvc0_tevlprog_validate(struct nvc0_context *nvc0)
{
struct nouveau_pushbuf *push = nvc0->base.pushbuf;
struct nvc0_program *tp = nvc0->tevlprog;
if (tp && nvc0_program_validate(nvc0, tp)) {
if (tp->tp.tess_mode != ~0) {
BEGIN_NVC0(push, NVC0_3D(TESS_MODE), 1);
PUSH_DATA (push, tp->tp.tess_mode);
}
BEGIN_NVC0(push, NVC0_3D(MACRO_TEP_SELECT), 1);
PUSH_DATA (push, 0x31);
BEGIN_NVC0(push, NVC0_3D(SP_START_ID(3)), 1);
PUSH_DATA (push, tp->code_base);
BEGIN_NVC0(push, NVC0_3D(SP_GPR_ALLOC(3)), 1);
PUSH_DATA (push, tp->max_gpr);
} else {
BEGIN_NVC0(push, NVC0_3D(MACRO_TEP_SELECT), 1);
PUSH_DATA (push, 0x30);
}
nvc0_program_update_context_state(nvc0, tp, 2);
}
void
nvc0_gmtyprog_validate(struct nvc0_context *nvc0)
{
struct nouveau_pushbuf *push = nvc0->base.pushbuf;
struct nvc0_program *gp = nvc0->gmtyprog;
if (gp)
nvc0_program_validate(nvc0, gp);
/* we allow GPs with no code for specifying stream output state only */
if (gp && gp->code_size) {
const boolean gp_selects_layer = gp->hdr[13] & (1 << 9);
BEGIN_NVC0(push, NVC0_3D(MACRO_GP_SELECT), 1);
PUSH_DATA (push, 0x41);
BEGIN_NVC0(push, NVC0_3D(SP_START_ID(4)), 1);
PUSH_DATA (push, gp->code_base);
BEGIN_NVC0(push, NVC0_3D(SP_GPR_ALLOC(4)), 1);
PUSH_DATA (push, gp->max_gpr);
BEGIN_NVC0(push, NVC0_3D(LAYER), 1);
PUSH_DATA (push, gp_selects_layer ? NVC0_3D_LAYER_USE_GP : 0);
} else {
IMMED_NVC0(push, NVC0_3D(LAYER), 0);
BEGIN_NVC0(push, NVC0_3D(MACRO_GP_SELECT), 1);
PUSH_DATA (push, 0x40);
}
nvc0_program_update_context_state(nvc0, gp, 3);
}
void
nvc0_tfb_validate(struct nvc0_context *nvc0)
{
struct nouveau_pushbuf *push = nvc0->base.pushbuf;
struct nvc0_transform_feedback_state *tfb;
unsigned b;
if (nvc0->gmtyprog) tfb = nvc0->gmtyprog->tfb;
else
if (nvc0->tevlprog) tfb = nvc0->tevlprog->tfb;
else
tfb = nvc0->vertprog->tfb;
IMMED_NVC0(push, NVC0_3D(TFB_ENABLE), (tfb && nvc0->num_tfbbufs) ? 1 : 0);
if (tfb && tfb != nvc0->state.tfb) {
for (b = 0; b < 4; ++b) {
if (tfb->varying_count[b]) {
unsigned n = (tfb->varying_count[b] + 3) / 4;
BEGIN_NVC0(push, NVC0_3D(TFB_STREAM(b)), 3);
PUSH_DATA (push, 0);
PUSH_DATA (push, tfb->varying_count[b]);
PUSH_DATA (push, tfb->stride[b]);
BEGIN_NVC0(push, NVC0_3D(TFB_VARYING_LOCS(b, 0)), n);
PUSH_DATAp(push, tfb->varying_index[b], n);
if (nvc0->tfbbuf[b])
nvc0_so_target(nvc0->tfbbuf[b])->stride = tfb->stride[b];
} else {
IMMED_NVC0(push, NVC0_3D(TFB_VARYING_COUNT(b)), 0);
}
}
}
nvc0->state.tfb = tfb;
if (!(nvc0->dirty & NVC0_NEW_TFB_TARGETS))
return;
nouveau_bufctx_reset(nvc0->bufctx_3d, NVC0_BIND_TFB);
for (b = 0; b < nvc0->num_tfbbufs; ++b) {
struct nvc0_so_target *targ = nvc0_so_target(nvc0->tfbbuf[b]);
struct nv04_resource *buf = nv04_resource(targ->pipe.buffer);
if (tfb)
targ->stride = tfb->stride[b];
if (!(nvc0->tfbbuf_dirty & (1 << b)))
continue;
if (!targ->clean)
nvc0_query_fifo_wait(push, targ->pq);
BEGIN_NVC0(push, NVC0_3D(TFB_BUFFER_ENABLE(b)), 5);
PUSH_DATA (push, 1);
PUSH_DATAh(push, buf->address + targ->pipe.buffer_offset);
PUSH_DATA (push, buf->address + targ->pipe.buffer_offset);
PUSH_DATA (push, targ->pipe.buffer_size);
if (!targ->clean) {
nvc0_query_pushbuf_submit(push, targ->pq, 0x4);
} else {
PUSH_DATA(push, 0); /* TFB_BUFFER_OFFSET */
targ->clean = FALSE;
}
BCTX_REFN(nvc0->bufctx_3d, TFB, buf, WR);
}
for (; b < 4; ++b)
IMMED_NVC0(push, NVC0_3D(TFB_BUFFER_ENABLE(b)), 0);
}