Merge branch 'master' of github.com:supertuxkart/stk-code

This commit is contained in:
hiker 2017-04-05 23:14:29 +10:00
commit 6c4f6d1cf5
75 changed files with 7845 additions and 527 deletions

View File

@ -107,15 +107,26 @@ if((WIN32 AND NOT MINGW) OR APPLE)
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/libpng")
include_directories("${PROJECT_SOURCE_DIR}/lib/libpng")
#build jpeg library
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/jpeglib")
include_directories("${PROJECT_SOURCE_DIR}/lib/jpeglib")
set(PNG_PNG_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lib/libpng/")
set(PNG_LIBRARY png15_static)
endif()
# Add jpeg-turbo library
if (APPLE)
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/jpeglib")
include_directories("${PROJECT_SOURCE_DIR}/lib/jpeglib")
set(JPEG_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lib/jpeglib/")
set(JPEG_LIBRARY jpeglib)
else()
find_package(JPEG REQUIRED)
include_directories(${JPEG_INCLUDE_DIR})
endif()
if(NOT SERVER_ONLY AND NOT USE_GLES2)
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/graphics_utils")
include_directories("${PROJECT_SOURCE_DIR}/lib/graphics_utils")
endif()
# Build the irrlicht library
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/irrlicht")
include_directories("${PROJECT_SOURCE_DIR}/lib/irrlicht/include")
@ -368,11 +379,12 @@ target_link_libraries(supertuxkart
${OGGVORBIS_LIBRARIES}
${OPENAL_LIBRARY}
${FREETYPE_LIBRARIES}
${JPEG_LIBRARIES}
)
if(NOT SERVER_ONLY)
if(NOT USE_GLES2)
target_link_libraries(supertuxkart ${OPENGL_LIBRARIES} glew)
target_link_libraries(supertuxkart ${OPENGL_LIBRARIES} glew graphics_utils)
else()
target_link_libraries(supertuxkart EGL GLESv2)
endif()

View File

@ -29,13 +29,16 @@ export ASSETS_PATHS="../data \
export ASSETS_DIRS="library models music sfx textures"
export TEXTURE_SIZE=256
export SOUND_QUALITY=64
export SOUND_QUALITY=42
export SOUND_MONO=1
export SOUND_SAMPLE=32000
export RUN_OPTIMIZE_SCRIPT=0
export DECREASE_QUALITY=1
################################################################################
export LANG=C
cd "`dirname "$0"`"
@ -191,7 +194,22 @@ convert_sound()
oggdec "$FILE" -o tmp.wav
if [ -s tmp.wav ]; then
oggenc --downmix -b $SOUND_QUALITY tmp.wav -o tmp.ogg
OGGENC_CMD=""
if [ "$SOUND_MONO" -gt 0 ]; then
OGGENC_CMD="$OGGENC_CMD --downmix"
fi
OGG_RATE=`ogginfo "$FILE" | grep "Rate: " | cut -f 2 -d " " \
| grep -o '[0-9]*'`
if [ ! -z "$OGG_RATE" ] && [ "$OGG_RATE" -gt "32000" ]; then
OGGENC_CMD="$OGGENC_CMD --resample 32000"
fi
OGGENC_CMD="$OGGENC_CMD -b $SOUND_QUALITY"
oggenc $OGGENC_CMD tmp.wav -o tmp.ogg
fi
if [ -s tmp.ogg ]; then

View File

@ -147,16 +147,8 @@
<spacer width="10" height="10"/>
<label text="Texture compression" I18N="Video settings"/>
</div>
<spacer height="4" width="10" />
<div layout="horizontal-row" proportion="1" height="fit">
<checkbox id="hd-textures"/>
<spacer width="10" height="10"/>
<label text="Use high definition textures" I18N="Video settings"/>
</div>
</div>
<spacer height="20" width="10" />
<div layout="horizontal-row" width="100%" proportion="1">
@ -168,9 +160,9 @@
<spacer height="4" width="10" />
<div layout="horizontal-row" width="100%" proportion="1">
<label text="Texture filtering" I18N="Video settings" width="40%"/>
<label text="Rendered image quality" I18N="Video settings" width="40%"/>
<spacer width="10" height="10"/>
<gauge id="filtering" min_value="0" max_value="5" width="50%" />
<gauge id="image_quality" min_value="0" max_value="3" width="50%" />
</div>
<spacer height="4" width="10" />

View File

@ -40,7 +40,7 @@ void main(void)
col = vec4(new_color.r, new_color.g, new_color.b, col.a);
}
#ifdef GL_ES
#if defined(GL_ES) && !defined(Advanced_Lighting_Enabled)
col.xyz *= color.xyz;
#else
col.xyz *= pow(color.xyz, vec3(2.2));

View File

@ -16,7 +16,13 @@ void main(void)
col.xyz = pow(col.xyz, vec3(2.2));
#endif
#endif
col.xyz *= pow(color.xyz, vec3(2.2));
if (col.a < 0.5) discard;
#if defined(GL_ES) && !defined(Advanced_Lighting_Enabled)
col.xyz *= color.xyz;
#else
col.xyz *= pow(color.xyz, vec3(2.2));
#endif
FragColor = vec4(col.xyz, 1.);
}

View File

@ -25,8 +25,13 @@ void main(void)
col.xyz = pow(col.xyz, vec3(2.2));
#endif
#endif
col.xyz *= pow(color.xyz, vec3(2.2));
if (col.a * color.a < 0.5) discard;
#if defined(GL_ES) && !defined(Advanced_Lighting_Enabled)
col.xyz *= color.xyz;
#else
col.xyz *= pow(color.xyz, vec3(2.2));
#endif
float mask = texture(colorization_mask, uv).a;
if (color_change.x > 0.0)

View File

@ -13,7 +13,9 @@ void main()
// Uncharted2 tonemap with Auria's custom coefficients
vec4 perChannel = (col * (6.9 * col + .5)) / (col * (5.2 * col + 1.7) + 0.06);
#if !(defined(GL_ES) && defined(Advanced_Lighting_Enabled))
perChannel = pow(perChannel, vec4(2.2));
#endif
vec2 inside = uv - 0.5;
float vignette = 1. - dot(inside, inside) * vignette_weight;

View File

@ -14,10 +14,6 @@ vec3 getLightFactor(vec3 diffuseMatColor, vec3 specularMatColor, float specMapVa
vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz;
vec3 SpecularComponent = texture(SpecularMap, tc).xyz;
float ao = texture(SSAO, tc).x;
#ifdef GL_ES
DiffuseComponent = pow(DiffuseComponent, vec3(1. / 2.2));
SpecularComponent = pow(SpecularComponent, vec3(1. / 2.2));
#endif
vec3 tmp = diffuseMatColor * DiffuseComponent * (1. - specMapValue) + specularMatColor * SpecularComponent * specMapValue;
vec3 emitCol = diffuseMatColor.xyz * diffuseMatColor.xyz * diffuseMatColor.xyz * 15.;
return tmp * ao + (emitMapValue * emitCol);

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 2.6)
if (UNIX OR MINGW)
add_definitions(-O3)
endif()
add_library(graphics_utils STATIC
mipmap/cpusimd.c
mipmap/imgresize.c
)

View File

@ -0,0 +1,571 @@
/* -----------------------------------------------------------------------------
*
* Copyright (c) 2008-2016 Alexis Naveros.
*
*
* The SIMD trigonometry functions are Copyright (C) 2007 Julien Pommier
* See copyright notice for simd4f_sin_ps(), simd4f_cos_ps(), simd4f_sincos_ps()
*
*
* Some functions are Copyright (C) 2008 José Fonseca
* See copyright notice for simd4f_exp2_ps(), simd4f_log2_ps(), simd4f_pow_ps()
*
*
* Portions developed under contract to the SURVICE Engineering Company.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* -----------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include "cpusimd.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
////
#if CPU_SSE_SUPPORT
const uint32_t CPU_ALIGN16 simd4fSignMask[4] = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
const uint32_t CPU_ALIGN16 simd4fSignMaskInv[4] = { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff };
const float CPU_ALIGN16 simd4fHalf[4] = { 0.5, 0.5, 0.5, 0.5 };
const float CPU_ALIGN16 simd4fOne[4] = { 1.0, 1.0, 1.0, 1.0 };
const float CPU_ALIGN16 simd4fTwo[4] = { 2.0, 2.0, 2.0, 2.0 };
const float CPU_ALIGN16 simd4fThree[4] = { 3.0, 3.0, 3.0, 3.0 };
const uint32_t CPU_ALIGN16 simd4uOne[4] = { 1, 1, 1, 1 };
const uint32_t CPU_ALIGN16 simd4uOneInv[4] = { ~1, ~1, ~1, ~1 };
const uint32_t CPU_ALIGN16 simd4uTwo[4] = { 2, 2, 2, 2 };
const uint32_t CPU_ALIGN16 simd4uFour[4] = { 4, 4, 4, 4 };
const float CPU_ALIGN16 simd4fQuarter[4] = { 0.25, 0.25, 0.25, 0.25 };
const float CPU_ALIGN16 simd4fPi[4] = { M_PI, M_PI, M_PI, M_PI };
const float CPU_ALIGN16 simd4fZeroOneTwoThree[4] = { 0.0, 1.0, 2.0, 3.0 };
const uint32_t CPU_ALIGN16 simd4fAlphaMask[4] = { 0x00000000, 0x00000000, 0x00000000, 0xffffffff };
const float CPU_ALIGN16 simd4f255[4] = { 255.0f, 255.0f, 255.0f, 255.0f };
const float CPU_ALIGN16 simd4f255Inv[4] = { 1.0f/255.0f, 1.0f/255.0f, 1.0f/255.0f, 1.0f/255.0f };
#endif
////
#if CPU_SSE2_SUPPORT
/* Copyright (C) 2007 Julien Pommier
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
(this is the zlib license)
*/
static const float CPU_ALIGN16 simd4f_cephes_FOPI[4] = { 1.27323954473516, 1.27323954473516, 1.27323954473516, 1.27323954473516 };
static const float CPU_ALIGN16 simd4f_minus_cephes_DP1[4] = { -0.78515625, -0.78515625, -0.78515625, -0.78515625 };
static const float CPU_ALIGN16 simd4f_minus_cephes_DP2[4] = { -2.4187564849853515625e-4, -2.4187564849853515625e-4, -2.4187564849853515625e-4, -2.4187564849853515625e-4 };
static const float CPU_ALIGN16 simd4f_minus_cephes_DP3[4] = { -3.77489497744594108e-8, -3.77489497744594108e-8, -3.77489497744594108e-8, -3.77489497744594108e-8 };
static const float CPU_ALIGN16 simd4f_sincof_p0[4] = { -1.9515295891E-4, -1.9515295891E-4, -1.9515295891E-4, -1.9515295891E-4 };
static const float CPU_ALIGN16 simd4f_sincof_p1[4] = { 8.3321608736E-3, 8.3321608736E-3, 8.3321608736E-3, 8.3321608736E-3 };
static const float CPU_ALIGN16 simd4f_sincof_p2[4] = { -1.6666654611E-1, -1.6666654611E-1, -1.6666654611E-1, -1.6666654611E-1 };
static const float CPU_ALIGN16 simd4f_coscof_p0[4] = { 2.443315711809948E-005, 2.443315711809948E-005, 2.443315711809948E-005, 2.443315711809948E-005 };
static const float CPU_ALIGN16 simd4f_coscof_p1[4] = { -1.388731625493765E-003, -1.388731625493765E-003, -1.388731625493765E-003, -1.388731625493765E-003 };
static const float CPU_ALIGN16 simd4f_coscof_p2[4] = { 4.166664568298827E-002, 4.166664568298827E-002, 4.166664568298827E-002, 4.166664568298827E-002 };
__m128 simd4f_sin_ps( __m128 x )
{
__m128 xmm1, xmm2, xmm3, sign_bit, y;
__m128i emm0, emm2;
xmm2 = _mm_setzero_ps();
sign_bit = x;
/* take the absolute value */
x = _mm_and_ps( x, *(__m128 *)simd4fSignMaskInv );
/* extract the sign bit (upper one) */
sign_bit = _mm_and_ps(sign_bit, *(__m128 *)simd4fSignMask);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(__m128 *)simd4f_cephes_FOPI);
/* store the integer part of y in mm0 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(__m128i*)simd4uOne);
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uOneInv);
y = _mm_cvtepi32_ps(emm2);
/* get the swap sign flag */
emm0 = _mm_and_si128(emm2, *(__m128i*)simd4uFour);
emm0 = _mm_slli_epi32(emm0, 29);
/* get the polynom selection mask
there is one polynom for 0 <= x <= Pi/4
and another one for Pi/4<x<=Pi/2
Both branches will be computed.
*/
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uTwo);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
__m128 swap_sign_bit = _mm_castsi128_ps(emm0);
__m128 poly_mask = _mm_castsi128_ps(emm2);
sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(__m128 *)simd4f_minus_cephes_DP1;
xmm2 = *(__m128 *)simd4f_minus_cephes_DP2;
xmm3 = *(__m128 *)simd4f_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
y = *(__m128 *)simd4f_coscof_p0;
__m128 z = _mm_mul_ps(x,x);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(__m128 *)simd4f_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(__m128 *)simd4f_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
__m128 tmp = _mm_mul_ps(z, *(__m128 *)simd4fHalf);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(__m128 *)simd4fOne);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
__m128 y2 = *(__m128 *)simd4f_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(__m128 *)simd4f_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(__m128 *)simd4f_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
y2 = _mm_and_ps(xmm3, y2);
y = _mm_andnot_ps(xmm3, y);
y = _mm_add_ps(y,y2);
/* update the sign */
y = _mm_xor_ps(y, sign_bit);
return y;
}
/* almost the same as sin_ps */
__m128 simd4f_cos_ps( __m128 x )
{
__m128 xmm1, xmm2, xmm3, y;
__m128i emm0, emm2;
xmm2 = _mm_setzero_ps();
/* take the absolute value */
x = _mm_and_ps(x, *(__m128*)simd4fSignMaskInv);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(__m128*)simd4f_cephes_FOPI);
/* store the integer part of y in mm0 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(__m128i*)simd4uOne);
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uOneInv);
y = _mm_cvtepi32_ps(emm2);
emm2 = _mm_sub_epi32(emm2, *(__m128i*)simd4uTwo);
/* get the swap sign flag */
emm0 = _mm_andnot_si128(emm2, *(__m128i*)simd4uFour);
emm0 = _mm_slli_epi32(emm0, 29);
/* get the polynom selection mask */
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uTwo);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
__m128 sign_bit = _mm_castsi128_ps(emm0);
__m128 poly_mask = _mm_castsi128_ps(emm2);
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(__m128*)simd4f_minus_cephes_DP1;
xmm2 = *(__m128*)simd4f_minus_cephes_DP2;
xmm3 = *(__m128*)simd4f_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
y = *(__m128*)simd4f_coscof_p0;
__m128 z = _mm_mul_ps(x,x);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(__m128*)simd4f_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(__m128*)simd4f_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
__m128 tmp = _mm_mul_ps(z, *(__m128*)simd4fHalf);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(__m128*)simd4fOne);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
__m128 y2 = *(__m128*)simd4f_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(__m128*)simd4f_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(__m128*)simd4f_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
y2 = _mm_and_ps(xmm3, y2); //, xmm3);
y = _mm_andnot_ps(xmm3, y);
y = _mm_add_ps(y,y2);
/* update the sign */
y = _mm_xor_ps(y, sign_bit);
return y;
}
/* since sin_ps and cos_ps are almost identical, sincos_ps could replace both of them..
it is almost as fast, and gives you a free cosine with your sine */
void simd4f_sincos_ps( __m128 x, __m128 *s, __m128 *c )
{
__m128 xmm1, xmm2, xmm3, sign_bit_sin, y;
__m128i emm0, emm2, emm4;
xmm3 = _mm_setzero_ps();
sign_bit_sin = x;
/* take the absolute value */
x = _mm_and_ps(x, *(__m128*)simd4fSignMaskInv);
/* extract the sign bit (upper one) */
sign_bit_sin = _mm_and_ps(sign_bit_sin, *(__m128*)simd4fSignMask);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(__m128*)simd4f_cephes_FOPI);
/* store the integer part of y in emm2 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(__m128i*)simd4uOne);
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uOneInv);
y = _mm_cvtepi32_ps(emm2);
emm4 = emm2;
/* get the swap sign flag for the sine */
emm0 = _mm_and_si128(emm2, *(__m128i*)simd4uFour);
emm0 = _mm_slli_epi32(emm0, 29);
__m128 swap_sign_bit_sin = _mm_castsi128_ps(emm0);
/* get the polynom selection mask for the sine*/
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uTwo);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
__m128 poly_mask = _mm_castsi128_ps(emm2);
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(__m128*)simd4f_minus_cephes_DP1;
xmm2 = *(__m128*)simd4f_minus_cephes_DP2;
xmm3 = *(__m128*)simd4f_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
emm4 = _mm_sub_epi32(emm4, *(__m128i*)simd4uTwo);
emm4 = _mm_andnot_si128(emm4, *(__m128i*)simd4uFour);
emm4 = _mm_slli_epi32(emm4, 29);
__m128 sign_bit_cos = _mm_castsi128_ps(emm4);
sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
__m128 z = _mm_mul_ps(x,x);
y = *(__m128*)simd4f_coscof_p0;
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(__m128*)simd4f_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(__m128*)simd4f_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
__m128 tmp = _mm_mul_ps(z, *(__m128*)simd4fHalf);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(__m128*)simd4fOne);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
__m128 y2 = *(__m128*)simd4f_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(__m128*)simd4f_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(__m128*)simd4f_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
__m128 ysin2 = _mm_and_ps(xmm3, y2);
__m128 ysin1 = _mm_andnot_ps(xmm3, y);
y2 = _mm_sub_ps(y2,ysin2);
y = _mm_sub_ps(y, ysin1);
xmm1 = _mm_add_ps(ysin1,ysin2);
xmm2 = _mm_add_ps(y,y2);
/* update the sign */
*s = _mm_xor_ps(xmm1, sign_bit_sin);
*c = _mm_xor_ps(xmm2, sign_bit_cos);
}
#endif
////
#if CPU_SSE2_SUPPORT
/* Copyright (C) 2008 José Fonseca
http://jrfonseca.blogspot.ca/2008/09/fast-sse2-pow-tables-or-polynomials.html
MIT license
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, sublicense, 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 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 NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS 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.
*/
#define POLY0(x,c0) _mm_set1_ps(c0)
#define POLY1(x,c0,c1) _mm_add_ps(_mm_mul_ps(POLY0(x, c1), x), _mm_set1_ps(c0))
#define POLY2(x,c0,c1,c2) _mm_add_ps(_mm_mul_ps(POLY1(x, c1, c2), x), _mm_set1_ps(c0))
#define POLY3(x,c0,c1,c2,c3) _mm_add_ps(_mm_mul_ps(POLY2(x, c1, c2, c3), x), _mm_set1_ps(c0))
#define POLY4(x,c0,c1,c2,c3,c4) _mm_add_ps(_mm_mul_ps(POLY3(x, c1, c2, c3, c4), x), _mm_set1_ps(c0))
#define POLY5(x,c0,c1,c2,c3,c4,c5) _mm_add_ps(_mm_mul_ps(POLY4(x, c1, c2, c3, c4, c5), x), _mm_set1_ps(c0))
#define EXP_POLY_DEGREE 3
#define LOG_POLY_DEGREE 5
__m128 simd4f_exp2_ps( __m128 x )
{
__m128i ipart;
__m128 fpart, expipart, expfpart;
x = _mm_min_ps( x, _mm_set1_ps( 129.00000f ) );
x = _mm_max_ps( x, _mm_set1_ps( -126.99999f ) );
/* ipart = int(x - 0.5) */
ipart = _mm_cvtps_epi32( _mm_sub_ps( x, _mm_set1_ps( 0.5f ) ) );
/* fpart = x - ipart */
fpart = _mm_sub_ps( x, _mm_cvtepi32_ps( ipart ) );
/* expipart = (float) (1 << ipart) */
expipart = _mm_castsi128_ps( _mm_slli_epi32( _mm_add_epi32( ipart, _mm_set1_epi32( 127 ) ), 23 ) );
/* minimax polynomial fit of 2**x, in range [-0.5, 0.5[ */
#if EXP_POLY_DEGREE == 5
expfpart = POLY5( fpart, 9.9999994e-1f, 6.9315308e-1f, 2.4015361e-1f, 5.5826318e-2f, 8.9893397e-3f, 1.8775767e-3f );
#elif EXP_POLY_DEGREE == 4
expfpart = POLY4( fpart, 1.0000026f, 6.9300383e-1f, 2.4144275e-1f, 5.2011464e-2f, 1.3534167e-2f );
#elif EXP_POLY_DEGREE == 3
expfpart = POLY3( fpart, 9.9992520e-1f, 6.9583356e-1f, 2.2606716e-1f, 7.8024521e-2f );
#elif EXP_POLY_DEGREE == 2
expfpart = POLY2( fpart, 1.0017247f, 6.5763628e-1f, 3.3718944e-1f );
#else
#error
#endif
return _mm_mul_ps(expipart, expfpart);
}
__m128 simd4f_log2_ps( __m128 x )
{
__m128i expmask, mantmask, i;
__m128 one, vexp, mant, logmant;
expmask = _mm_set1_epi32( 0x7f800000 );
mantmask = _mm_set1_epi32( 0x007fffff );
one = _mm_set1_ps( 1.0f );
i = _mm_castps_si128( x );
/* exp = (float) exponent(x) */
vexp = _mm_cvtepi32_ps( _mm_sub_epi32( _mm_srli_epi32( _mm_and_si128( i, expmask ), 23 ), _mm_set1_epi32( 127 ) ) );
/* mant = (float) mantissa(x) */
mant = _mm_or_ps( _mm_castsi128_ps( _mm_and_si128( i, mantmask ) ), one );
/* Minimax polynomial fit of log2(x)/(x - 1), for x in range [1, 2[
* These coefficients can be generate with
* http://www.boost.org/doc/libs/1_36_0/libs/math/doc/sf_and_dist/html/math_toolkit/toolkit/internals2/minimax.html
*/
#if LOG_POLY_DEGREE == 6
logmant = POLY5( mant, 3.11578814719469302614f, -3.32419399085241980044f, 2.59883907202499966007f, -1.23152682416275988241f, 0.318212422185251071475f, -0.0344359067839062357313f );
#elif LOG_POLY_DEGREE == 5
logmant = POLY4( mant, 2.8882704548164776201f, -2.52074962577807006663f, 1.48116647521213171641f, -0.465725644288844778798f, 0.0596515482674574969533f );
#elif LOG_POLY_DEGREE == 4
logmant = POLY3( mant, 2.61761038894603480148f, -1.75647175389045657003f, 0.688243882994381274313f, -0.107254423828329604454f );
#elif LOG_POLY_DEGREE == 3
logmant = POLY2( mant, 2.28330284476918490682f, -1.04913055217340124191f, 0.204446009836232697516f );
#else
#error
#endif
/* This effectively increases the polynomial degree by one, but ensures that log2(1) == 0*/
logmant = _mm_mul_ps( logmant, _mm_sub_ps(mant, one ) );
return _mm_add_ps( logmant, vexp );
}
__m128 simd4f_pow_ps( __m128 x, __m128 y )
{
return simd4f_exp2_ps( _mm_mul_ps( simd4f_log2_ps( x ), y ) );
}
#endif
////
#if CPU_SSE2_SUPPORT
/*
By Potatoswatter
http://stackoverflow.com/questions/6475373/optimizations-for-pow-with-const-non-integer-exponent
*/
#ifndef CC_ALWAYSINLINE
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#define CC_ALWAYSINLINE __attribute__((always_inline))
#else
#define CC_ALWAYSINLINE
#endif
#endif
static inline CC_ALWAYSINLINE __m128 simd4f_fastpow_ps( __m128 arg, uint32_t expnum, uint32_t expden, uint32_t coeffnum, uint32_t coeffden )
{
__m128 ret = arg;
float corrfactor, powfactor;
/* Apply a constant pre-correction factor. */
corrfactor = exp2( 127.0 * expden / expnum - 127.0 ) * pow( 1.0 * coeffnum / coeffden, 1.0 * expden / expnum );
powfactor = 1.0 * expnum / expden;
ret = _mm_mul_ps( ret, _mm_set1_ps( corrfactor ) );
/* Reinterpret arg as integer to obtain logarithm. */
ret = _mm_cvtepi32_ps( _mm_castps_si128( ret ) );
/* Multiply logarithm by power. */
ret = _mm_mul_ps( ret, _mm_set1_ps( powfactor ) );
/* Convert back to "integer" to exponentiate. */
ret = _mm_castsi128_ps( _mm_cvtps_epi32( ret ) );
return ret;
}
__m128 simd4f_pow12d5_ps( __m128 arg )
{
/* Lower exponents provide lower initial error, but too low causes overflow. */
__m128 xf = simd4f_fastpow_ps( arg, 4, 5, (int)( 1.38316186f * (float)1e9 ), (int)1e9 );
/* Imprecise 4-cycle sqrt is still far better than fastpow, good enough. */
__m128 xfm4 = _mm_rsqrt_ps( xf );
__m128 xf4 = _mm_mul_ps( xf, xfm4 );
/* Precisely calculate x^2 and x^3 */
__m128 x2 = _mm_mul_ps( arg, arg );
__m128 x3 = _mm_mul_ps( x2, arg );
/* Overestimate of x^2 * x^0.4 */
x2 = _mm_mul_ps( x2, xf4 );
/* Get x^-0.2 from x^0.4, and square it for x^-0.4. Combine into x^-0.6. */
__m128 xfm2 = _mm_rsqrt_ps( xf4 );
x3 = _mm_mul_ps( x3, xfm4 );
x3 = _mm_mul_ps( x3, xfm2 );
return _mm_mul_ps( _mm_add_ps( x2, x3 ), _mm_set1_ps( 1.0f/1.960131704207789f * 0.9999f ) );
}
__m128 simd4f_pow5d12_ps( __m128 arg )
{
/* 5/12 is too small, so compute the 4th root of 20/12 instead. */
/* 20/12 = 5/3 = 1 + 2/3 = 2 - 1/3. 2/3 is a suitable argument for fastpow. */
/* weighting coefficient: a^-1/2 = 2 a; a = 2^-2/3 */
__m128 xf = simd4f_fastpow_ps( arg, 2, 3, (int)( 0.629960524947437f * (float)1e9 ), (int)1e9 );
__m128 xover = _mm_mul_ps( arg, xf );
__m128 xfm1 = _mm_rsqrt_ps( xf );
__m128 x2 = _mm_mul_ps( arg, arg );
__m128 xunder = _mm_mul_ps( x2, xfm1 );
/* sqrt2 * over + 2 * sqrt2 * under */
__m128 xavg = _mm_mul_ps( _mm_set1_ps( 1.0f/( 3.0f * 0.629960524947437f ) * 0.999852f ), _mm_add_ps( xover, xunder ) );
xavg = _mm_mul_ps( xavg, _mm_rsqrt_ps( xavg ) );
xavg = _mm_mul_ps( xavg, _mm_rsqrt_ps( xavg ) );
return xavg;
}
#endif
////

