blob: 5b87286d4c5ea7428d23b5c3311dbdc63feddba7 [file] [log] [blame]
/**************************************************************************
*
* Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* 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, sub license, 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 (including the
* next paragraph) 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 NON-INFRINGEMENT.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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_inlines.h"
#include "util/u_memory.h"
#include "util/u_math.h"
#include "cell_context.h"
#include "cell_gen_fragment.h"
#include "cell_state.h"
#include "cell_state_emit.h"
#include "cell_batch.h"
#include "cell_texture.h"
#include "draw/draw_context.h"
#include "draw/draw_private.h"
/**
* Find/create a cell_command_fragment_ops object corresponding to the
* current blend/stencil/z/colormask/etc. state.
*/
static struct cell_command_fragment_ops *
lookup_fragment_ops(struct cell_context *cell)
{
struct cell_fragment_ops_key key;
struct cell_command_fragment_ops *ops;
/*
* Build key
*/
memset(&key, 0, sizeof(key));
key.blend = *cell->blend;
key.blend_color = cell->blend_color;
key.dsa = *cell->depth_stencil;
if (cell->framebuffer.cbufs[0])
key.color_format = cell->framebuffer.cbufs[0]->format;
else
key.color_format = PIPE_FORMAT_NONE;
if (cell->framebuffer.zsbuf)
key.zs_format = cell->framebuffer.zsbuf->format;
else
key.zs_format = PIPE_FORMAT_NONE;
/*
* Look up key in cache.
*/
ops = (struct cell_command_fragment_ops *)
util_keymap_lookup(cell->fragment_ops_cache, &key);
/*
* If not found, create/save new fragment ops command.
*/
if (!ops) {
struct spe_function spe_code_front, spe_code_back;
unsigned int facing_dependent, total_code_size;
if (0)
debug_printf("**** Create New Fragment Ops\n");
/* Prepare the buffer that will hold the generated code. The
* "0" passed in for the size means that the SPE code will
* use a default size.
*/
spe_init_func(&spe_code_front, 0);
spe_init_func(&spe_code_back, 0);
/* Generate new code. Always generate new code for both front-facing
* and back-facing fragments, even if it's the same code in both
* cases.
*/
cell_gen_fragment_function(cell, CELL_FACING_FRONT, &spe_code_front);
cell_gen_fragment_function(cell, CELL_FACING_BACK, &spe_code_back);
/* Make sure the code is a multiple of 8 bytes long; this is
* required to ensure that the dual pipe instruction alignment
* is correct. It's also important for the SPU unpacking,
* which assumes 8-byte boundaries.
*/
unsigned int front_code_size = spe_code_size(&spe_code_front);
while (front_code_size % 8 != 0) {
spe_lnop(&spe_code_front);
front_code_size = spe_code_size(&spe_code_front);
}
unsigned int back_code_size = spe_code_size(&spe_code_back);
while (back_code_size % 8 != 0) {
spe_lnop(&spe_code_back);
back_code_size = spe_code_size(&spe_code_back);
}
/* Determine whether the code we generated is facing-dependent, by
* determining whether the generated code is different for the front-
* and back-facing fragments.
*/
if (front_code_size == back_code_size && memcmp(spe_code_front.store, spe_code_back.store, front_code_size) == 0) {
/* Code is identical; only need one copy. */
facing_dependent = 0;
total_code_size = front_code_size;
}
else {
/* Code is different for front-facing and back-facing fragments.
* Need to send both copies.
*/
facing_dependent = 1;
total_code_size = front_code_size + back_code_size;
}
/* alloc new fragment ops command. Note that this structure
* has variant length based on the total code size required.
*/
ops = CALLOC_VARIANT_LENGTH_STRUCT(cell_command_fragment_ops, total_code_size);
/* populate the new cell_command_fragment_ops object */
ops->opcode[0] = CELL_CMD_STATE_FRAGMENT_OPS;
ops->total_code_size = total_code_size;
ops->front_code_index = 0;
memcpy(ops->code, spe_code_front.store, front_code_size);
if (facing_dependent) {
/* We have separate front- and back-facing code. Append the
* back-facing code to the buffer. Be careful because the code
* size is in bytes, but the buffer is of unsigned elements.
*/
ops->back_code_index = front_code_size / sizeof(spe_code_front.store[0]);
memcpy(ops->code + ops->back_code_index, spe_code_back.store, back_code_size);
}
else {
/* Use the same code for front- and back-facing fragments */
ops->back_code_index = ops->front_code_index;
}
/* Set the fields for the fallback case. Note that these fields
* (and the whole fallback case) will eventually go away.
*/
ops->dsa = *cell->depth_stencil;
ops->blend = *cell->blend;
ops->blend_color = cell->blend_color;
/* insert cell_command_fragment_ops object into keymap/cache */
util_keymap_insert(cell->fragment_ops_cache, &key, ops, NULL);
/* release rtasm buffer */
spe_release_func(&spe_code_front);
spe_release_func(&spe_code_back);
}
else {
if (0)
debug_printf("**** Re-use Fragment Ops\n");
}
return ops;
}
static void
emit_state_cmd(struct cell_context *cell, uint cmd,
const void *state, uint state_size)
{
uint32_t *dst = (uint32_t *)
cell_batch_alloc16(cell, ROUNDUP16(sizeof(opcode_t) + state_size));
*dst = cmd;
memcpy(dst + 4, state, state_size);
}
/**
* For state marked as 'dirty', construct a state-update command block
* and insert it into the current batch buffer.
*/
void
cell_emit_state(struct cell_context *cell)
{
if (cell->dirty & CELL_NEW_FRAMEBUFFER) {
struct pipe_surface *cbuf = cell->framebuffer.cbufs[0];
struct pipe_surface *zbuf = cell->framebuffer.zsbuf;
STATIC_ASSERT(sizeof(struct cell_command_framebuffer) % 16 == 0);
struct cell_command_framebuffer *fb
= cell_batch_alloc16(cell, sizeof(*fb));
fb->opcode[0] = CELL_CMD_STATE_FRAMEBUFFER;
fb->color_start = cell->cbuf_map[0];
fb->color_format = cbuf->format;
fb->depth_start = cell->zsbuf_map;
fb->depth_format = zbuf ? zbuf->format : PIPE_FORMAT_NONE;
fb->width = cell->framebuffer.width;
fb->height = cell->framebuffer.height;
#if 0
printf("EMIT color format %s\n", pf_name(fb->color_format));
printf("EMIT depth format %s\n", pf_name(fb->depth_format));
#endif
}
if (cell->dirty & (CELL_NEW_RASTERIZER)) {
STATIC_ASSERT(sizeof(struct cell_command_rasterizer) % 16 == 0);
struct cell_command_rasterizer *rast =
cell_batch_alloc16(cell, sizeof(*rast));
rast->opcode[0] = CELL_CMD_STATE_RASTERIZER;
rast->rasterizer = *cell->rasterizer;
}
if (cell->dirty & (CELL_NEW_FS)) {
/* Send new fragment program to SPUs */
STATIC_ASSERT(sizeof(struct cell_command_fragment_program) % 16 == 0);
struct cell_command_fragment_program *fp
= cell_batch_alloc16(cell, sizeof(*fp));
fp->opcode[0] = CELL_CMD_STATE_FRAGMENT_PROGRAM;
fp->num_inst = cell->fs->code.num_inst;
memcpy(&fp->code, cell->fs->code.store,
SPU_MAX_FRAGMENT_PROGRAM_INSTS * SPE_INST_SIZE);
if (0) {
int i;
printf("PPU Emit CELL_CMD_STATE_FRAGMENT_PROGRAM:\n");
for (i = 0; i < fp->num_inst; i++) {
printf(" %3d: 0x%08x\n", i, fp->code[i]);
}
}
}
if (cell->dirty & (CELL_NEW_FS_CONSTANTS)) {
const uint shader = PIPE_SHADER_FRAGMENT;
const uint num_const = cell->constants[shader].buffer->size / sizeof(float);
uint i, j;
float *buf = cell_batch_alloc16(cell, ROUNDUP16(32 + num_const * sizeof(float)));
uint32_t *ibuf = (uint32_t *) buf;
const float *constants = pipe_buffer_map(cell->pipe.screen,
cell->constants[shader].buffer,
PIPE_BUFFER_USAGE_CPU_READ);
ibuf[0] = CELL_CMD_STATE_FS_CONSTANTS;
ibuf[4] = num_const;
j = 8;
for (i = 0; i < num_const; i++) {
buf[j++] = constants[i];
}
pipe_buffer_unmap(cell->pipe.screen, cell->constants[shader].buffer);
}
if (cell->dirty & (CELL_NEW_FRAMEBUFFER |
CELL_NEW_DEPTH_STENCIL |
CELL_NEW_BLEND)) {
struct cell_command_fragment_ops *fops, *fops_cmd;
/* Note that cell_command_fragment_ops is a variant-sized record */
fops = lookup_fragment_ops(cell);
fops_cmd = cell_batch_alloc16(cell, ROUNDUP16(sizeof(*fops_cmd) + fops->total_code_size));
memcpy(fops_cmd, fops, sizeof(*fops) + fops->total_code_size);
}
if (cell->dirty & CELL_NEW_SAMPLER) {
uint i;
for (i = 0; i < CELL_MAX_SAMPLERS; i++) {
if (cell->dirty_samplers & (1 << i)) {
if (cell->sampler[i]) {
STATIC_ASSERT(sizeof(struct cell_command_sampler) % 16 == 0);
struct cell_command_sampler *sampler
= cell_batch_alloc16(cell, sizeof(*sampler));
sampler->opcode[0] = CELL_CMD_STATE_SAMPLER;
sampler->unit = i;
sampler->state = *cell->sampler[i];
}
}
}
cell->dirty_samplers = 0x0;
}
if (cell->dirty & CELL_NEW_TEXTURE) {
uint i;
for (i = 0;i < CELL_MAX_SAMPLERS; i++) {
if (cell->dirty_textures & (1 << i)) {
STATIC_ASSERT(sizeof(struct cell_command_texture) % 16 == 0);
struct cell_command_texture *texture =
(struct cell_command_texture *)
cell_batch_alloc16(cell, sizeof(*texture));
texture->opcode[0] = CELL_CMD_STATE_TEXTURE;
texture->unit = i;
if (cell->texture[i]) {
struct cell_texture *ct = cell->texture[i];
uint level;
for (level = 0; level < CELL_MAX_TEXTURE_LEVELS; level++) {
texture->start[level] = (ct->mapped +
ct->level_offset[level]);
texture->width[level] = u_minify(ct->base.width0, level);
texture->height[level] = u_minify(ct->base.height0, level);
texture->depth[level] = u_minify(ct->base.depth0, level);
}
texture->target = ct->base.target;
}
else {
uint level;
for (level = 0; level < CELL_MAX_TEXTURE_LEVELS; level++) {
texture->start[level] = NULL;
texture->width[level] = 0;
texture->height[level] = 0;
texture->depth[level] = 0;
}
texture->target = 0;
}
}
}
cell->dirty_textures = 0x0;
}
if (cell->dirty & CELL_NEW_VERTEX_INFO) {
emit_state_cmd(cell, CELL_CMD_STATE_VERTEX_INFO,
&cell->vertex_info, sizeof(struct vertex_info));
}
#if 0
if (cell->dirty & CELL_NEW_VS) {
const struct draw_context *const draw = cell->draw;
struct cell_shader_info info;
info.num_outputs = draw_num_shader_outputs(draw);
info.declarations = (uintptr_t) draw->vs.machine.Declarations;
info.num_declarations = draw->vs.machine.NumDeclarations;
info.instructions = (uintptr_t) draw->vs.machine.Instructions;
info.num_instructions = draw->vs.machine.NumInstructions;
info.immediates = (uintptr_t) draw->vs.machine.Imms;
info.num_immediates = draw->vs.machine.ImmLimit / 4;
emit_state_cmd(cell, CELL_CMD_STATE_BIND_VS, &info, sizeof(info));
}
#endif
}