diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c7ea9fc84..b8845af80 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,7 +3,7 @@ on: [push, pull_request] jobs: linux-os: - runs-on: ubuntu-latest + runs-on: ubuntu-16.04 steps: - name: Update APT run: sudo apt-get update @@ -20,6 +20,26 @@ jobs: run: mkdir build && cd build && ../configure - name: Build run: cd build && make -j2 + - name: Prepare appimagetool + run: | + cd build && + wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool && + chmod +x appimagetool && + sudo apt install -y appstream + - name: Clone love-appimages + uses: actions/checkout@v2 + with: + path: build/love-appimages + repository: pfirsich/love-appimages + - name: Build AppImage + run: | + cd build && + python3 love-appimages/build.py .. AppDir --builddir build --appimage love.AppImage + - name: Artifact + uses: actions/upload-artifact@v2-preview + with: + name: love.AppImage + path: build/love.AppImage windows-os: runs-on: windows-latest strategy: diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index acc9573c4..ce4d481ca 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -1873,7 +1873,6 @@ FA620A301AA2F8DB005DB4C2 /* wrap_Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Texture.cpp; sourceTree = ""; }; FA620A311AA2F8DB005DB4C2 /* wrap_Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Texture.h; sourceTree = ""; }; FA620A391AA305F6005DB4C2 /* types.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = types.cpp; sourceTree = ""; }; - FA665DC321C34C900074BBD6 /* wrap_GraphicsShader.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_GraphicsShader.lua; sourceTree = ""; }; FA6A2B641F5F7B6B0074C308 /* wrap_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Data.h; sourceTree = ""; }; FA6A2B651F5F7B6B0074C308 /* wrap_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Data.cpp; sourceTree = ""; }; FA6A2B681F5F7F560074C308 /* DataView.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DataView.cpp; sourceTree = ""; }; @@ -2862,7 +2861,6 @@ FADF54391E3DAFF700012CC0 /* wrap_Graphics.cpp */, FADF543A1E3DAFF700012CC0 /* wrap_Graphics.h */, FADF54371E3DAFBA00012CC0 /* wrap_Graphics.lua */, - FA665DC321C34C900074BBD6 /* wrap_GraphicsShader.lua */, FADF54281E3DAADA00012CC0 /* wrap_Mesh.cpp */, FADF54291E3DAADA00012CC0 /* wrap_Mesh.h */, FADF541E1E3DA52C00012CC0 /* wrap_ParticleSystem.cpp */, diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 3c5677336..7fbe0a7b5 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -104,8 +104,6 @@ bool isDebugEnabled() love::Type Graphics::type("graphics", &Module::type); -Graphics::DefaultShaderCode Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2]; - Graphics::Graphics() : width(0) , height(0) @@ -216,13 +214,8 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in return new ParticleSystem(texture, size); } -ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &optsource) +ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info) { - if (stage == ShaderStage::STAGE_MAX_ENUM) - throw love::Exception("Invalid shader stage."); - - const std::string &source = optsource.empty() ? getCurrentDefaultShaderCode().source[stage] : optsource; - ShaderStage *s = nullptr; std::string cachekey; @@ -243,7 +236,8 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s if (s == nullptr) { - s = newShaderStageInternal(stage, cachekey, source, getRenderer() == RENDERER_OPENGLES); + std::string glsl = Shader::createShaderStageCode(this, stage, source, info); + s = newShaderStageInternal(stage, cachekey, glsl, getRenderer() == RENDERER_OPENGLES); if (!cachekey.empty()) cachedShaderStages[stage][cachekey] = s; } @@ -251,15 +245,48 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s return s; } -Shader *Graphics::newShader(const std::string &vertex, const std::string &pixel) +Shader *Graphics::newShader(const std::vector &stagessource) { - if (vertex.empty() && pixel.empty()) - throw love::Exception("Error creating shader: no source code!"); + StrongRef stages[ShaderStage::STAGE_MAX_ENUM] = {}; - StrongRef vertexstage(newShaderStage(ShaderStage::STAGE_VERTEX, vertex), Acquire::NORETAIN); - StrongRef pixelstage(newShaderStage(ShaderStage::STAGE_PIXEL, pixel), Acquire::NORETAIN); + bool validstages[ShaderStage::STAGE_MAX_ENUM] = {}; + validstages[ShaderStage::STAGE_VERTEX] = true; + validstages[ShaderStage::STAGE_PIXEL] = true; - return newShaderInternal(vertexstage.get(), pixelstage.get()); + for (const std::string &source : stagessource) + { + Shader::SourceInfo info = Shader::getSourceInfo(source); + bool isanystage = false; + + for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++) + { + if (!validstages[i]) + continue; + + if (info.isStage[i]) + { + isanystage = true; + stages[i].set(newShaderStage((ShaderStage::StageType) i, source, info), Acquire::NORETAIN); + } + } + + if (!isanystage) + throw love::Exception("Could not parse shader code (missing 'position' or 'effect' function?)"); + } + + for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++) + { + auto stype = (ShaderStage::StageType) i; + if (validstages[i] && stages[i].get() == nullptr) + { + const std::string &source = Shader::getDefaultCode(Shader::STANDARD_DEFAULT, stype); + Shader::SourceInfo info = Shader::getSourceInfo(source); + stages[i].set(newShaderStage(stype, source, info), Acquire::NORETAIN); + } + + } + + return newShaderInternal(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL]); } Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength) @@ -293,26 +320,44 @@ void Graphics::cleanupCachedShaderStage(ShaderStage::StageType type, const std:: cachedShaderStages[type].erase(hashkey); } -bool Graphics::validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err) +bool Graphics::validateShader(bool gles, const std::vector &stagessource, std::string &err) { - if (vertex.empty() && pixel.empty()) - { - err = "Error validating shader: no source code!"; - return false; - } + StrongRef stages[ShaderStage::STAGE_MAX_ENUM] = {}; - StrongRef vertexstage; - StrongRef pixelstage; + bool validstages[ShaderStage::STAGE_MAX_ENUM] = {}; + validstages[ShaderStage::STAGE_VERTEX] = true; + validstages[ShaderStage::STAGE_PIXEL] = true; // Don't use cached shader stages, since the gles flag may not match the // current renderer. - if (!vertex.empty()) - vertexstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_VERTEX, vertex, gles), Acquire::NORETAIN); + for (const std::string &source : stagessource) + { + Shader::SourceInfo info = Shader::getSourceInfo(source); + bool isanystage = false; - if (!pixel.empty()) - pixelstage.set(new ShaderStageForValidation(this, ShaderStage::STAGE_PIXEL, pixel, gles), Acquire::NORETAIN); + for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++) + { + auto stype = (ShaderStage::StageType) i; - return Shader::validate(vertexstage.get(), pixelstage.get(), err); + if (!validstages[i]) + continue; + + if (info.isStage[i]) + { + isanystage = true; + std::string glsl = Shader::createShaderStageCode(this, stype, source, info); + stages[i].set(new ShaderStageForValidation(this, stype, glsl, gles), Acquire::NORETAIN); + } + } + + if (!isanystage) + { + err = "Could not parse shader code (missing 'position' or 'effect' function?)"; + return false; + } + } + + return Shader::validate(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL], err); } int Graphics::getWidth() const @@ -1742,14 +1787,6 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point) return p; } -const Graphics::DefaultShaderCode &Graphics::getCurrentDefaultShaderCode() const -{ - int languageindex = (int) getShaderLanguageTarget(); - int gammaindex = isGammaCorrect() ? 1 : 0; - - return defaultShaderCode[Shader::STANDARD_DEFAULT][languageindex][gammaindex]; -} - /** * Constants. **/ diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index d2aad6468..1a2f75640 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -415,11 +415,6 @@ public: } }; - struct DefaultShaderCode - { - std::string source[ShaderStage::STAGE_MAX_ENUM]; - }; - Graphics(); virtual ~Graphics(); @@ -436,8 +431,7 @@ public: SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferUsage usage); ParticleSystem *newParticleSystem(Texture *texture, int size); - ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source); - Shader *newShader(const std::string &vertex, const std::string &pixel); + Shader *newShader(const std::vector &stagessource); virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) = 0; virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength); @@ -448,7 +442,7 @@ public: Text *newText(Font *font, const std::vector &text = {}); - bool validateShader(bool gles, const std::string &vertex, const std::string &pixel, std::string &err); + bool validateShader(bool gles, const std::vector &stages, std::string &err); /** * Resets the current color, background color, line style, and so forth. @@ -827,9 +821,6 @@ public: static void flushBatchedDrawsGlobal(); - virtual Shader::Language getShaderLanguageTarget() const = 0; - const DefaultShaderCode &getCurrentDefaultShaderCode() const; - void cleanupCachedShaderStage(ShaderStage::StageType type, const std::string &cachekey); template @@ -869,9 +860,6 @@ public: static bool getConstant(StackType in, const char *&out); static std::vector getConstants(StackType); - // Default shader code (a shader is always required internally.) - static DefaultShaderCode defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2]; - protected: struct DisplayState @@ -945,6 +933,7 @@ protected: {} }; + ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source, const Shader::SourceInfo &info); virtual ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0; virtual Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0; virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0; diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index f94b952f6..fbfdc7ebe 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -28,12 +28,402 @@ // C++ #include +#include +#include namespace love { namespace graphics { +namespace glsl +{ +static const char global_syntax[] = R"( +#if !defined(GL_ES) && __VERSION__ < 140 + #define lowp + #define mediump + #define highp +#endif +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define LOVE_HIGHP_OR_MEDIUMP highp +#else + #define LOVE_HIGHP_OR_MEDIUMP mediump +#endif +#define number float +#define Image sampler2D +#define ArrayImage sampler2DArray +#define CubeImage samplerCube +#define VolumeImage sampler3D +#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3) + #define DepthImage sampler2DShadow + #define DepthArrayImage sampler2DArrayShadow + #define DepthCubeImage samplerCubeShadow +#endif +#define extern uniform +#ifdef GL_EXT_texture_array +#extension GL_EXT_texture_array : enable +#endif +#ifdef GL_OES_texture_3D +#extension GL_OES_texture_3D : enable +#endif +#ifdef GL_OES_standard_derivatives +#extension GL_OES_standard_derivatives : enable +#endif +)"; + +static const char global_uniforms[] = R"( +// According to the GLSL ES 1.0 spec, uniform precision must match between stages, +// but we can't guarantee that highp is always supported in fragment shaders... +// We *really* don't want to use mediump for these in vertex shaders though. +uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13]; + +// These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't +// do it as an initializer. +LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix; +LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix; +LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix; + +LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize; +LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor; + +#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix) + +// Alternate names +#define ViewSpaceFromLocal TransformMatrix +#define ClipSpaceFromView ProjectionMatrix +#define ClipSpaceFromLocal TransformProjectionMatrix +#define ViewNormalFromLocal NormalMatrix + +void love_initializeBuiltinUniforms() { + TransformMatrix = mat4( + love_UniformsPerDraw[0], + love_UniformsPerDraw[1], + love_UniformsPerDraw[2], + love_UniformsPerDraw[3] + ); + + ProjectionMatrix = mat4( + love_UniformsPerDraw[4], + love_UniformsPerDraw[5], + love_UniformsPerDraw[6], + love_UniformsPerDraw[7] + ); + + NormalMatrix = mat3( + love_UniformsPerDraw[8].xyz, + love_UniformsPerDraw[9].xyz, + love_UniformsPerDraw[10].xyz + ); + + love_ScreenSize = love_UniformsPerDraw[11]; + ConstantColor = love_UniformsPerDraw[12]; +} +)"; + +static const char global_functions[] = R"( +#ifdef GL_ES + #if __VERSION__ >= 300 || defined(GL_EXT_texture_array) + precision lowp sampler2DArray; + #endif + #if __VERSION__ >= 300 || defined(GL_OES_texture_3D) + precision lowp sampler3D; + #endif + #if __VERSION__ >= 300 + precision lowp sampler2DShadow; + precision lowp samplerCubeShadow; + precision lowp sampler2DArrayShadow; + #endif +#endif + +#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3) + #define Texel texture +#else + #if __VERSION__ >= 130 + #define texture2D Texel + #define texture3D Texel + #define textureCube Texel + #define texture2DArray Texel + #define love_texture2D texture + #define love_texture3D texture + #define love_textureCube texture + #define love_texture2DArray texture + #else + #define love_texture2D texture2D + #define love_texture3D texture3D + #define love_textureCube textureCube + #define love_texture2DArray texture2DArray + #endif + vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); } + vec4 Texel(samplerCube s, vec3 c) { return love_textureCube(s, c); } + #if __VERSION__ > 100 || defined(GL_OES_texture_3D) + vec4 Texel(sampler3D s, vec3 c) { return love_texture3D(s, c); } + #endif + #if __VERSION__ >= 130 || defined(GL_EXT_texture_array) + vec4 Texel(sampler2DArray s, vec3 c) { return love_texture2DArray(s, c); } + #endif + #ifdef PIXEL + vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); } + vec4 Texel(samplerCube s, vec3 c, float b) { return love_textureCube(s, c, b); } + #if __VERSION__ > 100 || defined(GL_OES_texture_3D) + vec4 Texel(sampler3D s, vec3 c, float b) { return love_texture3D(s, c, b); } + #endif + #if __VERSION__ >= 130 || defined(GL_EXT_texture_array) + vec4 Texel(sampler2DArray s, vec3 c, float b) { return love_texture2DArray(s, c, b); } + #endif + #endif + #define texture love_texture +#endif + +float gammaToLinearPrecise(float c) { + return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4); +} +vec3 gammaToLinearPrecise(vec3 c) { + bvec3 leq = lessThanEqual(c, vec3(0.04045)); + c.r = leq.r ? c.r / 12.92 : pow((c.r + 0.055) / 1.055, 2.4); + c.g = leq.g ? c.g / 12.92 : pow((c.g + 0.055) / 1.055, 2.4); + c.b = leq.b ? c.b / 12.92 : pow((c.b + 0.055) / 1.055, 2.4); + return c; +} +vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); } +float linearToGammaPrecise(float c) { + return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055; +} +vec3 linearToGammaPrecise(vec3 c) { + bvec3 lt = lessThanEqual(c, vec3(0.0031308)); + c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055; + c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055; + c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055; + return c; +} +vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); } + +// http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1 + +mediump float gammaToLinearFast(mediump float c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); } +mediump vec3 gammaToLinearFast(mediump vec3 c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); } +mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); } + +mediump float linearToGammaFast(mediump float c) { return max(1.055 * pow(max(c, 0.0), 0.41666666) - 0.055, 0.0); } +mediump vec3 linearToGammaFast(mediump vec3 c) { return max(1.055 * pow(max(c, vec3(0.0)), vec3(0.41666666)) - 0.055, vec3(0.0)); } +mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); } + +#define gammaToLinear gammaToLinearFast +#define linearToGamma linearToGammaFast + +#ifdef LOVE_GAMMA_CORRECT + #define gammaCorrectColor gammaToLinear + #define unGammaCorrectColor linearToGamma + #define gammaCorrectColorPrecise gammaToLinearPrecise + #define unGammaCorrectColorPrecise linearToGammaPrecise + #define gammaCorrectColorFast gammaToLinearFast + #define unGammaCorrectColorFast linearToGammaFast +#else + #define gammaCorrectColor + #define unGammaCorrectColor + #define gammaCorrectColorPrecise + #define unGammaCorrectColorPrecise + #define gammaCorrectColorFast + #define unGammaCorrectColorFast +#endif +)"; + +static const char vertex_header[] = R"( +#define love_Position gl_Position + +#if __VERSION__ >= 130 + #define attribute in + #define varying out + #ifndef LOVE_GLSL1_ON_GLSL3 + #define love_VertexID gl_VertexID + #define love_InstanceID gl_InstanceID + #endif +#endif + +#ifdef GL_ES + uniform mediump float love_PointSize; +#endif +)"; + +static const char vertex_functions[] = R"( +void setPointSize() { +#ifdef GL_ES + gl_PointSize = love_PointSize; +#endif +} +)"; + +static const char vertex_main[] = R"( +attribute vec4 VertexPosition; +attribute vec4 VertexTexCoord; +attribute vec4 VertexColor; + +varying vec4 VaryingTexCoord; +varying vec4 VaryingColor; + +vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition); + +void main() { + love_initializeBuiltinUniforms(); + VaryingTexCoord = VertexTexCoord; + VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor; + setPointSize(); + love_Position = position(ClipSpaceFromLocal, VertexPosition); +} +)"; + +static const char pixel_header[] = R"( +#ifdef GL_ES + precision mediump float; +#endif + +#define love_MaxRenderTargets gl_MaxDrawBuffers + +#if __VERSION__ >= 130 + #define varying in + // Some drivers seem to make the pixel shader do more work when multiple + // pixel shader outputs are defined, even when only one is actually used. + // TODO: We should use reflection or something instead of this, to determine + // how many outputs are actually used in the shader code. + #ifdef LOVE_MULTI_RENDER_TARGETS + layout(location = 0) out vec4 love_RenderTargets[love_MaxRenderTargets]; + #define love_PixelColor love_RenderTargets[0] + #else + layout(location = 0) out vec4 love_PixelColor; + #endif +#else + #ifdef LOVE_MULTI_RENDER_TARGETS + #define love_RenderTargets gl_FragData + #endif + #define love_PixelColor gl_FragColor +#endif + +// Legacy +#define love_MaxCanvases love_MaxRenderTargets +#define love_Canvases love_RenderTargets +#ifdef LOVE_MULTI_RENDER_TARGETS +#define LOVE_MULTI_CANVASES 1 +#endif + +// See Shader::updateScreenParams in Shader.cpp. +#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w)) +)"; + +static const char pixel_functions[] = R"( +uniform sampler2D love_VideoYChannel; +uniform sampler2D love_VideoCbChannel; +uniform sampler2D love_VideoCrChannel; + +vec4 VideoTexel(vec2 texcoords) { + vec3 yuv; + yuv[0] = Texel(love_VideoYChannel, texcoords).r; + yuv[1] = Texel(love_VideoCbChannel, texcoords).r; + yuv[2] = Texel(love_VideoCrChannel, texcoords).r; + yuv += vec3(-0.0627451017, -0.501960814, -0.501960814); + + vec4 color; + color.r = dot(yuv, vec3(1.164, 0.000, 1.596)); + color.g = dot(yuv, vec3(1.164, -0.391, -0.813)); + color.b = dot(yuv, vec3(1.164, 2.018, 0.000)); + color.a = 1.0; + + return gammaCorrectColor(color); +} +)"; + +static const char pixel_main[] = R"( +uniform sampler2D MainTex; +varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord; +varying mediump vec4 VaryingColor; + +vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord); + +void main() { + love_initializeBuiltinUniforms(); + love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord); +} +)"; + +static const char pixel_main_custom[] = R"( +varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord; +varying mediump vec4 VaryingColor; + +void effect(); + +void main() { + love_initializeBuiltinUniforms(); + effect(); +} +)"; + +struct StageInfo +{ + const char *name; + const char *header; + const char *functions; + const char *main; + const char *main_custom; +}; + +static const StageInfo stageInfo[] = +{ + { "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main }, + { "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom }, +}; + +static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == ShaderStage::STAGE_MAX_ENUM, "Stages array size must match ShaderStage enum."); + +struct Version +{ + std::string glsl; + std::string glsles; +}; + +// Indexed by Shader::Version +static const Version versions[] = +{ + { "#version 120", "#version 100" }, + { "#version 330 core", "#version 300 es" }, + { "#version 430 core", "#version 310 es" }, +}; + +static Shader::Language getTargetLanguage(const std::string &src) +{ + std::regex r("^\\s*#pragma language (\\w+)"); + std::smatch m; + std::string langstr = std::regex_search(src, m, r) ? m[0] : std::string("glsl1"); + Shader::Language lang = Shader::LANGUAGE_MAX_ENUM; + Shader::getConstant(langstr.c_str(), lang); + return lang; +} + +static bool isVertexCode(const std::string &src) +{ + std::regex r("vec4\\s+position\\s*\\("); + std::smatch m; + return std::regex_search(src, m, r); +} + +static bool isPixelCode(const std::string &src, bool &custompixel, bool &mrt) +{ + custompixel = false; + mrt = false; + std::smatch m; + if (std::regex_search(src, m, std::regex("vec4\\s+effect\\s*\\("))) + return true; + + if (std::regex_search(src, m, std::regex("void\\s+effect\\s*\\("))) + { + custompixel = true; + if (src.find("love_RenderTargets") != std::string::npos || src.find("love_Canvases") != std::string::npos) + mrt = true; + return true; + } + + return false; +} + +} // glsl + static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes."); love::Type Shader::type("Shader", &Object::type); @@ -41,6 +431,59 @@ love::Type Shader::type("Shader", &Object::type); Shader *Shader::current = nullptr; Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr}; +Shader::SourceInfo Shader::getSourceInfo(const std::string &src) +{ + SourceInfo info = {}; + info.language = glsl::getTargetLanguage(src); + info.isStage[ShaderStage::STAGE_VERTEX] = glsl::isVertexCode(src); + info.isStage[ShaderStage::STAGE_PIXEL] = glsl::isPixelCode(src, info.customPixelFunction, info.usesMRT); + return info; +} + +std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const Shader::SourceInfo &info) +{ + if (info.language == Shader::LANGUAGE_MAX_ENUM) + throw love::Exception("Invalid shader language"); + + const auto &features = gfx->getCapabilities().features; + + if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3]) + throw love::Exception("GLSL 3 shaders are not supported on this system."); + + if (info.language == LANGUAGE_GLSL4 && !features[Graphics::FEATURE_GLSL4]) + throw love::Exception("GLSL 4 shaders are not supported on this system."); + + bool gles = gfx->getRenderer() == Graphics::RENDERER_OPENGLES; + bool glsl1on3 = info.language == LANGUAGE_GLSL1 && features[Graphics::FEATURE_GLSL3]; + + Language lang = info.language; + if (glsl1on3) + lang = LANGUAGE_GLSL3; + + glsl::StageInfo stageinfo = glsl::stageInfo[stage]; + + std::stringstream ss; + + ss << (gles ? glsl::versions[lang].glsles : glsl::versions[lang].glsl) << "\n"; + ss << "#define " << stageinfo.name << " " << stageinfo.name << "\n"; + if (glsl1on3) + ss << "#define LOVE_GLSL1_ON_GLSL3 1\n"; + if (isGammaCorrect()) + ss << "#define LOVE_GAMMA_CORRECT 1\n"; + if (info.usesMRT) + ss << "#define LOVE_MULTI_RENDER_TARGETS 1"; + ss << glsl::global_syntax; + ss << stageinfo.header; + ss << glsl::global_uniforms; + ss << glsl::global_functions; + ss << stageinfo.functions; + ss << (info.customPixelFunction ? stageinfo.main_custom : stageinfo.main); + ss << ((!gles && (lang == Shader::LANGUAGE_GLSL1 || glsl1on3)) ? "#line 0\n" : "#line 1\n"); + ss << code; + + return ss.str(); +} + Shader::Shader(ShaderStage *vertex, ShaderStage *pixel) : stages() { @@ -157,6 +600,74 @@ void Shader::deinitialize() glslang::FinalizeProcess(); } +static const std::string defaultVertex = R"( +vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition) +{ + return clipSpaceFromLocal * localPosition; +} +)"; + +static const std::string defaultStandardPixel = R"( +vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) +{ + return Texel(tex, texcoord) * vcolor; +} +)"; + +static const std::string defaultVideoPixel = R"( +void effect() +{ + love_PixelColor = VideoTexel(VaryingTexCoord.xy) * VaryingColor; +} +)"; + +static const std::string defaultArrayPixel = R"( +uniform ArrayImage MainTex; +void effect() +{ + love_PixelColor = Texel(MainTex, VaryingTexCoord.xyz) * VaryingColor; +} +)"; + +const std::string &Shader::getDefaultCode(StandardShader shader, ShaderStage::StageType stage) +{ + if (stage == ShaderStage::STAGE_VERTEX) + return defaultVertex; + + static std::string nocode = ""; + + switch (shader) + { + case STANDARD_DEFAULT: return defaultStandardPixel; + case STANDARD_VIDEO: return defaultVideoPixel; + case STANDARD_ARRAY: return defaultArrayPixel; + case STANDARD_MAX_ENUM: return nocode; + } + + return nocode; +} + +static StringMap::Entry languageEntries[] = +{ + { "glsl1", Shader::LANGUAGE_GLSL1 }, + { "glsl3", Shader::LANGUAGE_GLSL3 }, + { "glsl4", Shader::LANGUAGE_GLSL4 }, +}; + +static StringMap languages(languageEntries, sizeof(languageEntries)); + +static StringMap::Entry builtinNameEntries[] = +{ + { "MainTex", Shader::BUILTIN_TEXTURE_MAIN }, + { "love_VideoYChannel", Shader::BUILTIN_TEXTURE_VIDEO_Y }, + { "love_VideoCbChannel", Shader::BUILTIN_TEXTURE_VIDEO_CB }, + { "love_VideoCrChannel", Shader::BUILTIN_TEXTURE_VIDEO_CR }, + { "love_UniformsPerDraw", Shader::BUILTIN_UNIFORMS_PER_DRAW }, + { "love_PointSize", Shader::BUILTIN_POINT_SIZE }, +}; + +static StringMap builtinNames(builtinNameEntries, sizeof(builtinNameEntries)); + bool Shader::getConstant(const char *in, Language &out) { return languages.find(in, out); @@ -177,27 +688,5 @@ bool Shader::getConstant(BuiltinUniform in, const char *&out) return builtinNames.find(in, out); } -StringMap::Entry Shader::languageEntries[] = -{ - { "glsl1", LANGUAGE_GLSL1 }, - { "essl1", LANGUAGE_ESSL1 }, - { "glsl3", LANGUAGE_GLSL3 }, - { "essl3", LANGUAGE_ESSL3 }, -}; - -StringMap Shader::languages(Shader::languageEntries, sizeof(Shader::languageEntries)); - -StringMap::Entry Shader::builtinNameEntries[] = -{ - { "MainTex", BUILTIN_TEXTURE_MAIN }, - { "love_VideoYChannel", BUILTIN_TEXTURE_VIDEO_Y }, - { "love_VideoCbChannel", BUILTIN_TEXTURE_VIDEO_CB }, - { "love_VideoCrChannel", BUILTIN_TEXTURE_VIDEO_CR }, - { "love_UniformsPerDraw", BUILTIN_UNIFORMS_PER_DRAW }, - { "love_PointSize", BUILTIN_POINT_SIZE }, -}; - -StringMap Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries)); - } // graphics } // love diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index b31e56546..f6b9b2a08 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -50,9 +50,8 @@ public: enum Language { LANGUAGE_GLSL1, - LANGUAGE_ESSL1, LANGUAGE_GLSL3, - LANGUAGE_ESSL3, + LANGUAGE_GLSL4, LANGUAGE_MAX_ENUM }; @@ -89,6 +88,14 @@ public: STANDARD_MAX_ENUM }; + struct SourceInfo + { + Language language; + bool isStage[ShaderStage::STAGE_MAX_ENUM]; + bool customPixelFunction; + bool usesMRT; + }; + struct MatrixSize { short columns; @@ -187,11 +194,16 @@ public: void checkMainTextureType(TextureType textype, bool isDepthSampler) const; void checkMainTexture(Texture *texture) const; + static SourceInfo getSourceInfo(const std::string &src); + static std::string createShaderStageCode(Graphics *gfx, ShaderStage::StageType stage, const std::string &code, const SourceInfo &info); + static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err); static bool initialize(); static void deinitialize(); + static const std::string &getDefaultCode(StandardShader shader, ShaderStage::StageType stage); + static bool getConstant(const char *in, Language &out); static bool getConstant(Language in, const char *&out); @@ -202,15 +214,6 @@ protected: StrongRef stages[ShaderStage::STAGE_MAX_ENUM]; -private: - - static StringMap::Entry languageEntries[]; - static StringMap languages; - - // Names for the built-in uniform variables. - static StringMap::Entry builtinNameEntries[]; - static StringMap builtinNames; - }; // Shader } // graphics diff --git a/src/modules/graphics/ShaderStage.cpp b/src/modules/graphics/ShaderStage.cpp index b953c0038..8a3132cd5 100644 --- a/src/modules/graphics/ShaderStage.cpp +++ b/src/modules/graphics/ShaderStage.cpp @@ -202,6 +202,13 @@ bool ShaderStage::getConstant(StageType in, const char *&out) return stageNames.find(in, out); } +const char *ShaderStage::getConstant(StageType in) +{ + const char *name = nullptr; + getConstant(in, name); + return name; +} + StringMap::Entry ShaderStage::stageNameEntries[] = { { "vertex", STAGE_VERTEX }, diff --git a/src/modules/graphics/ShaderStage.h b/src/modules/graphics/ShaderStage.h index 95b441783..6465c31c5 100644 --- a/src/modules/graphics/ShaderStage.h +++ b/src/modules/graphics/ShaderStage.h @@ -44,6 +44,7 @@ class ShaderStage : public love::Object, public Volatile, public Resource { public: + // Order is used for stages array in ShaderStage.cpp enum StageType { STAGE_VERTEX, @@ -61,6 +62,7 @@ public: static bool getConstant(const char *in, StageType &out); static bool getConstant(StageType in, const char *&out); + static const char *getConstant(StageType in); protected: diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index d2fa4ab7f..99b28e0ed 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -194,6 +194,8 @@ Texture::Texture(const Settings &settings, const Slices *slices) love::image::ImageDataBase *slice = slices->get(0, 0); format = slice->getFormat(); + if (sRGB) + format = getSRGBPixelFormat(format); pixelWidth = slice->getWidth(); pixelHeight = slice->getHeight(); diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 8cd3a7546..77e4f49e1 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -252,12 +252,11 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b // Restore the graphics state. restoreState(states.back()); - int gammacorrect = isGammaCorrect() ? 1 : 0; - Shader::Language target = getShaderLanguageTarget(); - // We always need a default shader. for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) { + auto stype = (Shader::StandardShader) i; + if (i == Shader::STANDARD_ARRAY && !capabilities.textureTypes[TEXTURE_2D_ARRAY]) continue; @@ -267,8 +266,10 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b { if (!Shader::standardShaders[i]) { - const auto &code = defaultShaderCode[i][target][gammacorrect]; - Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]); + std::vector stages; + stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_VERTEX)); + stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_PIXEL)); + Shader::standardShaders[i] = newShader(stages); } } catch (love::Exception &) @@ -1501,18 +1502,6 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo return supported.value; } -Shader::Language Graphics::getShaderLanguageTarget() const -{ - if (gl.isCoreProfile()) - return Shader::LANGUAGE_GLSL3; - else if (GLAD_ES_VERSION_3_0) - return Shader::LANGUAGE_ESSL3; - else if (GLAD_ES_VERSION_2_0) - return Shader::LANGUAGE_ESSL1; - else - return Shader::LANGUAGE_GLSL1; -} - } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index d5e2e6bc7..d23934eb9 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -106,8 +106,6 @@ public: Renderer getRenderer() const override; RendererInfo getRendererInfo() const override; - Shader::Language getShaderLanguageTarget() const override; - // Internal use. void cleanupRenderTexture(love::graphics::Texture *texture); diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 215e33f35..fd28cac54 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -1061,7 +1061,7 @@ void OpenGL::setTextureUnit(int textureunit) state.curTextureUnit = textureunit; } -void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev) +void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit) { if (texture != state.boundTextures[target][textureunit]) { @@ -1077,9 +1077,14 @@ void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureun else state.curTextureUnit = textureunit; } + else if (bindforedit && !restoreprev && textureunit != state.curTextureUnit) + { + glActiveTexture(GL_TEXTURE0 + textureunit); + state.curTextureUnit = textureunit; + } } -void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev) +void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit) { TextureType textype = TEXTURE_2D; GLuint handle = 0; @@ -1101,7 +1106,7 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr handle = getDefaultTexture(textype); } - bindTextureToUnit(textype, handle, textureunit, restoreprev); + bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit); } void OpenGL::deleteTexture(GLuint texture) diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 01334f89c..63be58a34 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -318,9 +318,10 @@ public: * * @param textureunit Index in the range of [0, maxtextureunits-1] * @param restoreprev Restore previously bound texture unit when done. + * @param bindforedit If false, the active texture unit may be left alone. **/ - void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev); - void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev); + void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit = true); + void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit = true); /** * Helper for deleting an OpenGL texture. diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 9011445f0..f93f126b9 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -440,7 +440,7 @@ void Shader::attach() { const TextureUnit &unit = textureUnits[i]; if (unit.active) - gl.bindTextureToUnit(unit.type, unit.texture, i, false); + gl.bindTextureToUnit(unit.type, unit.texture, i, false, false); } // send any pending uniforms to the shader program. @@ -637,7 +637,7 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex int texunit = info->ints[i]; if (shaderactive) - gl.bindTextureToUnit(info->textureType, gltex, texunit, false); + gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false); // Store texture id so it can be re-bound to the texture unit later. textureUnits[texunit].texture = gltex; diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index bb659b83a..d32dd112e 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -45,11 +45,6 @@ static const char graphics_lua[] = #include "wrap_Graphics.lua" ; -// This is in a separate file because VS2013 has a 16KB limit for raw strings.. -static const char graphics_shader_lua[] = -#include "wrap_GraphicsShader.lua" -; - namespace love { namespace graphics @@ -748,15 +743,15 @@ static int w__pushNewTexture(lua_State *L, Texture::Slices *slices, const Textur static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool checkType, bool checkDimensions, OptionalBool forceRenderTarget, Texture::Settings &s, bool &setdpiscale) { setdpiscale = false; + if (forceRenderTarget.hasValue) + s.renderTarget = forceRenderTarget.value; if (opt && lua_isnoneornil(L, idx)) return; luax_checktablefields(L, idx, "texture setting name", Texture::getConstant); - if (forceRenderTarget.hasValue) - s.renderTarget = forceRenderTarget.value; - else + if (!forceRenderTarget.hasValue) s.renderTarget = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_RENDER_TARGET), s.renderTarget); lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_FORMAT)); @@ -1366,7 +1361,7 @@ int w_newParticleSystem(lua_State *L) return 1; } -static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string &vertexsource, std::string &pixelsource) +static int w_getShaderSource(lua_State *L, int startidx, std::vector &stages) { using namespace love::filesystem; @@ -1426,61 +1421,23 @@ static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string if (!(has_arg1 || has_arg2)) luaL_checkstring(L, startidx); - luax_getfunction(L, "graphics", "_shaderCodeToGLSL"); - - // push vertexcode and pixelcode strings to the top of the stack - lua_pushboolean(L, gles); - if (has_arg1) - lua_pushvalue(L, startidx + 0); - else - lua_pushnil(L); - + stages.push_back(luax_checkstring(L, startidx + 0)); if (has_arg2) - lua_pushvalue(L, startidx + 1); - else - lua_pushnil(L); - - // call effectCodeToGLSL, returned values will be at the top of the stack - if (lua_pcall(L, 3, 2, 0) != 0) - return luaL_error(L, "%s", lua_tostring(L, -1)); - - // vertex shader code - if (lua_isstring(L, -2)) - vertexsource = luax_checkstring(L, -2); - else if (has_arg1 && has_arg2) - return luaL_error(L, "Could not parse vertex shader code (missing 'position' function?)"); - - // pixel shader code - if (lua_isstring(L, -1)) - pixelsource = luax_checkstring(L, -1); - else if (has_arg1 && has_arg2) - return luaL_error(L, "Could not parse pixel shader code (missing 'effect' function?)"); - - if (vertexsource.empty() && pixelsource.empty()) - { - // Original args had source code, but effectCodeToGLSL couldn't translate it - for (int i = startidx; i < startidx + 2; i++) - { - if (lua_isstring(L, i)) - return luaL_argerror(L, i, "missing 'position' or 'effect' function?"); - } - } + stages.push_back(luax_checkstring(L, startidx + 1)); return 0; } int w_newShader(lua_State *L) { - bool gles = instance()->getRenderer() == Graphics::RENDERER_OPENGLES; - - std::string vertexsource, pixelsource; - w_getShaderSource(L, 1, gles, vertexsource, pixelsource); + std::vector stages; + w_getShaderSource(L, 1, stages); bool should_error = false; try { - Shader *shader = instance()->newShader(vertexsource, pixelsource); + Shader *shader = instance()->newShader(stages); luax_pushtype(L, shader); shader->release(); } @@ -1504,14 +1461,14 @@ int w_validateShader(lua_State *L) { bool gles = luax_checkboolean(L, 1); - std::string vertexsource, pixelsource; - w_getShaderSource(L, 2, gles, vertexsource, pixelsource); + std::vector stages; + w_getShaderSource(L, 2, stages); bool success = true; std::string err; try { - success = instance()->validateShader(gles, vertexsource, pixelsource, err); + success = instance()->validateShader(gles, stages, err); } catch (love::Exception &e) { @@ -2312,46 +2269,6 @@ int w_getShader(lua_State *L) return 1; } -int w_setDefaultShaderCode(lua_State *L) -{ - for (int i = 0; i < 2; i++) - { - luaL_checktype(L, i + 1, LUA_TTABLE); - - for (int lang = 0; lang < Shader::LANGUAGE_MAX_ENUM; lang++) - { - const char *langname; - if (!Shader::getConstant((Shader::Language) lang, langname)) - continue; - - lua_getfield(L, i + 1, langname); - - lua_getfield(L, -1, "vertex"); - lua_getfield(L, -2, "pixel"); - lua_getfield(L, -3, "videopixel"); - lua_getfield(L, -4, "arraypixel"); - - std::string vertex = luax_checkstring(L, -4); - std::string pixel = luax_checkstring(L, -3); - std::string videopixel = luax_checkstring(L, -2); - std::string arraypixel = luax_checkstring(L, -1); - - lua_pop(L, 5); - - Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex; - Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_PIXEL] = pixel; - - Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex; - Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_PIXEL] = videopixel; - - Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex; - Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_PIXEL] = arraypixel; - } - } - - return 0; -} - int w_getSupported(lua_State *L) { const Graphics::Capabilities &caps = instance()->getCapabilities(); @@ -3226,7 +3143,6 @@ static const luaL_Reg functions[] = { "setShader", w_setShader }, { "getShader", w_getShader }, - { "_setDefaultShaderCode", w_setDefaultShaderCode }, { "getSupported", w_getSupported }, { "getTextureFormats", w_getTextureFormats }, @@ -3343,11 +3259,6 @@ extern "C" int luaopen_love_graphics(lua_State *L) else lua_error(L); - if (luaL_loadbuffer(L, (const char *)graphics_shader_lua, sizeof(graphics_shader_lua), "wrap_GraphicsShader.lua") == 0) - lua_call(L, 0, 0); - else - lua_error(L); - return n; } diff --git a/src/modules/graphics/wrap_GraphicsShader.lua b/src/modules/graphics/wrap_GraphicsShader.lua deleted file mode 100644 index f0631c6f6..000000000 --- a/src/modules/graphics/wrap_GraphicsShader.lua +++ /dev/null @@ -1,503 +0,0 @@ -R"luastring"--( --- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string. --- There is a matching delimiter at the bottom of the file. - ---[[ -Copyright (c) 2006-2020 LOVE Development Team - -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. ---]] - -local table_concat, table_insert = table.concat, table.insert -local ipairs = ipairs - -local GLSL = {} - -GLSL.VERSION = { -- index using [target][gles] - glsl1 = {[false]="#version 120", [true]="#version 100"}, - glsl3 = {[false]="#version 330 core", [true]="#version 300 es"}, - glsl4 = {[false]="#version 430 core", [true]="#version 310 es"}, -} - -GLSL.SYNTAX = [[ -#if !defined(GL_ES) && __VERSION__ < 140 - #define lowp - #define mediump - #define highp -#endif -#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) - #define LOVE_HIGHP_OR_MEDIUMP highp -#else - #define LOVE_HIGHP_OR_MEDIUMP mediump -#endif -#define number float -#define Image sampler2D -#define ArrayImage sampler2DArray -#define CubeImage samplerCube -#define VolumeImage sampler3D -#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3) - #define DepthImage sampler2DShadow - #define DepthArrayImage sampler2DArrayShadow - #define DepthCubeImage samplerCubeShadow -#endif -#define extern uniform -#ifdef GL_EXT_texture_array -#extension GL_EXT_texture_array : enable -#endif -#ifdef GL_OES_texture_3D -#extension GL_OES_texture_3D : enable -#endif -#ifdef GL_OES_standard_derivatives -#extension GL_OES_standard_derivatives : enable -#endif -]] - --- Uniforms shared by the vertex and pixel shader stages. -GLSL.UNIFORMS = [[ -// According to the GLSL ES 1.0 spec, uniform precision must match between stages, -// but we can't guarantee that highp is always supported in fragment shaders... -// We *really* don't want to use mediump for these in vertex shaders though. -uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13]; - -// These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't -// do it as an initializer. -LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix; -LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix; -LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix; - -LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize; -LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor; - -#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix) - -// Alternate names -#define ViewSpaceFromLocal TransformMatrix -#define ClipSpaceFromView ProjectionMatrix -#define ClipSpaceFromLocal TransformProjectionMatrix -#define ViewNormalFromLocal NormalMatrix - -void love_initializeBuiltinUniforms() { - TransformMatrix = mat4( - love_UniformsPerDraw[0], - love_UniformsPerDraw[1], - love_UniformsPerDraw[2], - love_UniformsPerDraw[3] - ); - - ProjectionMatrix = mat4( - love_UniformsPerDraw[4], - love_UniformsPerDraw[5], - love_UniformsPerDraw[6], - love_UniformsPerDraw[7] - ); - - NormalMatrix = mat3( - love_UniformsPerDraw[8].xyz, - love_UniformsPerDraw[9].xyz, - love_UniformsPerDraw[10].xyz - ); - - love_ScreenSize = love_UniformsPerDraw[11]; - ConstantColor = love_UniformsPerDraw[12]; -} -]] - -GLSL.FUNCTIONS = [[ -#ifdef GL_ES - #if __VERSION__ >= 300 || defined(GL_EXT_texture_array) - precision lowp sampler2DArray; - #endif - #if __VERSION__ >= 300 || defined(GL_OES_texture_3D) - precision lowp sampler3D; - #endif - #if __VERSION__ >= 300 - precision lowp sampler2DShadow; - precision lowp samplerCubeShadow; - precision lowp sampler2DArrayShadow; - #endif -#endif - -#if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3) - #define Texel texture -#else - #if __VERSION__ >= 130 - #define texture2D Texel - #define texture3D Texel - #define textureCube Texel - #define texture2DArray Texel - #define love_texture2D texture - #define love_texture3D texture - #define love_textureCube texture - #define love_texture2DArray texture - #else - #define love_texture2D texture2D - #define love_texture3D texture3D - #define love_textureCube textureCube - #define love_texture2DArray texture2DArray - #endif - vec4 Texel(sampler2D s, vec2 c) { return love_texture2D(s, c); } - vec4 Texel(samplerCube s, vec3 c) { return love_textureCube(s, c); } - #if __VERSION__ > 100 || defined(GL_OES_texture_3D) - vec4 Texel(sampler3D s, vec3 c) { return love_texture3D(s, c); } - #endif - #if __VERSION__ >= 130 || defined(GL_EXT_texture_array) - vec4 Texel(sampler2DArray s, vec3 c) { return love_texture2DArray(s, c); } - #endif - #ifdef PIXEL - vec4 Texel(sampler2D s, vec2 c, float b) { return love_texture2D(s, c, b); } - vec4 Texel(samplerCube s, vec3 c, float b) { return love_textureCube(s, c, b); } - #if __VERSION__ > 100 || defined(GL_OES_texture_3D) - vec4 Texel(sampler3D s, vec3 c, float b) { return love_texture3D(s, c, b); } - #endif - #if __VERSION__ >= 130 || defined(GL_EXT_texture_array) - vec4 Texel(sampler2DArray s, vec3 c, float b) { return love_texture2DArray(s, c, b); } - #endif - #endif - #define texture love_texture -#endif - -float gammaToLinearPrecise(float c) { - return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4); -} -vec3 gammaToLinearPrecise(vec3 c) { - bvec3 leq = lessThanEqual(c, vec3(0.04045)); - c.r = leq.r ? c.r / 12.92 : pow((c.r + 0.055) / 1.055, 2.4); - c.g = leq.g ? c.g / 12.92 : pow((c.g + 0.055) / 1.055, 2.4); - c.b = leq.b ? c.b / 12.92 : pow((c.b + 0.055) / 1.055, 2.4); - return c; -} -vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); } -float linearToGammaPrecise(float c) { - return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055; -} -vec3 linearToGammaPrecise(vec3 c) { - bvec3 lt = lessThanEqual(c, vec3(0.0031308)); - c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055; - c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055; - c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055; - return c; -} -vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); } - -// http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1 - -mediump float gammaToLinearFast(mediump float c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); } -mediump vec3 gammaToLinearFast(mediump vec3 c) { return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); } -mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); } - -mediump float linearToGammaFast(mediump float c) { return max(1.055 * pow(max(c, 0.0), 0.41666666) - 0.055, 0.0); } -mediump vec3 linearToGammaFast(mediump vec3 c) { return max(1.055 * pow(max(c, vec3(0.0)), vec3(0.41666666)) - 0.055, vec3(0.0)); } -mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); } - -#define gammaToLinear gammaToLinearFast -#define linearToGamma linearToGammaFast - -#ifdef LOVE_GAMMA_CORRECT - #define gammaCorrectColor gammaToLinear - #define unGammaCorrectColor linearToGamma - #define gammaCorrectColorPrecise gammaToLinearPrecise - #define unGammaCorrectColorPrecise linearToGammaPrecise - #define gammaCorrectColorFast gammaToLinearFast - #define unGammaCorrectColorFast linearToGammaFast -#else - #define gammaCorrectColor - #define unGammaCorrectColor - #define gammaCorrectColorPrecise - #define unGammaCorrectColorPrecise - #define gammaCorrectColorFast - #define unGammaCorrectColorFast -#endif]] - -GLSL.VERTEX = { - HEADER = [[ -#define love_Position gl_Position - -#if __VERSION__ >= 130 - #define attribute in - #define varying out - #ifndef LOVE_GLSL1_ON_GLSL3 - #define love_VertexID gl_VertexID - #define love_InstanceID gl_InstanceID - #endif -#endif - -#ifdef GL_ES - uniform mediump float love_PointSize; -#endif]], - - FUNCTIONS = [[ -void setPointSize() { -#ifdef GL_ES - gl_PointSize = love_PointSize; -#endif -}]], - - MAIN = [[ -attribute vec4 VertexPosition; -attribute vec4 VertexTexCoord; -attribute vec4 VertexColor; - -varying vec4 VaryingTexCoord; -varying vec4 VaryingColor; - -vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition); - -void main() { - love_initializeBuiltinUniforms(); - VaryingTexCoord = VertexTexCoord; - VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor; - setPointSize(); - love_Position = position(ClipSpaceFromLocal, VertexPosition); -}]], -} - -GLSL.PIXEL = { - HEADER = [[ -#ifdef GL_ES - precision mediump float; -#endif - -#define love_MaxRenderTargets gl_MaxDrawBuffers - -#if __VERSION__ >= 130 - #define varying in - // Some drivers seem to make the pixel shader do more work when multiple - // pixel shader outputs are defined, even when only one is actually used. - // TODO: We should use reflection or something instead of this, to determine - // how many outputs are actually used in the shader code. - #ifdef LOVE_MULTI_RENDER_TARGETS - layout(location = 0) out vec4 love_RenderTargets[love_MaxRenderTargets]; - #define love_PixelColor love_RenderTargets[0] - #else - layout(location = 0) out vec4 love_PixelColor; - #endif -#else - #ifdef LOVE_MULTI_RENDER_TARGETS - #define love_RenderTargets gl_FragData - #endif - #define love_PixelColor gl_FragColor -#endif - -// Legacy -#define love_MaxCanvases love_MaxRenderTargets -#define love_Canvases love_RenderTargets -#ifdef LOVE_MULTI_RENDER_TARGETS -#define LOVE_MULTI_CANVASES 1 -#endif - -// See Shader::updateScreenParams in Shader.cpp. -#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))]], - - FUNCTIONS = [[ -uniform sampler2D love_VideoYChannel; -uniform sampler2D love_VideoCbChannel; -uniform sampler2D love_VideoCrChannel; - -vec4 VideoTexel(vec2 texcoords) { - vec3 yuv; - yuv[0] = Texel(love_VideoYChannel, texcoords).r; - yuv[1] = Texel(love_VideoCbChannel, texcoords).r; - yuv[2] = Texel(love_VideoCrChannel, texcoords).r; - yuv += vec3(-0.0627451017, -0.501960814, -0.501960814); - - vec4 color; - color.r = dot(yuv, vec3(1.164, 0.000, 1.596)); - color.g = dot(yuv, vec3(1.164, -0.391, -0.813)); - color.b = dot(yuv, vec3(1.164, 2.018, 0.000)); - color.a = 1.0; - - return gammaCorrectColor(color); -}]], - - MAIN = [[ -uniform sampler2D MainTex; -varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord; -varying mediump vec4 VaryingColor; - -vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord); - -void main() { - love_initializeBuiltinUniforms(); - love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord); -}]], - - MAIN_CUSTOM = [[ -varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord; -varying mediump vec4 VaryingColor; - -void effect(); - -void main() { - love_initializeBuiltinUniforms(); - effect(); -}]], -} - -local function getLanguageTarget(code) - if not code then return nil end - return (code:match("^%s*#pragma language (%w+)")) or "glsl1" -end - -local function createShaderStageCode(stage, code, lang, gles, glsl1on3, gammacorrect, custom, multirendertarget) - stage = stage:upper() - local lines = { - GLSL.VERSION[lang][gles], - "#define " ..stage .. " " .. stage, - glsl1on3 and "#define LOVE_GLSL1_ON_GLSL3 1" or "", - gammacorrect and "#define LOVE_GAMMA_CORRECT 1" or "", - multirendertarget and "#define LOVE_MULTI_RENDER_TARGETS 1" or "", - GLSL.SYNTAX, - GLSL[stage].HEADER, - GLSL.UNIFORMS, - GLSL.FUNCTIONS, - GLSL[stage].FUNCTIONS, - custom and GLSL[stage].MAIN_CUSTOM or GLSL[stage].MAIN, - ((lang == "glsl1" or glsl1on3) and not gles) and "#line 0" or "#line 1", - code, - } - return table_concat(lines, "\n") -end - -local function isVertexCode(code) - return code:match("vec4%s+position%s*%(") ~= nil -end - -local function isPixelCode(code) - if code:match("vec4%s+effect%s*%(") then - return true - elseif code:match("void%s+effect%s*%(") then -- custom effect function - local multirendertargets = (code:match("love_RenderTargets") ~= nil) or (code:match("love_Canvases") ~= nil) - return true, true, multirendertargets - else - return false - end -end - -function love.graphics._shaderCodeToGLSL(gles, arg1, arg2) - local vertexcode, pixelcode - local is_custompixel = false -- whether pixel code has "effects" function instead of "effect" - local is_multicanvas = false - - if arg1 then - if isVertexCode(arg1) then - vertexcode = arg1 -- first arg contains vertex shader code - end - - local ispixel, isCustomPixel, isMultiCanvas = isPixelCode(arg1) - if ispixel then - pixelcode = arg1 -- first arg contains pixel shader code - is_custompixel, is_multicanvas = isCustomPixel, isMultiCanvas - end - end - - if arg2 then - if isVertexCode(arg2) then - vertexcode = arg2 -- second arg contains vertex shader code - end - - local ispixel, isCustomPixel, isMultiCanvas = isPixelCode(arg2) - if ispixel then - pixelcode = arg2 -- second arg contains pixel shader code - is_custompixel, is_multicanvas = isCustomPixel, isMultiCanvas - end - end - - local graphicsfeatures = love.graphics.getSupported() - local supportsGLSL3 = graphicsfeatures.glsl3 - local supportsGLSL4 = graphicsfeatures.glsl4 - local gammacorrect = love.graphics.isGammaCorrect() - - local targetlang = getLanguageTarget(pixelcode or vertexcode) - if getLanguageTarget(vertexcode or pixelcode) ~= targetlang then - error("vertex and pixel shader languages must match", 2) - end - - if targetlang == "glsl3" and not supportsGLSL3 then - error("GLSL 3 shaders are not supported on this system.", 2) - end - - if targetlang == "glsl4" and not supportsGLSL4 then - error("GLSL 4 shaders are not supported on this system.", 2) - end - - if targetlang ~= nil and not GLSL.VERSION[targetlang] then - error("Invalid shader language: " .. targetlang, 2) - end - - local lang = targetlang or "glsl1" - local glsl1on3 = false - if lang == "glsl1" and supportsGLSL3 then - lang = "glsl3" - glsl1on3 = true - end - - if vertexcode then - vertexcode = createShaderStageCode("VERTEX", vertexcode, lang, gles, glsl1on3, gammacorrect) - end - if pixelcode then - pixelcode = createShaderStageCode("PIXEL", pixelcode, lang, gles, glsl1on3, gammacorrect, is_custompixel, is_multicanvas) - end - - return vertexcode, pixelcode -end - -local defaultcode = { - vertex = [[ -vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition) { - return clipSpaceFromLocal * localPosition; -}]], - pixel = [[ -vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) { - return Texel(tex, texcoord) * vcolor; -}]], - videopixel = [[ -void effect() { - love_PixelColor = VideoTexel(VaryingTexCoord.xy) * VaryingColor; -}]], - arraypixel = [[ -uniform ArrayImage MainTex; -void effect() { - love_PixelColor = Texel(MainTex, VaryingTexCoord.xyz) * VaryingColor; -}]], -} - -local defaults = {} -local defaults_gammacorrect = {} - -local langs = { - glsl1 = {target="glsl1", gles=false}, - essl1 = {target="glsl1", gles=true}, - glsl3 = {target="glsl3", gles=false}, - essl3 = {target="glsl3", gles=true}, -} - -for lang, info in pairs(langs) do - for _, gammacorrect in ipairs{false, true} do - local t = gammacorrect and defaults_gammacorrect or defaults - t[lang] = { - vertex = createShaderStageCode("VERTEX", defaultcode.vertex, info.target, info.gles, false, gammacorrect), - pixel = createShaderStageCode("PIXEL", defaultcode.pixel, info.target, info.gles, false, gammacorrect, false), - videopixel = createShaderStageCode("PIXEL", defaultcode.videopixel, info.target, info.gles, false, gammacorrect, true), - arraypixel = createShaderStageCode("PIXEL", defaultcode.arraypixel, info.target, info.gles, false, gammacorrect, true), - } - end -end - -love.graphics._setDefaultShaderCode(defaults, defaults_gammacorrect) - --- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. ---)luastring"--" diff --git a/src/modules/image/magpie/STBHandler.cpp b/src/modules/image/magpie/STBHandler.cpp index 7046b7682..76b083f7c 100644 --- a/src/modules/image/magpie/STBHandler.cpp +++ b/src/modules/image/magpie/STBHandler.cpp @@ -30,12 +30,13 @@ static void loveSTBIAssert(bool test, const char *teststr) } // stb_image - #define STBI_ONLY_JPEG +#define STBI_ONLY_JPEG // #define STBI_ONLY_PNG #define STBI_ONLY_BMP #define STBI_ONLY_TGA #define STBI_ONLY_HDR #define STBI_NO_STDIO +#define STB_IMAGE_STATIC #define STB_IMAGE_IMPLEMENTATION #define STBI_ASSERT(A) loveSTBIAssert((A), #A) #include "libraries/stb/stb_image.h" diff --git a/src/modules/timer/Timer.cpp b/src/modules/timer/Timer.cpp index e9746e201..c2d0a5cdd 100644 --- a/src/modules/timer/Timer.cpp +++ b/src/modules/timer/Timer.cpp @@ -24,6 +24,7 @@ #include "common/delay.h" #include "Timer.h" +#include #if defined(LOVE_WINDOWS) #include #elif defined(LOVE_MACOS) || defined(LOVE_IOS) @@ -35,15 +36,6 @@ #include #endif -#if defined(LOVE_LINUX) -static inline double getTimeOfDay() -{ - timeval t; - gettimeofday(&t, NULL); - return (double) t.tv_sec + (double) t.tv_usec / 1000000.0; -} -#endif - namespace love { namespace timer @@ -109,52 +101,91 @@ double Timer::getAverageDelta() const return averageDelta; } -double Timer::getTimerPeriod() +#if defined(LOVE_LINUX) + +static inline timespec getTimeOfDay() { -#if defined(LOVE_MACOS) || defined(LOVE_IOS) - mach_timebase_info_data_t info; - mach_timebase_info(&info); - return (double) info.numer / (double) info.denom / 1000000000.0; -#elif defined(LOVE_WINDOWS) - LARGE_INTEGER temp; - if (QueryPerformanceFrequency(&temp) != 0 && temp.QuadPart != 0) - return 1.0 / (double) temp.QuadPart; -#endif - return 0; + timeval t; + gettimeofday(&t, NULL); + return timespec { t.tv_sec, t.tv_usec * 1000 }; } -double Timer::getTime() +static timespec getTimeAbsolute() { - // The timer period (reciprocal of the frequency.) - static const double timerPeriod = getTimerPeriod(); - -#if defined(LOVE_LINUX) - (void) timerPeriod; // Unused on linux - - double mt; // Check for POSIX timers and monotonic clocks. If not supported, use the gettimeofday fallback. #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) \ && (defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC)) - timespec t; + #ifdef CLOCK_MONOTONIC_RAW clockid_t clk_id = CLOCK_MONOTONIC_RAW; #else clockid_t clk_id = CLOCK_MONOTONIC; #endif + + timespec t; if (clock_gettime(clk_id, &t) == 0) - mt = (double) t.tv_sec + (double) t.tv_nsec / 1000000000.0; + return t; else + return getTimeOfDay(); #endif - mt = getTimeOfDay(); - return mt; -#elif defined(LOVE_MACOS) || defined(LOVE_IOS) - return (double) mach_absolute_time() * timerPeriod; -#elif defined(LOVE_WINDOWS) - LARGE_INTEGER microTime; - QueryPerformanceCounter(µTime); - return (double) microTime.QuadPart * timerPeriod; -#endif + return getTimeOfDay(); } +double Timer::getTime() +{ + static const timespec start = getTimeAbsolute(); + const timespec now = getTimeAbsolute(); + // tv_sec and tv_nsec should be signed on POSIX, so we are fine in just subtracting here. + const long sec = now.tv_sec - start.tv_sec; + const long nsec = now.tv_nsec - start.tv_nsec; + return (double) sec + (double) nsec / 1.0e9; +} + +#elif defined(LOVE_MACOS) || defined(LOVE_IOS) + +static mach_timebase_info_data_t getTimebaseInfo() +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + return info; +} + +double Timer::getTime() +{ + static const mach_timebase_info_data_t info = getTimebaseInfo(); + static const uint64_t start = mach_absolute_time(); + const uint64_t rel = mach_absolute_time() - start; + return ((double) rel * 1.0e-9) * (double) info.numer / (double) info.denom; +} + +#elif defined(LOVE_WINDOWS) + +static LARGE_INTEGER getTimeAbsolute() +{ + LARGE_INTEGER t; + QueryPerformanceCounter(&t); + return t; +} + +static LARGE_INTEGER getFrequency() +{ + LARGE_INTEGER freq; + // "On systems that run Windows XP or later, the function will always succeed and will thus never return zero." + QueryPerformanceFrequency(&freq); + return freq; +} + +double Timer::getTime() +{ + static const LARGE_INTEGER freq = getFrequency(); + static const LARGE_INTEGER start = getTimeAbsolute(); + const LARGE_INTEGER now = getTimeAbsolute(); + LARGE_INTEGER rel; + rel.QuadPart = now.QuadPart - start.QuadPart; + return (double) rel.QuadPart / (double) freq.QuadPart; +} + +#endif + } // timer } // love diff --git a/src/modules/timer/Timer.h b/src/modules/timer/Timer.h index 86eeae454..cfd9ab180 100644 --- a/src/modules/timer/Timer.h +++ b/src/modules/timer/Timer.h @@ -72,9 +72,11 @@ public: double getAverageDelta() const; /** - * Gets the amount of time passed since an unspecified time. Useful for - * profiling code or measuring intervals. The time is microsecond-precise, - * and increases monotonically. + * Gets the amount of time in seconds passed since its first invocation + * (which happens as part of the Timer constructor, + * which is called when the module is first opened). + * Useful for profiling code or measuring intervals. + * The time is microsecond-precise, and increases monotonically. * @return The time (in seconds) **/ static double getTime(); @@ -99,9 +101,6 @@ private: // The current timestep. double dt; - // Returns the timer period on some platforms. - static double getTimerPeriod(); - }; // Timer } // timer diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index 6c299ce6f..b750cd7d1 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -532,6 +532,7 @@ function love.init() -- Setup window here. if c.window and c.modules.window then + love.window.setTitle(c.window.title or c.title) assert(love.window.setMode(c.window.width, c.window.height, { fullscreen = c.window.fullscreen, @@ -551,7 +552,6 @@ function love.init() x = c.window.x, y = c.window.y, }), "Could not set window mode") - love.window.setTitle(c.window.title or c.title) if c.window.icon then assert(love.image, "If an icon is set in love.conf, love.image must be loaded!") love.window.setIcon(love.image.newImageData(c.window.icon)) diff --git a/src/scripts/boot.lua.h b/src/scripts/boot.lua.h index 632c0d183..56e992d1d 100644 --- a/src/scripts/boot.lua.h +++ b/src/scripts/boot.lua.h @@ -1055,6 +1055,9 @@ const unsigned char boot_lua[] = 0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54, + 0x69, 0x74, 0x6c, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x69, 0x74, 0x6c, + 0x65, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x2e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x29, 0x0a, 0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, @@ -1094,9 +1097,6 @@ const unsigned char boot_lua[] = 0x09, 0x09, 0x09, 0x79, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x79, 0x2c, 0x0a, 0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x74, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54, - 0x69, 0x74, 0x6c, 0x65, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x69, 0x74, 0x6c, - 0x65, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x2e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x29, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x69, 0x63, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x69, 0x6d, 0x61,