View File

@ -0,0 +1,410 @@
/* -----------------------------------------------------------------------------
*
* Copyright (c) 2008-2016 Alexis Naveros.
*
* The SIMD trigonometry functions are Copyright (C) 2007 Julien Pommier
* See copyright notice for simd4f_sin_ps(), simd4f_cos_ps(), simd4f_sincos_ps()
*
* Portions developed under contract to the SURVICE Engineering Company.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* -----------------------------------------------------------------------------
*/
#ifndef CPUSIMD_H
#define CPUSIMD_H
////
#if __MMX__ || CPU_ENABLE_MMX
#include <mmintrin.h>
#define CPU_MMX_SUPPORT (1)
#endif
#if __SSE__ || _M_X64 || _M_IX86_FP >= 1 || CPU_ENABLE_SSE
#include <xmmintrin.h>
#define CPU_SSE_SUPPORT (1)
#endif
#if __SSE2__ || _M_X64 || _M_IX86_FP >= 2 || CPU_ENABLE_SSE2
#include <emmintrin.h>
#define CPU_SSE2_SUPPORT (1)
#endif
#if __SSE3__ || __AVX__ || CPU_ENABLE_SSE3
#include <pmmintrin.h>
#define CPU_SSE3_SUPPORT (1)
#endif
#if __SSSE3__ || __AVX__ || CPU_ENABLE_SSSE3
#include <tmmintrin.h>
#define CPU_SSSE3_SUPPORT (1)
#endif
#if __SSE4_1__ || __AVX__ || CPU_ENABLE_SSE4_1
#include <smmintrin.h>
#define CPU_SSE4_1_SUPPORT (1)
#endif
#if __SSE4_2__ || CPU_ENABLE_SSE4_2
#include <nmmintrin.h>
#define CPU_SSE4_2_SUPPORT (1)
#endif
#if __SSE4A__ || CPU_ENABLE_SSE4A
#include <ammintrin.h>
#define CPU_SSE4A_SUPPORT (1)
#endif
#if __AVX__ || CPU_ENABLE_AVX
#include <immintrin.h>
#define CPU_AVX_SUPPORT (1)
#endif
#if __AVX2__ || CPU_ENABLE_AVX2
#include <immintrin.h>
#define CPU_AVX2_SUPPORT (1)
#endif
#if __XOP__ || CPU_ENABLE_XOP
#include <immintrin.h>
#define CPU_XOP_SUPPORT (1)
#endif
#if __FMA3__ || CPU_ENABLE_FMA3
#include <immintrin.h>
#define CPU_FMA3_SUPPORT (1)
#endif
#if __FMA4__ || CPU_ENABLE_FMA4
#include <immintrin.h>
#define CPU_FMA4_SUPPORT (1)
#endif
#if __RDRND__ || CPU_ENABLE_RDRND
#include <immintrin.h>
#define CPU_RDRND_SUPPORT (1)
#endif
#if __POPCNT__ || CPU_ENABLE_POPCNT
#include <popcntintrin.h>
#define CPU_POPCNT_SUPPORT (1)
#endif
#if __LZCNT__ || CPU_ENABLE_LZCNT
#include <lzcntintrin.h>
#define CPU_LZCNT_SUPPORT (1)
#endif
#if __F16C__ || CPU_ENABLE_F16C
#include <f16cintrin.h>
#define CPU_F16C_SUPPORT (1)
#endif
#if __BMI__ || CPU_ENABLE_BMI
#include <bmiintrin.h>
#define CPU_BMI_SUPPORT (1)
#endif
#if __BMI2__ || CPU_ENABLE_BMI2
#include <bmi2intrin.h>
#define CPU_BMI2_SUPPORT (1)
#endif
#if __TBM__ || CPU_ENABLE_TBM
#include <tbmintrin.h>
#define CPU_TBM_SUPPORT (1)
#endif
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#define CPU_ALIGN16 __attribute__((aligned(16)))
#define CPU_ALIGN32 __attribute__((aligned(32)))
#define CPU_ALIGN64 __attribute__((aligned(64)))
#elif defined(_MSC_VER)
#define CPU_ALIGN16 __declspec(align(16))
#define CPU_ALIGN64 __declspec(align(64))
#else
#define CPU_ALIGN16
#define CPU_ALIGN32
#define CPU_ALIGN64
#warning "SSE/AVX Disabled: Unsupported Compiler."
#undef CPU_SSE_SUPPORT
#undef CPU_SSE2_SUPPORT
#undef CPU_SSE3_SUPPORT
#undef CPU_SSSE3_SUPPORT
#undef CPU_SSE4_1_SUPPORT
#undef CPU_SSE4_2_SUPPORT
#undef CPU_AVX_SUPPORT
#undef CPU_AVX2_SUPPORT
#undef CPU_XOP_SUPPORT
#undef CPU_FMA3_SUPPORT
#undef CPU_FMA4_SUPPORT
#endif
////
#if CPU_SSE_SUPPORT
#define CPU_APPROX_DIV_FLOAT(z,w) _mm_cvtss_f32(_mm_mul_ss(_mm_set_ss(z),_mm_rcp_ss(_mm_set_ss(w))))
#define CPU_APPROX_SQRT_FLOAT(z) _mm_cvtss_f32(_mm_mul_ss(_mm_set_ss(z),_mm_rsqrt_ss(_mm_set_ss(z))))
#define CPU_APPROX_RSQRT_FLOAT(z) _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(z)))
#define CPU_APPROX_DIVSQRT_FLOAT(z,w) _mm_cvtss_f32(_mm_mul_ss(_mm_set_ss(z),_mm_rsqrt_ss(_mm_set_ss(w))))
#else
#define CPU_APPROX_DIV_FLOAT(z,w) ((z)/(w))
#define CPU_APPROX_SQRT_FLOAT(z) (sqrtf(z))
#define CPU_APPROX_RSQRT_FLOAT(z) (1.0/sqrtf(z))
#define CPU_APPROX_DIVSQRT_FLOAT(z,w) ((z)/sqrtf(w))
#endif
#if CPU_SSE3_SUPPORT
#define CPU_HADD_PS(vx,vy) _mm_hadd_ps(vx,vy)
#define CPU_HADD_PD(vx,vy) _mm_hadd_pd(vx,vy)
#elif CPU_SSE_SUPPORT
static inline __m128 CPU_HADD_PS( __m128 vx, __m128 vy )
{
__m128 vh, vl;
vh = _mm_shuffle_ps( vx, vy, _MM_SHUFFLE(3,1,3,1) );
vl = _mm_shuffle_ps( vx, vy, _MM_SHUFFLE(2,0,2,0) );
return _mm_add_ps( vh, vl );
}
#define CPU_HADD_PD(vx,vy) _mm_add_sd(vx,_mm_unpackhi_pd(vy,vy))
#endif
#if CPU_SSE4_1_SUPPORT
#define CPU_CVT_U8_TO_I32(x,vzero) _mm_cvtepu8_epi32(x)
#define CPU_CVT_S8_TO_I32(x,vzero) _mm_cvtepi8_epi32(x)
#elif CPU_SSE2_SUPPORT
#define CPU_CVT_U8_TO_I32(x,vzero) _mm_unpacklo_epi16(_mm_unpacklo_epi8((x),(vzero)),(vzero))
static inline __m128i CPU_CVT_S8_TO_I32( __m128i vx, __m128i vzero )
{
__m128i vsign;
vsign = _mm_cmpgt_epi8( vzero, vx );
return _mm_unpacklo_epi16( _mm_unpacklo_epi8( vx, vsign ), _mm_unpacklo_epi8( vsign, vsign ) );
}
#endif
#if CPU_SSE4_1_SUPPORT
#define CPU_BLENDV_PS(x,y,mask) _mm_blendv_ps(x,y,mask)
#define CPU_BLENDV_PD(x,y,mask) _mm_blendv_pd(x,y,mask)
#elif CPU_SSE2_SUPPORT
#define CPU_BLENDV_PS(x,y,mask) _mm_or_ps(_mm_andnot_ps(mask,x),_mm_and_ps(y,mask))
#define CPU_BLENDV_PD(x,y,mask) _mm_or_pd(_mm_andnot_pd(mask,x),_mm_and_pd(y,mask))
#endif
/*
CPU_FMADD = ((f0*f1)+t0)
CPU_FMSUB = ((f0*f1)-t0)
*/
#if CPU_FMA3_SUPPORT
#define CPU_FMADD_SS(f0,f1,t0) _mm_fmadd_ss(f0,f1,t0)
#define CPU_FMADD_PS(f0,f1,t0) _mm_fmadd_ps(f0,f1,t0)
#define CPU_FMADD_SD(f0,f1,t0) _mm_fmadd_sd(f0,f1,t0)
#define CPU_FMADD_PD(f0,f1,t0) _mm_fmadd_pd(f0,f1,t0)
#define CPU_FMSUB_SS(f0,f1,t0) _mm_fmsub_ss(f0,f1,t0)
#define CPU_FMSUB_PS(f0,f1,t0) _mm_fmsub_ps(f0,f1,t0)
#define CPU_FMSUB_SD(f0,f1,t0) _mm_fmsub_sd(f0,f1,t0)
#define CPU_FMSUB_PD(f0,f1,t0) _mm_fmsub_pd(f0,f1,t0)
#define CPU_FMADD256_SS(f0,f1,t0) _mm256_fmadd_ss(f0,f1,t0)
#define CPU_FMADD256_PS(f0,f1,t0) _mm256_fmadd_ps(f0,f1,t0)
#define CPU_FMADD256_SD(f0,f1,t0) _mm256_fmadd_sd(f0,f1,t0)
#define CPU_FMADD256_PD(f0,f1,t0) _mm256_fmadd_pd(f0,f1,t0)
#define CPU_FMSUB256_SS(f0,f1,t0) _mm256_fmsub_ss(f0,f1,t0)
#define CPU_FMSUB256_PS(f0,f1,t0) _mm256_fmsub_ps(f0,f1,t0)
#define CPU_FMSUB256_SD(f0,f1,t0) _mm256_fmsub_sd(f0,f1,t0)
#define CPU_FMSUB256_PD(f0,f1,t0) _mm256_fmsub_pd(f0,f1,t0)
#elif CPU_FMA4_SUPPORT
#define CPU_FMADD_SS(f0,f1,t0) _mm_macc_ss(f0,f1,t0)
#define CPU_FMADD_PS(f0,f1,t0) _mm_macc_ps(f0,f1,t0)
#define CPU_FMADD_SD(f0,f1,t0) _mm_macc_sd(f0,f1,t0)
#define CPU_FMADD_PD(f0,f1,t0) _mm_macc_pd(f0,f1,t0)
#define CPU_FMSUB_SS(f0,f1,t0) _mm_msub_ss(f0,f1,t0)
#define CPU_FMSUB_PS(f0,f1,t0) _mm_msub_ps(f0,f1,t0)
#define CPU_FMSUB_SD(f0,f1,t0) _mm_msub_sd(f0,f1,t0)
#define CPU_FMSUB_PD(f0,f1,t0) _mm_msub_pd(f0,f1,t0)
#define CPU_FMADD256_SS(f0,f1,t0) _mm256_macc_ss(f0,f1,t0)
#define CPU_FMADD256_PS(f0,f1,t0) _mm256_macc_ps(f0,f1,t0)
#define CPU_FMADD256_SD(f0,f1,t0) _mm256_macc_sd(f0,f1,t0)
#define CPU_FMADD256_PD(f0,f1,t0) _mm256_macc_pd(f0,f1,t0)
#define CPU_FMSUB256_SS(f0,f1,t0) _mm256_msub_ss(f0,f1,t0)
#define CPU_FMSUB256_PS(f0,f1,t0) _mm256_msub_ps(f0,f1,t0)
#define CPU_FMSUB256_SD(f0,f1,t0) _mm256_msub_sd(f0,f1,t0)
#define CPU_FMSUB256_PD(f0,f1,t0) _mm256_msub_pd(f0,f1,t0)
#else
#define CPU_FMADD_SS(f0,f1,t0) _mm_add_ss(_mm_mul_ss(f0,f1),t0)
#define CPU_FMADD_PS(f0,f1,t0) _mm_add_ps(_mm_mul_ps(f0,f1),t0)
#define CPU_FMADD_SD(f0,f1,t0) _mm_add_sd(_mm_mul_sd(f0,f1),t0)
#define CPU_FMADD_PD(f0,f1,t0) _mm_add_pd(_mm_mul_pd(f0,f1),t0)
#define CPU_FMSUB_SS(f0,f1,t0) _mm_sub_ss(_mm_mul_ss(f0,f1),t0)
#define CPU_FMSUB_PS(f0,f1,t0) _mm_sub_ps(_mm_mul_ps(f0,f1),t0)
#define CPU_FMSUB_SD(f0,f1,t0) _mm_sub_sd(_mm_mul_sd(f0,f1),t0)
#define CPU_FMSUB_PD(f0,f1,t0) _mm_sub_pd(_mm_mul_pd(f0,f1),t0)
#define CPU_FMADD256_SS(f0,f1,t0) _mm256_add_ss(_mm256_mul_ss(f0,f1),t0)
#define CPU_FMADD256_PS(f0,f1,t0) _mm256_add_ps(_mm256_mul_ps(f0,f1),t0)
#define CPU_FMADD256_SD(f0,f1,t0) _mm256_add_sd(_mm256_mul_sd(f0,f1),t0)
#define CPU_FMADD256_PD(f0,f1,t0) _mm256_add_pd(_mm256_mul_pd(f0,f1),t0)
#define CPU_FMSUB256_SS(f0,f1,t0) _mm256_sub_ss(_mm256_mul_ss(f0,f1),t0)
#define CPU_FMSUB256_PS(f0,f1,t0) _mm256_sub_ps(_mm256_mul_ps(f0,f1),t0)
#define CPU_FMSUB256_SD(f0,f1,t0) _mm256_sub_sd(_mm256_mul_sd(f0,f1),t0)
#define CPU_FMSUB256_PD(f0,f1,t0) _mm256_sub_pd(_mm256_mul_pd(f0,f1),t0)
#endif
////
#if CPU_SSE_SUPPORT
extern const uint32_t simd4fSignMask[4];
extern const uint32_t simd4fSignMaskInv[4];
extern const float simd4fHalf[4];
extern const float simd4fOne[4];
extern const float simd4fTwo[4];
extern const float simd4fThree[4];
extern const uint32_t simd4uOne[4];
extern const uint32_t simd4uOneInv[4];
extern const uint32_t simd4uTwo[4];
extern const uint32_t simd4uFour[4];
extern const float simd4fQuarter[4];
extern const float simd4fPi[4];
extern const float simd4fZeroOneTwoThree[4];
extern const uint32_t simd4fAlphaMask[4];
extern const float simd4f255[4];
extern const float simd4f255Inv[4];
#endif
#if CPU_SSE2_SUPPORT
/* Input range between -8192 and 8192 */
__m128 simd4f_sin_ps( __m128 x );
__m128 simd4f_cos_ps( __m128 x );
void simd4f_sincos_ps( __m128 x, __m128 *s, __m128 *c );
#endif
#if CPU_SSE2_SUPPORT
__m128 simd4f_exp2_ps( __m128 x );
__m128 simd4f_log2_ps( __m128 x );
__m128 simd4f_pow_ps( __m128 x, __m128 y );
#endif
#if CPU_SSE2_SUPPORT
__m128 simd4f_pow12d5_ps( __m128 arg );
__m128 simd4f_pow5d12_ps( __m128 arg );
#endif
////
#if CPU_SSE2_SUPPORT
#ifndef CC_ALWAYSINLINE
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#define CC_ALWAYSINLINE __attribute__((always_inline))
#else
#define CC_ALWAYSINLINE
#endif
#endif
static inline CC_ALWAYSINLINE __m128 simd4f_pow12d5_inline_ps( __m128 vx )
{
__m128 vpow, vpwsqrtinv, vpwsqrt, vx2;
vx2 = _mm_mul_ps( vx, vx );
vpow = _mm_castsi128_ps( _mm_cvtps_epi32( _mm_mul_ps( _mm_cvtepi32_ps( _mm_castps_si128( _mm_mul_ps( vx, _mm_set1_ps( 5417434112.0f ) ) ) ), _mm_set1_ps( 0.8f ) ) ) );
vpwsqrtinv = _mm_rsqrt_ps( vpow );
vpwsqrt = _mm_mul_ps( vpow, vpwsqrtinv );
return _mm_mul_ps( _mm_add_ps( _mm_mul_ps( vx2, vpwsqrt ), _mm_mul_ps( _mm_mul_ps( _mm_mul_ps( vx2, vx ), vpwsqrtinv ), _mm_rsqrt_ps( vpwsqrt ) ) ), _mm_set1_ps( 0.51011878327f ) );
}
static inline CC_ALWAYSINLINE __m128 simd4f_pow5d12_inline_ps( __m128 vx )
{
__m128 vpow;
vpow = _mm_castsi128_ps( _mm_cvtps_epi32( _mm_mul_ps( _mm_cvtepi32_ps( _mm_castps_si128( _mm_mul_ps( vx, _mm_set1_ps( 6521909350804488192.0f ) ) ) ), _mm_set1_ps( 0.666666666666f ) ) ) );
vx = _mm_mul_ps( _mm_add_ps( _mm_mul_ps( vx, vpow ), _mm_mul_ps( _mm_mul_ps( vx, vx ), _mm_rsqrt_ps( vpow ) ) ), _mm_set1_ps( 0.5290553722f ) );
#if 0
vx = _mm_mul_ps( vx, _mm_rsqrt_ps( vx ) );
vx = _mm_mul_ps( vx, _mm_rsqrt_ps( vx ) );
#else
vx = _mm_sqrt_ps( vx );
vx = _mm_sqrt_ps( vx );
#endif
return vx;
}
#endif
////
#if CPU_SSE_SUPPORT
static inline void simdPrintDebugSSE4f( char *str, __m128 v )
{
float CPU_ALIGN16 store[4];
_mm_store_ps( (void *)store, v );
printf( "%s %f %f %f %f\n", str, (double)store[0], (double)store[1], (double)store[2], (double)store[3] );
return;
}
static inline void simdPrintDebugSSE2d( char *str, __m128d v )
{
double CPU_ALIGN16 store[2];
_mm_store_pd( (void *)store, v );
printf( "%s %f %f\n", str, store[0], store[1] );
return;
}
static inline void simdPrintDebugSSE16u8( char *str, __m128i v )
{
uint8_t CPU_ALIGN16 store[16];
_mm_store_si128( (void *)store, v );
printf( "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", str, store[0], store[1], store[2], store[3], store[4], store[5], store[6], store[7], store[8], store[9], store[10], store[11], store[12], store[13], store[14], store[15] );
return;
}
static inline void simdPrintDebugSSE8u16( char *str, __m128i v )
{
uint16_t CPU_ALIGN16 store[8];
_mm_store_si128( (void *)store, v );
printf( "%s %d %d %d %d %d %d %d %d\n", str, store[0], store[1], store[2], store[3], store[4], store[5], store[6], store[7] );
return;
}
static inline void simdPrintDebugSSE4u32( char *str, __m128i v )
{
uint32_t CPU_ALIGN16 store[4];
_mm_store_si128( (void *)store, v );
printf( "%s %d %d %d %d\n", str, store[0], store[1], store[2], store[3] );
return;
}
static inline void simdPrintDebugSSE2u64( char *str, __m128i v )
{
uint64_t CPU_ALIGN16 store[2];
_mm_store_si128( (void *)store, v );
printf( "%s %lld %lld\n", str, (long long)store[0], (long long)store[1] );
return;
}
#endif
////
#endif

View File

