164164 #endif
165165#endif
166166
167- // Fast power-of-two texture wrap (SW_REPEAT mode only)
168- // When defined, textures whose width/height are powers of two use a bitmask
169- // wrap (`x & (size-1)`) instead of `floorf`-based fractional wrap or the signed `%` chain in the linear sampler
170- // Saves a software divide on Xtensa and a few instructions everywhere
171- // NPOT textures keep using the original path via a runtime `(size & (size-1)) == 0` check,
172- // so SW_REPEAT remains correct for them
173- // The only observable behavior change is for POT textures sampled with negative UV coordinates:
174- // bitmask wrap (two's complement) can differ from `sw_fract` by one texel
175- // Off by default to keep bit-for-bit behavior; opt in if you control your asset UVs
176- //
177- //#define SW_TEXTURE_REPEAT_POT_FAST
167+ // Full NPOT texture support (enabled by default)
168+ // When disabled, SW_REPEAT requires POT on its axis (fast bitmask wrap)
169+ // SW_CLAMP remains supported for any dimension, per-axis
170+ #define SW_SUPPORT_NPOT_TEXTURE true
178171
179172//----------------------------------------------------------------------------------
180173// OpenGL Compatibility Types
@@ -2406,6 +2399,14 @@ static inline bool sw_texture_alloc(sw_texture_t *texture, const void *data, int
24062399 int bpp = SW_PIXELFORMAT_SIZE [format ];
24072400 int newSize = w * h * bpp ;
24082401
2402+ if (texture -> pixels == NULL )
2403+ {
2404+ texture -> minFilter = SW_NEAREST ;
2405+ texture -> magFilter = SW_NEAREST ;
2406+ texture -> sWrap = SW_CLAMP ;
2407+ texture -> tWrap = SW_CLAMP ;
2408+ }
2409+
24092410 if (newSize > texture -> allocSz )
24102411 {
24112412 void * ptr = SW_REALLOC (texture -> pixels , newSize );
@@ -2470,38 +2471,25 @@ static inline void sw_texture_sample_nearest(float *SW_RESTRICT color, const sw_
24702471{
24712472 int x , y ;
24722473
2473- #ifdef SW_TEXTURE_REPEAT_POT_FAST
2474- if ((tex -> sWrap == SW_REPEAT ) && ((tex -> width & tex -> wMinus1 ) == 0 ))
2475- {
2476- x = (int )(u * tex -> width ) & tex -> wMinus1 ;
2477- }
2478- else
2479- #endif
2480- {
2481- u = (tex -> sWrap == SW_REPEAT )? sw_fract (u ) : sw_saturate (u );
2482- x = (int )(u * tex -> width );
2483- }
2474+ #if SW_SUPPORT_NPOT_TEXTURE
2475+ if (tex -> sWrap == SW_REPEAT ) x = (int )(sw_fract (u )* tex -> width );
2476+ else x = (int )(sw_saturate (u )* tex -> width );
24842477
2485- #ifdef SW_TEXTURE_REPEAT_POT_FAST
2486- if ((tex -> tWrap == SW_REPEAT ) && ((tex -> height & tex -> hMinus1 ) == 0 ))
2487- {
2488- y = (int )(v * tex -> height ) & tex -> hMinus1 ;
2489- }
2490- else
2478+ if (tex -> tWrap == SW_REPEAT ) y = (int )(sw_fract (v )* tex -> height );
2479+ else y = (int )(sw_saturate (v )* tex -> height );
2480+ #else
2481+ if (tex -> sWrap == SW_REPEAT ) x = (int )(u * tex -> width ) & tex -> wMinus1 ;
2482+ else x = (int )(sw_saturate (u )* tex -> width );
2483+
2484+ if (tex -> tWrap == SW_REPEAT ) y = (int )(v * tex -> height ) & tex -> hMinus1 ;
2485+ else y = (int )(sw_saturate (v )* tex -> height );
24912486#endif
2492- {
2493- v = (tex -> tWrap == SW_REPEAT )? sw_fract (v ) : sw_saturate (v );
2494- y = (int )(v * tex -> height );
2495- }
24962487
24972488 tex -> readColor (color , tex -> pixels , y * tex -> width + x );
24982489}
24992490
25002491static inline void sw_texture_sample_linear (float * SW_RESTRICT color , const sw_texture_t * SW_RESTRICT tex , float u , float v )
25012492{
2502- // TODO: With a bit more cleverness the number of operations can
2503- // be clearly reduced, but for now it works fine
2504-
25052493 float xf = (u * tex -> width ) - 0.5f ;
25062494 float yf = (v * tex -> height ) - 0.5f ;
25072495
@@ -2514,41 +2502,36 @@ static inline void sw_texture_sample_linear(float *SW_RESTRICT color, const sw_t
25142502 int x1 = x0 + 1 ;
25152503 int y1 = y0 + 1 ;
25162504
2517- if (tex -> sWrap == SW_CLAMP )
2505+ if (tex -> sWrap == SW_REPEAT )
25182506 {
2519- x0 = (x0 > tex -> wMinus1 )? tex -> wMinus1 : x0 ;
2520- x1 = (x1 > tex -> wMinus1 )? tex -> wMinus1 : x1 ;
2521- }
2522- #ifdef SW_TEXTURE_REPEAT_POT_FAST
2523- else if ((tex -> width & tex -> wMinus1 ) == 0 )
2524- {
2525- // POT fast path: bitmask wrap covers negative ints via two's complement
2507+ #if SW_SUPPORT_NPOT_TEXTURE
2508+ x0 = (x0 %tex -> width + tex -> width )%tex -> width ;
2509+ x1 = (x1 %tex -> width + tex -> width )%tex -> width ;
2510+ #else
25262511 x0 = x0 & tex -> wMinus1 ;
25272512 x1 = x1 & tex -> wMinus1 ;
2513+ #endif
25282514 }
2529- #endif
25302515 else
25312516 {
2532- x0 = (x0 % tex -> width + tex -> width )% tex -> width ;
2533- x1 = (x1 % tex -> width + tex -> width )% tex -> width ;
2517+ x0 = sw_clamp_int (x0 , 0 , tex -> wMinus1 ) ;
2518+ x1 = sw_clamp_int (x1 , 0 , tex -> wMinus1 ) ;
25342519 }
25352520
2536- if (tex -> tWrap == SW_CLAMP )
2537- {
2538- y0 = (y0 > tex -> hMinus1 )? tex -> hMinus1 : y0 ;
2539- y1 = (y1 > tex -> hMinus1 )? tex -> hMinus1 : y1 ;
2540- }
2541- #ifdef SW_TEXTURE_REPEAT_POT_FAST
2542- else if ((tex -> height & tex -> hMinus1 ) == 0 )
2521+ if (tex -> tWrap == SW_REPEAT )
25432522 {
2523+ #if SW_SUPPORT_NPOT_TEXTURE
2524+ y0 = (y0 %tex -> height + tex -> height )%tex -> height ;
2525+ y1 = (y1 %tex -> height + tex -> height )%tex -> height ;
2526+ #else
25442527 y0 = y0 & tex -> hMinus1 ;
25452528 y1 = y1 & tex -> hMinus1 ;
2529+ #endif
25462530 }
2547- #endif
25482531 else
25492532 {
2550- y0 = (y0 % tex -> height + tex -> height )% tex -> height ;
2551- y1 = (y1 % tex -> height + tex -> height )% tex -> height ;
2533+ y0 = sw_clamp_int (y0 , 0 , tex -> hMinus1 ) ;
2534+ y1 = sw_clamp_int (y1 , 0 , tex -> hMinus1 ) ;
25522535 }
25532536
25542537 float c00 [4 ], c10 [4 ], c01 [4 ], c11 [4 ];
@@ -5187,11 +5170,25 @@ void swTexParameteri(int param, int value)
51875170 case SW_TEXTURE_WRAP_S :
51885171 {
51895172 if (!sw_is_texture_wrap_valid (value )) { RLSW .errCode = SW_INVALID_ENUM ; return ; }
5173+ #if !SW_SUPPORT_NPOT_TEXTURE
5174+ if (value == SW_REPEAT && (RLSW .boundTexture -> width & RLSW .boundTexture -> wMinus1 ) != 0 )
5175+ {
5176+ RLSW .errCode = SW_INVALID_OPERATION ;
5177+ return ;
5178+ }
5179+ #endif
51905180 RLSW .boundTexture -> sWrap = (SWwrap )value ;
51915181 } break ;
51925182 case SW_TEXTURE_WRAP_T :
51935183 {
51945184 if (!sw_is_texture_wrap_valid (value )) { RLSW .errCode = SW_INVALID_ENUM ; return ; }
5185+ #if !SW_SUPPORT_NPOT_TEXTURE
5186+ if (value == SW_REPEAT && (RLSW .boundTexture -> height & RLSW .boundTexture -> hMinus1 ) != 0 )
5187+ {
5188+ RLSW .errCode = SW_INVALID_OPERATION ;
5189+ return ;
5190+ }
5191+ #endif
51955192 RLSW .boundTexture -> tWrap = (SWwrap )value ;
51965193 } break ;
51975194 default : RLSW .errCode = SW_INVALID_ENUM ; break ;
@@ -5574,8 +5571,7 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con
55745571 if (v1 -> position [1 ] > v2 -> position [1 ]) { const sw_vertex_t * tmp = v1 ; v1 = v2 ; v2 = tmp ; }
55755572 if (v0 -> position [1 ] > v1 -> position [1 ]) { const sw_vertex_t * tmp = v0 ; v0 = v1 ; v1 = tmp ; }
55765573
5577- // Extracting coordinates from the sorted vertices
5578- // Put x away for safe keeping; only y is used right now; silences warnings
5574+ // Extracting Y coordinates from the sorted vertices
55795575 float y0 = v0 -> position [1 ];
55805576 float y1 = v1 -> position [1 ];
55815577 float y2 = v2 -> position [1 ];
0 commit comments