diff options
Diffstat (limited to 'mesalib/src/mesa/main')
-rw-r--r-- | mesalib/src/mesa/main/config.h | 11 | ||||
-rw-r--r-- | mesalib/src/mesa/main/dd.h | 14 | ||||
-rw-r--r-- | mesalib/src/mesa/main/extensions.c | 1 | ||||
-rw-r--r-- | mesalib/src/mesa/main/get_hash_params.py | 7 | ||||
-rwxr-xr-x | mesalib/src/mesa/main/imports.h | 6 | ||||
-rw-r--r-- | mesalib/src/mesa/main/mtypes.h | 1 | ||||
-rw-r--r-- | mesalib/src/mesa/main/teximage.c | 321 | ||||
-rw-r--r-- | mesalib/src/mesa/main/teximage.h | 10 | ||||
-rw-r--r-- | mesalib/src/mesa/main/texstore.c | 87 | ||||
-rw-r--r-- | mesalib/src/mesa/main/texstore.h | 7 | ||||
-rw-r--r-- | mesalib/src/mesa/main/uniform_query.cpp | 2 |
11 files changed, 443 insertions, 24 deletions
diff --git a/mesalib/src/mesa/main/config.h b/mesalib/src/mesa/main/config.h index 04175991e..a6d44f9fe 100644 --- a/mesalib/src/mesa/main/config.h +++ b/mesalib/src/mesa/main/config.h @@ -293,6 +293,17 @@ #define PERFQUERY_HAVE_GPA_EXTENDED_COUNTERS 0 /*@}*/ +/** For GL_ARB_compute_shader */ +/*@{*/ +#define MAX_COMPUTE_UNIFORM_BLOCKS 12 +#define MAX_COMPUTE_TEXTURE_IMAGE_UNITS 16 +#define MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 8 +#define MAX_COMPUTE_ATOMIC_COUNTERS 8 +#define MAX_COMPUTE_SHARED_MEMORY_SIZE 32768 +#define MAX_COMPUTE_UNIFORM_COMPONENTS 512 +#define MAX_COMPUTE_IMAGE_UNIFORMS 8 +/*@}*/ + /* * Color channel component order * diff --git a/mesalib/src/mesa/main/dd.h b/mesalib/src/mesa/main/dd.h index 633ea2c3a..89765351e 100644 --- a/mesalib/src/mesa/main/dd.h +++ b/mesalib/src/mesa/main/dd.h @@ -239,6 +239,20 @@ struct dd_function_table { struct gl_texture_image *texImage ); /** + * Called by glClearTex[Sub]Image + * + * Clears a rectangular region of the image to a given value. The + * clearValue argument is either NULL or points to a single texel to use as + * the clear value in the same internal format as the texture image. If it + * is NULL then the texture should be cleared to zeroes. + */ + void (*ClearTexSubImage)(struct gl_context *ctx, + struct gl_texture_image *texImage, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + const GLvoid *clearValue); + + /** * Called by glCopyTex[Sub]Image[123]D(). * * This function should copy a rectangular region in the rb to a single diff --git a/mesalib/src/mesa/main/extensions.c b/mesalib/src/mesa/main/extensions.c index 92e3f0d68..9ac8377a4 100644 --- a/mesalib/src/mesa/main/extensions.c +++ b/mesalib/src/mesa/main/extensions.c @@ -90,6 +90,7 @@ static const struct extension extension_table[] = { { "GL_ARB_blend_func_extended", o(ARB_blend_func_extended), GL, 2009 }, { "GL_ARB_buffer_storage", o(ARB_buffer_storage), GL, 2013 }, { "GL_ARB_clear_buffer_object", o(dummy_true), GL, 2012 }, + { "GL_ARB_clear_texture", o(ARB_clear_texture), GL, 2013 }, { "GL_ARB_color_buffer_float", o(ARB_color_buffer_float), GL, 2004 }, { "GL_ARB_compressed_texture_pixel_storage", o(dummy_true), GL, 2011 }, { "GL_ARB_compute_shader", o(ARB_compute_shader), GL, 2012 }, diff --git a/mesalib/src/mesa/main/get_hash_params.py b/mesalib/src/mesa/main/get_hash_params.py index d45962d95..35d6172a3 100644 --- a/mesalib/src/mesa/main/get_hash_params.py +++ b/mesalib/src/mesa/main/get_hash_params.py @@ -774,6 +774,13 @@ descriptor=[ # GL_ARB_compute_shader [ "MAX_COMPUTE_WORK_GROUP_INVOCATIONS", "CONTEXT_INT(Const.MaxComputeWorkGroupInvocations), extra_ARB_compute_shader" ], + [ "MAX_COMPUTE_UNIFORM_BLOCKS", "CONST(MAX_COMPUTE_UNIFORM_BLOCKS), extra_ARB_compute_shader" ], + [ "MAX_COMPUTE_TEXTURE_IMAGE_UNITS", "CONST(MAX_COMPUTE_TEXTURE_IMAGE_UNITS), extra_ARB_compute_shader" ], + [ "MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS", "CONST(MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS), extra_ARB_compute_shader" ], + [ "MAX_COMPUTE_ATOMIC_COUNTERS", "CONST(MAX_COMPUTE_ATOMIC_COUNTERS), extra_ARB_compute_shader" ], + [ "MAX_COMPUTE_SHARED_MEMORY_SIZE", "CONST(MAX_COMPUTE_SHARED_MEMORY_SIZE), extra_ARB_compute_shader" ], + [ "MAX_COMPUTE_UNIFORM_COMPONENTS", "CONST(MAX_COMPUTE_UNIFORM_COMPONENTS), extra_ARB_compute_shader" ], + [ "MAX_COMPUTE_IMAGE_UNIFORMS", "CONST(MAX_COMPUTE_IMAGE_UNIFORMS), extra_ARB_compute_shader" ], # GL_ARB_gpu_shader5 [ "MAX_GEOMETRY_SHADER_INVOCATIONS", "CONST(MAX_GEOMETRY_SHADER_INVOCATIONS), extra_ARB_gpu_shader5" ], diff --git a/mesalib/src/mesa/main/imports.h b/mesalib/src/mesa/main/imports.h index ba869286f..aee495a4c 100755 --- a/mesalib/src/mesa/main/imports.h +++ b/mesalib/src/mesa/main/imports.h @@ -273,10 +273,12 @@ static inline int IROUND_POS(float f) return (int) (f + 0.5F); } +#ifdef __x86_64__ +# include <xmmintrin.h> +#endif /** * Convert float to int using a fast method. The rounding mode may vary. - * XXX We could use an x86-64/SSE2 version here. */ static inline int F_TO_I(float f) { @@ -291,6 +293,8 @@ static inline int F_TO_I(float f) fistp r } return r; +#elif defined(__x86_64__) + return _mm_cvt_ss2si(_mm_load_ss(&f)); #else return IROUND(f); #endif diff --git a/mesalib/src/mesa/main/mtypes.h b/mesalib/src/mesa/main/mtypes.h index 91d9172f9..3f60a5530 100644 --- a/mesalib/src/mesa/main/mtypes.h +++ b/mesalib/src/mesa/main/mtypes.h @@ -3527,6 +3527,7 @@ struct gl_extensions GLboolean ARB_base_instance; GLboolean ARB_blend_func_extended; GLboolean ARB_buffer_storage; + GLboolean ARB_clear_texture; GLboolean ARB_color_buffer_float; GLboolean ARB_compute_shader; GLboolean ARB_conservative_depth; diff --git a/mesalib/src/mesa/main/teximage.c b/mesalib/src/mesa/main/teximage.c index a06959463..fb2dee7d8 100644 --- a/mesalib/src/mesa/main/teximage.c +++ b/mesalib/src/mesa/main/teximage.c @@ -51,6 +51,7 @@ #include "textureview.h" #include "mtypes.h" #include "glformats.h" +#include "texstore.h" /** @@ -2010,6 +2011,43 @@ _mesa_legal_texture_base_format_for_target(struct gl_context *ctx, return true; } +static bool +texture_formats_agree(GLenum internalFormat, + GLenum format) +{ + GLboolean colorFormat; + GLboolean is_format_depth_or_depthstencil; + GLboolean is_internalFormat_depth_or_depthstencil; + + /* Even though there are no color-index textures, we still have to support + * uploading color-index data and remapping it to RGB via the + * GL_PIXEL_MAP_I_TO_[RGBA] tables. + */ + const GLboolean indexFormat = (format == GL_COLOR_INDEX); + + is_internalFormat_depth_or_depthstencil = + _mesa_is_depth_format(internalFormat) || + _mesa_is_depthstencil_format(internalFormat); + + is_format_depth_or_depthstencil = + _mesa_is_depth_format(format) || + _mesa_is_depthstencil_format(format); + + colorFormat = _mesa_is_color_format(format); + + if (_mesa_is_color_format(internalFormat) && !colorFormat && !indexFormat) + return false; + + if (is_internalFormat_depth_or_depthstencil != + is_format_depth_or_depthstencil) + return false; + + if (_mesa_is_ycbcr_format(internalFormat) != _mesa_is_ycbcr_format(format)) + return false; + + return true; +} + /** * Test the glTexImage[123]D() parameters for errors. * @@ -2043,17 +2081,8 @@ texture_error_check( struct gl_context *ctx, GLint width, GLint height, GLint depth, GLint border ) { - GLboolean colorFormat; - GLboolean is_format_depth_or_depthstencil; - GLboolean is_internalFormat_depth_or_depthstencil; GLenum err; - /* Even though there are no color-index textures, we still have to support - * uploading color-index data and remapping it to RGB via the - * GL_PIXEL_MAP_I_TO_[RGBA] tables. - */ - const GLboolean indexFormat = (format == GL_COLOR_INDEX); - /* Note: for proxy textures, some error conditions immediately generate * a GL error in the usual way. But others do not generate a GL error. * Instead, they cause the width, height, depth, format fields of the @@ -2136,18 +2165,7 @@ texture_error_check( struct gl_context *ctx, } /* make sure internal format and format basically agree */ - is_internalFormat_depth_or_depthstencil = - _mesa_is_depth_format(internalFormat) || - _mesa_is_depthstencil_format(internalFormat); - - is_format_depth_or_depthstencil = - _mesa_is_depth_format(format) || - _mesa_is_depthstencil_format(format); - - colorFormat = _mesa_is_color_format(format); - if ((_mesa_is_color_format(internalFormat) && !colorFormat && !indexFormat) || - (is_internalFormat_depth_or_depthstencil != is_format_depth_or_depthstencil) || - (_mesa_is_ycbcr_format(internalFormat) != _mesa_is_ycbcr_format(format))) { + if (!texture_formats_agree(internalFormat, format)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTexImage%dD(incompatible internalFormat = %s, format = %s)", dimensions, _mesa_lookup_enum_by_nr(internalFormat), @@ -3818,6 +3836,266 @@ _mesa_CopyTexSubImage3D( GLenum target, GLint level, x, y, width, height); } +static bool +check_clear_tex_image(struct gl_context *ctx, + const char *function, + struct gl_texture_image *texImage, + GLenum format, GLenum type, + const void *data, + GLubyte *clearValue) +{ + struct gl_texture_object *texObj = texImage->TexObject; + static const GLubyte zeroData[MAX_PIXEL_BYTES]; + GLenum internalFormat = texImage->InternalFormat; + GLenum err; + + if (texObj->Target == GL_TEXTURE_BUFFER) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(buffer texture)", function); + return false; + } + + if (_mesa_is_compressed_format(ctx, internalFormat)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(compressed texture)", function); + return false; + } + + err = _mesa_error_check_format_and_type(ctx, format, type); + if (err != GL_NO_ERROR) { + _mesa_error(ctx, err, + "%s(incompatible format = %s, type = %s)", + function, + _mesa_lookup_enum_by_nr(format), + _mesa_lookup_enum_by_nr(type)); + return false; + } + + /* make sure internal format and format basically agree */ + if (!texture_formats_agree(internalFormat, format)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(incompatible internalFormat = %s, format = %s)", + function, + _mesa_lookup_enum_by_nr(internalFormat), + _mesa_lookup_enum_by_nr(format)); + return false; + } + + if (ctx->Version >= 30 || ctx->Extensions.EXT_texture_integer) { + /* both source and dest must be integer-valued, or neither */ + if (_mesa_is_format_integer_color(texImage->TexFormat) != + _mesa_is_enum_format_integer(format)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(integer/non-integer format mismatch)", + function); + return false; + } + } + + if (!_mesa_texstore(ctx, + 1, /* dims */ + texImage->_BaseFormat, + texImage->TexFormat, + 0, /* dstRowStride */ + &clearValue, + 1, 1, 1, /* srcWidth/Height/Depth */ + format, type, + data ? data : zeroData, + &ctx->DefaultPacking)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid format)", function); + return false; + } + + return true; +} + +static struct gl_texture_object * +get_tex_obj_for_clear(struct gl_context *ctx, + const char *function, + GLuint texture) +{ + struct gl_texture_object *texObj; + + if (texture == 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(zero texture)", function); + return NULL; + } + + texObj = _mesa_lookup_texture(ctx, texture); + + if (texObj == NULL) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(non-gen name)", function); + return NULL; + } + + if (texObj->Target == 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(unbound tex)", function); + return NULL; + } + + return texObj; +} + +static int +get_tex_images_for_clear(struct gl_context *ctx, + const char *function, + struct gl_texture_object *texObj, + GLint level, + struct gl_texture_image **texImages) +{ + GLenum target; + int i; + + if (level < 0 || level >= MAX_TEXTURE_LEVELS) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid level)", function); + return 0; + } + + if (texObj->Target == GL_TEXTURE_CUBE_MAP) { + for (i = 0; i < MAX_FACES; i++) { + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; + + texImages[i] = _mesa_select_tex_image(ctx, texObj, target, level); + if (texImages[i] == NULL) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(invalid level)", function); + return 0; + } + } + + return MAX_FACES; + } + + texImages[0] = _mesa_select_tex_image(ctx, texObj, texObj->Target, level); + + if (texImages[0] == NULL) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid level)", function); + return 0; + } + + return 1; +} + +void GLAPIENTRY +_mesa_ClearTexSubImage( GLuint texture, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum type, const void *data ) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_texture_object *texObj; + struct gl_texture_image *texImages[MAX_FACES]; + GLubyte clearValue[MAX_FACES][MAX_PIXEL_BYTES]; + int i, numImages; + int minDepth, maxDepth; + + texObj = get_tex_obj_for_clear(ctx, "glClearTexSubImage", texture); + + if (texObj == NULL) + return; + + _mesa_lock_texture(ctx, texObj); + + numImages = get_tex_images_for_clear(ctx, "glClearTexSubImage", + texObj, level, texImages); + if (numImages == 0) + goto out; + + if (numImages == 1) { + minDepth = -(int) texImages[0]->Border; + maxDepth = texImages[0]->Depth; + } else { + minDepth = 0; + maxDepth = numImages; + } + + if (xoffset < -(GLint) texImages[0]->Border || + yoffset < -(GLint) texImages[0]->Border || + zoffset < minDepth || + width < 0 || + height < 0 || + depth < 0 || + xoffset + width > texImages[0]->Width || + yoffset + height > texImages[0]->Height || + zoffset + depth > maxDepth) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glClearSubTexImage(invalid dimensions)"); + goto out; + } + + if (numImages == 1) { + if (check_clear_tex_image(ctx, "glClearTexSubImage", + texImages[0], + format, type, data, clearValue[0])) { + ctx->Driver.ClearTexSubImage(ctx, + texImages[0], + xoffset, yoffset, zoffset, + width, height, depth, + data ? clearValue[0] : NULL); + } + } else { + for (i = zoffset; i < zoffset + depth; i++) { + if (!check_clear_tex_image(ctx, "glClearTexSubImage", + texImages[i], + format, type, data, clearValue[i])) + goto out; + } + for (i = zoffset; i < zoffset + depth; i++) { + ctx->Driver.ClearTexSubImage(ctx, + texImages[i], + xoffset, yoffset, 0, + width, height, 1, + data ? clearValue[i] : NULL); + } + } + + out: + _mesa_unlock_texture(ctx, texObj); +} + +void GLAPIENTRY +_mesa_ClearTexImage( GLuint texture, GLint level, + GLenum format, GLenum type, const void *data ) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_texture_object *texObj; + struct gl_texture_image *texImages[MAX_FACES]; + GLubyte clearValue[MAX_FACES][MAX_PIXEL_BYTES]; + int i, numImages; + + texObj = get_tex_obj_for_clear(ctx, "glClearTexImage", texture); + + if (texObj == NULL) + return; + + _mesa_lock_texture(ctx, texObj); + + numImages = get_tex_images_for_clear(ctx, "glClearTexImage", + texObj, level, texImages); + + for (i = 0; i < numImages; i++) { + if (!check_clear_tex_image(ctx, "glClearTexImage", + texImages[i], + format, type, data, + clearValue[i])) + goto out; + } + + for (i = 0; i < numImages; i++) { + ctx->Driver.ClearTexSubImage(ctx, texImages[i], + -(GLint) texImages[i]->Border, /* xoffset */ + -(GLint) texImages[i]->Border, /* yoffset */ + -(GLint) texImages[i]->Border, /* zoffset */ + texImages[i]->Width, + texImages[i]->Height, + texImages[i]->Depth, + data ? clearValue[i] : NULL); + } + +out: + _mesa_unlock_texture(ctx, texObj); +} + @@ -4578,7 +4856,6 @@ _mesa_TexStorage2DMultisample(GLenum target, GLsizei samples, "glTexStorage2DMultisample"); } - void GLAPIENTRY _mesa_TexStorage3DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, diff --git a/mesalib/src/mesa/main/teximage.h b/mesalib/src/mesa/main/teximage.h index dd1504b40..42305f44f 100644 --- a/mesalib/src/mesa/main/teximage.h +++ b/mesalib/src/mesa/main/teximage.h @@ -261,6 +261,16 @@ _mesa_CopyTexSubImage3D( GLenum target, GLint level, extern void GLAPIENTRY +_mesa_ClearTexSubImage( GLuint texture, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum type, const void *data ); + +extern void GLAPIENTRY +_mesa_ClearTexImage( GLuint texture, GLint level, + GLenum format, GLenum type, const void *data ); + +extern void GLAPIENTRY _mesa_CompressedTexImage1D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, diff --git a/mesalib/src/mesa/main/texstore.c b/mesalib/src/mesa/main/texstore.c index d363f9faa..aea53f8d1 100644 --- a/mesalib/src/mesa/main/texstore.c +++ b/mesalib/src/mesa/main/texstore.c @@ -3830,6 +3830,21 @@ _mesa_texstore_can_use_memcpy(struct gl_context *ctx, return GL_FALSE; } + /* Depth texture data needs clamping in following cases: + * - Floating point dstFormat with signed srcType: clamp to [0.0, 1.0]. + * - Fixed point dstFormat with signed srcType: clamp to [0, 2^n -1]. + * + * All the cases except one (float dstFormat with float srcType) are ruled + * out by _mesa_format_matches_format_and_type() check above. Handle the + * remaining case here. + */ + if ((baseInternalFormat == GL_DEPTH_COMPONENT || + baseInternalFormat == GL_DEPTH_STENCIL) && + (srcType == GL_FLOAT || + srcType == GL_FLOAT_32_UNSIGNED_INT_24_8_REV)) { + return GL_FALSE; + } + return GL_TRUE; } @@ -4079,6 +4094,78 @@ _mesa_store_texsubimage(struct gl_context *ctx, GLuint dims, format, type, pixels, packing, "glTexSubImage"); } +static void +clear_image_to_zero(GLubyte *dstMap, GLint dstRowStride, + GLsizei width, GLsizei height, + GLsizei clearValueSize) +{ + GLsizei y; + + for (y = 0; y < height; y++) { + memset(dstMap, 0, clearValueSize * width); + dstMap += dstRowStride; + } +} + +static void +clear_image_to_value(GLubyte *dstMap, GLint dstRowStride, + GLsizei width, GLsizei height, + const GLvoid *clearValue, + GLsizei clearValueSize) +{ + GLsizei y, x; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + memcpy(dstMap, clearValue, clearValueSize); + dstMap += clearValueSize; + } + dstMap += dstRowStride - clearValueSize * width; + } +} + +/* + * Fallback for Driver.ClearTexSubImage(). + */ +void +_mesa_store_cleartexsubimage(struct gl_context *ctx, + struct gl_texture_image *texImage, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + const GLvoid *clearValue) +{ + GLubyte *dstMap; + GLint dstRowStride; + GLsizeiptr clearValueSize; + GLsizei z; + + clearValueSize = _mesa_get_format_bytes(texImage->TexFormat); + + for (z = 0; z < depth; z++) { + ctx->Driver.MapTextureImage(ctx, texImage, + z + zoffset, xoffset, yoffset, + width, height, + GL_MAP_WRITE_BIT, + &dstMap, &dstRowStride); + if (dstMap == NULL) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glClearTex*Image"); + return; + } + + if (clearValue) { + clear_image_to_value(dstMap, dstRowStride, + width, height, + clearValue, + clearValueSize); + } else { + clear_image_to_zero(dstMap, dstRowStride, + width, height, + clearValueSize); + } + + ctx->Driver.UnmapTextureImage(ctx, texImage, z + zoffset); + } +} /** * Fallback for Driver.CompressedTexImage() diff --git a/mesalib/src/mesa/main/texstore.h b/mesalib/src/mesa/main/texstore.h index c4cfffde6..dd1e1d015 100644 --- a/mesalib/src/mesa/main/texstore.h +++ b/mesalib/src/mesa/main/texstore.h @@ -118,6 +118,13 @@ _mesa_store_texsubimage(struct gl_context *ctx, GLuint dims, extern void +_mesa_store_cleartexsubimage(struct gl_context *ctx, + struct gl_texture_image *texImage, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + const GLvoid *clearValue); + +extern void _mesa_store_compressed_teximage(struct gl_context *ctx, GLuint dims, struct gl_texture_image *texImage, GLsizei imageSize, const GLvoid *data); diff --git a/mesalib/src/mesa/main/uniform_query.cpp b/mesalib/src/mesa/main/uniform_query.cpp index 480bc6f16..609d94bd7 100644 --- a/mesalib/src/mesa/main/uniform_query.cpp +++ b/mesalib/src/mesa/main/uniform_query.cpp @@ -91,7 +91,7 @@ _mesa_GetActiveUniformsiv(GLuint program, if (uniformCount < 0) { _mesa_error(ctx, GL_INVALID_VALUE, - "glGetUniformIndices(uniformCount < 0)"); + "glGetActiveUniformsiv(uniformCount < 0)"); return; } |