@ -0,0 +1,77 @@
/* -----------------------------------------------------------------------------
*
* Copyright (c) 2007-2017 Alexis Naveros.
* Portions developed under contract to the SURVICE Engineering Company.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* -----------------------------------------------------------------------------
*/
#ifndef IMG_H
#define IMG_H
typedef struct
{
int width;
int height;
int type;
int bytesperpixel;
int bytesperline;
} imgFormat;
enum
{
IMG_FORMAT_TYPE_ANY,
IMG_FORMAT_TYPE_RGB24,
IMG_FORMAT_TYPE_BGR24,
IMG_FORMAT_TYPE_RGBX32,
IMG_FORMAT_TYPE_BGRX32,
IMG_FORMAT_TYPE_RGBA32,
IMG_FORMAT_TYPE_BGRA32,
IMG_FORMAT_TYPE_GRAYSCALE,
IMG_FORMAT_TYPE_GRAYALPHA
};
typedef struct
{
imgFormat format;
void *data;
} imgImage;
////
void imgCopyRect( imgImage *image, int dstx, int dsty, int srcx, int srcy, int sizex, int sizey );
void (*imgBlendGetFunction( imgImage *dstimage, imgImage *srcimage ))( imgImage *dstimage, int dstx, int dsty, imgImage *srcimage );
int imgBlendImage( imgImage *dstimage, int dstx, int dsty, imgImage *srcimage );
void imgAllocCopy( imgImage *dst, imgImage *src );
void imgAllocCopyExtendBorder( imgImage *dstimage, imgImage *srcimage, int extendsize );
void imgAllocExtractChannel( imgImage *dst, imgImage *src, int channelindex );
void imgAllocExtractChannelExtendBorder( imgImage *dstimage, imgImage *srcimage, int channelindex, int extendsize );
void imgAllocCopyChannelToAlpha( imgImage *dstimage, imgImage *srcimage, int channelindex, unsigned char r, unsigned char g, unsigned char b );
void imgAllocAdjustBrightnessContrast( imgImage *dstimage, imgImage *srcimage, float brightness, float contrast );
void imgFree( imgImage *image );
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,150 @@
/* -----------------------------------------------------------------------------
*
* Copyright (c) 2014-2017 Alexis Naveros.
* Portions developed under contract to the SURVICE Engineering Company.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* -----------------------------------------------------------------------------
*/
#ifndef IMGRESIZE_H
#define IMGRESIZE_H
typedef struct
{
/* Specify filter type, from the IM_REDUCE_FILTER_* list */
int filter;
/* High quality, a little slow: hopcount=3; */
/* Good quality, much faster: hopcount=2; */
int hopcount;
/* Strong preservation/amplification of details: alpha=2.0f; */
/* Mild preservation/amplification of details: alpha=6.0f; */
float alpha;
/* NORMALMAP filters: factor to amyplify normals on X and Y before normalization */
float amplifynormal;
/* NORMALMAP_SUSTAIN filters: Preserve a factor of deviation "energy" as calculated by sqrtf(x*x+y*y) */
float normalsustainfactor;
} imReduceOptions;
static inline void imReduceSetOptions( imReduceOptions *options, int filter, int hopcount, float alpha, float amplifynormal, float normalsustainfactor )
{
options->filter = filter;
options->hopcount = hopcount;
options->alpha = alpha;
options->amplifynormal = amplifynormal;
options->normalsustainfactor = normalsustainfactor;
return;
}
/* Reduce the image's dimensions by an integer divisor ~ this is fairly fast */
int imReduceImageKaiserDataDivisor( unsigned char *dstdata, unsigned char *srcdata, int width, int height, int bytesperpixel, int bytesperline, int sizedivisor, imReduceOptions *options );
/* Same as imReduceImageKaiserDataDivisor(), but imgdst is allocated */
int imReduceImageKaiserDivisor( imgImage *imgdst, imgImage *imgsrc, int sizedivisor, imReduceOptions *options );
/* Reduce the image's dimensions to match the newwidth and newheight ~ this is a little slower */
int imReduceImageKaiserData( unsigned char *dstdata, unsigned char *srcdata, int width, int height, int bytesperpixel, int bytesperline, int newwidth, int newheight, imReduceOptions *options );
/* Same as imReduceImageKaiserData(), but imgdst is allocated */
int imReduceImageKaiser( imgImage *imgdst, imgImage *imgsrc, int newwidth, int newheight, imReduceOptions *options );
/* Resize by half with a dumb box filter ~ don't use that except for the smallest mipmaps */
/* Filters with ALPHANORM and/or SUSTAIN keywords are processed as the regular base filter only */
int imReduceImageHalfBoxData( unsigned char *dstdata, unsigned char *srcdata, int width, int height, int bytesperpixel, int bytesperline, imReduceOptions *options );
int imReduceImageHalfBox( imgImage *imgdst, imgImage *imgsrc, imReduceOptions *options );
/*
Keywords for image reduction filters
LINEAR: Data is linear, note that this is *not* the format of typical diffuse textures
SRGB: Color is in sRGB space, any alpha is presumed linear
NORMALMAP: RGB represents a XYZ vector as (2.0*RGB)-1.0f, any alpha is presumed linear
ALPHANORM: Alpha normalization, the weight of pixels is proportional to their alpha values
(do you have "black" fully transparent pixels? please use an ALPHANORM filter)
SUSTAIN: The "energy" of the normal map is sustained, amplified to preserve the level of details
Note that this filter is rather slow (set options->normalsustainfactor to 0.75 or so)
*/
enum
{
/* Linear space */
IM_REDUCE_FILTER_LINEAR,
IM_REDUCE_FILTER_LINEAR_ALPHANORM,
/* sRGB space (probably what you want for diffuse textures) */
IM_REDUCE_FILTER_SRGB,
IM_REDUCE_FILTER_SRGB_ALPHANORM,
/* RGB represents a XYZ vector as (2.0*RGB)-1.0f, any alpha is presumed linear */
IM_REDUCE_FILTER_NORMALMAP,
IM_REDUCE_FILTER_NORMALMAP_ALPHANORM,
IM_REDUCE_FILTER_NORMALMAP_SUSTAIN,
IM_REDUCE_FILTER_NORMALMAP_SUSTAIN_ALPHANORM,
/* Custom specialized filters */
IM_REDUCE_FILTER_WATERMAP,
IM_REDUCE_FILTER_PLANTMAP,
IM_REDUCE_FILTER_FOLLIAGE,
IM_REDUCE_FILTER_SKY,
IM_REDUCE_FILTER_FOG
};
////
#define IM_MIPMAP_CASCADE_MAX (16)
typedef struct
{
int width;
int height;
int layercount;
int bytesperpixel;
int bytesperline;
imReduceOptions *options;
void *mipmap[IM_MIPMAP_CASCADE_MAX];
} imMipmapCascade;
int imBuildMipmapCascade( imMipmapCascade *cascade, void *imagedata, int width, int height, int layercount, int bytesperpixel, int bytesperline, imReduceOptions *options, int cascadeflags );
void imFreeMipmapCascade( imMipmapCascade *cascade );
/* For base texture, propagate RGB channels to neighbors if they are fully transparent (ignored if bytesperpixel != 4 ) */
#define IM_CASCADE_FLAGS_COLOR_BORDER_BASE (0x1)
/* For generated mipmaps, propagate RGB channels to neighbors if they are fully transparent (ignored if bytesperpixel != 4 ) */
#define IM_CASCADE_FLAGS_COLOR_BORDER_MIPMAPS (0x2)
////
void imPropagateAlphaBorder( unsigned char *imagedata, int width, int height, int bytesperpixel, int bytesperline );
////
#endif

View File

@ -98,6 +98,8 @@ public:
//! fills the surface with given color
virtual void fill(const SColor &color) =0;
virtual void setDeleteMemory(bool val) = 0;
//! get the amount of Bits per Pixel of the given color format
static u32 getBitsPerPixelFromFormat(const ECOLOR_FORMAT format)
{

View File

@ -42,7 +42,9 @@ public:
//! Creates a surface from the file
/** \param file File handle to check.
\return Pointer to newly created image, or 0 upon error. */
virtual IImage* loadImage(io::IReadFile* file) const = 0;
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const = 0;
virtual core::dimension2du getImageSize(io::IReadFile* file) const { return core::dimension2du(0, 0); }
virtual bool supportThreadedLoading() const { return false; }
};

View File

@ -198,6 +198,16 @@ public:
virtual u64 getHandle() = 0;
virtual void unloadHandle() {}
virtual u32 getTextureSize() const { return 0; }
virtual void threadedReload(void* ptr, void* param) const {}
virtual void threadedSubImage(void* ptr) const {}
virtual void cleanThreadedLoader() {}
virtual int getThreadedLoadTextureCounter() const { return 0; }
protected:
//! Helper function, helps to get the desired texture creation format from the flags.

View File

@ -1186,7 +1186,7 @@ namespace video
\return The created image.
If you no longer need the image, you should call IImage::drop().
See IReferenceCounted::drop() for more information. */
virtual IImage* createImageFromFile(io::IReadFile* file) =0;
virtual IImage* createImageFromFile(io::IReadFile* file, video::IImageLoader** loader = NULL) =0;
//! Writes the provided image to a file.
/** Requires that there is a suitable image writer registered

View File

@ -104,9 +104,10 @@ bool breakable (wchar_t c)
if ((c > 12287 && c < 40960) || //Common CJK words
(c > 44031 && c < 55204) || //Hangul
(c > 63743 && c < 64256) || //More Chinese
c == 173 || c == L' ' || c == 0) //Soft hyphen and white space
return true;
return false;
c == 173 || c == L' ' || //Soft hyphen and white space
c == 47 || c == 92) //Slash and blackslash
return true;
return false;
}
} // end namespace gui
} // end namespace irr

View File

@ -457,6 +457,10 @@ inline SColor CImage::getPixelBox( s32 x, s32 y, s32 fx, s32 fy, s32 bias ) cons
return c;
}
void CImage::setDeleteMemory(bool val)
{
DeleteMemory = val;
}
} // end namespace video
} // end namespace irr

View File

@ -103,6 +103,8 @@ public:
//! fills the surface with given color
virtual void fill(const SColor &color);
virtual void setDeleteMemory(bool val);
private:
//! assumes format and size has been set and creates the rest

View File

@ -216,7 +216,7 @@ void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
//! creates a surface from the file
IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const
IImage* CImageLoaderBMP::loadImage(io::IReadFile* file, bool skip_checking) const
{
SBMPHeader header;

View File

@ -81,7 +81,7 @@ public:
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
//! creates a surface from the file
virtual IImage* loadImage(io::IReadFile* file) const;
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
private:

View File

@ -135,7 +135,7 @@ bool CImageLoaderJPG::isALoadableFileFormat(io::IReadFile* file) const
}
//! creates a surface from the file
IImage* CImageLoaderJPG::loadImage(io::IReadFile* file) const
IImage* CImageLoaderJPG::loadImage(io::IReadFile* file, bool skip_checking) const
{
#ifndef _IRR_COMPILE_WITH_LIBJPEG_
os::Printer::log("Can't load as not compiled with _IRR_COMPILE_WITH_LIBJPEG_:", file->getFileName(), ELL_DEBUG);

View File

@ -49,7 +49,7 @@ public:
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
//! creates a surface from the file
virtual IImage* loadImage(io::IReadFile* file) const;
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
private:

View File

@ -27,14 +27,14 @@ namespace video
// PNG function for error handling
static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
{
os::Printer::log("PNG fatal error", msg, ELL_ERROR);
printf("PNG fatal error: %s\n", msg);
longjmp(png_jmpbuf(png_ptr), 1);
}
// PNG function for warning handling
static void png_cpexcept_warn(png_structp png_ptr, png_const_charp msg)
{
os::Printer::log("PNG warning", msg, ELL_WARNING);
//os::Printer::log("PNG warning", msg, ELL_WARNING);
}
// PNG function for file reading
@ -86,7 +86,7 @@ bool CImageLoaderPng::isALoadableFileFormat(io::IReadFile* file) const
// load in the image data
IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
IImage* CImageLoaderPng::loadImage(io::IReadFile* file, bool skip_checking) const
{
#ifdef _IRR_COMPILE_WITH_LIBPNG_
if (!file)
@ -96,27 +96,17 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
//Used to point to image rows
u8** RowPointers = 0;
png_byte buffer[8];
// Read the first few bytes of the PNG file
if( file->read(buffer, 8) != 8 )
{
os::Printer::log("LOAD PNG: can't read file\n", file->getFileName(), ELL_ERROR);
if (skip_checking)
file->seek(8);
else if (!isALoadableFileFormat(file))
return 0;
}
// Check if it really is a PNG file
if( png_sig_cmp(buffer, 0, 8) )
{
os::Printer::log("LOAD PNG: not really a png\n", file->getFileName(), ELL_ERROR);
return 0;
}
// Allocate the png read struct
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn);
if (!png_ptr)
{
os::Printer::log("LOAD PNG: Internal PNG create read struct failure\n", file->getFileName(), ELL_ERROR);
//os::Printer::log("LOAD PNG: Internal PNG create read struct failure\n", file->getFileName(), ELL_ERROR);
return 0;
}
@ -124,7 +114,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
os::Printer::log("LOAD PNG: Internal PNG create info struct failure\n", file->getFileName(), ELL_ERROR);
//os::Printer::log("LOAD PNG: Internal PNG create info struct failure\n", file->getFileName(), ELL_ERROR);
png_destroy_read_struct(&png_ptr, NULL, NULL);
return 0;
}
@ -184,20 +174,6 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
int intent;
const double screen_gamma = 2.2;
if (png_get_sRGB(png_ptr, info_ptr, &intent))
png_set_gamma(png_ptr, screen_gamma, 0.45455);
else
{
double image_gamma;
if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
png_set_gamma(png_ptr, screen_gamma, image_gamma);
else
png_set_gamma(png_ptr, screen_gamma, 0.45455);
}
// Update the changes in between, as we need to get the new color type
// for proper processing of the RGBA type
png_read_update_info(png_ptr, info_ptr);
@ -229,7 +205,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
image = new CImage(ECF_R8G8B8, core::dimension2d<u32>(Width, Height));
if (!image)
{
os::Printer::log("LOAD PNG: Internal PNG create image struct failure\n", file->getFileName(), ELL_ERROR);
//os::Printer::log("LOAD PNG: Internal PNG create image struct failure\n", file->getFileName(), ELL_ERROR);
png_destroy_read_struct(&png_ptr, NULL, NULL);
return 0;
}
@ -238,7 +214,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
RowPointers = new png_bytep[Height];
if (!RowPointers)
{
os::Printer::log("LOAD PNG: Internal PNG create row pointers failure\n", file->getFileName(), ELL_ERROR);
//os::Printer::log("LOAD PNG: Internal PNG create row pointers failure\n", file->getFileName(), ELL_ERROR);
png_destroy_read_struct(&png_ptr, NULL, NULL);
delete image;
return 0;
@ -276,6 +252,26 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
#endif // _IRR_COMPILE_WITH_LIBPNG_
}
core::dimension2du CImageLoaderPng::getImageSize(io::IReadFile* file) const
{
#ifdef _IRR_COMPILE_WITH_LIBPNG_
if (!file || !isALoadableFileFormat(file))
return core::dimension2du(0, 0);
core::dimension2d<u32> dim;
file->seek(16);
file->read(&dim.Width, 4);
file->seek(20);
file->read(&dim.Height, 4);
file->seek(0);
#ifndef __BIG_ENDIAN__
dim.Width = os::Byteswap::byteswap(dim.Width);
dim.Height = os::Byteswap::byteswap(dim.Height);
#endif
return dim;
#else
return core::dimension2du(0, 0);
#endif // _IRR_COMPILE_WITH_LIBPNG_
}
IImageLoader* createImageLoaderPNG()
{
@ -287,4 +283,3 @@ IImageLoader* createImageLoaderPNG()
}//end namespace video
#endif

View File

@ -25,15 +25,17 @@ class CImageLoaderPng : public IImageLoader
{
public:
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".png")
virtual bool isALoadableFileExtension(const io::path& filename) const;
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".png")
virtual bool isALoadableFileExtension(const io::path& filename) const;
//! returns true if the file maybe is able to be loaded by this class
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
//! returns true if the file maybe is able to be loaded by this class
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
//! creates a surface from the file
virtual IImage* loadImage(io::IReadFile* file) const;
//! creates a surface from the file
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
virtual core::dimension2du getImageSize(io::IReadFile* file) const;
virtual bool supportThreadedLoading() const { return true; }
};

View File

@ -103,6 +103,7 @@ CNullDriver::CNullDriver(io::IFileSystem* io, const core::dimension2d<u32>& scre
// DriverAttributes->addInt("MaxGeometryVerticesOut", 0);
// DriverAttributes->addFloat("MaxTextureLODBias", 0.f);
DriverAttributes->addInt("Version", 1);
DriverAttributes->setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(2048, 2048));
// DriverAttributes->addInt("ShaderLanguageVersion", 0);
// DriverAttributes->addInt("AntiAlias", 0);
@ -1278,7 +1279,7 @@ IImage* CNullDriver::createImageFromFile(const io::path& filename)
//! Creates a software image from a file.
IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
IImage* CNullDriver::createImageFromFile(io::IReadFile* file, video::IImageLoader** loader)
{
if (!file)
return 0;
@ -1292,6 +1293,11 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
{
if (SurfaceLoader[i]->isALoadableFileExtension(file->getFileName()))
{
if (loader)
{
*loader = SurfaceLoader[i];
return 0;
}
// reset file position which might have changed due to previous loadImage calls
file->seek(0);
image = SurfaceLoader[i]->loadImage(file);
@ -1307,6 +1313,11 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
file->seek(0);
if (SurfaceLoader[i]->isALoadableFileFormat(file))
{
if (loader)
{
*loader = SurfaceLoader[i];
return 0;
}
file->seek(0);
image = SurfaceLoader[i]->loadImage(file);
if (image)

View File

@ -348,7 +348,7 @@ namespace video
virtual IImage* createImageFromFile(const io::path& filename);
//! Creates a software image from a file.
virtual IImage* createImageFromFile(io::IReadFile* file);
virtual IImage* createImageFromFile(io::IReadFile* file, video::IImageLoader** loader = NULL);
//! Creates a software image from a byte array.
/** \param useForeignMemory: If true, the image will use the data pointer

View File

@ -1,5 +1,5 @@
# Modify this file to change the last-modified date when you add/remove a file.
# This will then trigger a new cmake run automatically.
# Modify this file to change the last-modified date when you add/remove a file.
# This will then trigger a new cmake run automatically.
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")

View File

@ -102,7 +102,7 @@ int getRAM()
*/
int getNumProcessors()
{
#ifdef __linux__
#if defined(__linux__) || defined(__CYGWIN__)
return sysconf(_SC_NPROCESSORS_CONF);
#endif
#ifdef WIN32

View File

@ -82,6 +82,7 @@ namespace HardwareStats
// ========================================================================
void reportHardwareStats();
const std::string& getOSVersion();
int getNumProcessors();
}; // HardwareStats
#endif

View File

@ -553,6 +553,33 @@ namespace UserConfigParams
&m_video_group, "Max texture size when high definition textures are "
"disabled"));
PARAM_PREFIX BoolUserConfigParam m_hq_mipmap
PARAM_DEFAULT(BoolUserConfigParam(false, "hq_mipmap",
&m_video_group, "Generate mipmap for textures using "
"high quality method with SSE"));
// ---- Recording
PARAM_PREFIX GroupUserConfigParam m_recording_group
PARAM_DEFAULT( GroupUserConfigParam("Recording", "Recording Settings") );
PARAM_PREFIX BoolUserConfigParam m_record_bmp
PARAM_DEFAULT(BoolUserConfigParam(false, "record_bmp",
&m_recording_group, "Record video using uncompressed bitmap, notice: this "
"will require a lot of space and fast disk access."));
PARAM_PREFIX BoolUserConfigParam m_limit_game_fps
PARAM_DEFAULT(BoolUserConfigParam(true, "limit_game_fps",
&m_recording_group, "Limit game framerate not beyond the fps of recording "
"video."));
PARAM_PREFIX IntUserConfigParam m_record_compression
PARAM_DEFAULT(IntUserConfigParam(90, "record_compression",
&m_recording_group, "Specify the compression level of recording video"));
PARAM_PREFIX IntUserConfigParam m_record_fps
PARAM_DEFAULT(IntUserConfigParam(30, "record_fps",
&m_recording_group, "Specify the fps of recording video"));
// ---- Debug - not saved to config file
/** If gamepad debugging is enabled. */
PARAM_PREFIX bool m_unit_testing PARAM_DEFAULT(false);
@ -617,9 +644,6 @@ namespace UserConfigParams
/** True if graphical profiler should be displayed */
PARAM_PREFIX bool m_profiler_enabled PARAM_DEFAULT( false );
/** True if hardware skinning should be enabled */
PARAM_PREFIX bool m_hw_skinning_enabled PARAM_DEFAULT( false );
// not saved to file
// ---- Networking

View File

@ -127,8 +127,13 @@ void CameraNormal::smoothMoveCamera(float dt)
delta2 = 1;
btTransform btt = m_kart->getTrans();
m_kart_position = btt.getOrigin();// m_kart_position + (btt.getOrigin() - m_kart_position) * delta2;
m_kart_rotation = m_kart_rotation.normalized().slerp(btt.getRotation().normalized(), delta2);
m_kart_position = btt.getOrigin();
btQuaternion q1, q2;
q1 = m_kart_rotation.normalized();
q2 = btt.getRotation().normalized();
if (dot(q1, q2) < 0.0f)
q2 = -q2;
m_kart_rotation = q1.slerp(q2, delta2);
btt.setOrigin(m_kart_position);
btt.setRotation(m_kart_rotation);

View File

@ -52,6 +52,7 @@ void CentralVideoSettings::init()
hasGS = false;
hasTextureFilterAnisotropic = false;
hasTextureSwizzle = false;
hasPixelBufferObject = false;
#if defined(USE_GLES2)
hasBGRA = false;
@ -196,6 +197,11 @@ void CentralVideoSettings::init()
hasTextureSwizzle = true;
Log::info("GLDriver", "ARB Texture Swizzle Present");
}
if (hasGLExtension("GL_ARB_pixel_buffer_object"))
{
hasPixelBufferObject = true;
Log::info("GLDriver", "ARB Pixel Buffer Object Present");
}
// Only unset the high def textures if they are set as default. If the
// user has enabled them (bit 1 set), then leave them enabled.
if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_HIGHDEFINITION_TEXTURES) &&
@ -238,7 +244,7 @@ void CentralVideoSettings::init()
hasTextureStorage = true;
hasTextureSwizzle = true;
}
if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_EXPLICIT_ATTRIB_LOCATION) &&
m_glsl == true)
{
@ -476,4 +482,14 @@ bool CentralVideoSettings::isARBTextureSwizzleUsable() const
return m_glsl && hasTextureSwizzle;
}
bool CentralVideoSettings::isARBPixelBufferObjectUsable() const
{
return hasPixelBufferObject;
}
bool CentralVideoSettings::supportsThreadedTextureLoading() const
{
return isARBPixelBufferObjectUsable() && isARBBufferStorageUsable() && isARBTextureStorageUsable();
}
#endif // !SERVER_ONLY

View File

@ -44,6 +44,7 @@ private:
bool hasMultiDrawIndirect;
bool hasTextureFilterAnisotropic;
bool hasTextureSwizzle;
bool hasPixelBufferObject;
#if defined(USE_GLES2)
bool hasBGRA;
@ -84,6 +85,7 @@ public:
bool isARBExplicitAttribLocationUsable() const;
bool isEXTTextureFilterAnisotropicUsable() const;
bool isARBTextureSwizzleUsable() const;
bool isARBPixelBufferObjectUsable() const;
#if defined(USE_GLES2)
bool isEXTTextureFormatBGRA8888Usable() const;
@ -98,6 +100,7 @@ public:
bool supportsComputeShadersFiltering() const;
bool supportsAsyncInstanceUpload() const;
bool supportsHardwareSkinning() const;
bool supportsThreadedTextureLoading() const;
// "Macro" around feature support and user config
bool isShadowEnabled() const;

View File

@ -0,0 +1,120 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2017 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
#include "graphics/hq_mipmap_generator.hpp"
#include "graphics/stk_tex_manager.hpp"
#undef DUMP_MIPMAP
#ifdef DUMP_MIPMAP
#include "graphics/irr_driver.hpp"
#include "utils/string_utils.hpp"
#endif
#include <cassert>
extern "C"
{
#include <mipmap/img.h>
#include <mipmap/imgresize.h>
}
// ----------------------------------------------------------------------------
HQMipmapGenerator::HQMipmapGenerator(const io::path& name, uint8_t* data,
const core::dimension2d<u32>& size,
GLuint texture_name, TexConfig* tc)
: video::ITexture(name), m_orig_data(data), m_size(size),
m_texture_name(texture_name), m_texture_size(0),
m_mipmap_data(NULL), m_tex_config(tc)
{
assert(m_tex_config != NULL);
unsigned width = m_size.Width;
unsigned height = m_size.Height;
while (true)
{
width = width < 2 ? 1 : width >> 1;
height = height < 2 ? 1 : height >> 1;
m_mipmap_sizes.emplace_back(core::dimension2du(width, height),
m_texture_size);
m_texture_size += width * height * 4;
if (width == 1 && height == 1)
break;
}
m_texture_size = unsigned(m_mipmap_sizes.back().second) + 4;
m_mipmap_data = malloc(sizeof(imMipmapCascade));
} // HQMipmapGenerator
// ----------------------------------------------------------------------------
void HQMipmapGenerator::threadedReload(void* ptr, void* param) const
{
imReduceOptions options;
imReduceSetOptions(&options,
m_tex_config->m_normal_map ?
IM_REDUCE_FILTER_NORMALMAP: m_tex_config->m_srgb ?
IM_REDUCE_FILTER_SRGB : IM_REDUCE_FILTER_LINEAR/*filter*/,
2/*hopcount*/, 2.0f/*alpha*/, 1.0f/*amplifynormal*/,
0.0f/*normalsustainfactor*/);
imMipmapCascade* mm_cascade = (imMipmapCascade*)m_mipmap_data;
#ifdef DEBUG
int ret = imBuildMipmapCascade(mm_cascade, m_orig_data, m_size.Width,
m_size.Height, 1/*layercount*/, 4, m_size.Width * 4, &options, 0);
assert(ret == 1);
#else
imBuildMipmapCascade(mm_cascade, m_orig_data, m_size.Width,
m_size.Height, 1/*layercount*/, 4, m_size.Width * 4, &options, 0);
#endif
for (unsigned int i = 0; i < m_mipmap_sizes.size(); i++)
{
const unsigned size = m_mipmap_sizes[i].first.getArea() * 4;
memcpy((uint8_t*)ptr + m_mipmap_sizes[i].second,
mm_cascade->mipmap[i + 1], size);
#ifdef DUMP_MIPMAP
video::IImage* image = irr_driver->getVideoDriver()
->createImageFromData(video::ECF_A8R8G8B8, m_mipmap_sizes[i].first,
mm_cascade->mipmap[i + 1], false/*ownForeignMemory*/);
irr_driver->getVideoDriver()->writeImageToFile(image, std::string
(StringUtils::toString(i) + "_" +
StringUtils::getBasename(NamedPath.getPtr())).c_str());
image->drop();
#endif
}
} // threadedReload
// ----------------------------------------------------------------------------
void HQMipmapGenerator::threadedSubImage(void* ptr) const
{
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
glBindTexture(GL_TEXTURE_2D, m_texture_name);
for (unsigned int i = 0; i < m_mipmap_sizes.size(); i++)
{
glTexSubImage2D(GL_TEXTURE_2D, i + 1, 0, 0,
m_mipmap_sizes[i].first.Width, m_mipmap_sizes[i].first.Height,
GL_BGRA, GL_UNSIGNED_BYTE,
(uint8_t*)ptr + m_mipmap_sizes[i].second);
}
delete this;
#endif
} // threadedSubImage
// ----------------------------------------------------------------------------
void HQMipmapGenerator::cleanThreadedLoader()
{
delete[] m_orig_data;
imFreeMipmapCascade((imMipmapCascade*)m_mipmap_data);
free(m_mipmap_data);
} // cleanThreadedLoader
#endif

