summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gallium/auxiliary/draw/draw_pt_emit.c2
-rw-r--r--src/gallium/auxiliary/util/p_debug.c97
-rw-r--r--src/gallium/auxiliary/util/u_blit.c3
-rw-r--r--src/gallium/auxiliary/util/u_pack_color.h51
-rw-r--r--src/gallium/drivers/softpipe/sp_context.c2
-rw-r--r--src/gallium/drivers/softpipe/sp_fs_sse.c2
-rw-r--r--src/gallium/include/pipe/p_debug.h12
-rw-r--r--src/gallium/include/pipe/p_format.h83
-rw-r--r--src/gallium/winsys/xlib/xm_winsys.c7
-rw-r--r--src/mesa/main/pixel.c15
-rw-r--r--src/mesa/main/pixel.h3
-rw-r--r--src/mesa/state_tracker/st_atom_sampler.c5
-rw-r--r--src/mesa/state_tracker/st_cb_bitmap.c6
-rw-r--r--src/mesa/state_tracker/st_cb_fbo.c2
-rw-r--r--src/mesa/state_tracker/st_cb_texture.c59
15 files changed, 233 insertions, 116 deletions
diff --git a/src/gallium/auxiliary/draw/draw_pt_emit.c b/src/gallium/auxiliary/draw/draw_pt_emit.c
index 35707af8a8..f9ac16786e 100644
--- a/src/gallium/auxiliary/draw/draw_pt_emit.c
+++ b/src/gallium/auxiliary/draw/draw_pt_emit.c
@@ -58,6 +58,8 @@ void draw_pt_emit_prepare( struct pt_emit *emit,
return;
}
+ memset(&hw_key, 0, sizeof(hw_key));
+
/* Must do this after set_primitive() above:
*/
vinfo = draw->render->get_vertex_info(draw->render);
diff --git a/src/gallium/auxiliary/util/p_debug.c b/src/gallium/auxiliary/util/p_debug.c
index cd612e23b3..8ef2880191 100644
--- a/src/gallium/auxiliary/util/p_debug.c
+++ b/src/gallium/auxiliary/util/p_debug.c
@@ -41,6 +41,7 @@
#include "pipe/p_compiler.h"
#include "pipe/p_util.h"
#include "pipe/p_debug.h"
+#include "pipe/p_format.h"
#include "util/u_string.h"
@@ -324,3 +325,99 @@ debug_dump_flags(const struct debug_named_value *names,
}
+
+
+
+
+char *pf_sprint_name( char *str, enum pipe_format format )
+{
+ strcpy( str, "PIPE_FORMAT_" );
+ switch (pf_layout( format )) {
+ case PIPE_FORMAT_LAYOUT_RGBAZS:
+ {
+ pipe_format_rgbazs_t rgbazs = (pipe_format_rgbazs_t) format;
+ uint i;
+ uint scale = 1 << (pf_exp8( rgbazs ) * 3);
+
+ for (i = 0; i < 4; i++) {
+ uint size = pf_size_xyzw( rgbazs, i );
+
+ if (size == 0) {
+ break;
+ }
+ switch (pf_swizzle_xyzw( rgbazs, i )) {
+ case PIPE_FORMAT_COMP_R:
+ strcat( str, "R" );
+ break;
+ case PIPE_FORMAT_COMP_G:
+ strcat( str, "G" );
+ break;
+ case PIPE_FORMAT_COMP_B:
+ strcat( str, "B" );
+ break;
+ case PIPE_FORMAT_COMP_A:
+ strcat( str, "A" );
+ break;
+ case PIPE_FORMAT_COMP_0:
+ strcat( str, "0" );
+ break;
+ case PIPE_FORMAT_COMP_1:
+ strcat( str, "1" );
+ break;
+ case PIPE_FORMAT_COMP_Z:
+ strcat( str, "Z" );
+ break;
+ case PIPE_FORMAT_COMP_S:
+ strcat( str, "S" );
+ break;
+ }
+ util_snprintf( &str[strlen( str )], 32, "%u", size * scale );
+ }
+ if (i != 0) {
+ strcat( str, "_" );
+ }
+ switch (pf_type( rgbazs )) {
+ case PIPE_FORMAT_TYPE_UNKNOWN:
+ strcat( str, "NONE" );
+ break;
+ case PIPE_FORMAT_TYPE_FLOAT:
+ strcat( str, "FLOAT" );
+ break;
+ case PIPE_FORMAT_TYPE_UNORM:
+ strcat( str, "UNORM" );
+ break;
+ case PIPE_FORMAT_TYPE_SNORM:
+ strcat( str, "SNORM" );
+ break;
+ case PIPE_FORMAT_TYPE_USCALED:
+ strcat( str, "USCALED" );
+ break;
+ case PIPE_FORMAT_TYPE_SSCALED:
+ strcat( str, "SSCALED" );
+ break;
+ }
+ }
+ break;
+ case PIPE_FORMAT_LAYOUT_YCBCR:
+ {
+ pipe_format_ycbcr_t ycbcr = (pipe_format_ycbcr_t) format;
+
+ strcat( str, "YCBCR" );
+ if (pf_rev( ycbcr )) {
+ strcat( str, "_REV" );
+ }
+ }
+ break;
+ }
+ return str;
+}
+
+
+void debug_print_format(const char *msg, unsigned fmt )
+{
+ char fmtstr[80];
+
+ pf_sprint_name(fmtstr, (enum pipe_format)fmt);
+
+ debug_printf("%s: %s\n", msg, fmtstr);
+}
diff --git a/src/gallium/auxiliary/util/u_blit.c b/src/gallium/auxiliary/util/u_blit.c
index 1105066cb8..9e9912c6e4 100644
--- a/src/gallium/auxiliary/util/u_blit.c
+++ b/src/gallium/auxiliary/util/u_blit.c
@@ -264,6 +264,9 @@ util_blit_pixels(struct blit_state *ctx,
dstY1 = tmp;
}
+ assert(screen->is_format_supported(screen, src->format, PIPE_TEXTURE));
+ assert(screen->is_format_supported(screen, dst->format, PIPE_SURFACE));
+
/*
* XXX for now we're always creating a temporary texture.
* Strictly speaking that's not always needed.
diff --git a/src/gallium/auxiliary/util/u_pack_color.h b/src/gallium/auxiliary/util/u_pack_color.h
index 50ef44992b..0b917c005f 100644
--- a/src/gallium/auxiliary/util/u_pack_color.h
+++ b/src/gallium/auxiliary/util/u_pack_color.h
@@ -89,9 +89,40 @@ util_pack_color_ub(ubyte r, ubyte g, ubyte b, ubyte a,
*d = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3);
}
return;
+ case PIPE_FORMAT_A1R5G5B5_UNORM:
+ {
+ ushort *d = (ushort *) dest;
+ *d = ((a & 0x80) << 8) | ((r & 0xf8) << 7) | ((g & 0xf8) << 2) | (b >> 3);
+ }
+ return;
+ case PIPE_FORMAT_A4R4G4B4_UNORM:
+ {
+ ushort *d = (ushort *) dest;
+ *d = ((a & 0xf0) << 8) | ((r & 0xf0) << 4) | ((g & 0xf0) << 0) | (b >> 4);
+ }
+ return;
+ case PIPE_FORMAT_R32G32B32A32_FLOAT:
+ {
+ float *d = (float *) dest;
+ d[0] = (float)r / 255.0f;
+ d[1] = (float)g / 255.0f;
+ d[2] = (float)b / 255.0f;
+ d[3] = (float)a / 255.0f;
+ }
+ return;
+ case PIPE_FORMAT_R32G32B32_FLOAT:
+ {
+ float *d = (float *) dest;
+ d[0] = (float)r / 255.0f;
+ d[1] = (float)g / 255.0f;
+ d[2] = (float)b / 255.0f;
+ }
+ return;
+
/* XXX lots more cases to add */
default:
- debug_printf("gallium: unhandled format in util_pack_color_ub()");
+ debug_print_format("gallium: unhandled format in util_pack_color_ub()", format);
+ assert(0);
}
}
@@ -155,6 +186,18 @@ util_pack_color(const float rgba[4], enum pipe_format format, void *dest)
*d = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3);
}
return;
+ case PIPE_FORMAT_A1R5G5B5_UNORM:
+ {
+ ushort *d = (ushort *) dest;
+ *d = ((a & 0x80) << 8) | ((r & 0xf8) << 7) | ((g & 0xf8) << 2) | (b >> 3);
+ }
+ return;
+ case PIPE_FORMAT_A4R4G4B4_UNORM:
+ {
+ ushort *d = (ushort *) dest;
+ *d = ((a & 0xf0) << 8) | ((r & 0xf0) << 4) | ((g & 0xf0) << 0) | (b >> 4);
+ }
+ return;
case PIPE_FORMAT_R32G32B32A32_FLOAT:
{
float *d = (float *) dest;
@@ -174,7 +217,8 @@ util_pack_color(const float rgba[4], enum pipe_format format, void *dest)
return;
/* XXX lots more cases to add */
default:
- debug_printf("gallium: unhandled format in util_pack_color()");
+ debug_print_format("gallium: unhandled format in util_pack_color()", format);
+ assert(0);
}
}
@@ -201,7 +245,8 @@ util_pack_z(enum pipe_format format, double z)
case PIPE_FORMAT_Z24X8_UNORM:
return ((uint) (z * 0xffffff)) << 8;
default:
- debug_printf("gallium: unhandled fomrat in util_pack_z()");
+ debug_print_format("gallium: unhandled format in util_pack_z()", format);
+ assert(0);
return 0;
}
}
diff --git a/src/gallium/drivers/softpipe/sp_context.c b/src/gallium/drivers/softpipe/sp_context.c
index f2ce0fdc6e..edf91ecafa 100644
--- a/src/gallium/drivers/softpipe/sp_context.c
+++ b/src/gallium/drivers/softpipe/sp_context.c
@@ -127,7 +127,7 @@ softpipe_create( struct pipe_screen *screen,
struct softpipe_context *softpipe = CALLOC_STRUCT(softpipe_context);
uint i;
-#if defined(__i386__) || defined(__386__)
+#ifdef PIPE_ARCH_X86
softpipe->use_sse = GETENV( "GALLIUM_NOSSE" ) == NULL;
#else
softpipe->use_sse = FALSE;
diff --git a/src/gallium/drivers/softpipe/sp_fs_sse.c b/src/gallium/drivers/softpipe/sp_fs_sse.c
index 4d569e1e22..25fdfea491 100644
--- a/src/gallium/drivers/softpipe/sp_fs_sse.c
+++ b/src/gallium/drivers/softpipe/sp_fs_sse.c
@@ -40,7 +40,7 @@
#include "tgsi/exec/tgsi_sse2.h"
-#if defined(__i386__) || defined(__386__)
+#ifdef PIPE_ARCH_X86
#include "rtasm/rtasm_x86sse.h"
diff --git a/src/gallium/include/pipe/p_debug.h b/src/gallium/include/pipe/p_debug.h
index 235c47abac..7a7312ea12 100644
--- a/src/gallium/include/pipe/p_debug.h
+++ b/src/gallium/include/pipe/p_debug.h
@@ -59,7 +59,6 @@ extern "C" {
#endif
#endif
-
void _debug_vprintf(const char *format, va_list ap);
@@ -103,16 +102,19 @@ debug_printf(const char *format, ...)
#endif
+#ifdef DEBUG
/**
* Dump a blob in hex to the same place that debug_printf sends its
* messages.
*/
-#ifdef DEBUG
-void debug_print_blob( const char *name,
- const void *blob,
- unsigned size );
+void debug_print_blob( const char *name, const void *blob, unsigned size );
+
+/* Print a message along with a prettified format string
+ */
+void debug_print_format(const char *msg, unsigned fmt );
#else
#define debug_print_blob(_name, _blob, _size) ((void)0)
+#define debug_print_format(_msg, _fmt) ((void)0)
#endif
diff --git a/src/gallium/include/pipe/p_format.h b/src/gallium/include/pipe/p_format.h
index 94c2eeb62b..bc23fe142e 100644
--- a/src/gallium/include/pipe/p_format.h
+++ b/src/gallium/include/pipe/p_format.h
@@ -337,88 +337,7 @@ enum pipe_format {
/**
* Builds pipe format name from format token.
*/
-static INLINE char *pf_sprint_name( char *str, enum pipe_format format )
-{
- strcpy( str, "PIPE_FORMAT_" );
- switch (pf_layout( format )) {
- case PIPE_FORMAT_LAYOUT_RGBAZS:
- {
- pipe_format_rgbazs_t rgbazs = (pipe_format_rgbazs_t) format;
- uint i;
- uint scale = 1 << (pf_exp8( rgbazs ) * 3);
-
- for (i = 0; i < 4; i++) {
- uint size = pf_size_xyzw( rgbazs, i );
-
- if (size == 0) {
- break;
- }
- switch (pf_swizzle_xyzw( rgbazs, i )) {
- case PIPE_FORMAT_COMP_R:
- strcat( str, "R" );
- break;
- case PIPE_FORMAT_COMP_G:
- strcat( str, "G" );
- break;
- case PIPE_FORMAT_COMP_B:
- strcat( str, "B" );
- break;
- case PIPE_FORMAT_COMP_A:
- strcat( str, "A" );
- break;
- case PIPE_FORMAT_COMP_0:
- strcat( str, "0" );
- break;
- case PIPE_FORMAT_COMP_1:
- strcat( str, "1" );
- break;
- case PIPE_FORMAT_COMP_Z:
- strcat( str, "Z" );
- break;
- case PIPE_FORMAT_COMP_S:
- strcat( str, "S" );
- break;
- }
- util_snprintf( &str[strlen( str )], 32, "%u", size * scale );
- }
- if (i != 0) {
- strcat( str, "_" );
- }
- switch (pf_type( rgbazs )) {
- case PIPE_FORMAT_TYPE_UNKNOWN:
- strcat( str, "NONE" );
- break;
- case PIPE_FORMAT_TYPE_FLOAT:
- strcat( str, "FLOAT" );
- break;
- case PIPE_FORMAT_TYPE_UNORM:
- strcat( str, "UNORM" );
- break;
- case PIPE_FORMAT_TYPE_SNORM:
- strcat( str, "SNORM" );
- break;
- case PIPE_FORMAT_TYPE_USCALED:
- strcat( str, "USCALED" );
- break;
- case PIPE_FORMAT_TYPE_SSCALED:
- strcat( str, "SSCALED" );
- break;
- }
- }
- break;
- case PIPE_FORMAT_LAYOUT_YCBCR:
- {
- pipe_format_ycbcr_t ycbcr = (pipe_format_ycbcr_t) format;
-
- strcat( str, "YCBCR" );
- if (pf_rev( ycbcr )) {
- strcat( str, "_REV" );
- }
- }
- break;
- }
- return str;
-}
+extern char *pf_sprint_name( char *str, enum pipe_format format );
/**
* Return bits for a particular component.
diff --git a/src/gallium/winsys/xlib/xm_winsys.c b/src/gallium/winsys/xlib/xm_winsys.c
index 5a424d0ac7..8a89278cde 100644
--- a/src/gallium/winsys/xlib/xm_winsys.c
+++ b/src/gallium/winsys/xlib/xm_winsys.c
@@ -296,10 +296,9 @@ xm_flush_frontbuffer(struct pipe_winsys *pws,
struct pipe_surface *surf,
void *context_private)
{
- /* The Xlib driver's front color surfaces are actually X Windows so
- * this flush is a no-op.
- * If we instead did front buffer rendering to a temporary XImage,
- * this would be the place to copy the Ximage to the on-screen Window.
+ /*
+ * The front color buffer is actually just another XImage buffer.
+ * This function copies that XImage to the actual X Window.
*/
XMesaContext xmctx = (XMesaContext) context_private;
xmesa_display_surface(xmctx->xm_buffer, surf);
diff --git a/src/mesa/main/pixel.c b/src/mesa/main/pixel.c
index eb4fd6e7c9..0e9915dd38 100644
--- a/src/mesa/main/pixel.c
+++ b/src/mesa/main/pixel.c
@@ -1341,6 +1341,21 @@ _mesa_scale_and_bias_depth(const GLcontext *ctx, GLuint n,
}
+void
+_mesa_scale_and_bias_depth_uint(const GLcontext *ctx, GLuint n,
+ GLuint depthValues[])
+{
+ const GLdouble max = (double) 0xffffffff;
+ const GLdouble scale = ctx->Pixel.DepthScale;
+ const GLdouble bias = ctx->Pixel.DepthBias * max;
+ GLuint i;
+ for (i = 0; i < n; i++) {
+ GLdouble d = (GLdouble) depthValues[i] * scale + bias;
+ d = CLAMP(d, 0.0, max);
+ depthValues[i] = (GLuint) d;
+ }
+}
+
/**********************************************************************/
/***** State Management *****/
diff --git a/src/mesa/main/pixel.h b/src/mesa/main/pixel.h
index 09155cfd70..3ba5b6689a 100644
--- a/src/mesa/main/pixel.h
+++ b/src/mesa/main/pixel.h
@@ -116,6 +116,9 @@ extern void
_mesa_scale_and_bias_depth(const GLcontext *ctx, GLuint n,
GLfloat depthValues[]);
+extern void
+_mesa_scale_and_bias_depth_uint(const GLcontext *ctx, GLuint n,
+ GLuint depthValues[]);
extern void
_mesa_update_pixel( GLcontext *ctx, GLuint newstate );
diff --git a/src/mesa/state_tracker/st_atom_sampler.c b/src/mesa/state_tracker/st_atom_sampler.c
index 5dd242ac66..e1ddb53b80 100644
--- a/src/mesa/state_tracker/st_atom_sampler.c
+++ b/src/mesa/state_tracker/st_atom_sampler.c
@@ -159,6 +159,11 @@ update_samplers(struct st_context *st)
sampler->max_lod = MIN2(texobj->MaxLevel, texobj->MaxLod);
#endif
+ sampler->border_color[0] = texobj->BorderColor[RCOMP];
+ sampler->border_color[1] = texobj->BorderColor[GCOMP];
+ sampler->border_color[2] = texobj->BorderColor[BCOMP];
+ sampler->border_color[3] = texobj->BorderColor[ACOMP];
+
sampler->max_anisotropy = texobj->MaxAnisotropy;
if (sampler->max_anisotropy > 1.0) {
sampler->min_img_filter = PIPE_TEX_FILTER_ANISO;
diff --git a/src/mesa/state_tracker/st_cb_bitmap.c b/src/mesa/state_tracker/st_cb_bitmap.c
index 6c2d9a4b89..836758a336 100644
--- a/src/mesa/state_tracker/st_cb_bitmap.c
+++ b/src/mesa/state_tracker/st_cb_bitmap.c
@@ -589,6 +589,11 @@ st_flush_bitmap_cache(struct st_context *st)
pipe_surface_unmap(surf);
pipe_surface_reference(&surf, NULL);
+ /* flush in case the previous texture contents haven't been
+ * used yet. XXX this is not ideal! Revisit.
+ */
+ st->pipe->flush( st->pipe, 0x0, NULL );
+
pipe->texture_update(pipe, cache->texture, 0, 0x1);
draw_bitmap_quad(st->ctx,
@@ -597,7 +602,6 @@ st_flush_bitmap_cache(struct st_context *st)
st->ctx->Current.RasterPos[2],
BITMAP_CACHE_WIDTH, BITMAP_CACHE_HEIGHT,
cache->texture);
-
}
reset_cache(st);
}
diff --git a/src/mesa/state_tracker/st_cb_fbo.c b/src/mesa/state_tracker/st_cb_fbo.c
index 69dde56e55..2d741d9390 100644
--- a/src/mesa/state_tracker/st_cb_fbo.c
+++ b/src/mesa/state_tracker/st_cb_fbo.c
@@ -370,6 +370,8 @@ st_render_texture(GLcontext *ctx,
att->TextureLevel,
att->Zoffset);
assert(strb->surface);
+ assert(screen->is_format_supported(screen, strb->surface->format, PIPE_TEXTURE));
+ assert(screen->is_format_supported(screen, strb->surface->format, PIPE_SURFACE));
init_renderbuffer_bits(strb, pt->format);
diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c
index 3454c3e8e8..3d9c550d8c 100644
--- a/src/mesa/state_tracker/st_cb_texture.c
+++ b/src/mesa/state_tracker/st_cb_texture.c
@@ -31,6 +31,7 @@
#include "main/image.h"
#include "main/macros.h"
#include "main/mipmap.h"
+#include "main/pixel.h"
#include "main/texcompress.h"
#include "main/texformat.h"
#include "main/teximage.h"
@@ -1038,7 +1039,6 @@ fallback_copy_texsubimage(GLcontext *ctx,
const uint face = texture_face(target);
struct pipe_texture *pt = stImage->pt;
struct pipe_surface *src_surf, *dest_surf;
- GLfloat *data;
GLint row, yStep;
st_flush(ctx->st, PIPE_FLUSH_RENDER_CACHE, NULL);
@@ -1056,25 +1056,38 @@ fallback_copy_texsubimage(GLcontext *ctx,
dest_surf = screen->get_tex_surface(screen, pt, face, level, destZ);
- /* buffer for one row */
- data = (GLfloat *) malloc(width * 4 * sizeof(GLfloat));
+ assert(width <= MAX_WIDTH);
- /* do copy row by row */
- for (row = 0; row < height; row++) {
- pipe_get_tile_rgba(pipe, src_surf, srcX, srcY + row, width, 1, data);
-
- /* XXX we're ignoring convolution for now */
- if (ctx->_ImageTransferState) {
- _mesa_apply_rgba_transfer_ops(ctx,
- ctx->_ImageTransferState & ~IMAGE_CONVOLUTION_BIT,
- width, (GLfloat (*)[4])data);
+ /*
+ * To avoid a large temp memory allocation, do copy row by row.
+ */
+ if (baseFormat == GL_DEPTH_COMPONENT) {
+ const GLboolean scaleOrBias = (ctx->Pixel.DepthScale != 1.0F ||
+ ctx->Pixel.DepthBias != 0.0F);
+
+ for (row = 0; row < height; row++, srcY++, destY += yStep) {
+ uint data[MAX_WIDTH];
+ pipe_get_tile_z(pipe, src_surf, srcX, srcY, width, 1, data);
+ if (scaleOrBias) {
+ _mesa_scale_and_bias_depth_uint(ctx, width, data);
+ }
+ pipe_put_tile_z(pipe, dest_surf, destX, destY, width, 1, data);
+ }
+ }
+ else {
+ /* RGBA format */
+ for (row = 0; row < height; row++, srcY++, destY += yStep) {
+ float data[4 * MAX_WIDTH];
+ pipe_get_tile_rgba(pipe, src_surf, srcX, srcY, width, 1, data);
+ /* XXX we're ignoring convolution for now */
+ if (ctx->_ImageTransferState) {
+ _mesa_apply_rgba_transfer_ops(ctx,
+ ctx->_ImageTransferState & ~IMAGE_CONVOLUTION_BIT,
+ width, (GLfloat (*)[4]) data);
+ }
+ pipe_put_tile_rgba(pipe, dest_surf, destX, destY, width, 1, data);
}
-
- pipe_put_tile_rgba(pipe, dest_surf, destX, destY, width, 1, data);
- destY += yStep;
}
-
- free(data);
}
@@ -1111,6 +1124,7 @@ do_copy_texsubimage(GLcontext *ctx,
struct pipe_surface *dest_surface;
uint dest_format, src_format;
uint do_flip = FALSE;
+ GLboolean use_fallback = GL_TRUE;
(void) texImage;
@@ -1178,7 +1192,12 @@ do_copy_texsubimage(GLcontext *ctx,
srcX, srcY,
/* size */
width, height);
- } else {
+ use_fallback = GL_FALSE;
+ }
+ else if (screen->is_format_supported(screen, strb->surface->format,
+ PIPE_TEXTURE) &&
+ screen->is_format_supported(screen, dest_surface->format,
+ PIPE_SURFACE)) {
util_blit_pixels(ctx->st->blit,
strb->surface,
srcX, do_flip ? srcY + height : srcY,
@@ -1186,10 +1205,12 @@ do_copy_texsubimage(GLcontext *ctx,
dest_surface,
destX, destY, destX + width, destY + height,
0.0, PIPE_TEX_MIPFILTER_NEAREST);
+ use_fallback = GL_FALSE;
}
#endif
}
- else {
+
+ if (use_fallback) {
fallback_copy_texsubimage(ctx, target, level,
strb, stImage, baseFormat,
destX, destY, destZ,