summaryrefslogtreecommitdiff
path: root/src/gallium/drivers/cell/ppu
diff options
context:
space:
mode:
authorRobert Ellison <papillo@tungstengraphics.com>2008-09-17 02:30:20 -0600
committerRobert Ellison <papillo@tungstengraphics.com>2008-09-17 02:32:43 -0600
commit858ced051551aa5d0ddd41936253d3a4ee5c142f (patch)
tree1ea8b4b05a09cc9d63be5491cef6c19b8c49efdd /src/gallium/drivers/cell/ppu
parenta3a797ffa84975330d5632ce7a71c65c9c2ad0d8 (diff)
CELL: fleshing out the blending fragment ops
- Added two new debug flags (to be used with the CELL_DEBUG environment variable). The first, "CELL_DEBUG=fragops", activates SPE fragment ops debug messages. The second, "CELL_DEBUG=fragopfallback", will eventually be used to disable the use of generated SPE code for fragment ops in favor of the default fallback reference routine. (During development, though, the parity of this flag is reversed: all users will get the reference code *unless* CELL_DEBUG=fragopfallback is set. This will prevent hiccups in code generation from affecting the other developers.) - Formalized debug message usage and macros in spu/spu_main.c. - Added lots of new code to ppu/cell_gen_fragment.c to extend the number of supported source RGB factors from 4 to 15, and to complete the list of supported blend equations. More coming, to complete the source and destination RGB and alpha factors, and to complete the rest of the fragment operations...
Diffstat (limited to 'src/gallium/drivers/cell/ppu')
-rw-r--r--src/gallium/drivers/cell/ppu/cell_context.c2
-rw-r--r--src/gallium/drivers/cell/ppu/cell_gen_fragment.c272
-rw-r--r--src/gallium/drivers/cell/ppu/cell_state_emit.c5
3 files changed, 267 insertions, 12 deletions
diff --git a/src/gallium/drivers/cell/ppu/cell_context.c b/src/gallium/drivers/cell/ppu/cell_context.c
index b418271dca..62e213ea35 100644
--- a/src/gallium/drivers/cell/ppu/cell_context.c
+++ b/src/gallium/drivers/cell/ppu/cell_context.c
@@ -89,6 +89,8 @@ static const struct debug_named_value cell_debug_flags[] = {
{"checker", CELL_DEBUG_CHECKER},/**< modulate tile clear color by SPU ID */
{"asm", CELL_DEBUG_ASM}, /**< dump SPU asm code */
{"sync", CELL_DEBUG_SYNC}, /**< SPUs do synchronous DMA */
+ {"fragops", CELL_DEBUG_FRAGMENT_OPS}, /**< SPUs emit fragment ops debug messages*/
+ {"fragopfallback", CELL_DEBUG_FRAGMENT_OP_FALLBACK}, /**< SPUs use reference implementation for fragment ops*/
{NULL, 0}
};
diff --git a/src/gallium/drivers/cell/ppu/cell_gen_fragment.c b/src/gallium/drivers/cell/ppu/cell_gen_fragment.c
index 06219d4e98..2c8c9e0d2c 100644
--- a/src/gallium/drivers/cell/ppu/cell_gen_fragment.c
+++ b/src/gallium/drivers/cell/ppu/cell_gen_fragment.c
@@ -229,7 +229,36 @@ gen_alpha_test(const struct pipe_depth_stencil_alpha_state *dsa,
spe_release_register(f, amask_reg);
}
+/* This is a convenient and oft-used sequence. It chooses
+ * the smaller of each element of reg1 and reg2, and combines them
+ * into the result register, as follows:
+ *
+ * The Float Compare Greater Than (fcgt) instruction will put
+ * 1s into compare_reg where reg1 > reg2, and 0s where reg1 <= reg2.
+ *
+ * Then the Select Bits (selb) instruction will take bits from
+ * reg1 where compare_reg is 0, and from reg2 where compare_reg is
+ * 1. Ergo, result_reg will have the bits from reg1 where reg1 <= reg2,
+ * and the bits from reg2 where reg1 > reg2, which is exactly the
+ * MIN operation.
+ */
+#define FLOAT_VECTOR_MIN(f, result_reg, reg1, reg2) {\
+ int compare_reg = spe_allocate_available_register(f); \
+ spe_fcgt(f, compare_reg, reg1, reg2); \
+ spe_selb(f, result_reg, reg1, reg2, compare_reg); \
+ spe_release_register(f, compare_reg); \
+}
+/* The FLOAT_VECTOR_MAX sequence is similar to the FLOAT_VECTOR_MIN
+ * sequence above, except that the registers specified when selecting
+ * bits are reversed.
+ */
+#define FLOAT_VECTOR_MAX(f, result_reg, reg1, reg2) {\
+ int compare_reg = spe_allocate_available_register(f); \
+ spe_fcgt(f, compare_reg, reg1, reg2); \
+ spe_selb(f, result_reg, reg2, reg1, compare_reg); \
+ spe_release_register(f, compare_reg); \
+}
/**
* Generate SPE code to implement the given blend mode for a quad of pixels.
@@ -242,6 +271,7 @@ gen_alpha_test(const struct pipe_depth_stencil_alpha_state *dsa,
*/
static void
gen_blend(const struct pipe_blend_state *blend,
+ const struct pipe_blend_color *blend_color,
struct spe_function *f,
enum pipe_format color_format,
int fragR_reg, int fragG_reg, int fragB_reg, int fragA_reg,
@@ -262,10 +292,53 @@ gen_blend(const struct pipe_blend_state *blend,
int fbB_reg = spe_allocate_available_register(f);
int fbA_reg = spe_allocate_available_register(f);
- int one_reg = spe_allocate_available_register(f);
int tmp_reg = spe_allocate_available_register(f);
- boolean one_reg_set = false; /* avoid setting one_reg more than once */
+ /* These values might or might not eventually get put into
+ * registers. We avoid allocating them and setting them until
+ * they're actually needed; then we avoid setting them more than
+ * once, and release them at the end of code generation.
+ */
+ boolean one_reg_set = false;
+ int one_reg;
+#define SET_ONE_REG_IF_UNSET(f) if (!one_reg_set) {\
+ one_reg = spe_allocate_available_register(f); \
+ spe_load_float(f, one_reg, 1.0f); \
+ one_reg_set = true; \
+}
+#define RELEASE_ONE_REG_IF_USED(f) if (one_reg_set) {\
+ spe_release_register(f, one_reg); \
+}
+
+ boolean const_color_set = false;
+ int constR_reg, constG_reg, constB_reg;
+#define SET_CONST_COLOR_IF_UNSET(f, blend_color) if (!const_color_set) {\
+ constR_reg = spe_allocate_available_register(f); \
+ constG_reg = spe_allocate_available_register(f); \
+ constG_reg = spe_allocate_available_register(f); \
+ spe_load_float(f, constR_reg, blend_color->color[0]); \
+ spe_load_float(f, constG_reg, blend_color->color[1]); \
+ spe_load_float(f, constB_reg, blend_color->color[2]); \
+ const_color_set = true;\
+}
+#define RELEASE_CONST_COLOR_IF_USED(f) if (const_color_set) {\
+ spe_release_register(f, constR_reg); \
+ spe_release_register(f, constG_reg); \
+ spe_release_register(f, constB_reg); \
+}
+
+ boolean const_alpha_set = false;
+ int constA_reg;
+#define SET_CONST_ALPHA_IF_UNSET(f, blend_color) if (!const_alpha_set) {\
+ constA_reg = spe_allocate_available_register(f); \
+ spe_load_float(f, constA_reg, blend_color->color[3]); \
+ const_alpha_set = true; \
+}
+#define RELEASE_CONST_ALPHA_IF_USED(f) if (const_alpha_set) {\
+ spe_release_register(f, constA_reg); \
+}
+
+ /* Real code starts here */
ASSERT(blend->blend_enable);
@@ -348,30 +421,161 @@ gen_blend(const struct pipe_blend_state *blend,
/*
- * Compute Src RGB terms
+ * Compute Src RGB terms. We're actually looking for the value
+ * of (the appropriate RGB factors) * (the incoming source RGB color).
*/
switch (blend->rgb_src_factor) {
case PIPE_BLENDFACTOR_ONE:
+ /* factors = (1,1,1), so term = (R,G,B) */
spe_move(f, term1R_reg, fragR_reg);
spe_move(f, term1G_reg, fragG_reg);
spe_move(f, term1B_reg, fragB_reg);
break;
case PIPE_BLENDFACTOR_ZERO:
- spe_zero(f, term1R_reg);
- spe_zero(f, term1G_reg);
- spe_zero(f, term1B_reg);
+ /* factors = (0,0,0), so term = (0,0,0) */
+ spe_load_float(f, term1R_reg, 0.0f);
+ spe_load_float(f, term1G_reg, 0.0f);
+ spe_load_float(f, term1B_reg, 0.0f);
break;
case PIPE_BLENDFACTOR_SRC_COLOR:
+ /* factors = (R,G,B), so term = (R*R, G*G, B*B) */
spe_fm(f, term1R_reg, fragR_reg, fragR_reg);
spe_fm(f, term1G_reg, fragG_reg, fragG_reg);
spe_fm(f, term1B_reg, fragB_reg, fragB_reg);
break;
case PIPE_BLENDFACTOR_SRC_ALPHA:
+ /* factors = (A,A,A), so term = (R*A, G*A, B*A) */
spe_fm(f, term1R_reg, fragR_reg, fragA_reg);
spe_fm(f, term1G_reg, fragG_reg, fragA_reg);
spe_fm(f, term1B_reg, fragB_reg, fragA_reg);
break;
- /* XXX more cases */
+ case PIPE_BLENDFACTOR_INV_SRC_COLOR:
+ /* factors = (1-R,1-G,1-B), so term = (R*(1-R), G*(1-G), B*(1-B)) */
+ /* we'll need the optional constant {1,1,1,1} register */
+ SET_ONE_REG_IF_UNSET(f)
+ /* tmp = 1 - R */
+ spe_fs(f, tmp_reg, one_reg, fragR_reg);
+ /* term = R * tmp */
+ spe_fm(f, term1R_reg, fragR_reg, tmp_reg);
+ /* repeat for G and B */
+ spe_fs(f, tmp_reg, one_reg, fragG_reg);
+ spe_fm(f, term1G_reg, fragG_reg, tmp_reg);
+ spe_fs(f, tmp_reg, one_reg, fragB_reg);
+ spe_fm(f, term1B_reg, fragB_reg, tmp_reg);
+ break;
+ case PIPE_BLENDFACTOR_DST_COLOR:
+ /* factors = (Rfb,Gfb,Bfb), so term = (R*Rfb, G*Gfb, B*Bfb) */
+ spe_fm(f, term1R_reg, fragR_reg, fbR_reg);
+ spe_fm(f, term1G_reg, fragG_reg, fbG_reg);
+ spe_fm(f, term1B_reg, fragB_reg, fbB_reg);
+ break;
+ case PIPE_BLENDFACTOR_INV_DST_COLOR:
+ /* factors = (1-Rfb,1-Gfb,1-Bfb), so term = (R*(1-Rfb),G*(1-Gfb),B*(1-Bfb)) */
+ /* we'll need the optional constant {1,1,1,1} register */
+ SET_ONE_REG_IF_UNSET(f)
+ /* tmp = 1 - Rfb */
+ spe_fs(f, tmp_reg, one_reg, fbR_reg);
+ /* term = R * tmp */
+ spe_fm(f, term1R_reg, fragR_reg, tmp_reg);
+ /* repeat for G and B */
+ spe_fs(f, tmp_reg, one_reg, fbG_reg);
+ spe_fm(f, term1G_reg, fragG_reg, tmp_reg);
+ spe_fs(f, tmp_reg, one_reg, fbB_reg);
+ spe_fm(f, term1B_reg, fragB_reg, tmp_reg);
+ break;
+ case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
+ /* factors = (1-A,1-A,1-A), so term = (R*(1-A),G*(1-A),B*(1-A)) */
+ /* we'll need the optional constant {1,1,1,1} register */
+ SET_ONE_REG_IF_UNSET(f)
+ /* tmp = 1 - A */
+ spe_fs(f, tmp_reg, one_reg, fragA_reg);
+ /* term = R * tmp */
+ spe_fm(f, term1R_reg, fragR_reg, tmp_reg);
+ /* repeat for G and B with the same (1-A) factor */
+ spe_fm(f, term1G_reg, fragG_reg, tmp_reg);
+ spe_fm(f, term1B_reg, fragB_reg, tmp_reg);
+ break;
+ case PIPE_BLENDFACTOR_DST_ALPHA:
+ /* factors = (Afb, Afb, Afb), so term = (R*Afb, G*Afb, B*Afb) */
+ spe_fm(f, term1R_reg, fragR_reg, fbA_reg);
+ spe_fm(f, term1G_reg, fragG_reg, fbA_reg);
+ spe_fm(f, term1B_reg, fragB_reg, fbA_reg);
+ break;
+ case PIPE_BLENDFACTOR_INV_DST_ALPHA:
+ /* factors = (1-Afb, 1-Afb, 1-Afb), so term = (R*(1-Afb),G*(1-Afb),B*(1-Afb)) */
+ /* we'll need the optional constant {1,1,1,1} register */
+ SET_ONE_REG_IF_UNSET(f)
+ /* tmp = 1 - A */
+ spe_fs(f, tmp_reg, one_reg, fbA_reg);
+ /* term = R * tmp, G*tmp, and B*tmp */
+ spe_fm(f, term1R_reg, fragR_reg, tmp_reg);
+ spe_fm(f, term1G_reg, fragG_reg, tmp_reg);
+ spe_fm(f, term1B_reg, fragB_reg, tmp_reg);
+ break;
+ case PIPE_BLENDFACTOR_CONST_COLOR:
+ /* We'll need the optional blend color registers */
+ SET_CONST_COLOR_IF_UNSET(f,blend_color)
+ /* now, factor = (Rc,Gc,Bc), so term = (R*Rc,G*Gc,B*Bc) */
+ spe_fm(f, term1R_reg, fragR_reg, constR_reg);
+ spe_fm(f, term1G_reg, fragG_reg, constG_reg);
+ spe_fm(f, term1B_reg, fragB_reg, constB_reg);
+ break;
+ case PIPE_BLENDFACTOR_CONST_ALPHA:
+ /* we'll need the optional constant alpha register */
+ SET_CONST_ALPHA_IF_UNSET(f, blend_color)
+ /* factor = (Ac,Ac,Ac), so term = (R*Ac,G*Ac,B*Ac) */
+ spe_fm(f, term1R_reg, fragR_reg, constA_reg);
+ spe_fm(f, term1G_reg, fragG_reg, constA_reg);
+ spe_fm(f, term1B_reg, fragB_reg, constA_reg);
+ break;
+ case PIPE_BLENDFACTOR_INV_CONST_COLOR:
+ /* We need both the optional {1,1,1,1} register, and the optional
+ * constant color registers
+ */
+ SET_ONE_REG_IF_UNSET(f)
+ SET_CONST_COLOR_IF_UNSET(f, blend_color)
+ /* factor = (1-Rc,1-Gc,1-Bc), so term = (R*(1-Rc),G*(1-Gc),B*(1-Bc)) */
+ spe_fs(f, tmp_reg, one_reg, constR_reg);
+ spe_fm(f, term1R_reg, fragR_reg, tmp_reg);
+ spe_fs(f, tmp_reg, one_reg, constG_reg);
+ spe_fm(f, term1G_reg, fragG_reg, tmp_reg);
+ spe_fs(f, tmp_reg, one_reg, constB_reg);
+ spe_fm(f, term1B_reg, fragB_reg, tmp_reg);
+ break;
+ case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
+ /* We need the optional {1,1,1,1} register and the optional
+ * constant alpha register
+ */
+ SET_ONE_REG_IF_UNSET(f)
+ SET_CONST_ALPHA_IF_UNSET(f, blend_color)
+ /* factor = (1-Ac,1-Ac,1-Ac), so term = (R*(1-Ac),G*(1-Ac),B*(1-Ac)) */
+ spe_fs(f, tmp_reg, one_reg, constA_reg);
+ spe_fm(f, term1R_reg, fragR_reg, tmp_reg);
+ spe_fm(f, term1G_reg, fragG_reg, tmp_reg);
+ spe_fm(f, term1B_reg, fragB_reg, tmp_reg);
+ break;
+ case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
+ /* We'll need the optional {1,1,1,1} register */
+ SET_ONE_REG_IF_UNSET(f)
+ /* factor = (min(A,1-Afb),min(A,1-Afb),min(A,1-Afb)), so
+ * term = (R*min(A,1-Afb), G*min(A,1-Afb), B*min(A,1-Afb))
+ */
+ /* tmp = 1 - Afb */
+ spe_fs(f, tmp_reg, one_reg, fbA_reg);
+ /* tmp = min(A,tmp) */
+ FLOAT_VECTOR_MIN(f, tmp_reg, fragA_reg, tmp_reg)
+ /* term = R*tmp */
+ spe_fm(f, term1R_reg, fragR_reg, tmp_reg);
+ spe_fm(f, term1G_reg, fragG_reg, tmp_reg);
+ spe_fm(f, term1B_reg, fragB_reg, tmp_reg);
+ break;
+
+ /* non-OpenGL cases? */
+ case PIPE_BLENDFACTOR_SRC1_COLOR:
+ case PIPE_BLENDFACTOR_SRC1_ALPHA:
+ case PIPE_BLENDFACTOR_INV_SRC1_COLOR:
+ case PIPE_BLENDFACTOR_INV_SRC1_ALPHA:
+
default:
ASSERT(0);
}
@@ -421,6 +625,7 @@ gen_blend(const struct pipe_blend_state *blend,
case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
/* one = {1.0, 1.0, 1.0, 1.0} */
if (!one_reg_set) {
+ one_reg = spe_allocate_available_register(f);
spe_load_float(f, one_reg, 1.0f);
one_reg_set = true;
}
@@ -432,6 +637,14 @@ gen_blend(const struct pipe_blend_state *blend,
spe_fm(f, term2B_reg, fbB_reg, tmp_reg);
break;
/* XXX more cases */
+ // GL_ONE_MINUS_SRC_COLOR
+ // GL_DST_COLOR
+ // GL_ONE_MINUS_DST_COLOR
+ // GL_DST_ALPHA
+ // GL_CONSTANT_COLOR
+ // GL_ONE_MINUS_CONSTANT_COLOR
+ // GL_CONSTANT_ALPHA
+ // GL_ONE_MINUS_CONSTANT_ALPHA
default:
ASSERT(0);
}
@@ -452,6 +665,7 @@ gen_blend(const struct pipe_blend_state *blend,
case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
/* one = {1.0, 1.0, 1.0, 1.0} */
if (!one_reg_set) {
+ one_reg = spe_allocate_available_register(f);
spe_load_float(f, one_reg, 1.0f);
one_reg_set = true;
}
@@ -461,6 +675,14 @@ gen_blend(const struct pipe_blend_state *blend,
spe_fm(f, term2A_reg, fbA_reg, tmp_reg);
break;
/* XXX more cases */
+ // GL_ONE_MINUS_SRC_COLOR
+ // GL_DST_COLOR
+ // GL_ONE_MINUS_DST_COLOR
+ // GL_DST_ALPHA
+ // GL_CONSTANT_COLOR
+ // GL_ONE_MINUS_CONSTANT_COLOR
+ // GL_CONSTANT_ALPHA
+ // GL_ONE_MINUS_CONSTANT_ALPHA
default:
ASSERT(0);
}
@@ -479,7 +701,21 @@ gen_blend(const struct pipe_blend_state *blend,
spe_fs(f, fragG_reg, term1G_reg, term2G_reg);
spe_fs(f, fragB_reg, term1B_reg, term2B_reg);
break;
- /* XXX more cases */
+ case PIPE_BLEND_REVERSE_SUBTRACT:
+ spe_fs(f, fragR_reg, term2R_reg, term1R_reg);
+ spe_fs(f, fragG_reg, term2G_reg, term1G_reg);
+ spe_fs(f, fragB_reg, term2B_reg, term1B_reg);
+ break;
+ case PIPE_BLEND_MIN:
+ FLOAT_VECTOR_MIN(f, fragR_reg, term1R_reg, term2R_reg)
+ FLOAT_VECTOR_MIN(f, fragG_reg, term1G_reg, term2G_reg)
+ FLOAT_VECTOR_MIN(f, fragB_reg, term1B_reg, term2B_reg)
+ break;
+ case PIPE_BLEND_MAX:
+ FLOAT_VECTOR_MAX(f, fragR_reg, term1R_reg, term2R_reg)
+ FLOAT_VECTOR_MAX(f, fragG_reg, term1G_reg, term2G_reg)
+ FLOAT_VECTOR_MAX(f, fragB_reg, term1B_reg, term2B_reg)
+ break;
default:
ASSERT(0);
}
@@ -494,7 +730,15 @@ gen_blend(const struct pipe_blend_state *blend,
case PIPE_BLEND_SUBTRACT:
spe_fs(f, fragA_reg, term1A_reg, term2A_reg);
break;
- /* XXX more cases */
+ case PIPE_BLEND_REVERSE_SUBTRACT:
+ spe_fs(f, fragA_reg, term2A_reg, term1A_reg);
+ break;
+ case PIPE_BLEND_MIN:
+ FLOAT_VECTOR_MIN(f, fragA_reg, term1A_reg, term2A_reg)
+ break;
+ case PIPE_BLEND_MAX:
+ FLOAT_VECTOR_MAX(f, fragA_reg, term1A_reg, term2A_reg)
+ break;
default:
ASSERT(0);
}
@@ -514,8 +758,12 @@ gen_blend(const struct pipe_blend_state *blend,
spe_release_register(f, fbB_reg);
spe_release_register(f, fbA_reg);
- spe_release_register(f, one_reg);
spe_release_register(f, tmp_reg);
+
+ /* Free any optional registers that actually got used */
+ RELEASE_ONE_REG_IF_USED(f)
+ RELEASE_CONST_COLOR_IF_USED(f)
+ RELEASE_CONST_ALPHA_IF_USED(f)
}
@@ -629,6 +877,7 @@ cell_gen_fragment_function(struct cell_context *cell, struct spe_function *f)
const struct pipe_depth_stencil_alpha_state *dsa =
&cell->depth_stencil->base;
const struct pipe_blend_state *blend = &cell->blend->base;
+ const struct pipe_blend_color *blend_color = &cell->blend_color;
const enum pipe_format color_format = cell->framebuffer.cbufs[0]->format;
/* For SPE function calls: reg $3 = first param, $4 = second param, etc. */
@@ -651,7 +900,6 @@ cell_gen_fragment_function(struct cell_context *cell, struct spe_function *f)
int fbRGBA_reg; /**< framebuffer's RGBA colors for quad */
int fbZS_reg; /**< framebuffer's combined z/stencil values for quad */
- spe_init_func(f, SPU_MAX_FRAGMENT_OPS_INSTS * SPE_INST_SIZE);
spe_allocate_register(f, x_reg);
spe_allocate_register(f, y_reg);
spe_allocate_register(f, color_tile_reg);
@@ -816,7 +1064,7 @@ cell_gen_fragment_function(struct cell_context *cell, struct spe_function *f)
if (blend->blend_enable) {
- gen_blend(blend, f, color_format,
+ gen_blend(blend, blend_color, f, color_format,
fragR_reg, fragG_reg, fragB_reg, fragA_reg, fbRGBA_reg);
}
diff --git a/src/gallium/drivers/cell/ppu/cell_state_emit.c b/src/gallium/drivers/cell/ppu/cell_state_emit.c
index 2da3097983..8a389cd6aa 100644
--- a/src/gallium/drivers/cell/ppu/cell_state_emit.c
+++ b/src/gallium/drivers/cell/ppu/cell_state_emit.c
@@ -100,14 +100,19 @@ cell_emit_state(struct cell_context *cell)
= cell_batch_alloc(cell, sizeof(*fops));
struct spe_function spe_code;
+ /* Prepare the buffer that will hold the generated code. */
+ spe_init_func(&spe_code, SPU_MAX_FRAGMENT_OPS_INSTS * SPE_INST_SIZE);
+
/* generate new code */
cell_gen_fragment_function(cell, &spe_code);
+
/* put the new code into the batch buffer */
fops->opcode = CELL_CMD_STATE_FRAGMENT_OPS;
memcpy(&fops->code, spe_code.store,
SPU_MAX_FRAGMENT_OPS_INSTS * SPE_INST_SIZE);
fops->dsa = cell->depth_stencil->base;
fops->blend = cell->blend->base;
+
/* free codegen buffer */
spe_release_func(&spe_code);
}