View File

@ -0,0 +1,103 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2017 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_HQ_MIPMAP_GENERATOR_HPP
#define HEADER_HQ_MIPMAP_GENERATOR_HPP
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
#include "graphics/gl_headers.hpp"
#include "utils/no_copy.hpp"
#include "utils/types.hpp"
#include <vector>
#include <ITexture.h>
using namespace irr;
struct TexConfig;
class HQMipmapGenerator : public video::ITexture, NoCopy
{
private:
uint8_t* m_orig_data;
core::dimension2d<u32> m_size;
GLuint m_texture_name;
unsigned int m_texture_size;
void* m_mipmap_data;
TexConfig* m_tex_config;
std::vector<std::pair<core::dimension2d<u32>, size_t> > m_mipmap_sizes;
public:
// ------------------------------------------------------------------------
HQMipmapGenerator(const io::path& name, uint8_t* data,
const core::dimension2d<u32>& size, GLuint texture_name,
TexConfig* tc);
// ------------------------------------------------------------------------
virtual ~HQMipmapGenerator() {}
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0)
{ return NULL; }
// ------------------------------------------------------------------------
virtual void unlock() {}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getOriginalSize() const
{ return m_size; }
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getSize() const { return m_size; }
// ------------------------------------------------------------------------
virtual video::E_DRIVER_TYPE getDriverType() const
{
#if defined(USE_GLES2)
return video::EDT_OGLES2;
#else
return video::EDT_OPENGL;
#endif
}
// ------------------------------------------------------------------------
virtual video::ECOLOR_FORMAT getColorFormat() const
{ return video::ECF_A8R8G8B8; }
// ------------------------------------------------------------------------
virtual u32 getPitch() const { return 0; }
// ------------------------------------------------------------------------
virtual bool hasMipMaps() const { return false; }
// ------------------------------------------------------------------------
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u32 getOpenGLTextureName() const { return m_texture_name; }
// ------------------------------------------------------------------------
virtual u64 getHandle() { return 0; }
// ------------------------------------------------------------------------
virtual unsigned int getTextureSize() const { return m_texture_size; }
// ------------------------------------------------------------------------
virtual void threadedReload(void* ptr, void* param) const;
// ------------------------------------------------------------------------
virtual void threadedSubImage(void* ptr) const;
// ------------------------------------------------------------------------
virtual void cleanThreadedLoader();
}; // HQMipmapGenerator
#endif
#endif

View File

@ -61,6 +61,7 @@
#include "states_screens/dialogs/confirm_resolution_dialog.hpp"
#include "states_screens/state_manager.hpp"
#include "tracks/track_manager.hpp"
#include "utils/avi_writer.hpp"
#include "utils/constants.hpp"
#include "utils/log.hpp"
#include "utils/profiler.hpp"
@ -146,6 +147,7 @@ IrrDriver::IrrDriver()
m_last_light_bucket_distance = 0;
m_clear_color = video::SColor(255, 100, 101, 140);
m_skinning_joint = 0;
m_recording = false;
} // IrrDriver
@ -167,6 +169,9 @@ IrrDriver::~IrrDriver()
#endif
delete m_wind;
delete m_renderer;
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
AVIWriter::kill();
#endif
} // ~IrrDriver
// ----------------------------------------------------------------------------
@ -716,13 +721,11 @@ void IrrDriver::initDevice()
// ----------------------------------------------------------------------------
void IrrDriver::setMaxTextureSize()
{
if( (UserConfigParams::m_high_definition_textures & 0x01) == 0)
{
io::IAttributes &att = m_video_driver->getNonConstDriverAttributes();
att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(
UserConfigParams::m_max_texture_size,
UserConfigParams::m_max_texture_size));
}
const unsigned max =
(UserConfigParams::m_high_definition_textures & 0x01) == 0 ?
UserConfigParams::m_max_texture_size : 2048;
io::IAttributes &att = m_video_driver->getNonConstDriverAttributes();
att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(max, max));
} // setMaxTextureSize
// ----------------------------------------------------------------------------
@ -923,6 +926,9 @@ void IrrDriver::applyResolutionSettings()
// (we're sure to update main.cpp at some point and forget this one...)
VAOManager::getInstance()->kill();
STKTexManager::getInstance()->kill();
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
AVIWriter::kill();
#endif
// initDevice will drop the current device.
if (CVS->isGLSL())
{
@ -1842,6 +1848,9 @@ void IrrDriver::update(float dt)
PropertyAnimator::get()->update(dt);
STKTexManager::getInstance()
->checkThreadedLoadTextures(true/*util_queue_empty*/);
World *world = World::getWorld();
if (world)
@ -1884,8 +1893,41 @@ void IrrDriver::update(float dt)
// menu.
//if(World::getWorld() && World::getWorld()->isRacePhase())
// printRenderStats();
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
if (m_recording)
AVIWriter::getInstance()->captureFrameBufferImage(dt);
#endif
} // update
// ----------------------------------------------------------------------------
void IrrDriver::setRecording(bool val)
{
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
if (!CVS->isARBPixelBufferObjectUsable())
{
Log::warn("irr_driver", "PBO extension missing, can't record video.");
return;
}
if (m_recording == val)
return;
m_recording = val;
if (m_recording == true)
{
std::string track_name = World::getWorld() != NULL ?
race_manager->getTrackName() : "menu";
AVIWriter::setRecordingTarget(file_manager->getScreenshotDir() +
track_name);
AVIWriter::getInstance()->resetFrameBufferImage();
MessageQueue::add(MessageQueue::MT_GENERIC,
_("Video recording started."));
}
else
{
AVIWriter::getInstance()->stopRecording();
}
#endif
} // setRecording
// ----------------------------------------------------------------------------
void IrrDriver::requestScreenshot()
@ -2095,4 +2137,3 @@ GLuint IrrDriver::getDepthStencilTexture()
return m_renderer->getDepthStencilTexture();
} // getDepthStencilTexture

View File

@ -163,6 +163,7 @@ private:
bool m_lightviz;
bool m_distortviz;
bool m_boundingboxesviz;
bool m_recording;
/** Background colour to reset a buffer. Can be changed by each track. */
irr::video::SColor m_clear_color;
@ -414,6 +415,10 @@ public:
// ------------------------------------------------------------------------
bool getBoundingBoxesViz() { return m_boundingboxesviz; }
// ------------------------------------------------------------------------
bool isRecording() const { return m_recording; }
// ------------------------------------------------------------------------
void setRecording(bool val);
// ------------------------------------------------------------------------
u32 getRenderPass() { return m_renderpass; }
// ------------------------------------------------------------------------
std::vector<LightNode *> getLights() { return m_lights; }

View File

@ -526,9 +526,9 @@ void Material::install(bool srgb, bool premul_alpha)
}
else
{
m_texture = STKTexManager::getInstance()->getTexture
(m_original_full_path, srgb, premul_alpha, false/*set_material*/,
srgb/*mesh_tex*/);
TexConfig tc(srgb, premul_alpha, srgb/*mesh_tex*/);
m_texture = STKTexManager::getInstance()
->getTexture(m_original_full_path, &tc);
}
if (m_texture == NULL) return;
@ -771,9 +771,8 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
STKTexManager* stm = STKTexManager::getInstance();
if (m_gloss_map.size() > 0 && CVS->isDefferedEnabled())
{
glossytex = stm->getTexture(m_gloss_map, false/*srgb*/,
false/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/);
TexConfig gtc(false/*srgb*/, false/*premul_alpha*/);
glossytex = stm->getTexture(m_gloss_map, &gtc);
}
else
{
@ -787,9 +786,11 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
stm->STKTexManager::getInstance()->getUnicolorTexture(SColor(0, 0, 0, 0));
if (m_colorization_mask.size() > 0)
{
TexConfig cmtc(false/*srgb*/, false/*premul_alpha*/,
true/*mesh_tex*/, false/*set_material*/,
true/*color_mask*/);
colorization_mask_tex = stm->getTexture(m_colorization_mask,
false/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/, false/*no_upload*/, true/*single_channel*/);
&cmtc);
}
m->setTexture(2, colorization_mask_tex);
}
@ -845,32 +846,29 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
m->setTexture(1, glossytex);
return;
case SHADERTYPE_SPLATTING:
tex = stm->getTexture(m_splatting_texture_1,
true/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/);
{
TexConfig stc(true/*srgb*/, false/*premul_alpha*/,
true/*mesh_tex*/, false/*set_material*/);
tex = stm->getTexture(m_splatting_texture_1, &stc);
m->setTexture(3, tex);
if (m_splatting_texture_2.size() > 0)
{
tex = stm->getTexture(m_splatting_texture_2,
true/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/);
tex = stm->getTexture(m_splatting_texture_2, &stc);
}
m->setTexture(4, tex);
if (m_splatting_texture_3.size() > 0)
{
tex = stm->getTexture(m_splatting_texture_3,
true/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/);
tex = stm->getTexture(m_splatting_texture_3, &stc);
}
m->setTexture(5, tex);
if (m_splatting_texture_4.size() > 0)
{
tex = stm->getTexture(m_splatting_texture_4,
false/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/);
TexConfig s4tc(false/*srgb*/, false/*premul_alpha*/,
true/*mesh_tex*/, false/*set_material*/);
tex = stm->getTexture(m_splatting_texture_4, &s4tc);
}
m->setTexture(6, tex);
m->setTexture(7, glossytex);
@ -878,6 +876,7 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
// Material and shaders
m->MaterialType = Shaders::getShader(ES_SPLATTING);
return;
}
case SHADERTYPE_WATER:
m->setTexture(1, irr_driver->getTexture(FileManager::TEXTURE,
"waternormals.jpg"));
@ -912,9 +911,10 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
{
if (CVS->isDefferedEnabled())
{
tex = stm->getTexture(m_normal_map_tex, false/*srgb*/,
false/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/);
TexConfig nmtc(false/*srgb*/, false/*premul_alpha*/,
true/*mesh_tex*/, false/*set_material*/,
false/*color_mask*/, true/*normal_map*/);
tex = stm->getTexture(m_normal_map_tex, &nmtc);
}
else
tex = stm->STKTexManager::getInstance()->getUnicolorTexture(SColor(0, 0, 0, 0));

View File

@ -49,10 +49,10 @@ static GLuint generateRTT(const core::dimension2du &res, GLint internalFormat, G
return result;
}
RTT::RTT(size_t width, size_t height)
RTT::RTT(size_t width, size_t height, float rtt_scale)
{
m_width = width;
m_height = height;
m_width = width * rtt_scale;
m_height = height * rtt_scale;
m_shadow_FBO = NULL;
m_RH_FBO = NULL;
m_RSM = NULL;
@ -60,14 +60,13 @@ RTT::RTT(size_t width, size_t height)
using namespace video;
using namespace core;
dimension2du res(int(width * UserConfigParams::m_scale_rtts_factor),
int(height * UserConfigParams::m_scale_rtts_factor) );
dimension2du res(m_width, m_height);
const dimension2du half = res/2;
const dimension2du quarter = res/4;
const dimension2du eighth = res/8;
const u16 shadowside = u16(1024 * UserConfigParams::m_scale_rtts_factor);
const u16 shadowside = u16(1024 * rtt_scale);
const dimension2du shadowsize0(shadowside, shadowside);
const dimension2du shadowsize1(shadowside / 2, shadowside / 2);
const dimension2du shadowsize2(shadowside / 4, shadowside / 4);
@ -294,7 +293,12 @@ RTT::RTT(size_t width, size_t height)
glClear(GL_COLOR_BUFFER_BIT);
getFBO(FBO_COMBINED_DIFFUSE_SPECULAR).bind();
glClearColor(.5, .5, .5, .5);
float color = 0.5;
#if defined(USE_GLES2)
if (!CVS->isDefferedEnabled())
color = pow(color, 1. / 2.2);
#endif
glClearColor(color, color, color, color);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
#if !defined(USE_GLES2)

View File

@ -144,7 +144,7 @@ enum TypeRTT : unsigned int
class RTT
{
public:
RTT(size_t width, size_t height);
RTT(size_t width, size_t height, float rtt_scale = 1.0f);
~RTT();
size_t getWidth () const { return m_width ; }

View File

@ -199,10 +199,8 @@ void ShaderBasedRenderer::uploadLightingData() const
void ShaderBasedRenderer::computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode,
size_t width, size_t height)
{
float w = width * UserConfigParams::m_scale_rtts_factor;
float h = height * UserConfigParams::m_scale_rtts_factor;
m_current_screen_size = core::vector2df(w, h);
m_shadow_matrices.computeMatrixesAndCameras(camnode, int(w), int(h),
m_current_screen_size = core::vector2df((float)width, (float)height);
m_shadow_matrices.computeMatrixesAndCameras(camnode, width, height,
m_rtts->getDepthStencilTexture());
} // computeMatrixesAndCameras
@ -669,7 +667,8 @@ void ShaderBasedRenderer::onLoadWorld()
const core::recti &viewport = Camera::getCamera(0)->getViewport();
size_t width = viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X;
size_t height = viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y;
RTT* rtts = new RTT(width, height);
RTT* rtts = new RTT(width, height, CVS->isDefferedEnabled() ?
UserConfigParams::m_scale_rtts_factor : 1.0f);
setRTT(rtts);
}
@ -787,9 +786,7 @@ void ShaderBasedRenderer::render(float dt)
RaceGUIBase *rg = world->getRaceGUI();
if (rg) rg->update(dt);
bool force_rtt = UserConfigParams::m_scale_rtts_factor != 1.0f;
if (!CVS->isDefferedEnabled() && !force_rtt)
if (!CVS->isDefferedEnabled())
{
prepareForwardRenderer();
}
@ -803,12 +800,10 @@ void ShaderBasedRenderer::render(float dt)
oss << "drawAll() for kart " << cam;
PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60,
0x00, 0x00);
camera->activate(!CVS->isDefferedEnabled() && !force_rtt);
camera->activate(!CVS->isDefferedEnabled());
rg->preRenderCallback(camera); // adjusts start referee
irr_driver->getSceneManager()->setActiveCamera(camnode);
const core::recti &viewport = camera->getViewport();
if (!CVS->isDefferedEnabled())
glEnable(GL_FRAMEBUFFER_SRGB);
@ -816,12 +811,12 @@ void ShaderBasedRenderer::render(float dt)
m_lighting_passes.updateLightsInfo(camnode, dt);
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("UBO upload", 0x0, 0xFF, 0x0);
computeMatrixesAndCameras(camnode, viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y);
computeMatrixesAndCameras(camnode, m_rtts->getWidth(), m_rtts->getHeight());
m_shadow_matrices.updateSunOrthoMatrices();
if(CVS->isARBUniformBufferObjectUsable())
uploadLightingData();
PROFILER_POP_CPU_MARKER();
renderScene(camnode, dt, track->hasShadows(), force_rtt);
renderScene(camnode, dt, track->hasShadows(), false);
if (irr_driver->getBoundingBoxesViz())
{
@ -830,7 +825,7 @@ void ShaderBasedRenderer::render(float dt)
debugPhysics();
if (CVS->isDefferedEnabled() || force_rtt)
if (CVS->isDefferedEnabled())
{
renderPostProcessing(camera);
}

View File

@ -51,6 +51,62 @@ const std::string& ShaderFilesManager::getHeader()
return shader_header;
} // getHeader
// ----------------------------------------------------------------------------
void ShaderFilesManager::readFile(const std::string& file,
std::ostringstream& code)
{
std::ifstream stream(file_manager->getShader(file), std::ios::in);
if (!stream.is_open())
{
Log::error("ShaderFilesManager", "Can not open '%s'.", file.c_str());
return;
}
const std::string stk_include = "#stk_include";
std::string line;
while (std::getline(stream, line))
{
const std::size_t pos = line.find(stk_include);
// load the custom file pointed by the #stk_include directive
if (pos != std::string::npos)
{
// find the start "
std::size_t pos = line.find("\"");
if (pos == std::string::npos)
{
Log::error("ShaderFilesManager", "Invalid #stk_include"
" line: '%s'.", line.c_str());
continue;
}
std::string filename = line.substr(pos + 1);
// find the end "
pos = filename.find("\"");
if (pos == std::string::npos)
{
Log::error("ShaderFilesManager", "Invalid #stk_include"
" line: '%s'.", line.c_str());
continue;
}
filename = filename.substr(0, pos);
// read the whole include file
readFile(filename, code);
}
else
{
code << "\n" << line;
}
}
stream.close();
}
// ----------------------------------------------------------------------------
/** Loads a single shader. This is NOT cached, use addShaderFile for that.
* \param file Filename of the shader to load.
@ -90,7 +146,9 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
if (CVS->isARBExplicitAttribLocationUsable())
{
#if !defined(USE_GLES2)
code << "#extension GL_ARB_explicit_attrib_location : enable\n";
#endif
code << "#define Explicit_Attrib_Location_Usable\n";
}
@ -106,12 +164,14 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
code << "#define VSLayer\n";
if (CVS->needsRGBBindlessWorkaround())
code << "#define SRGBBindlessFix\n";
if (CVS->isDefferedEnabled())
code << "#define Advanced_Lighting_Enabled\n";
#if !defined(USE_GLES2)
// shader compilation fails with some drivers if there is no precision
// qualifier
if (type == GL_FRAGMENT_SHADER)
code << "precision mediump float;\n";
code << "precision highp float;\n";
#else
int range[2], precision;
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range,
@ -126,69 +186,7 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
code << getHeader();
std::ifstream stream(file_manager->getShader(file), std::ios::in);
if (stream.is_open())
{
const std::string stk_include = "#stk_include";
std::string line;
while (std::getline(stream, line))
{
const std::size_t pos = line.find(stk_include);
// load the custom file pointed by the #stk_include directive
if (pos != std::string::npos)
{
// find the start "
std::size_t pos = line.find("\"");
if (pos == std::string::npos)
{
Log::error("ShaderFilesManager", "Invalid #stk_include"
" line: '%s'.", line.c_str());
continue;
}
std::string filename = line.substr(pos + 1);
// find the end "
pos = filename.find("\"");
if (pos == std::string::npos)
{
Log::error("ShaderFilesManager", "Invalid #stk_include"
" line: '%s'.", line.c_str());
continue;
}
filename = filename.substr(0, pos);
// read the whole include file
std::ifstream include_stream(file_manager->getShader(filename), std::ios::in);
if (!include_stream.is_open())
{
Log::error("ShaderFilesManager", "Couldn't open included"
" shader: '%s'.", filename.c_str());
continue;
}
std::string include_line = "";
while (std::getline(include_stream, include_line))
{
code << "\n" << include_line;
}
include_stream.close();
}
else
{
code << "\n" << line;
}
}
stream.close();
}
else
{
Log::error("ShaderFilesManager", "Can not open '%s'.", file.c_str());
}
readFile(file, code);
Log::info("ShaderFilesManager", "Compiling shader : %s", file.c_str());
const std::string &source = code.str();

View File

@ -38,6 +38,7 @@ private:
// ------------------------------------------------------------------------
const std::string& getHeader();
void readFile(const std::string& file, std::ostringstream& code);
public:
// ------------------------------------------------------------------------

View File

@ -163,19 +163,6 @@ void Skybox::generateCubeMapFromTextures()
assert(img != NULL);
img->copyToScaling(rgba[i], size, size);
#if defined(USE_GLES2)
if (CVS->isEXTTextureFormatBGRA8888Usable())
{
// BGRA image returned by getTextureImage causes black sky in gles
for (unsigned int j = 0; j < size * size; j++)
{
char tmp_val = rgba[i][j * 4];
rgba[i][j * 4] = rgba[i][j * 4 + 2];
rgba[i][j * 4 + 2] = tmp_val;
}
}
#endif
if (i == 2 || i == 3)
{
char *tmp = new char[size * size * 4];
@ -196,7 +183,8 @@ void Skybox::generateCubeMapFromTextures()
GL_COMPRESSED_SRGB_ALPHA : GL_SRGB_ALPHA;
GLint format = GL_BGRA;
#else
GLint internal_format = GL_RGBA8;
GLint internal_format = CVS->isDefferedEnabled() ? GL_SRGB8_ALPHA8
: GL_RGBA8;
GLint format = GL_RGBA;
#endif

View File

@ -565,15 +565,12 @@ void SphericalHarmonics::setTextures(const std::vector<video::ITexture *> &spher
assert(img != NULL);
img->copyToScaling(sh_rgba[i], sh_w, sh_h);
#if defined(USE_GLES2)
if (!CVS->isEXTTextureFormatBGRA8888Usable())
// Code here assume color format is BGRA
for (unsigned int j = 0; j < sh_w * sh_h; j++)
{
// Code here assume color format is BGRA
for (unsigned int j = 0; j < sh_w * sh_h; j++)
{
char tmp_val = sh_rgba[i][j * 4];
sh_rgba[i][j * 4] = sh_rgba[i][j * 4 + 2];
sh_rgba[i][j * 4 + 2] = tmp_val;
}
char tmp_val = sh_rgba[i][j * 4];
sh_rgba[i][j * 4] = sh_rgba[i][j * 4 + 2];
sh_rgba[i][j * 4 + 2] = tmp_val;
}
#endif
} //for (unsigned i = 0; i < 6; i++)

View File

@ -40,9 +40,9 @@ Stars::Stars(AbstractKart *kart)
m_parent_kart_node = kart->getNode();
m_enabled = false;
TexConfig stc(true/*srgb*/, true/*premul_alpha*/);
video::ITexture* texture = STKTexManager::getInstance()->getTexture
("starparticle.png", true/*srgb*/, true/*premul_alpha*/,
false/*set_material*/, true/*mesh_tex*/);
("starparticle.png", &stc);
Material* star_material =
material_manager->getMaterial("starparticle.png");

View File

@ -1044,10 +1044,11 @@ void STKMeshLoader::loadTextures(SB3dMaterial& material) const
else
full_path = fs->getFileBasename(B3dTexture->TextureName);
TexConfig mtc(i <= 1 ? true : false/*srgb*/, false/*premul_alpha*/,
true/*mesh_tex*/, true/*set_material*/);
video::ITexture* tex =
STKTexManager::getInstance()->getTexture(full_path.c_str(),
i <= 1 ? true : false/*is_srgb*/, false/*premul_alpha*/,
true/*set_material*/);
&mtc);
material.Material.setTexture(i, tex);
if (material.Textures[i]->Flags & 0x10) // Clamp U

View File

@ -16,19 +16,107 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "graphics/stk_tex_manager.hpp"
#include "config/hardware_stats.hpp"
#include "config/user_config.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/materials.hpp"
#include "graphics/threaded_tex_loader.hpp"
#include "graphics/stk_texture.hpp"
#include "io/file_manager.hpp"
#include "utils/string_utils.hpp"
#include "utils/log.hpp"
#include <algorithm>
// ----------------------------------------------------------------------------
STKTexManager::STKTexManager() : m_pbo(0), m_thread_size(0),
m_threaded_load_textures_counter(0)
{
createThreadedTexLoaders();
} // STKTexManager
// ----------------------------------------------------------------------------
STKTexManager::~STKTexManager()
{
removeTexture(NULL/*texture*/, true/*remove_all*/);
destroyThreadedTexLoaders();
} // ~STKTexManager
// ----------------------------------------------------------------------------
void STKTexManager::createThreadedTexLoaders()
{
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
if (CVS->supportsThreadedTextureLoading())
{
pthread_mutex_init(&m_threaded_load_textures_mutex, NULL);
pthread_cond_init(&m_cond_request, NULL);
m_thread_size = HardwareStats::getNumProcessors();
if (m_thread_size == 0)
m_thread_size = 1;
m_thread_size = core::clamp(m_thread_size, 1,
UserConfigParams::m_hq_mipmap ? m_thread_size : 3);
const unsigned max_tex_size =
(UserConfigParams::m_high_definition_textures & 0x01) == 0 ?
UserConfigParams::m_max_texture_size : 2048;
const unsigned each_capacity = max_tex_size * max_tex_size * 4;
const unsigned pbo_size = each_capacity * m_thread_size;
Log::info("STKTexManager", "%d thread(s) for texture loading,"
" each capacity %d MB.", m_thread_size,
each_capacity / 1024 / 1024);
if (UserConfigParams::m_hq_mipmap)
Log::info("STKTexManager", "High quality mipmap enabled.");
glGenBuffers(1, &m_pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo);
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, pbo_size, NULL,
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT |
GL_MAP_COHERENT_BIT);
uint8_t* pbo_ptr = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER,
0, pbo_size, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT |
GL_MAP_COHERENT_BIT);
size_t offset = 0;
for (int i = 0; i < m_thread_size; i++)
{
m_all_tex_loaders.push_back(new ThreadedTexLoader(each_capacity,
offset, pbo_ptr + offset, &m_threaded_load_textures_mutex,
&m_cond_request, this));
offset += each_capacity;
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
#endif
} // createThreadedTexLoaders
// ----------------------------------------------------------------------------
void STKTexManager::destroyThreadedTexLoaders()
{
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
if (CVS->supportsThreadedTextureLoading())
{
STKTexture* delete_ttl = new STKTexture((uint8_t*)NULL, "delete_ttl",
0, false, true);
for (int i = 0; i < m_thread_size; i++)
addThreadedLoadTexture(delete_ttl);
for (int i = 0; i < m_thread_size; i++)
{
if (!m_all_tex_loaders[i]->waitForReadyToDeleted(2.0f))
{
Log::info("STKTexManager", "ThreadedTexLoader %d not stopping,"
"exiting anyway.", i);
}
delete m_all_tex_loaders[i];
}
delete delete_ttl;
glDeleteBuffers(1, &m_pbo);
pthread_mutex_destroy(&m_threaded_load_textures_mutex);
pthread_cond_destroy(&m_cond_request);
m_pbo = 0;
m_thread_size = 0;
m_threaded_load_textures_counter = 0;
m_all_tex_loaders.clear();
}
#endif
} // destroyThreadedTexLoaders
// ----------------------------------------------------------------------------
STKTexture* STKTexManager::findTextureInFileSystem(const std::string& filename,
std::string* full_path)
@ -55,10 +143,8 @@ STKTexture* STKTexManager::findTextureInFileSystem(const std::string& filename,
} // findTextureInFileSystem
// ----------------------------------------------------------------------------
video::ITexture* STKTexManager::getTexture(const std::string& path, bool srgb,
bool premul_alpha,
bool set_material, bool mesh_tex,
bool no_upload, bool single_channel,
video::ITexture* STKTexManager::getTexture(const std::string& path,
TexConfig* tc, bool no_upload,
bool create_if_unfound)
{
auto ret = m_all_textures.find(path);
@ -79,8 +165,7 @@ video::ITexture* STKTexManager::getTexture(const std::string& path, bool srgb,
if (create_if_unfound)
{
new_texture = new STKTexture(full_path.empty() ? path : full_path,
srgb, premul_alpha, set_material, mesh_tex, no_upload,
single_channel);
tc, no_upload);
if (new_texture->getOpenGLTextureName() == 0 && !no_upload)
{
const char* name = new_texture->getName().getPtr();
@ -95,6 +180,11 @@ video::ITexture* STKTexManager::getTexture(const std::string& path, bool srgb,
delete new_texture;
return NULL;
}
if (new_texture->useThreadedLoading())
{
addThreadedLoadTexture(new_texture);
checkThreadedLoadTextures(false/*util_queue_empty*/);
}
}
if (create_if_unfound && !no_upload)
@ -204,6 +294,10 @@ core::stringw STKTexManager::reloadTexture(const irr::core::stringw& name)
if (p.second == NULL || !p.second->isMeshTexture())
continue;
p.second->reload();
if (p.second->useThreadedLoading())
{
addThreadedLoadTexture(p.second);
}
Log::info("STKTexManager", "%s reloaded",
p.second->getName().getPtr());
}
@ -226,6 +320,10 @@ core::stringw STKTexManager::reloadTexture(const irr::core::stringw& name)
if (fname == tex_name || fname == tex_path)
{
p.second->reload();
if (p.second->useThreadedLoading())
{
addThreadedLoadTexture(p.second);
}
result += tex_name.c_str();
result += L" ";
break;
@ -271,3 +369,78 @@ void STKTexManager::setTextureErrorMessage(const std::string &error,
else
m_texture_error_message = StringUtils::insertValues(error, detail);
} // setTextureErrorMessage
// ----------------------------------------------------------------------------
void STKTexManager::checkThreadedLoadTextures(bool util_queue_empty)
{
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
if (!CVS->supportsThreadedTextureLoading()) return;
bool uploaded = false;
bool empty_queue = false;
if (util_queue_empty)
{
while (true)
{
pthread_mutex_lock(&m_threaded_load_textures_mutex);
empty_queue = m_threaded_load_textures_counter == 0;
pthread_mutex_unlock(&m_threaded_load_textures_mutex);
if (empty_queue)
{
for (ThreadedTexLoader* ttl : m_all_tex_loaders)
{
if (ttl->lastQueueReady())
{
ttl->lock();
ttl->setFinishLoading();
uploaded = true;
ttl->unlock(false/*finish_it*/);
}
}
break;
}
else
{
checkThreadedLoadTextures(false/*util_queue_empty*/);
}
}
}
if (empty_queue && !uploaded)
return;
uploaded = false;
for (ThreadedTexLoader* ttl : m_all_tex_loaders)
{
ttl->lock();
if (ttl->finishedLoading())
{
if (!uploaded)
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo);
uploaded = true;
}
ttl->handleCompletedTextures();
}
else
{
ttl->unlock(false/*finish_it*/);
}
}
if (uploaded)
{
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
GLenum reason = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
if (reason != GL_ALREADY_SIGNALED)
{
do
{
reason = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT,
1000000);
}
while (reason == GL_TIMEOUT_EXPIRED);
}
glDeleteSync(sync);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
for (ThreadedTexLoader* ttl : m_all_tex_loaders)
ttl->unlock(true/*finish_it*/);
}
#endif
} // checkThreadedLoadTextures

View File

@ -23,17 +23,43 @@
#include "utils/singleton.hpp"
#include "irrString.h"
#include "ITexture.h"
#include <pthread.h>
#include <algorithm>
#include <cassert>
#include <string>
#include <queue>
#include <unordered_map>
#include <vector>
class STKTexture;
class ThreadedTexLoader;
namespace irr
{
namespace video { class ITexture; class SColor; }
namespace video { class SColor; }
}
struct TexConfig
{
bool m_srgb;
bool m_premul_alpha;
bool m_mesh_tex;
bool m_set_material;
bool m_colorization_mask;
bool m_normal_map;
TexConfig(bool srgb = false, bool premul_alpha = false,
bool mesh_tex = true, bool set_material = false,
bool color_mask = false, bool normal_map = false)
{
m_srgb = srgb;
m_premul_alpha = premul_alpha;
m_mesh_tex = mesh_tex;
m_set_material = set_material;
m_colorization_mask = color_mask;
m_normal_map = normal_map;
}
};
class STKTexManager : public Singleton<STKTexManager>, NoCopy
{
private:
@ -43,22 +69,43 @@ private:
* This is used to specify details like: "while loading kart '...'" */
std::string m_texture_error_message;
std::vector<ThreadedTexLoader*> m_all_tex_loaders;
GLuint m_pbo;
int m_thread_size;
class SmallestTexture
{
public:
inline bool operator()(const irr::video::ITexture* a,
const irr::video::ITexture* b) const
{
return a->getTextureSize() > b->getTextureSize();
}
};
std::priority_queue<irr::video::ITexture*,
std::vector<irr::video::ITexture*>, SmallestTexture>
m_threaded_load_textures;
int m_threaded_load_textures_counter;
pthread_mutex_t m_threaded_load_textures_mutex;
pthread_cond_t m_cond_request;
// ------------------------------------------------------------------------
STKTexture* findTextureInFileSystem(const std::string& filename,
std::string* full_path);
public:
// ------------------------------------------------------------------------
STKTexManager() {}
STKTexManager();
// ------------------------------------------------------------------------
~STKTexManager();
// ------------------------------------------------------------------------
irr::video::ITexture* getTexture(const std::string& path,
bool srgb = false,
bool premul_alpha = false,
bool set_material = false,
bool mesh_tex = false,
TexConfig* tc = NULL,
bool no_upload = false,
bool single_channel = false,
bool create_if_unfound = true);
// ------------------------------------------------------------------------
irr::video::ITexture* getUnicolorTexture(const irr::video::SColor &c);
@ -127,6 +174,35 @@ public:
return getTexture(filename, std::string(error_message),
std::string(detail));
} // getTexture
// ------------------------------------------------------------------------
void checkThreadedLoadTextures(bool util_queue_empty);
// ------------------------------------------------------------------------
irr::video::ITexture* getThreadedLoadTexture()
{ return m_threaded_load_textures.top(); }
// ------------------------------------------------------------------------
void setThreadedLoadTextureCounter(int val)
{
m_threaded_load_textures_counter += val;
assert(m_threaded_load_textures_counter >= 0);
}
// ------------------------------------------------------------------------
void addThreadedLoadTexture(irr::video::ITexture* t)
{
pthread_mutex_lock(&m_threaded_load_textures_mutex);
m_threaded_load_textures.push(t);
setThreadedLoadTextureCounter(t->getThreadedLoadTextureCounter());
pthread_cond_signal(&m_cond_request);
pthread_mutex_unlock(&m_threaded_load_textures_mutex);
}
// ------------------------------------------------------------------------
void removeThreadedLoadTexture() { m_threaded_load_textures.pop(); }
// ------------------------------------------------------------------------
bool isThreadedLoadTexturesEmpty()
{ return m_threaded_load_textures.empty(); }
// ------------------------------------------------------------------------
void createThreadedTexLoaders();
// ------------------------------------------------------------------------
void destroyThreadedTexLoaders();
}; // STKTexManager

View File

@ -18,10 +18,12 @@
#include "graphics/stk_texture.hpp"
#include "config/user_config.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/hq_mipmap_generator.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/material.hpp"
#include "graphics/material_manager.hpp"
#include "graphics/materials.hpp"
#include "graphics/stk_tex_manager.hpp"
#include "modes/profile_world.hpp"
#include "utils/log.hpp"
#include "utils/string_utils.hpp"
@ -33,23 +35,27 @@
static const uint8_t CACHE_VERSION = 1;
#endif
// ----------------------------------------------------------------------------
STKTexture::STKTexture(const std::string& path, bool srgb, bool premul_alpha,
bool set_material, bool mesh_tex, bool no_upload,
bool single_channel)
: video::ITexture(path.c_str()), m_texture_handle(0), m_srgb(srgb),
m_premul_alpha(premul_alpha), m_mesh_texture(mesh_tex),
m_single_channel(single_channel), m_material(NULL),
m_texture_name(0), m_texture_size(0), m_texture_image(NULL)
STKTexture::STKTexture(const std::string& path, TexConfig* tc, bool no_upload)
: video::ITexture(path.c_str()), m_texture_handle(0),
m_single_channel(false), m_tex_config(NULL), m_material(NULL),
m_texture_name(0), m_texture_size(0), m_texture_image(NULL),
m_file(NULL), m_img_loader(NULL)
{
if (set_material)
if (tc != NULL)
{
m_material = material_manager->getMaterialFor(this);
m_mesh_texture = true;
m_tex_config = (TexConfig*)malloc(sizeof(TexConfig));
memcpy(m_tex_config, tc, sizeof(TexConfig));
m_single_channel = m_tex_config->m_colorization_mask;
if (m_tex_config->m_set_material)
m_material = material_manager->getMaterialFor(this);
}
#ifndef SERVER_ONLY
if (!CVS->isGLSL())
m_srgb = false;
if (m_tex_config && !CVS->isGLSL())
m_tex_config->m_srgb = false;
#ifdef USE_GLES2
if (m_tex_config && !CVS->isDefferedEnabled())
m_tex_config->m_srgb = false;
#endif
if (!CVS->isARBTextureSwizzleUsable())
m_single_channel = false;
#endif
@ -58,24 +64,25 @@ STKTexture::STKTexture(const std::string& path, bool srgb, bool premul_alpha,
// ----------------------------------------------------------------------------
STKTexture::STKTexture(uint8_t* data, const std::string& name, size_t size,
bool single_channel)
: video::ITexture(name.c_str()), m_texture_handle(0), m_srgb(false),
m_premul_alpha(false), m_mesh_texture(false),
m_single_channel(single_channel), m_material(NULL),
m_texture_name(0), m_texture_size(0), m_texture_image(NULL)
bool single_channel, bool delete_ttl)
: video::ITexture(name.c_str()), m_texture_handle(0),
m_single_channel(single_channel), m_tex_config(NULL),
m_material(NULL), m_texture_name(0), m_texture_size(0),
m_texture_image(NULL), m_file(NULL), m_img_loader(NULL)
{
m_size.Width = size;
m_size.Height = size;
m_orig_size = m_size;
reload(false/*no_upload*/, data);
if (!delete_ttl)
reload(false/*no_upload*/, data);
} // STKTexture
// ----------------------------------------------------------------------------
STKTexture::STKTexture(video::IImage* img, const std::string& name)
: video::ITexture(name.c_str()), m_texture_handle(0), m_srgb(false),
m_premul_alpha(false), m_mesh_texture(false),
m_single_channel(false), m_material(NULL), m_texture_name(0),
m_texture_size(0), m_texture_image(NULL)
: video::ITexture(name.c_str()), m_texture_handle(0),
m_single_channel(false), m_tex_config(NULL), m_material(NULL),
m_texture_name(0), m_texture_size(0), m_texture_image(NULL),
m_file(NULL), m_img_loader(NULL)
{
reload(false/*no_upload*/, NULL/*preload_data*/, img);
} // STKTexture
@ -92,6 +99,7 @@ STKTexture::~STKTexture()
#endif // !SERVER_ONLY
if (m_texture_image != NULL)
m_texture_image->drop();
free(m_tex_config);
} // ~STKTexture
// ----------------------------------------------------------------------------
@ -108,11 +116,10 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
return;
}
#ifndef SERVER_ONLY
irr_driver->getDevice()->getLogger()->setLogLevel(ELL_NONE);
std::string compressed_texture;
#if !defined(USE_GLES2)
if (!no_upload && m_mesh_texture && CVS->isTextureCompressionEnabled())
if (!no_upload && isMeshTexture() && CVS->isTextureCompressionEnabled())
{
std::string orig_file = NamedPath.getPtr();
@ -157,77 +164,75 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
uint8_t* data = preload_data;
if (data == NULL)
{
orig_img = preload_img ? preload_img :
irr_driver->getVideoDriver()->createImageFromFile(NamedPath);
if (orig_img == NULL)
if (preload_img)
orig_img = preload_img;
else
{
return;
}
if (orig_img->getDimension().Width == 0 ||
orig_img->getDimension().Height == 0)
{
orig_img->drop();
return;
m_file = irr_driver->getDevice()->getFileSystem()
->createAndOpenFile(NamedPath);
if (m_file == NULL)
return;
irr_driver->getVideoDriver()->createImageFromFile(m_file,
&m_img_loader);
if (m_img_loader == NULL)
return;
m_file->seek(0);
m_orig_size = m_img_loader->getImageSize(m_file);
if ((!m_material || m_material->getAlphaMask().empty()) &&
useThreadedLoading() && !no_upload)
{
if (m_orig_size.Width == 0 || m_orig_size.Height == 0)
{
m_file->drop();
m_file = NULL;
m_img_loader = NULL;
return;
}
}
else
{
orig_img = m_img_loader->loadImage(m_file);
m_file->drop();
m_file = NULL;
if (orig_img == NULL || orig_img->getDimension().Width == 0 ||
orig_img->getDimension().Height == 0)
{
if (orig_img)
orig_img->drop();
return;
}
m_img_loader = NULL;
}
}
orig_img = resizeImage(orig_img, &m_orig_size, &m_size);
applyMask(orig_img);
data = (uint8_t*)orig_img->lock();
if (m_single_channel)
data = orig_img ? (uint8_t*)orig_img->lock() : NULL;
if (m_single_channel && !useThreadedLoading())
{
uint8_t* sc = new uint8_t[m_size.Width * m_size.Height];
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
sc[i] = data[4 * i + 3];
data = singleChannelConversion(data);
orig_img->unlock();
orig_img->drop();
orig_img = NULL;
data = sc;
}
}
const unsigned int w = m_size.Width;
const unsigned int h = m_size.Height;
unsigned int format = m_single_channel ? GL_RED : GL_BGRA;
unsigned int internal_format = m_single_channel ? GL_R8 : GL_RGBA;
unsigned int internal_format = m_single_channel ? GL_R8 : isSrgb() ?
GL_SRGB8_ALPHA8 : GL_RGBA8;
#if !defined(USE_GLES2)
if (m_mesh_texture && CVS->isTextureCompressionEnabled())
if (isMeshTexture() && CVS->isTextureCompressionEnabled())
{
internal_format = m_single_channel ? GL_COMPRESSED_RED_RGTC1 :
m_srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT :
isSrgb() ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT :
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
else
{
internal_format =
m_single_channel ? GL_R8 : m_srgb ? GL_SRGB_ALPHA : GL_RGBA;
}
#endif
#if defined(USE_GLES2)
if (!CVS->isEXTTextureFormatBGRA8888Usable() && !m_single_channel)
{
format = GL_RGBA;
for (unsigned int i = 0; i < w * h; i++)
{
uint8_t tmp_val = data[i * 4];
data[i * 4] = data[i * 4 + 2];
data[i * 4 + 2] = tmp_val;
}
}
#endif
if (m_premul_alpha && !m_single_channel)
{
for (unsigned int i = 0; i < w * h; i++)
{
float alpha = data[4 * i + 3];
if (alpha > 0.0f)
alpha = pow(alpha / 255.f, 1.f / 2.2f);
data[i * 4] = (uint8_t)(data[i * 4] * alpha);
data[i * 4 + 1] = (uint8_t)(data[i * 4 + 1] * alpha);
data[i * 4 + 2] = (uint8_t)(data[i * 4 + 2] * alpha);
}
}
if (!useThreadedLoading())
formatConversion(data, &format, w, h);
if (!no_upload)
{
@ -246,17 +251,35 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
}
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, format,
GL_UNSIGNED_BYTE, data);
if (useThreadedLoading())
{
int levels = 1;
int width = w;
int height = h;
while (true)
{
width = width < 2 ? 1 : width >> 1;
height = height < 2 ? 1 : height >> 1;
levels++;
if (width == 1 && height == 1)
break;
}
glTexStorage2D(GL_TEXTURE_2D, levels, internal_format, w, h);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, format,
GL_UNSIGNED_BYTE, data);
}
}
else
else if (!useThreadedLoading())
{
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format,
GL_UNSIGNED_BYTE, data);
}
if (orig_img)
orig_img->unlock();
if (hasMipMaps())
if (!useThreadedLoading() && hasMipMaps())
glGenerateMipmap(GL_TEXTURE_2D);
}
@ -273,54 +296,74 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
if (!no_upload)
glBindTexture(GL_TEXTURE_2D, 0);
irr_driver->getDevice()->getLogger()->setLogLevel(ELL_WARNING);
#endif // !SERVER_ONLY
} // reload
//-----------------------------------------------------------------------------
void STKTexture::formatConversion(uint8_t* data, unsigned int* format,
unsigned int w, unsigned int h) const
{
#if defined(USE_GLES2)
if (!m_single_channel)
{
if (format)
*format = GL_RGBA;
for (unsigned int i = 0; i < w * h; i++)
{
uint8_t tmp_val = data[i * 4];
data[i * 4] = data[i * 4 + 2];
data[i * 4 + 2] = tmp_val;
}
}
#endif
if (isPremulAlpha() && !m_single_channel)
{
for (unsigned int i = 0; i < w * h; i++)
{
float alpha = data[4 * i + 3];
if (alpha > 0.0f)
{
alpha /= 255.0f;
#if defined(USE_GLES2)
if (CVS->isDefferedEnabled())
#endif
alpha = pow(alpha, 1.0f / 2.2f);
}
data[i * 4] = (uint8_t)(data[i * 4] * alpha);
data[i * 4 + 1] = (uint8_t)(data[i * 4 + 1] * alpha);
data[i * 4 + 2] = (uint8_t)(data[i * 4 + 2] * alpha);
}
}
} // formatConversion
// ----------------------------------------------------------------------------
video::IImage* STKTexture::resizeImage(video::IImage* orig_img,
core::dimension2du* new_img_size,
core::dimension2du* new_tex_size)
core::dimension2du* orig_size,
core::dimension2du* final_size) const
{
video::IImage* image = orig_img;
#ifndef SERVER_ONLY
const core::dimension2du& old_size = image->getDimension();
core::dimension2du img_size = old_size;
const float ratio = float(img_size.Width) / float(img_size.Height);
const unsigned int drv_max_size =
irr_driver->getVideoDriver()->getMaxTextureSize().Width;
if ((img_size.Width > drv_max_size) && (ratio >= 1.0f))
{
img_size.Width = drv_max_size;
img_size.Height = (unsigned)(drv_max_size / ratio);
}
else if (img_size.Height > drv_max_size)
{
img_size.Height = drv_max_size;
img_size.Width = (unsigned)(drv_max_size * ratio);
}
if (img_size != old_size)
{
video::IImage* new_img = irr_driver->getVideoDriver()
->createImage(video::ECF_A8R8G8B8, img_size);
image->copyToScaling(new_img);
image->drop();
image = new_img;
}
if (image == NULL)
assert(orig_size && orig_size->Width > 0 && orig_size->Height > 0);
core::dimension2du img_size = image ? image->getDimension() : *orig_size;
core::dimension2du tex_size = img_size.getOptimalSize
(!irr_driver->getVideoDriver()->queryFeature(video::EVDF_TEXTURE_NPOT));
const core::dimension2du& max_size = irr_driver->getVideoDriver()
->getDriverAttributes().getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
if (max_size.Width > 0 && tex_size.Width > max_size.Width)
if (tex_size.Width > max_size.Width)
tex_size.Width = max_size.Width;
if (max_size.Height > 0 && tex_size.Height > max_size.Height)
if (tex_size.Height > max_size.Height)
tex_size.Height = max_size.Height;
if (orig_size && final_size)
{
*orig_size = img_size;
*final_size = tex_size;
}
if (image == NULL)
return NULL;
if (image->getColorFormat() != video::ECF_A8R8G8B8 ||
tex_size != img_size)
{
@ -334,11 +377,6 @@ video::IImage* STKTexture::resizeImage(video::IImage* orig_img,
image = new_texture;
}
if (new_img_size && new_tex_size)
{
*new_img_size = img_size;
*new_tex_size = tex_size;
}
#endif // !SERVER_ONLY
return image;
} // resizeImage
@ -548,3 +586,101 @@ void STKTexture::unloadHandle()
}
#endif
} // unloadHandle
//-----------------------------------------------------------------------------
bool STKTexture::useThreadedLoading() const
{
#ifdef SERVER_ONLY
return false;
#else
return CVS->supportsThreadedTextureLoading() &&
!CVS->isTextureCompressionEnabled() && isMeshTexture() &&
m_img_loader && m_img_loader->supportThreadedLoading();
#endif
} // useThreadedLoading
//-----------------------------------------------------------------------------
void STKTexture::threadedReload(void* ptr, void* param) const
{
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
video::IImage* orig_img =
m_img_loader->loadImage(m_file, true/*skip_checking*/);
orig_img = resizeImage(orig_img);
uint8_t* data = (uint8_t*)orig_img->lock();
if (m_single_channel)
{
data = singleChannelConversion(data);
orig_img->unlock();
orig_img->drop();
orig_img = NULL;
}
formatConversion(data, NULL, m_size.Width, m_size.Height);
memcpy(ptr, data, m_texture_size);
if (orig_img)
{
orig_img->unlock();
orig_img->setDeleteMemory(false);
orig_img->drop();
}
if (useHQMipmap())
{
HQMipmapGenerator* hqmg = new HQMipmapGenerator(NamedPath, data,
m_size, m_texture_name, m_tex_config);
((STKTexManager*)(param))->addThreadedLoadTexture(hqmg);
}
else
delete[] data;
#endif
} // threadedReload
//-----------------------------------------------------------------------------
void STKTexture::threadedSubImage(void* ptr) const
{
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
glBindTexture(GL_TEXTURE_2D, m_texture_name);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_size.Width, m_size.Height,
m_single_channel ? GL_RED : GL_BGRA, GL_UNSIGNED_BYTE, ptr);
if (useHQMipmap())
return;
if (hasMipMaps())
glGenerateMipmap(GL_TEXTURE_2D);
#endif
} // threadedSubImage
//-----------------------------------------------------------------------------
void STKTexture::cleanThreadedLoader()
{
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
assert(m_file);
m_file->drop();
m_file = NULL;
m_img_loader = NULL;
#endif
} // cleanThreadedLoader
//-----------------------------------------------------------------------------
bool STKTexture::useHQMipmap() const
{
return !m_single_channel && UserConfigParams::m_hq_mipmap &&
m_size.Width > 1 && m_size.Height > 1;
} // useHQMipmap
//-----------------------------------------------------------------------------
bool STKTexture::isSrgb() const
{
return m_tex_config && m_tex_config->m_srgb;
} // isSrgb
//-----------------------------------------------------------------------------
bool STKTexture::isPremulAlpha() const
{
return m_tex_config && m_tex_config->m_premul_alpha;
} // isPremulAlpha
//-----------------------------------------------------------------------------
bool STKTexture::isMeshTexture() const
{
return m_tex_config && m_tex_config->m_mesh_tex;
} // isMeshTexture

View File

@ -24,8 +24,15 @@
#include <string>
#include <ITexture.h>
namespace irr
{
namespace io { class IReadFile; }
namespace video { class IImageLoader; }
}
using namespace irr;
struct TexConfig;
class Material;
class STKTexture : public video::ITexture, NoCopy
@ -35,7 +42,9 @@ private:
uint64_t m_texture_handle;
bool m_srgb, m_premul_alpha, m_mesh_texture, m_single_channel;
bool m_single_channel;
TexConfig* m_tex_config;
Material* m_material;
@ -45,26 +54,44 @@ private:
video::IImage* m_texture_image;
io::IReadFile* m_file;
video::IImageLoader* m_img_loader;
// ------------------------------------------------------------------------
video::IImage* resizeImage(video::IImage* orig_img,
core::dimension2du* new_img_size = NULL,
core::dimension2du* new_tex_size = NULL);
core::dimension2du* orig_size = NULL,
core::dimension2du* final_size = NULL) const;
// ------------------------------------------------------------------------
void applyMask(video::IImage* orig_img);
// ------------------------------------------------------------------------
bool loadCompressedTexture(const std::string& file_name);
// ------------------------------------------------------------------------
void saveCompressedTexture(const std::string& file_name);
// ------------------------------------------------------------------------
void formatConversion(uint8_t* data, unsigned int* format, unsigned int w,
unsigned int h) const;
// ------------------------------------------------------------------------
uint8_t* singleChannelConversion(uint8_t* data) const
{
uint8_t* sc = new uint8_t[m_size.Width * m_size.Height];
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
sc[i] = data[4 * i + 3];
return sc;
}
// ------------------------------------------------------------------------
bool useHQMipmap() const;
// ------------------------------------------------------------------------
bool isSrgb() const;
// ------------------------------------------------------------------------
bool isPremulAlpha() const;
public:
// ------------------------------------------------------------------------
STKTexture(const std::string& path, bool srgb = false,
bool premul_alpha = false, bool set_material = false,
bool mesh_tex = false, bool no_upload = false,
bool single_channel = false);
STKTexture(const std::string& path, TexConfig* tc, bool no_upload = false);
// ------------------------------------------------------------------------
STKTexture(uint8_t* data, const std::string& name, size_t size,
bool single_channel = false);
bool single_channel = false, bool delete_ttl = false);
// ------------------------------------------------------------------------
STKTexture(video::IImage* img, const std::string& name);
// ------------------------------------------------------------------------
@ -108,20 +135,27 @@ public:
// ------------------------------------------------------------------------
virtual void unloadHandle();
// ------------------------------------------------------------------------
bool isSrgb() const { return m_srgb; }
// ------------------------------------------------------------------------
bool isPremulAlpha() const { return m_premul_alpha; }
// ------------------------------------------------------------------------
bool isMeshTexture() const { return m_mesh_texture; }
// ------------------------------------------------------------------------
void setMeshTexture(bool val) { m_mesh_texture = val; }
// ------------------------------------------------------------------------
unsigned int getTextureSize() const { return m_texture_size; }
virtual unsigned int getTextureSize() const { return m_texture_size; }
// ------------------------------------------------------------------------
void reload(bool no_upload = false, uint8_t* preload_data = NULL,
video::IImage* preload_img = NULL);
// ------------------------------------------------------------------------
video::IImage* getTextureImage() { return m_texture_image; }
// ------------------------------------------------------------------------
bool useThreadedLoading() const;
// ------------------------------------------------------------------------
virtual void threadedReload(void* ptr, void* param) const;
// ------------------------------------------------------------------------
virtual void threadedSubImage(void* ptr) const;
// ------------------------------------------------------------------------
virtual void cleanThreadedLoader();
// ------------------------------------------------------------------------
virtual int getThreadedLoadTextureCounter() const
{
return useHQMipmap() ? 2 : 1;
}
// ------------------------------------------------------------------------
bool isMeshTexture() const;
}; // STKTexture

View File

@ -0,0 +1,100 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2017 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
#include "graphics/threaded_tex_loader.hpp"
#include "graphics/stk_tex_manager.hpp"
#include "utils/string_utils.hpp"
#include "utils/vs.hpp"
#include <cassert>
#include <ITexture.h>
// ----------------------------------------------------------------------------
void* ThreadedTexLoader::startRoutine(void *obj)
{
ThreadedTexLoader* ttl = (ThreadedTexLoader*)obj;
VS::setThreadName((std::string("ThrTexLoader") +
StringUtils::toString(ttl->m_pbo_offset / 1024 / 1024)).c_str());
while (true)
{
pthread_mutex_lock(&ttl->m_mutex);
bool finished = ttl->finishedLoading();
pthread_mutex_unlock(&ttl->m_mutex);
if (finished)
{
continue;
}
pthread_mutex_lock(ttl->m_texture_queue_mutex);
bool waiting = ttl->m_stktm->isThreadedLoadTexturesEmpty();
ttl->m_last_queue_ready.setAtomic(!ttl->m_completed_textures.empty() &&
waiting);
while (waiting)
{
pthread_cond_wait(ttl->m_cond_request, ttl->m_texture_queue_mutex);
waiting = ttl->m_stktm->isThreadedLoadTexturesEmpty();
}
irr::video::ITexture* target_tex =
ttl->m_stktm->getThreadedLoadTexture();
if (strcmp(target_tex->getName().getPtr(), "delete_ttl") == 0)
{
ttl->m_stktm->removeThreadedLoadTexture();
ttl->m_stktm->setThreadedLoadTextureCounter(-1);
pthread_mutex_unlock(ttl->m_texture_queue_mutex);
ttl->setCanBeDeleted();
return NULL;
}
assert(target_tex->getTextureSize() <= ttl->m_tex_capacity);
if (target_tex->getTextureSize() + ttl->m_tex_size_loaded >
ttl->m_tex_capacity)
{
pthread_mutex_lock(&ttl->m_mutex);
ttl->setFinishLoading();
pthread_mutex_unlock(&ttl->m_mutex);
pthread_mutex_unlock(ttl->m_texture_queue_mutex);
continue;
}
ttl->m_stktm->removeThreadedLoadTexture();
pthread_mutex_unlock(ttl->m_texture_queue_mutex);
target_tex->threadedReload(ttl->m_pbo_ptr + ttl->m_tex_size_loaded,
ttl->m_stktm);
target_tex->cleanThreadedLoader();
ttl->m_tex_size_loaded += target_tex->getTextureSize();
ttl->m_completed_textures.push_back(target_tex);
pthread_mutex_lock(ttl->m_texture_queue_mutex);
ttl->m_stktm->setThreadedLoadTextureCounter(-1);
pthread_mutex_unlock(ttl->m_texture_queue_mutex);
}
return NULL;
} // startRoutine
// ----------------------------------------------------------------------------
void ThreadedTexLoader::handleCompletedTextures()
{
assert(m_locked);
size_t offset = m_pbo_offset;
for (irr::video::ITexture* tex : m_completed_textures)
{
size_t cur_offset = tex->getTextureSize();
tex->threadedSubImage((void*)offset);
offset += cur_offset;
}
m_completed_textures.clear();
} // handleCompletedTextures
#endif

View File

@ -0,0 +1,118 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2017 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_THREADED_TEX_LOADER_HPP
#define HEADER_THREADED_TEX_LOADER_HPP
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
#include "utils/can_be_deleted.hpp"
#include "utils/no_copy.hpp"
#include "utils/synchronised.hpp"
#include "utils/types.hpp"
#include <vector>
namespace irr
{
namespace video { class ITexture; }
}
class STKTexManager;
class ThreadedTexLoader : public NoCopy, public CanBeDeleted
{
private:
const unsigned m_tex_capacity;
const size_t m_pbo_offset;
uint8_t* m_pbo_ptr;
pthread_mutex_t m_mutex;
pthread_mutex_t* m_texture_queue_mutex;
pthread_cond_t* m_cond_request;
STKTexManager* m_stktm;
unsigned m_tex_size_loaded;
pthread_t m_thread;
bool m_finished_loading, m_locked;
Synchronised<bool> m_last_queue_ready;
std::vector<irr::video::ITexture*> m_completed_textures;
public:
// ------------------------------------------------------------------------
static void* startRoutine(void *obj);
// ------------------------------------------------------------------------
ThreadedTexLoader(unsigned capacity, size_t offset, uint8_t* pbo_ptr,
pthread_mutex_t* mutex, pthread_cond_t* cond,
STKTexManager* stktm)
: m_tex_capacity(capacity), m_pbo_offset(offset),
m_pbo_ptr(pbo_ptr), m_texture_queue_mutex(mutex),
m_cond_request(cond), m_stktm(stktm),
m_tex_size_loaded(0), m_finished_loading(false),
m_locked(false), m_last_queue_ready(false)
{
pthread_mutex_init(&m_mutex, NULL);
pthread_create(&m_thread, NULL, &startRoutine, this);
}
// ------------------------------------------------------------------------
~ThreadedTexLoader()
{
pthread_mutex_destroy(&m_mutex);
pthread_join(m_thread, NULL);
}
// ------------------------------------------------------------------------
bool finishedLoading() const { return m_finished_loading; }
// ------------------------------------------------------------------------
void setFinishLoading()
{
m_last_queue_ready.setAtomic(false);
m_finished_loading = true;
m_tex_size_loaded = 0;
}
// ------------------------------------------------------------------------
bool lastQueueReady() const { return m_last_queue_ready.getAtomic(); }
// ------------------------------------------------------------------------
void handleCompletedTextures();
// ------------------------------------------------------------------------
void lock()
{
pthread_mutex_lock(&m_mutex);
m_locked = true;
}
// ------------------------------------------------------------------------
void unlock(bool finish_it)
{
if (!m_locked) return;
m_locked = false;
if (finish_it)
m_finished_loading = false;
pthread_mutex_unlock(&m_mutex);
}
}; // ThreadedTexLoader
#endif
#endif

View File

@ -20,18 +20,25 @@
#include "guiengine/message_queue.hpp"
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/scalable_font.hpp"
#include "guiengine/skin.hpp"
#include "utils/synchronised.hpp"
#include "utils/translation.hpp"
#include "IGUIElement.h"
#include "IGUIEnvironment.h"
#include "IGUIStaticText.h"
using namespace GUIEngine;
namespace MessageQueue
{
// ============================================================================
/** The area which the message is drawn. */
core::recti g_area;
// ============================================================================
/** A small helper class to store and sort messages to be displayed. */
class Message
{
@ -45,11 +52,15 @@ private:
* or friend-message::neutral. */
std::string m_render_type;
/** The text label, can do linebreak if needed. */
gui::IGUIStaticText* m_text;
public:
Message(MessageQueue::MessageType mt, const core::stringw &message)
{
m_message_type = mt;
m_message = message;
m_text = NULL;
if(mt==MessageQueue::MT_ACHIEVEMENT)
m_render_type = "achievement-message::neutral";
else if (mt==MessageQueue::MT_ERROR)
@ -60,6 +71,11 @@ public:
m_render_type = "friend-message::neutral";
} // Message
// ------------------------------------------------------------------------
~Message()
{
assert(m_text != NULL);
m_text->drop();
}
/** Returns the message. */
const core::stringw & getMessage() const { return m_message; }
// ------------------------------------------------------------------------
@ -72,6 +88,39 @@ public:
{
return m_render_type;
}
// ------------------------------------------------------------------------
/** Init the message text, do linebreak as required. */
void init()
{
const GUIEngine::BoxRenderParams &brp =
GUIEngine::getSkin()->getBoxRenderParams(m_render_type);
const unsigned width = irr_driver->getActualScreenSize().Width;
const unsigned height = irr_driver->getActualScreenSize().Height;
const unsigned max_width = width - (brp.m_left_border +
brp.m_right_border);
m_text =
GUIEngine::getGUIEnv()->addStaticText(m_message.c_str(),
core::recti(0, 0, max_width, height));
m_text->setRightToLeft(translations->isRTLText(m_message));
core::dimension2du dim(m_text->getTextWidth(),
m_text->getTextHeight());
dim.Width += brp.m_left_border + brp.m_right_border;
int x = (width - dim.Width) / 2;
int y = height - int(1.5f * dim.Height);
g_area = irr::core::recti(x, y, x + dim.Width, y + dim.Height);
m_text->setRelativePosition(g_area);
m_text->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
m_text->grab();
m_text->remove();
}
// ------------------------------------------------------------------------
/** Draw the message. */
void draw()
{
assert(m_text != NULL);
m_text->draw();
}
}; // class Message
// ============================================================================
@ -88,11 +137,10 @@ public:
} // operator ()
}; // operator()
// ============================================================================
/** List of all messages. */
std::priority_queue<Message*, std::vector<Message*>,
CompareMessages> g_all_messages;
Synchronised<std::priority_queue<Message*, std::vector<Message*>,
CompareMessages> > g_all_messages;
/** How long the current message has been displayed. The special value
* -1 indicates that a new message was added when the queue was empty. */
@ -103,26 +151,18 @@ float g_max_display_time = 5.0f;
/** The label widget used to show the current message. */
SkinWidgetContainer *g_container = NULL;
core::recti g_area;
// ============================================================================
void createLabel(const Message *message)
void createLabel(Message *message)
{
if(!g_container)
g_container = new SkinWidgetContainer();
gui::ScalableFont *font = GUIEngine::getFont();
core::dimension2du dim = font->getDimension(message->getMessage().c_str());
g_current_display_time = 0.0f;
// Maybe make this time dependent on message length as well?
g_max_display_time = 5.0f;
const GUIEngine::BoxRenderParams &brp =
GUIEngine::getSkin()->getBoxRenderParams(message->getRenderType());
dim.Width +=brp.m_left_border + brp.m_right_border;
int x = (UserConfigParams::m_width - dim.Width) / 2;
int y = UserConfigParams::m_height - int(1.5f*dim.Height);
g_area = irr::core::recti(x, y, x+dim.Width, y+dim.Height);
message->init();
} // createLabel
// ----------------------------------------------------------------------------
@ -130,9 +170,16 @@ void createLabel(const Message *message)
* position of the message. */
void updatePosition()
{
if (g_all_messages.empty()) return;
Message *last = g_all_messages.top();
g_all_messages.lock();
bool empty = g_all_messages.getData().empty();
if (empty)
{
g_all_messages.unlock();
return;
}
Message *last = g_all_messages.getData().top();
createLabel(last);
g_all_messages.unlock();
} // updatePosition
// ----------------------------------------------------------------------------
@ -143,13 +190,15 @@ void updatePosition()
void add(MessageType mt, const irr::core::stringw &message)
{
Message *m = new Message(mt, message);
if(g_all_messages.empty())
g_all_messages.lock();
if (g_all_messages.getData().empty())
{
// Indicate that there is a new message, which should
// which needs a new label etc. to be computed.
g_current_display_time =-1.0f;
}
g_all_messages.push(m);
g_all_messages.getData().push(m);
g_all_messages.unlock();
} // add
// ----------------------------------------------------------------------------
@ -161,32 +210,38 @@ void add(MessageType mt, const irr::core::stringw &message)
*/
void update(float dt)
{
if(g_all_messages.empty()) return;
g_all_messages.lock();
bool empty = g_all_messages.getData().empty();
g_all_messages.unlock();
if (empty) return;
g_all_messages.lock();
g_current_display_time += dt;
if(g_current_display_time > g_max_display_time)
if (g_current_display_time > g_max_display_time)
{
Message *last = g_all_messages.top();
g_all_messages.pop();
Message *last = g_all_messages.getData().top();
g_all_messages.getData().pop();
delete last;
if(g_all_messages.empty()) return;
if (g_all_messages.getData().empty())
{
g_all_messages.unlock();
return;
}
g_current_display_time = -1.0f;
}
Message *current = g_all_messages.getData().top();
// Create new data for the display.
if(g_current_display_time < 0)
if (g_current_display_time < 0)
{
createLabel(g_all_messages.top());
createLabel(current);
}
g_all_messages.unlock();
Message *current = g_all_messages.top();
GUIEngine::getSkin()->drawMessage(g_container, g_area,
current->getRenderType());
gui::ScalableFont *font = GUIEngine::getFont();
video::SColor color(255, 0, 0, 0);
font->draw(current->getMessage(), g_area, color, true, true);
current->draw();
} // update
} // namespace GUIEngine

View File

@ -342,9 +342,7 @@ video::ITexture* IconButtonWidget::getDeactivatedTexture(video::ITexture* textur
name += "_disabled";
STKTexManager* stkm = STKTexManager::getInstance();
STKTexture* disabled_stk_tex = static_cast<STKTexture*>(stkm->getTexture
(name, false/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
false/*mesh_tex*/, false /*no_upload*/, false/*single_channel*/,
false/*create_if_unfound*/));
(name, NULL/*tc*/, false /*no_upload*/, false/*create_if_unfound*/));
if (disabled_stk_tex == NULL)
{
SColor c;

View File

@ -273,8 +273,18 @@ void InputManager::handleStaticAction(int key, int value)
case KEY_PRINT:
// on windows we don't get a press event, only release. So
// save on release only (to avoid saving twice on other platforms)
if (value ==0 )
irr_driver->requestScreenshot();
if (value == 0)
{
if (control_is_pressed)
{
const bool is_recording = irr_driver->isRecording();
irr_driver->setRecording(!is_recording);
}
else
{
irr_driver->requestScreenshot();
}
}
break;
case KEY_F11:
if(value && shift_is_pressed && world && RewindManager::isEnabled())

View File

@ -117,7 +117,9 @@ float MainLoop::getLimitedDt()
// Throttle fps if more than maximum, which can reduce
// the noise the fan on a graphics card makes.
// When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus
const int max_fps = (StateManager::get()->throttleFPS() ? 30 : UserConfigParams::m_max_fps);
const int max_fps = (irr_driver->isRecording() &&
UserConfigParams::m_limit_game_fps ? UserConfigParams::m_record_fps :
StateManager::get()->throttleFPS() ? 60 : UserConfigParams::m_max_fps);
if (dt > 0)
{
const int current_fps = (int)(1000.0f / dt);

View File

@ -331,13 +331,12 @@ void SoccerWorld::initKartList()
std::string blue_path =
file_manager->getAsset(FileManager::GUI, "soccer_player_blue.png");
TexConfig btc(true/*srgb*/, true/*premul_alpha*/);
video::ITexture* red = STKTexManager::getInstance()->getTexture
(red_path, true/*srgb*/, true/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/);
(red_path, &btc);
video::ITexture* blue = STKTexManager::getInstance()->getTexture
(blue_path, true/*srgb*/, true/*premul_alpha*/, false/*set_material*/,
true/*mesh_tex*/);
(blue_path, &btc);
//Assigning indicators
for(unsigned int i = 0; i < kart_amount; i++)

View File

@ -179,9 +179,9 @@ void ThreeStrikesBattle::kartAdded(AbstractKart* kart, scene::ISceneNode* node)
// Add heart billboard above it
std::string heart_path =
file_manager->getAsset(FileManager::GUI, "heart.png");
TexConfig btc(true/*srgb*/, true/*premul_alpha*/);
video::ITexture* heart = STKTexManager::getInstance()->getTexture
(heart_path, true/*srgb*/, true/*premul_alpha*/,
false/*set_material*/, true/*mesh_tex*/);
(heart_path, &btc);
float height = kart->getKartHeight() + 0.5f;

View File

@ -61,8 +61,6 @@ void CustomVideoSettingsDialog::beforeAddingWidgets()
getWidget<CheckBoxWidget>("anim_gfx")->setState(UserConfigParams::m_graphical_effects);
getWidget<CheckBoxWidget>("weather_gfx")->setState(UserConfigParams::m_weather_effects);
getWidget<CheckBoxWidget>("dof")->setState(UserConfigParams::m_dof);
getWidget<CheckBoxWidget>("hd-textures")
->setState((UserConfigParams::m_high_definition_textures & 0x01)==0x01);
SpinnerWidget* kart_anim = getWidget<SpinnerWidget>("steering_animations");
kart_anim->addLabel(_("Disabled")); // 0
@ -79,33 +77,24 @@ void CustomVideoSettingsDialog::beforeAddingWidgets()
//I18N: Geometry level disabled : lowest level, no details
geometry_level->addLabel(_("Disabled"));
//I18N: Geometry level low : few details are displayed
geometry_level->addLabel(_("low"));
geometry_level->addLabel(_("Low"));
//I18N: Geometry level high : everything is displayed
geometry_level->addLabel(_("high"));
geometry_level->addLabel(_("High"));
geometry_level->setValue(
UserConfigParams::m_geometry_level == 2 ? 0 :
UserConfigParams::m_geometry_level == 0 ? 2 : 1);
SpinnerWidget* filtering = getWidget<SpinnerWidget>("filtering");
int value = 0;
if (UserConfigParams::m_anisotropic == 2) value = 2;
else if (UserConfigParams::m_anisotropic == 4) value = 3;
else if (UserConfigParams::m_anisotropic == 8) value = 4;
else if (UserConfigParams::m_anisotropic == 16) value = 5;
else if (UserConfigParams::m_trilinear) value = 1;
filtering->addLabel(_("Bilinear")); // 0
filtering->addLabel(_("Trilinear")); // 1
filtering->addLabel(_("Anisotropic x2")); // 2
filtering->addLabel(_("Anisotropic x4")); // 3
filtering->addLabel(_("Anisotropic x8")); // 4
filtering->addLabel(_("Anisotropic x16")); // 5
filtering->setValue(value);
SpinnerWidget* filtering = getWidget<SpinnerWidget>("image_quality");
filtering->addLabel(_("Very Low"));
filtering->addLabel(_("Low"));
filtering->addLabel(_("High"));
filtering->addLabel(_("Very High"));
filtering->setValue(OptionsScreenVideo::getImageQuality());
SpinnerWidget* shadows = getWidget<SpinnerWidget>("shadows");
shadows->addLabel(_("Disabled")); // 0
shadows->addLabel(_("low")); // 1
shadows->addLabel(_("high")); // 2
shadows->addLabel(_("Low")); // 1
shadows->addLabel(_("High")); // 2
if (CVS->supportsShadows())
shadows->setValue(UserConfigParams::m_shadows_resolution / 512);
else
@ -195,11 +184,6 @@ GUIEngine::EventPropagation CustomVideoSettingsDialog::processEvent(const std::s
UserConfigParams::m_weather_effects =
getWidget<CheckBoxWidget>("weather_gfx")->getState();
// Set bit 0 for enabled/disabled, and set bit 1 to indicate that this
// is now a user's choice and should not be overwritten by any default
UserConfigParams::m_high_definition_textures =
getWidget<CheckBoxWidget>("hd-textures")->getState() ? 0x03 : 0x02;
UserConfigParams::m_show_steering_animations =
getWidget<SpinnerWidget>("steering_animations")->getValue();
@ -207,33 +191,8 @@ GUIEngine::EventPropagation CustomVideoSettingsDialog::processEvent(const std::s
getWidget<SpinnerWidget>("geometry_detail")->getValue();
UserConfigParams::m_geometry_level = val == 2 ? 0 : val == 0 ? 2 : 1;
switch (getWidget<SpinnerWidget>("filtering")->getValue())
{
case 0:
UserConfigParams::m_anisotropic = 0;
UserConfigParams::m_trilinear = false;
break;
case 1:
UserConfigParams::m_anisotropic = 0;
UserConfigParams::m_trilinear = true;
break;
case 2:
UserConfigParams::m_anisotropic = 2;
UserConfigParams::m_trilinear = true;
break;
case 3:
UserConfigParams::m_anisotropic = 4;
UserConfigParams::m_trilinear = true;
break;
case 4:
UserConfigParams::m_anisotropic = 8;
UserConfigParams::m_trilinear = true;
break;
case 5:
UserConfigParams::m_anisotropic = 16;
UserConfigParams::m_trilinear = true;
break;
}
OptionsScreenVideo::setImageQuality(getWidget<SpinnerWidget>
("image_quality")->getValue());
user_config->saveConfig();

View File

@ -39,7 +39,6 @@ DebugSliderDialog::DebugSliderDialog() : ModalDialog(0.85f, 0.25f, MODAL_DIALOG_
loadFromFile("debug_slider.stkgui");
}
#if !defined(__APPLE__)
void DebugSliderDialog::setSliderHook(std::string id, unsigned min, unsigned max, std::function<int()> G, std::function<void(int)> S)
{
getWidget<SpinnerWidget>(id.c_str())->setValue(G());
@ -47,7 +46,6 @@ void DebugSliderDialog::setSliderHook(std::string id, unsigned min, unsigned max
getWidget<SpinnerWidget>(id.c_str())->setMax(max);
Setters[id] = S;
}
#endif
// ------------------------------------------------------------------------------------------------------
@ -66,7 +64,6 @@ void DebugSliderDialog::onEnterPressedInternal()
GUIEngine::EventPropagation DebugSliderDialog::processEvent(const std::string& eventSource)
{
#if !defined(__APPLE__)
if (Setters.find(eventSource) == Setters.end())
return GUIEngine::EVENT_LET;
@ -74,9 +71,6 @@ GUIEngine::EventPropagation DebugSliderDialog::processEvent(const std::string& e
Log::info("DebugSlider", "Value for <%s> : %i", eventSource.c_str(), value);
Setters[eventSource](value);
return GUIEngine::EVENT_BLOCK;
#else
return GUIEngine::EVENT_LET;
#endif
}
// ------------------------------------------------------------------------------------------------------

View File

@ -33,17 +33,13 @@ class DebugSliderDialog : public GUIEngine::ModalDialog
private:
std::string m_id;
#if !defined(__APPLE__)
std::map<std::string, std::function<void(int)> >Setters;
#endif
public:
DebugSliderDialog();
~DebugSliderDialog() {};
#if !defined(__APPLE__)
void setSliderHook(std::string id, unsigned min, unsigned max, std::function<int()> G, std::function<void(int)> S);
#endif
void changeLabel(std::string id, std::string new_label);
virtual void onEnterPressedInternal() OVERRIDE;

View File

@ -23,6 +23,7 @@
#include "graphics/central_settings.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/shared_gpu_objects.hpp"
#include "graphics/stk_tex_manager.hpp"
#include "guiengine/screen.hpp"
#include "guiengine/widgets/button_widget.hpp"
#include "guiengine/widgets/check_box_widget.hpp"
@ -53,32 +54,32 @@ void OptionsScreenVideo::initPresets()
({
false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, false /* weather */,
false /* animatedScenery */, 0 /* animatedCharacters */, 0 /* anisotropy */,
false /* depth of field */, false /* global illumination */, true /* degraded IBL */, 0 /* hd_textures */
false /* animatedScenery */, 0 /* animatedCharacters */, 0 /* image_quality */,
false /* depth of field */, false /* global illumination */, true /* degraded IBL */
});
m_presets.push_back
({
false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, false /* weather */,
true /* animatedScenery */, 1 /* animatedCharacters */, 4 /* anisotropy */,
false /* depth of field */, false /* global illumination */, true /* degraded IBL */, 0 /* hd_textures */
true /* animatedScenery */, 1 /* animatedCharacters */, 1 /* image_quality */,
false /* depth of field */, false /* global illumination */, true /* degraded IBL */
});
m_presets.push_back
({
true /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, true /* weather */,
true /* animatedScenery */, 1 /* animatedCharacters */, 4 /* anisotropy */,
false /* depth of field */, false /* global illumination */, true /* degraded IBL */, 1 /* hd_textures */
true /* animatedScenery */, 1 /* animatedCharacters */, 2 /* image_quality */,
false /* depth of field */, false /* global illumination */, true /* degraded IBL */
});
m_presets.push_back
({
true /* light */, 0 /* shadow */, false /* bloom */, true /* motionblur */,
true /* lightshaft */, true /* glow */, true /* mlaa */, false /* ssao */, true /* weather */,
true /* animatedScenery */, 1 /* animatedCharacters */, 8 /* anisotropy */,
false /* depth of field */, false /* global illumination */, false /* degraded IBL */, 1 /* hd_textures */
true /* animatedScenery */, 1 /* animatedCharacters */, 2 /* image_quality */,
false /* depth of field */, false /* global illumination */, false /* degraded IBL */
});
m_presets.push_back
@ -91,8 +92,8 @@ void OptionsScreenVideo::initPresets()
#else
2 /* animatedCharacters */,
#endif
16 /* anisotropy */,
true /* depth of field */, false /* global illumination */, false /* degraded IBL */, 1 /* hd_textures */
3 /* image_quality */,
true /* depth of field */, false /* global illumination */, false /* degraded IBL */
});
m_presets.push_back
@ -105,8 +106,8 @@ void OptionsScreenVideo::initPresets()
#else
2 /* animatedCharacters */,
#endif
16 /* anisotropy */,
true /* depth of field */, true /* global illumination */, false /* degraded IBL */, 1 /* hd_textures */
3 /* image_quality */,
true /* depth of field */, true /* global illumination */, false /* degraded IBL */
});
} // initPresets
@ -139,10 +140,79 @@ struct Resolution
}
};
// ----------------------------------------------------------------------------
int OptionsScreenVideo::getImageQuality()
{
if (UserConfigParams::m_scale_rtts_factor == 0.8f &&
UserConfigParams::m_trilinear == false &&
UserConfigParams::m_anisotropic == 0 &&
(UserConfigParams::m_high_definition_textures & 0x01) == 0x00 &&
UserConfigParams::m_hq_mipmap == false)
return 0;
if (UserConfigParams::m_scale_rtts_factor == 1.0f &&
UserConfigParams::m_trilinear == true &&
UserConfigParams::m_anisotropic == 2 &&
(UserConfigParams::m_high_definition_textures & 0x01) == 0x00 &&
UserConfigParams::m_hq_mipmap == false)
return 1;
if (UserConfigParams::m_scale_rtts_factor == 1.0f &&
UserConfigParams::m_trilinear == true &&
UserConfigParams::m_anisotropic == 4 &&
(UserConfigParams::m_high_definition_textures & 0x01) == 0x01 &&
UserConfigParams::m_hq_mipmap == false)
return 2;
if (UserConfigParams::m_scale_rtts_factor == 1.0f &&
UserConfigParams::m_trilinear == true &&
UserConfigParams::m_anisotropic == 16 &&
(UserConfigParams::m_high_definition_textures & 0x01) == 0x01 &&
UserConfigParams::m_hq_mipmap == true)
return 3;
return 2;
} // getImageQuality
// ----------------------------------------------------------------------------
void OptionsScreenVideo::setImageQuality(int quality)
{
switch (quality)
{
case 0:
UserConfigParams::m_scale_rtts_factor = 0.8f;
UserConfigParams::m_trilinear = false;
UserConfigParams::m_anisotropic = 0;
UserConfigParams::m_high_definition_textures = 0x02;
UserConfigParams::m_hq_mipmap = false;
break;
case 1:
UserConfigParams::m_scale_rtts_factor = 1.0f;
UserConfigParams::m_trilinear = true;
UserConfigParams::m_anisotropic = 2;
UserConfigParams::m_high_definition_textures = 0x02;
UserConfigParams::m_hq_mipmap = false;
break;
case 2:
UserConfigParams::m_scale_rtts_factor = 1.0f;
UserConfigParams::m_trilinear = true;
UserConfigParams::m_anisotropic = 4;
UserConfigParams::m_high_definition_textures = 0x03;
UserConfigParams::m_hq_mipmap = false;
break;
case 3:
UserConfigParams::m_scale_rtts_factor = 1.0f;
UserConfigParams::m_trilinear = true;
UserConfigParams::m_anisotropic = 16;
UserConfigParams::m_high_definition_textures = 0x03;
UserConfigParams::m_hq_mipmap = true;
break;
default:
assert(false);
}
} // setImageQuality
// ----------------------------------------------------------------------------
OptionsScreenVideo::OptionsScreenVideo() : Screen("options_video.stkgui"),
m_prev_adv_pipline(false)
m_prev_adv_pipline(false),
m_prev_img_quality(-1)
{
m_inited = false;
initPresets();
@ -168,6 +238,7 @@ void OptionsScreenVideo::init()
{
Screen::init();
m_prev_adv_pipline = UserConfigParams::m_dynamic_lights;
m_prev_img_quality = getImageQuality();
RibbonWidget* ribbon = getWidget<RibbonWidget>("options_choice");
assert(ribbon != NULL);
ribbon->select( "tab_video", PLAYER_ID_GAME_MASTER );
@ -341,7 +412,7 @@ void OptionsScreenVideo::updateGfxSlider()
{
if (m_presets[l].animatedCharacters == UserConfigParams::m_show_steering_animations &&
m_presets[l].animatedScenery == UserConfigParams::m_graphical_effects &&
m_presets[l].anisotropy == UserConfigParams::m_anisotropic &&
m_presets[l].image_quality == getImageQuality() &&
m_presets[l].bloom == UserConfigParams::m_bloom &&
m_presets[l].glow == UserConfigParams::m_glow &&
m_presets[l].lights == UserConfigParams::m_dynamic_lights &&
@ -354,8 +425,7 @@ void OptionsScreenVideo::updateGfxSlider()
m_presets[l].weather == UserConfigParams::m_weather_effects &&
m_presets[l].dof == UserConfigParams::m_dof &&
m_presets[l].global_illumination == UserConfigParams::m_gi &&
m_presets[l].degraded_ibl == UserConfigParams::m_degraded_IBL &&
m_presets[l].hd_textures == (UserConfigParams::m_high_definition_textures & 0x01))
m_presets[l].degraded_ibl == UserConfigParams::m_degraded_IBL)
{
gfx->setValue(l + 1);
found = true;
@ -395,6 +465,19 @@ void OptionsScreenVideo::updateTooltip()
//I18N: if no kart animations are enabled
const core::stringw none = _LTR("None");
//I18N: in the graphical options tooltip;
// indicates the rendered image quality is very low
const core::stringw very_low = _LTR("Very Low");
//I18N: in the graphical options tooltip;
// indicates the rendered image quality is low
const core::stringw low = _LTR("Low");
//I18N: in the graphical options tooltip;
// indicates the rendered image quality is high
const core::stringw high = _LTR("High");
//I18N: in the graphical options tooltip;
// indicates the rendered image quality is very high
const core::stringw very_high = _LTR("Very High");
//I18N: in graphical options
// tooltip = tooltip + L"\n" + _("Pixel shaders: %s",
// UserConfigParams::m_pixel_shaders ? enabled : disabled);
@ -448,9 +531,11 @@ void OptionsScreenVideo::updateTooltip()
UserConfigParams::m_gi ? enabled : disabled);
//I18N: in graphical options
tooltip = tooltip + L"\n" + _("Use high definition textures: %s",
(UserConfigParams::m_high_definition_textures & 0x1) == 0 ? disabled : enabled);
int quality = getImageQuality();
tooltip = tooltip + L"\n" + _("Rendered image quality: %s",
quality == 0 ? very_low : quality == 1 ? low : quality == 2 ?
high : very_high);
gfx->setTooltip(tooltip);
} // updateTooltip
@ -518,7 +603,7 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name,
UserConfigParams::m_show_steering_animations = m_presets[level].animatedCharacters;
UserConfigParams::m_graphical_effects = m_presets[level].animatedScenery;
UserConfigParams::m_anisotropic = m_presets[level].anisotropy;
setImageQuality(m_presets[level].image_quality);
UserConfigParams::m_bloom = m_presets[level].bloom;
UserConfigParams::m_glow = m_presets[level].glow;
UserConfigParams::m_dynamic_lights = m_presets[level].lights;
@ -532,7 +617,6 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name,
UserConfigParams::m_dof = m_presets[level].dof;
UserConfigParams::m_gi = m_presets[level].global_illumination;
UserConfigParams::m_degraded_IBL = m_presets[level].degraded_ibl;
UserConfigParams::m_high_definition_textures = 0x02 | m_presets[level].hd_textures;
updateGfxSlider();
}
@ -563,6 +647,12 @@ void OptionsScreenVideo::tearDown()
{
if (m_prev_adv_pipline != UserConfigParams::m_dynamic_lights)
irr_driver->sameRestart();
else if (m_prev_img_quality != getImageQuality())
{
irr_driver->setMaxTextureSize();
STKTexManager::getInstance()->destroyThreadedTexLoaders();
STKTexManager::getInstance()->createThreadedTexLoaders();
}
Screen::tearDown();
// save changes when leaving screen
user_config->saveConfig();

View File

@ -38,12 +38,11 @@ struct GFXPreset
bool weather;
bool animatedScenery;
int animatedCharacters;
int anisotropy;
int image_quality;
/** Depth of field */
bool dof;
bool global_illumination;
bool degraded_ibl;
int hd_textures;
};
/**
@ -54,6 +53,7 @@ class OptionsScreenVideo : public GUIEngine::Screen, public GUIEngine::ScreenSin
{
private:
bool m_prev_adv_pipline;
int m_prev_img_quality;
OptionsScreenVideo();
bool m_inited;
std::vector<GFXPreset> m_presets;
@ -81,6 +81,8 @@ public:
virtual void unloaded() OVERRIDE;
void updateGfxSlider();
static int getImageQuality();
static void setImageQuality(int quality);
};
#endif

View File

@ -128,8 +128,11 @@ RaceGUI::RaceGUI()
m_is_tutorial = (race_manager->getTrackName() == "tutorial");
// Load speedmeter texture before rendering the first frame
m_speed_meter_icon = material_manager->getMaterial("speedback.png");
m_speed_meter_icon->getTexture();
m_speed_bar_icon = material_manager->getMaterial("speedfore.png");
m_speed_bar_icon->getTexture();
//createMarkerTexture();
} // RaceGUI
@ -401,8 +404,7 @@ void RaceGUI::drawGlobalMiniMap()
const Vec3& xyz = kart->getXYZ();
Vec3 draw_at;
track->mapPoint2MiniMap(xyz, &draw_at);
draw_at *= UserConfigParams::m_scale_rtts_factor;
video::ITexture* icon = sta ?
irr_driver->getTexture(FileManager::GUI, "heart.png") :
kart->getKartProperties()->getMinimapIcon();
@ -424,7 +426,7 @@ void RaceGUI::drawGlobalMiniMap()
{
Vec3 draw_at;
track->mapPoint2MiniMap(sw->getBallPosition(), &draw_at);
draw_at *= UserConfigParams::m_scale_rtts_factor;
video::ITexture* icon =
irr_driver->getTexture(FileManager::GUI, "soccer_ball_normal.png");

View File

@ -395,8 +395,7 @@ void RaceGUIOverworld::drawGlobalMiniMap()
kart_xyz= kart->getXYZ();
Vec3 draw_at;
track->mapPoint2MiniMap(kart_xyz, &draw_at);
draw_at *= UserConfigParams::m_scale_rtts_factor;
video::ITexture* icon = kart->getKartProperties()->getMinimapIcon();
core::rect<s32> source(core::position2di(0, 0), icon->getSize());
int marker_half_size = (kart->getController()->isLocalPlayerController()
@ -433,8 +432,7 @@ void RaceGUIOverworld::drawGlobalMiniMap()
Vec3 draw_at;
track->mapPoint2MiniMap(challenges[n].m_position, &draw_at);
draw_at *= UserConfigParams::m_scale_rtts_factor;
const ChallengeData* challenge = unlock_manager->getChallengeData(challenges[n].m_challenge_id);
const unsigned int val = challenge->getNumTrophies();
bool unlocked = (PlayerManager::getCurrentPlayer()->getPoints() >= val);

View File

@ -2176,9 +2176,7 @@ void Track::handleSky(const XMLNode &xml_node, const std::string &filename)
if (CVS->isGLSL())
{
t = STKTexManager::getInstance()->getTexture(v[i],
false/*srgb*/, false/*premul_alpha*/,
false/*set_material*/, false/*mesh_tex*/,
true/*no_upload*/);
(TexConfig*)NULL/*tex_config*/, true/*no_upload*/);
}
else
#endif // !SERVER_ONLY
@ -2222,9 +2220,7 @@ void Track::handleSky(const XMLNode &xml_node, const std::string &filename)
if (CVS->isGLSL())
{
t = STKTexManager::getInstance()->getTexture(v[i],
false/*srgb*/, false/*premul_alpha*/,
false/*set_material*/, false/*mesh_tex*/,
true/*no_upload*/);
(TexConfig*)NULL/*tex_config*/, true/*no_upload*/);
}
else
#endif // !SERVER_ONLY

View File

@ -862,9 +862,9 @@ TrackObjectPresentationBillboard::TrackObjectPresentationBillboard(
xml_node.get("start", &m_fade_out_start);
xml_node.get("end", &m_fade_out_end );
}
TexConfig tc(true/*srgb*/, true/*premul_alpha*/);
video::ITexture* texture = STKTexManager::getInstance()->getTexture
(file_manager->searchTexture(texture_name), true/*srgb*/,
true/*premul_alpha*/, false/*set_material*/, true/*mesh_tex*/);
(file_manager->searchTexture(texture_name), &tc);
if (texture == NULL)
{

625
src/utils/avi_writer.cpp Normal file
View File

@ -0,0 +1,625 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2015 Dawid Gan
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
#include "utils/avi_writer.hpp"
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/message_queue.hpp"
#include "utils/translation.hpp"
#include "utils/vs.hpp"
#include <jpeglib.h>
#include <cstring>
Synchronised<std::string> AVIWriter::m_recording_target("");
// ----------------------------------------------------------------------------
AVIWriter::AVIWriter() : m_idle(true)
{
resetFrameBufferImage();
resetCaptureFormat();
m_file = NULL;
m_last_junk_chunk = 0;
m_end_of_header = 0;
m_movi_start = 0;
m_stream_bytes = 0;
m_total_frames = 0;
m_chunk_fcc = 0;
m_width = irr_driver->getActualScreenSize().Width;
m_height = irr_driver->getActualScreenSize().Height;
glGenBuffers(3, m_pbo);
for (int i = 0; i < 3; i++)
{
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[i]);
glBufferData(GL_PIXEL_PACK_BUFFER, m_width * m_height * 4, NULL,
GL_STREAM_READ);
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
pthread_cond_init(&m_cond_request, NULL);
pthread_create(&m_thread, NULL, &startRoutine, this);
} // AVIWriter
// ----------------------------------------------------------------------------
AVIWriter::~AVIWriter()
{
glDeleteBuffers(3, m_pbo);
addFrameBufferImage(NULL, 0);
if (!waitForReadyToDeleted(2.0f))
Log::info("AVIWriter", "AVIWriter not stopping, exiting anyway.");
pthread_join(m_thread, NULL);
pthread_cond_destroy(&m_cond_request);
} // ~AVIWriter
// ----------------------------------------------------------------------------
void AVIWriter::resetFrameBufferImage()
{
m_pbo_use = 0;
m_accumulated_time = 0.0f;
m_remaining_time = 0.0f;
} // resetFrameBufferImage
// ----------------------------------------------------------------------------
void AVIWriter::resetCaptureFormat()
{
m_img_quality = UserConfigParams::m_record_compression;
m_msec_per_frame = unsigned(1000 / UserConfigParams::m_record_fps);
m_avi_format =
UserConfigParams::m_record_bmp ? AVI_FORMAT_BMP : AVI_FORMAT_JPG;
} // resetCaptureFormat
// ----------------------------------------------------------------------------
void* AVIWriter::startRoutine(void *obj)
{
VS::setThreadName("AVIWriter");
AVIWriter* avi_writer = (AVIWriter*)obj;
while (true)
{
avi_writer->m_fbi_queue.lock();
bool waiting = avi_writer->m_fbi_queue.getData().empty();
while (waiting)
{
pthread_cond_wait(&avi_writer->m_cond_request,
avi_writer->m_fbi_queue.getMutex());
waiting = avi_writer->m_fbi_queue.getData().empty();
}
auto& p = avi_writer->m_fbi_queue.getData().front();
uint8_t* fbi = p.first;
int frame_count = p.second;
if (frame_count == -1)
{
avi_writer->closeFile();
avi_writer->m_idle.setAtomic(true);
avi_writer->m_fbi_queue.getData().pop_front();
avi_writer->m_fbi_queue.unlock();
continue;
}
else if (fbi == NULL)
{
avi_writer->closeFile(false/*delete_file*/, true/*exiting*/);
avi_writer->setCanBeDeleted();
avi_writer->m_fbi_queue.getData().pop_front();
avi_writer->m_fbi_queue.unlock();
return NULL;
}
const bool too_slow = avi_writer->m_fbi_queue.getData().size() > 50;
avi_writer->m_fbi_queue.getData().pop_front();
avi_writer->m_fbi_queue.unlock();
if (too_slow)
{
MessageQueue::add(MessageQueue::MT_ERROR,
_("Encoding is too slow, dropping frames."));
delete [] fbi;
avi_writer->cleanAllFrameBufferImages();
continue;
}
if (avi_writer->m_file == NULL)
{
bool ret = avi_writer->createFile();
if (!ret)
{
delete [] fbi;
avi_writer->cleanAllFrameBufferImages();
continue;
}
}
uint8_t* orig_fbi = fbi;
const unsigned width = avi_writer->m_width;
const unsigned height = avi_writer->m_height;
const unsigned area = width * height;
int size = area * 4;
int dest = size - 3;
int src = size - 4;
int copied = 0;
while (true)
{
if (copied++ > 1)
memcpy(fbi + dest, fbi + src, 3);
else
memmove(fbi + dest, fbi + src, 3);
if (src == 0)
break;
dest -= 3;
src -= 4;
}
fbi = fbi + area;
const int pitch = width * 3;
uint8_t* p2 = fbi + (height - 1) * pitch;
uint8_t* tmp_buf = new uint8_t[pitch];
for (unsigned i = 0; i < height; i += 2)
{
memcpy(tmp_buf, fbi, pitch);
memcpy(fbi, p2, pitch);
memcpy(p2, tmp_buf, pitch);
fbi += pitch;
p2 -= pitch;
}
delete [] tmp_buf;
size = area * 3;
if (avi_writer->m_avi_format == AVI_FORMAT_JPG)
{
uint8_t* jpg = new uint8_t[size];
size = avi_writer->bmpToJpg(orig_fbi + area, jpg, size);
delete [] orig_fbi;
orig_fbi = jpg;
}
while (frame_count != 0)
{
AVIErrCode code = avi_writer->addImage
(avi_writer->m_avi_format == AVI_FORMAT_JPG ? orig_fbi :
orig_fbi + area, size);
if (code == AVI_SIZE_LIMIT_ERR)
{
avi_writer->createFile();
continue;
}
else if (code == AVI_IO_ERR)
break;
frame_count--;
}
delete [] orig_fbi;
}
return NULL;
} // startRoutine
// ----------------------------------------------------------------------------
int AVIWriter::getFrameCount(float dt)
{
const float frame_rate = 0.001f * m_msec_per_frame;
m_accumulated_time += dt;
if (m_accumulated_time < frame_rate && m_remaining_time < frame_rate)
{
return 0;
}
int frame_count = 1;
m_remaining_time += m_accumulated_time - frame_rate;
m_accumulated_time = 0.0f;
while (m_remaining_time > frame_rate)
{
frame_count++;
m_remaining_time -= frame_rate;
}
return frame_count;
} // getFrameCount
// ----------------------------------------------------------------------------
void AVIWriter::captureFrameBufferImage(float dt)
{
glReadBuffer(GL_BACK);
int pbo_read = -1;
if (m_pbo_use > 3 && m_pbo_use % 3 == 0)
m_pbo_use = 3;
if (m_pbo_use >= 3)
{
int frame_count = getFrameCount(dt);
if (frame_count != 0)
{
pbo_read = m_pbo_use % 3;
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_read]);
void* ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
const unsigned size = m_width * m_height * 4;
uint8_t* fbi = new uint8_t[size];
memcpy(fbi, ptr, size);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
addFrameBufferImage(fbi, frame_count);
}
}
int pbo_use = m_pbo_use++ % 3;
assert(pbo_read == -1 || pbo_use == pbo_read);
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]);
glReadPixels(0, 0, m_width, m_height,
m_avi_format == AVI_FORMAT_JPG ? GL_RGBA: GL_BGRA,
GL_UNSIGNED_BYTE, NULL);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
} // captureFrameBufferImage
// ----------------------------------------------------------------------------
bool AVIWriter::addJUNKChunk(std::string str, unsigned int min_size)
{
int size = str.size() < min_size ? min_size : str.size() + 1;
size = (size + 1) & 0xfffffffe;
CHUNK chunk;
chunk.fcc = FOURCC('J', 'U', 'N', 'K');
chunk.cb = size;
char* buffer = (char*)calloc(size, 1);
strcpy(buffer, str.c_str());
int num = fwrite(&chunk, 1, sizeof(chunk), m_file);
if (num != sizeof(chunk))
goto error;
num = fwrite(buffer, 1, size * sizeof(char), m_file);
free(buffer);
if (num != size)
goto error;
m_last_junk_chunk = ftell(m_file);
if (m_last_junk_chunk < 0)
goto error;
return true;
error:
closeFile(true/*delete_file*/);
return false;
} // addJUNKChunk
// ----------------------------------------------------------------------------
AVIErrCode AVIWriter::addImage(unsigned char* buffer, int buf_size)
{
if (m_file == NULL)
goto error;
int num; num = ftell(m_file);
if (num < 0)
goto error;
if (m_total_frames >= MAX_FRAMES)
goto size_limit;
CHUNK chunk;
chunk.fcc = m_chunk_fcc;
chunk.cb = buf_size;
m_index_table[m_total_frames].Offset = num;
m_index_table[m_total_frames].Length = chunk.cb;
m_index_table[m_total_frames].fcc = chunk.fcc;
num = fwrite(&chunk, 1, sizeof(chunk), m_file);
if (num != sizeof(chunk))
goto error;
num = fwrite(buffer, 1, buf_size, m_file);
if (num != buf_size)
goto error;
int fill_size; fill_size = (sizeof(chunk) + buf_size) & 0x00000001;
if (fill_size > 0)
{
uint32_t filler = 0;
num = fwrite(&filler, 1, fill_size, m_file);
if (num != fill_size)
goto error;
}
m_stream_bytes += sizeof(chunk) + buf_size + fill_size;
m_total_frames++;
num = ftell(m_file);
if (num < 0)
goto error;
if (((num - m_last_junk_chunk) > 20000) && (!addJUNKChunk("", 1)))
goto error;
// check if we reached the file size limit
if (num >= MAX_FILE_SIZE)
goto size_limit;
return AVI_SUCCESS;
error:
closeFile(true/*delete_file*/);
return AVI_IO_ERR;
size_limit:
MessageQueue::add(MessageQueue::MT_GENERIC,
_("Video exceeded size limit, starting a new one."));
closeFile();
return AVI_SIZE_LIMIT_ERR;
} // addImage
// ----------------------------------------------------------------------------
bool AVIWriter::closeFile(bool delete_file, bool exiting)
{
if (m_file == NULL)
return false;
if (delete_file)
goto error;
// add the index
int idx_start; idx_start = ftell(m_file);
if (idx_start < 0)
goto error;
CHUNK chunk;
chunk.fcc = FOURCC('i', 'd', 'x', '1');
chunk.cb = sizeof(AVIINDEXENTRY) * m_total_frames;
int num; num = fwrite(&chunk, 1, sizeof(chunk), m_file);
if (num != sizeof(chunk))
goto error;
for (unsigned int i = 0; i < m_total_frames; i++)
{
AVIINDEXENTRY Index;
Index.ckid = m_index_table[i].fcc;
Index.dwFlags = AVIIF_KEYFRAME;
Index.dwChunkOffset = m_index_table[i].Offset;
Index.dwChunkLength = m_index_table[i].Length;
num = fwrite(&Index, 1, sizeof(Index), m_file);
if (num != sizeof(Index))
goto error;
}
// update the header
if (m_total_frames > 0 && m_msec_per_frame > 0)
{
num = fseek(m_file, 0, SEEK_END);
if (num < 0)
goto error;
int size; size = ftell(m_file);
if (size < 0)
goto error;
num = fseek(m_file, 0, SEEK_SET);
if (num < 0)
goto error;
m_avi_hdr.riff.cb = size - sizeof(m_avi_hdr.riff);
m_avi_hdr.avih.dwMaxBytesPerSec = (uint32_t)
(((m_stream_bytes / m_total_frames) * m_format_hdr.strh.dwRate) /
m_msec_per_frame + 0.5f);
m_avi_hdr.avih.dwTotalFrames = m_total_frames;
num = fwrite(&m_avi_hdr, 1, sizeof(m_avi_hdr), m_file);
if (num != sizeof(m_avi_hdr))
goto error;
m_format_hdr.strh.dwLength = m_total_frames;
num = fwrite(&m_format_hdr, 1, sizeof(m_format_hdr), m_file);
if (num != sizeof(m_format_hdr))
goto error;
}
// update the movi section
m_movi_chunk.cb = idx_start - m_movi_start;
num = fseek(m_file, m_movi_start - sizeof(m_movi_chunk), SEEK_SET);
if (num < 0)
goto error;
num = fwrite(&m_movi_chunk, 1, sizeof(m_movi_chunk), m_file);
if (num != sizeof(m_movi_chunk))
goto error;
fclose(m_file);
m_file = NULL;
if (!exiting)
{
MessageQueue::add(MessageQueue::MT_GENERIC,
_("Video saved in \"%s\".", m_filename.c_str()));
}
return true;
error:
if (!exiting)
{
MessageQueue::add(MessageQueue::MT_ERROR,
_("Error when saving video."));
}
fclose(m_file);
remove(m_filename.c_str());
m_file = NULL;
return false;
} // closeFile
// ----------------------------------------------------------------------------
bool AVIWriter::createFile()
{
m_idle.setAtomic(false);
time_t rawtime;
time(&rawtime);
tm* timeInfo = localtime(&rawtime);
char time_buffer[256];
sprintf(time_buffer, "%i.%02i.%02i_%02i.%02i.%02i",
timeInfo->tm_year + 1900, timeInfo->tm_mon + 1,
timeInfo->tm_mday, timeInfo->tm_hour,
timeInfo->tm_min, timeInfo->tm_sec);
m_filename = m_recording_target.getAtomic() + "-" + time_buffer + ".avi";
m_stream_bytes = 0;
m_total_frames = 0;
m_movi_start = 0;
m_last_junk_chunk = 0;
BitmapInfoHeader bitmap_hdr;
bitmap_hdr.biSize = sizeof(BitmapInfoHeader);
bitmap_hdr.biWidth = m_width;
bitmap_hdr.biHeight = m_height;
bitmap_hdr.biPlanes = 1;
bitmap_hdr.biBitCount = 24;
bitmap_hdr.biCompression = 0;
bitmap_hdr.biSizeImage = (m_width * m_height * 3 * bitmap_hdr.biPlanes);
bitmap_hdr.biXPelsPerMeter = 0;
bitmap_hdr.biYPelsPerMeter = 0;
bitmap_hdr.biClrUsed = 0;
bitmap_hdr.biClrImportant = 0;
memset(&m_avi_hdr, '\0', sizeof(m_avi_hdr));
m_avi_hdr.riff.fcc = FOURCC('R', 'I', 'F', 'F');
m_avi_hdr.riff.cb = 0; // update when finished (size of the file - 8)
m_avi_hdr.avi = FOURCC('A', 'V', 'I', ' ');
m_avi_hdr.list1.fcc = FOURCC('L', 'I', 'S', 'T');
m_avi_hdr.list1.cb = 0;
m_avi_hdr.hdrl = FOURCC('h', 'd', 'r', 'l');
m_avi_hdr.avihhdr.fcc = FOURCC('a', 'v', 'i', 'h');
m_avi_hdr.avihhdr.cb = sizeof(m_avi_hdr.avih);
m_avi_hdr.avih.dwMicroSecPerFrame = m_msec_per_frame * 1000;
m_avi_hdr.avih.dwMaxBytesPerSec = 0; // update when finished
m_avi_hdr.avih.dwPaddingGranularity = 0;
m_avi_hdr.avih.dwFlags = AVIF_WASCAPTUREFILE | AVIF_HASINDEX;
m_avi_hdr.avih.dwTotalFrames = 0; // update when finished
m_avi_hdr.avih.dwInitialFrames = 0;
m_avi_hdr.avih.dwStreams = 1; // 1 = video, 2 = video and audio
m_avi_hdr.avih.dwSuggestedBufferSize = 0; // can be just 0
m_avi_hdr.avih.dwWidth = m_width;
m_avi_hdr.avih.dwHeight = m_height;
m_format_hdr.list.fcc = FOURCC('L', 'I', 'S', 'T');
m_format_hdr.list.cb = (sizeof(m_format_hdr) - 8) +
sizeof(BitmapInfoHeader);
m_format_hdr.strl = FOURCC('s', 't', 'r', 'l');
m_format_hdr.strhhdr.fcc = FOURCC('s', 't', 'r', 'h');
m_format_hdr.strhhdr.cb = sizeof(m_format_hdr.strh);
m_format_hdr.strh.fccType = FOURCC('v', 'i', 'd', 's');
m_format_hdr.strh.fccHandler = CC_DIB;
m_format_hdr.strh.dwFlags = 0;
m_format_hdr.strh.wPriority = 0;
m_format_hdr.strh.wLanguage = 0;
m_format_hdr.strh.dwInitialFrames = 0;
m_format_hdr.strh.dwScale = m_msec_per_frame;
m_format_hdr.strh.dwRate = 1000;
m_format_hdr.strh.dwStart = 0;
m_format_hdr.strh.dwLength = 0; // update when finished
m_format_hdr.strh.dwSuggestedBufferSize = 0; // can be just 0
m_format_hdr.strh.dwQuality = m_img_quality * 100;
m_format_hdr.strh.dwSampleSize = 0;
m_format_hdr.strh.Left = 0;
m_format_hdr.strh.Top = 0;
m_format_hdr.strh.Right = m_avi_hdr.avih.dwWidth;
m_format_hdr.strh.Bottom = m_avi_hdr.avih.dwHeight;
m_format_hdr.strfhdr.fcc = FOURCC('s', 't', 'r', 'f');
m_format_hdr.strfhdr.cb = sizeof(BitmapInfoHeader);
// Format specific changes
if (m_avi_format == AVI_FORMAT_JPG)
{
m_format_hdr.strh.fccHandler = CC_MJPG;
bitmap_hdr.biCompression = FOURCC('M', 'J', 'P', 'G');
m_chunk_fcc = FOURCC('0', '0', 'd', 'c');
}
else if (m_avi_format == AVI_FORMAT_BMP)
{
bitmap_hdr.biHeight = -m_height;
bitmap_hdr.biCompression = 0;
m_chunk_fcc = FOURCC('0', '0', 'd', 'b');
}
const uint32_t fcc_movi = FOURCC('m', 'o', 'v', 'i');
m_file = fopen(m_filename.c_str(), "wb");
if (m_file == NULL)
return false;
int num = fwrite(&m_avi_hdr, 1, sizeof(m_avi_hdr), m_file);
if (num != sizeof(m_avi_hdr))
goto error;
num = fwrite(&m_format_hdr, 1, sizeof(m_format_hdr), m_file);
if (num != sizeof(m_format_hdr))
goto error;
num = fwrite(&bitmap_hdr, 1, sizeof(BitmapInfoHeader), m_file);
if (num != sizeof(BitmapInfoHeader))
goto error;
m_end_of_header = ftell(m_file);
if (m_end_of_header < 0)
goto error;
if (!addJUNKChunk("", 2840))
goto error;
m_avi_hdr.list1.cb = m_end_of_header - sizeof(m_avi_hdr.riff) -
sizeof(m_avi_hdr.avi) - sizeof(m_avi_hdr.list1);
m_movi_chunk.fcc = FOURCC('L', 'I', 'S', 'T');
m_movi_chunk.cb = 0; // update when finished
num = fwrite(&m_movi_chunk, 1, sizeof(m_movi_chunk), m_file);
if (num != sizeof(m_movi_chunk))
goto error;
m_movi_start = ftell(m_file);
if (m_movi_start < 0)
goto error;
num = fwrite(&fcc_movi, 1, sizeof(fcc_movi), m_file);
if (num != sizeof(fcc_movi))
goto error;
return true;
error:
closeFile(true/*delete_file*/);
return false;
} // createFile
// ----------------------------------------------------------------------------
int AVIWriter::bmpToJpg(unsigned char* image_data, unsigned char* image_output,
unsigned long buf_length)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
cinfo.image_width = m_width;
cinfo.image_height = m_height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, m_img_quality, true);
jpeg_mem_dest(&cinfo, &image_output, &buf_length);
jpeg_start_compress(&cinfo, true);
JSAMPROW jrow[1];
while (cinfo.next_scanline < cinfo.image_height)
{
jrow[0] = &image_data[cinfo.next_scanline * m_width * 3];
jpeg_write_scanlines(&cinfo, jrow, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
return buf_length;
} // bmpToJpg
#endif

259
src/utils/avi_writer.hpp Normal file
View File

@ -0,0 +1,259 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2015 Dawid Gan
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
#include "graphics/gl_headers.hpp"
#include "utils/can_be_deleted.hpp"
#include "utils/no_copy.hpp"
#include "utils/singleton.hpp"
#include "utils/synchronised.hpp"
#include <string>
#include <list>
#include <pthread.h>
#define FOURCC(a,b,c,d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)))
const uint32_t CC_MJPG = FOURCC('m', 'j', 'p', 'g');
const uint32_t CC_DIB = FOURCC('\0', '\0', '\0', '\0');
const uint32_t CC_VIDS = FOURCC('v', 'i', 'd', 's');
const uint32_t AVIF_HASINDEX = 0x00000010;
const uint32_t AVIF_MUSTUSEINDEX = 0x00000020;
const uint32_t AVIF_ISINTERLEAVED = 0x00000100;
const uint32_t AVIF_TRUSTCKTYPE = 0x00000800;
const uint32_t AVIF_WASCAPTUREFILE = 0x00010000;
const uint32_t AVIF_COPYRIGHTED = 0x00020000;
const uint32_t AVISF_DISABLED = 0x00000001;
const uint32_t AVISF_VIDEO_PALCHANGES = 0x00010000;
const uint32_t AVIIF_LIST = 0x00000001;
const uint32_t AVIIF_KEYFRAME = 0x00000010;
const uint32_t AVIIF_FIRSTPART = 0x00000020;
const uint32_t AVIIF_LASTPART = 0x00000040;
const uint32_t AVIIF_MIDPART = 0x00000060;
const uint32_t AVIIF_NOTIME = 0x00000100;
const uint32_t AVIIF_COMPUSE = 0x0FFF0000;
enum AVIFormat
{
AVI_FORMAT_BMP,
AVI_FORMAT_JPG
};
enum AVIErrCode
{
AVI_SUCCESS,
AVI_SIZE_LIMIT_ERR,
AVI_IO_ERR
};
const int MAX_FRAMES = 1000000;
const int MAX_FILE_SIZE = 2000000000;
struct MainAVIHeader
{
uint32_t dwMicroSecPerFrame;
uint32_t dwMaxBytesPerSec;
uint32_t dwPaddingGranularity;
uint32_t dwFlags;
uint32_t dwTotalFrames;
uint32_t dwInitialFrames;
uint32_t dwStreams;
uint32_t dwSuggestedBufferSize;
uint32_t dwWidth;
uint32_t dwHeight;
uint32_t dwReserved[4];
};
struct AVIStreamHeader
{
uint32_t fccType;
uint32_t fccHandler;
uint32_t dwFlags;
uint16_t wPriority;
uint16_t wLanguage;
uint32_t dwInitialFrames;
uint32_t dwScale;
uint32_t dwRate;
uint32_t dwStart;
uint32_t dwLength;
uint32_t dwSuggestedBufferSize;
uint32_t dwQuality;
uint32_t dwSampleSize;
uint16_t Left;
uint16_t Top;
uint16_t Right;
uint16_t Bottom;
};
struct BitmapInfoHeader
{
uint32_t biSize;
uint32_t biWidth;
uint32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
uint32_t biXPelsPerMeter;
uint32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
};
struct AVIINDEXENTRY
{
uint32_t ckid;
uint32_t dwFlags;
uint32_t dwChunkOffset;
uint32_t dwChunkLength;
};
struct CHUNK
{
uint32_t fcc;
uint32_t cb;
};
struct AVIHeader
{
CHUNK riff;
uint32_t avi;
CHUNK list1;
uint32_t hdrl;
CHUNK avihhdr;
MainAVIHeader avih;
};
struct FormatHeader
{
CHUNK list;
uint32_t strl;
CHUNK strhhdr;
AVIStreamHeader strh;
CHUNK strfhdr;
};
struct IndexTable
{
uint32_t Offset;
uint32_t Length;
uint32_t fcc;
};
class AVIWriter : public CanBeDeleted, public NoCopy,
public Singleton<AVIWriter>
{
private:
FILE* m_file;
static Synchronised<std::string> m_recording_target;
std::string m_filename;
int m_last_junk_chunk, m_end_of_header, m_movi_start, m_img_quality,
m_width, m_height;
unsigned int m_msec_per_frame, m_stream_bytes, m_total_frames, m_pbo_use;
float m_accumulated_time, m_remaining_time;
AVIFormat m_avi_format;
AVIHeader m_avi_hdr;
CHUNK m_movi_chunk;
FormatHeader m_format_hdr;
IndexTable m_index_table[MAX_FRAMES];
uint32_t m_chunk_fcc;
Synchronised<std::list<std::pair<uint8_t*, int> > > m_fbi_queue;
Synchronised<bool> m_idle;
pthread_t m_thread;
pthread_cond_t m_cond_request;
GLuint m_pbo[3];
// ------------------------------------------------------------------------
int bmpToJpg(unsigned char* image_data, unsigned char* image_output,
unsigned long buf_length);
// ------------------------------------------------------------------------
AVIErrCode addImage(unsigned char* buffer, int size);
// ------------------------------------------------------------------------
bool closeFile(bool delete_file = false, bool exiting = false);
// ------------------------------------------------------------------------
bool createFile();
// ------------------------------------------------------------------------
bool addJUNKChunk(std::string str, unsigned int min_size);
// ------------------------------------------------------------------------
void addFrameBufferImage(uint8_t* fbi, int frame_count)
{
m_fbi_queue.lock();
m_fbi_queue.getData().emplace_back(fbi, frame_count);
pthread_cond_signal(&m_cond_request);
m_fbi_queue.unlock();
}
// ------------------------------------------------------------------------
int getFrameCount(float dt);
// ------------------------------------------------------------------------
void cleanAllFrameBufferImages()
{
m_fbi_queue.lock();
for (auto& p : m_fbi_queue.getData())
delete [] p.first;
m_fbi_queue.getData().clear();
m_fbi_queue.unlock();
}
// ------------------------------------------------------------------------
bool isIdle() const { return m_idle.getAtomic(); }
public:
// ------------------------------------------------------------------------
AVIWriter();
// ------------------------------------------------------------------------
~AVIWriter();
// ------------------------------------------------------------------------
static void* startRoutine(void *obj);
// ------------------------------------------------------------------------
static void setRecordingTarget(const std::string& name)
{
m_recording_target.setAtomic(name);
}
// ------------------------------------------------------------------------
void captureFrameBufferImage(float dt);
// ------------------------------------------------------------------------
void resetFrameBufferImage();
// ------------------------------------------------------------------------
void resetCaptureFormat();
// ------------------------------------------------------------------------
void stopRecording() { addFrameBufferImage(NULL, -1); }
};
#endif

View File

@ -134,6 +134,8 @@ enum DebugMenuCommand
DEBUG_SCRIPT_CONSOLE,
DEBUG_RUN_CUTSCENE,
DEBUG_TEXTURE_CONSOLE,
DEBUG_START_RECORDING,
DEBUG_STOP_RECORDING
}; // DebugMenuCommand
// -----------------------------------------------------------------------------
@ -541,7 +543,6 @@ bool handleContextMenuAction(s32 cmd_id)
break;
case DEBUG_VISUAL_VALUES:
{
#if !defined(__APPLE__)
DebugSliderDialog *dsd = new DebugSliderDialog();
dsd->setSliderHook("red_slider", 0, 255,
[](){ return int(irr_driver->getAmbientLight().r * 255.f); },
@ -576,12 +577,10 @@ bool handleContextMenuAction(s32 cmd_id)
[](){ return int(irr_driver->getSSAOSigma() * 10.f); },
[](int v){irr_driver->setSSAOSigma(v / 10.f); }
);
#endif
}
break;
case DEBUG_ADJUST_LIGHTS:
{
#if !defined(__APPLE__)
// Some sliders use multipliers because the spinner widget
// only supports integers
DebugSliderDialog *dsd = new DebugSliderDialog();
@ -635,7 +634,6 @@ bool handleContextMenuAction(s32 cmd_id)
[](int v){ findNearestLight()->setRadius(float(v)); }
);
dsd->changeLabel("SSAO Sigma", "[None]");
#endif
break;
}
case DEBUG_SCRIPT_CONSOLE:
@ -711,6 +709,12 @@ bool handleContextMenuAction(s32 cmd_id)
return false;
});
break;
case DEBUG_START_RECORDING:
irr_driver->setRecording(true);
break;
case DEBUG_STOP_RECORDING:
irr_driver->setRecording(false);
break;
} // switch
return false;
}
@ -793,8 +797,13 @@ bool onEvent(const SEvent &event)
sub->addItem(L"Toggle smooth camera", DEBUG_GUI_CAM_SMOOTH);
sub->addItem(L"Attach fps camera to kart", DEBUG_GUI_CAM_ATTACH);
mnu->addItem(L"Change camera target >",-1,true, true);
mnu->addItem(L"Recording >",-1,true, true);
sub = mnu->getSubMenu(4);
sub->addItem(L"Start recording", DEBUG_START_RECORDING);
sub->addItem(L"Stop recording", DEBUG_STOP_RECORDING);
mnu->addItem(L"Change camera target >",-1,true, true);
sub = mnu->getSubMenu(5);
sub->addItem(L"To kart one", DEBUG_VIEW_KART_ONE);
sub->addItem(L"To kart two", DEBUG_VIEW_KART_TWO);
sub->addItem(L"To kart three", DEBUG_VIEW_KART_THREE);
@ -805,7 +814,7 @@ bool onEvent(const SEvent &event)
sub->addItem(L"To kart eight", DEBUG_VIEW_KART_EIGHT);
mnu->addItem(L"Font >",-1,true, true);
sub = mnu->getSubMenu(5);
sub = mnu->getSubMenu(6);
sub->addItem(L"Dump glyph pages of fonts", DEBUG_FONT_DUMP_GLYPH_PAGE);
sub->addItem(L"Reload all fonts", DEBUG_FONT_RELOAD);

View File

@ -40,6 +40,10 @@
# include <windows.h>
#endif
#if defined(__linux__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__)
# include <pthread.h>
#endif
namespace VS
{
#if defined(_MSC_VER) && defined(DEBUG)
@ -77,6 +81,13 @@ namespace VS
{
}
} // setThreadName
#elif defined(__linux__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__)
static void setThreadName(const char* name)
{
#if __GLIBC__ > 2 || __GLIBC_MINOR__ > 11
pthread_setname_np(pthread_self(), name);
#endif
} // setThreadName
#else
static void setThreadName(const char* name)