From f6e9277a6d9f579d85bc1928516a2a2feaf6c60b Mon Sep 17 00:00:00 2001 From: slime Date: Fri, 30 Sep 2022 15:17:46 -0300 Subject: [PATCH 1/8] macOS: fix colors appearing oversaturated on P3 displays. The fix only applies to macOS 11+ when using OpenGL. --- src/common/macosx.h | 8 ++++++++ src/common/macosx.mm | 21 +++++++++++++++++++-- src/modules/window/sdl/Window.cpp | 4 ++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/common/macosx.h b/src/common/macosx.h index f473ba331..00769aece 100644 --- a/src/common/macosx.h +++ b/src/common/macosx.h @@ -27,6 +27,8 @@ #include +typedef struct SDL_Window SDL_Window; + namespace love { namespace macosx @@ -55,6 +57,12 @@ std::string getExecutablePath(); **/ void requestAttention(bool continuous); +/** + * Explicitly sets the window's color space to be sRGB - which stops the OS + * from interpreting the backbuffer output as P3 on P3-capable displays. + **/ +void setWindowSRGBColorSpace(SDL_Window *window); + } // macosx } // love diff --git a/src/common/macosx.mm b/src/common/macosx.mm index dc491f898..1fa3e343c 100644 --- a/src/common/macosx.mm +++ b/src/common/macosx.mm @@ -26,9 +26,11 @@ #import #ifdef LOVE_MACOSX_SDL_DIRECT_INCLUDE -# include +#include +#include #else -# include +#include +#include #endif namespace love @@ -93,6 +95,21 @@ void requestAttention(bool continuous) } } +void setWindowSRGBColorSpace(SDL_Window *window) +{ + @autoreleasepool + { + // This works on earlier macOS versions, but performance may be worse + // (at least, it was back when I tested in December 2016). + if (@available(macOS 11.0, *)) + { + SDL_SysWMinfo info = {}; + if (SDL_GetWindowWMInfo(window, &info)) + info.info.cocoa.window.colorSpace = [NSColorSpace sRGBColorSpace]; + } + } +} + } // osx } // love diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index a6cf71db6..767a3fc56 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -311,6 +311,10 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla return false; } +#ifdef LOVE_MACOS + love::macos::setWindowSRGBColorSpace(window); +#endif + context = SDL_GL_CreateContext(window); if (!context) From 7e0f8ca791de6d2786d8f44ca8a2e4c0c8d92215 Mon Sep 17 00:00:00 2001 From: Klonan <11986037+Klonan@users.noreply.github.com> Date: Fri, 16 Sep 2022 11:00:19 +0200 Subject: [PATCH 2/8] FIx polyline adding NaN normals, which breaks the line rendering --- src/modules/graphics/Polyline.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/modules/graphics/Polyline.cpp b/src/modules/graphics/Polyline.cpp index 61a5b191c..f3dd603f2 100644 --- a/src/modules/graphics/Polyline.cpp +++ b/src/modules/graphics/Polyline.cpp @@ -24,6 +24,7 @@ // C++ #include +#include // treat adjacent segments with angles between their directions <5 degree as straight static const float LINES_PARALLEL_EPS = 0.05f; @@ -176,6 +177,12 @@ void MiterJoinPolyline::renderEdge(std::vector &anchors, std::vector &anchors, std::vector 0) { // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2 + assert(ns == ns); //NaN check normals.push_back(ns); normals.push_back(-ns); } @@ -193,6 +201,7 @@ void MiterJoinPolyline::renderEdge(std::vector &anchors, std::vector Date: Sun, 30 Oct 2022 12:30:42 -0300 Subject: [PATCH 3/8] Remove NaN-check C assert calls in line drawing code. Users can pass NaN point values into line drawing code. love shouldn't abort the entire program if that happens. --- src/modules/graphics/Polyline.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/modules/graphics/Polyline.cpp b/src/modules/graphics/Polyline.cpp index f3dd603f2..d5184823d 100644 --- a/src/modules/graphics/Polyline.cpp +++ b/src/modules/graphics/Polyline.cpp @@ -24,7 +24,6 @@ // C++ #include -#include // treat adjacent segments with angles between their directions <5 degree as straight static const float LINES_PARALLEL_EPS = 0.05f; @@ -192,7 +191,6 @@ void MiterJoinPolyline::renderEdge(std::vector &anchors, std::vector 0) { // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2 - assert(ns == ns); //NaN check normals.push_back(ns); normals.push_back(-ns); } @@ -201,7 +199,6 @@ void MiterJoinPolyline::renderEdge(std::vector &anchors, std::vector Date: Sat, 14 Jan 2023 12:13:35 -0400 Subject: [PATCH 4/8] Improve variable name readability in line rendering code. --- src/modules/graphics/Polyline.cpp | 138 +++++++++++++++--------------- src/modules/graphics/Polyline.h | 20 ++--- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/modules/graphics/Polyline.cpp b/src/modules/graphics/Polyline.cpp index d5184823d..1188fa15c 100644 --- a/src/modules/graphics/Polyline.cpp +++ b/src/modules/graphics/Polyline.cpp @@ -49,26 +49,26 @@ void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, flo // compute sleeve bool is_looping = (coords[0] == coords[count - 1]); - Vector2 s; + Vector2 segment; if (!is_looping) // virtual starting point at second point mirrored on first point - s = coords[1] - coords[0]; + segment = coords[1] - coords[0]; else // virtual starting point at last vertex - s = coords[0] - coords[count - 2]; + segment = coords[0] - coords[count - 2]; - float len_s = s.getLength(); - Vector2 ns = s.getNormal(halfwidth / len_s); + float segmentLength = segment.getLength(); + Vector2 segmentNormal = segment.getNormal(halfwidth / segmentLength); - Vector2 q, r(coords[0]); + Vector2 pointA, pointB(coords[0]); for (size_t i = 0; i + 1 < count; i++) { - q = r; - r = coords[i + 1]; - renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth); + pointA = pointB; + pointB = coords[i + 1]; + renderEdge(anchors, normals, segment, segmentLength, segmentNormal, pointA, pointB, halfwidth); } - q = r; - r = is_looping ? coords[1] : r + s; - renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth); + pointA = pointB; + pointB = is_looping ? coords[1] : pointB + segment; + renderEdge(anchors, normals, segment, segmentLength, segmentNormal, pointA, pointB, halfwidth); vertex_count = normals.size(); @@ -108,8 +108,8 @@ void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, flo } void NoneJoinPolyline::renderEdge(std::vector &anchors, std::vector &normals, - Vector2 &s, float &len_s, Vector2 &ns, - const Vector2 &q, const Vector2 &r, float hw) + Vector2 &segment, float &segmentLength, Vector2 &segmentNormal, + const Vector2 &pointA, const Vector2 &pointB, float halfWidth) { // ns1------ns2 // | | @@ -117,19 +117,19 @@ void NoneJoinPolyline::renderEdge(std::vector &anchors, std::vector &anchors, std::vector &anchors, std::vector &normals, - Vector2 &s, float &len_s, Vector2 &ns, - const Vector2 &q, const Vector2 &r, float hw) + Vector2 &segment, float &segmentLength, Vector2 &segmentNormal, + const Vector2 &pointA, const Vector2 &pointB, float halfwidth) { - Vector2 t = (r - q); - float len_t = t.getLength(); - if (len_t == 0.0f) + Vector2 newSegment = (pointB - pointA); + float newSegmentLength = newSegment.getLength(); + if (newSegmentLength == 0.0f) { // degenerate segment, skip it return; } - Vector2 nt = t.getNormal(hw / len_t); + Vector2 newSegmentNormal = newSegment.getNormal(halfwidth / newSegmentLength); - anchors.push_back(q); - anchors.push_back(q); + anchors.push_back(pointA); + anchors.push_back(pointA); - float det = Vector2::cross(s, t); - if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && Vector2::dot(s, t) > 0) + float det = Vector2::cross(segment, newSegment); + if (fabs(det) / (segmentLength * newSegmentLength) < LINES_PARALLEL_EPS && Vector2::dot(segment, newSegment) > 0) { // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2 - normals.push_back(ns); - normals.push_back(-ns); + normals.push_back(segmentNormal); + normals.push_back(-segmentNormal); } else { // cramers rule - float lambda = Vector2::cross((nt - ns), t) / det; - Vector2 d = ns + s * lambda; + float lambda = Vector2::cross((newSegmentNormal - segmentNormal), newSegment) / det; + Vector2 d = segmentNormal + segment * lambda; normals.push_back(d); normals.push_back(-d); } - s = t; - ns = nt; - len_s = len_t; + segment = newSegment; + segmentNormal = newSegmentNormal; + segmentLength = newSegmentLength; } /** Calculate line boundary points. @@ -226,52 +226,52 @@ void MiterJoinPolyline::renderEdge(std::vector &anchors, std::vector &anchors, std::vector &normals, - Vector2 &s, float &len_s, Vector2 &ns, - const Vector2 &q, const Vector2 &r, float hw) + Vector2 &segment, float &segmentLength, Vector2 &segmentNormal, + const Vector2 &pointA, const Vector2 &pointB, float halfWidth) { - Vector2 t = (r - q); - float len_t = t.getLength(); + Vector2 newSegment = (pointB - pointA); + float newSegmentLength = newSegment.getLength(); - float det = Vector2::cross(s, t); - if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && Vector2::dot(s, t) > 0) + float det = Vector2::cross(segment, newSegment); + if (fabs(det) / (segmentLength * newSegmentLength) < LINES_PARALLEL_EPS && Vector2::dot(segment, newSegment) > 0) { // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2 - Vector2 n = t.getNormal(hw / len_t); - anchors.push_back(q); - anchors.push_back(q); - normals.push_back(n); - normals.push_back(-n); - s = t; - len_s = len_t; + Vector2 newSegmentNormal = newSegment.getNormal(halfWidth / newSegmentLength); + anchors.push_back(pointA); + anchors.push_back(pointA); + normals.push_back(newSegmentNormal); + normals.push_back(-newSegmentNormal); + segment = newSegment; + newSegmentLength = newSegmentLength; return; // early out } // cramers rule - Vector2 nt = t.getNormal(hw / len_t); - float lambda = Vector2::cross((nt - ns), t) / det; - Vector2 d = ns + s * lambda; + Vector2 newSegmentNormal = newSegment.getNormal(halfWidth / newSegmentLength); + float lambda = Vector2::cross((newSegmentNormal - segmentNormal), newSegment) / det; + Vector2 d = segmentNormal + segment * lambda; - anchors.push_back(q); - anchors.push_back(q); - anchors.push_back(q); - anchors.push_back(q); + anchors.push_back(pointA); + anchors.push_back(pointA); + anchors.push_back(pointA); + anchors.push_back(pointA); if (det > 0) // 'left' turn -> intersection on the top { normals.push_back(d); - normals.push_back(-ns); + normals.push_back(-segmentNormal); normals.push_back(d); - normals.push_back(-nt); + normals.push_back(-newSegmentNormal); } else { - normals.push_back(ns); + normals.push_back(segmentNormal); normals.push_back(-d); - normals.push_back(nt); + normals.push_back(newSegmentNormal); normals.push_back(-d); } - s = t; - len_s = len_t; - ns = nt; + segment = newSegment; + segmentLength = newSegmentLength; + segmentNormal = newSegmentNormal; } void Polyline::calc_overdraw_vertex_count(bool is_looping) diff --git a/src/modules/graphics/Polyline.h b/src/modules/graphics/Polyline.h index 802df1400..b0f36321c 100644 --- a/src/modules/graphics/Polyline.h +++ b/src/modules/graphics/Polyline.h @@ -77,18 +77,18 @@ protected: /** Calculate line boundary points. * - * @param[out] anchors Anchor points defining the core line. - * @param[out] normals Normals defining the edge of the sleeve. - * @param[in,out] s Direction of segment pq (updated to the segment qr). - * @param[in,out] len_s Length of segment pq (updated to the segment qr). - * @param[in,out] ns Normal on the segment pq (updated to the segment qr). - * @param[in] q Current point on the line. - * @param[in] r Next point on the line. - * @param[in] hw Half line width (see Polyline.render()). + * @param[out] anchors Anchor points defining the core line. + * @param[out] normals Normals defining the edge of the sleeve. + * @param[in,out] segment Direction of segment pq (updated to the segment qr). + * @param[in,out] segmentLength Length of segment pq (updated to the segment qr). + * @param[in,out] segmentNormal Normal on the segment pq (updated to the segment qr). + * @param[in] pointA Current point on the line (q). + * @param[in] pointB Next point on the line (r). + * @param[in] halfWidth Half line width (see Polyline.render()). */ virtual void renderEdge(std::vector &anchors, std::vector &normals, - Vector2 &s, float &len_s, Vector2 &ns, - const Vector2 &q, const Vector2 &r, float hw) = 0; + Vector2 &segment, float &segmentLength, Vector2 &segmentNormal, + const Vector2 &pointA, const Vector2 &pointB, float halfWidth) = 0; Vector2 *vertices; Vector2 *overdraw; From a71cfdcb50b9e470f2bbc5b078732f4cc686b81c Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sat, 14 Jan 2023 12:17:57 -0400 Subject: [PATCH 5/8] Fix typo --- src/modules/graphics/Polyline.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/graphics/Polyline.cpp b/src/modules/graphics/Polyline.cpp index 1188fa15c..6861e759f 100644 --- a/src/modules/graphics/Polyline.cpp +++ b/src/modules/graphics/Polyline.cpp @@ -242,7 +242,8 @@ void BevelJoinPolyline::renderEdge(std::vector &anchors, std::vector Date: Tue, 21 Feb 2023 19:00:29 -0600 Subject: [PATCH 6/8] graphics: fix for anti-parallel line segments (#1894) Handle the case when a line doubles back on itself, eg love.graphics.setLineJoin("bevel") love.graphics.line({ 180, 400, 200, 400, 100, 400 }) --- src/modules/graphics/Polyline.cpp | 33 +++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/modules/graphics/Polyline.cpp b/src/modules/graphics/Polyline.cpp index 6861e759f..5ce7e2a2f 100644 --- a/src/modules/graphics/Polyline.cpp +++ b/src/modules/graphics/Polyline.cpp @@ -188,11 +188,26 @@ void MiterJoinPolyline::renderEdge(std::vector &anchors, std::vector 0) + if (fabs(det) / (segmentLength * newSegmentLength) < LINES_PARALLEL_EPS) { // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2 normals.push_back(segmentNormal); normals.push_back(-segmentNormal); + + if (Vector2::dot(segment, newSegment) < 0) + { + // line reverses direction; because the normal flips, the + // triangle strip would twist here, so insert a zero-size + // quad to contain the twist + // ____.___.____ + // | |\ /| | + // p q X q r + // |____|/ \|____| + anchors.push_back(pointA); + anchors.push_back(pointA); + normals.push_back(-segmentNormal); + normals.push_back(segmentNormal); + } } else { @@ -233,14 +248,24 @@ void BevelJoinPolyline::renderEdge(std::vector &anchors, std::vector 0) + if (fabs(det) / (segmentLength * newSegmentLength) < LINES_PARALLEL_EPS) { // lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2 Vector2 newSegmentNormal = newSegment.getNormal(halfWidth / newSegmentLength); anchors.push_back(pointA); anchors.push_back(pointA); - normals.push_back(newSegmentNormal); - normals.push_back(-newSegmentNormal); + normals.push_back(segmentNormal); + normals.push_back(-segmentNormal); + + if (Vector2::dot(segment, newSegment) < 0) + { + // line reverses direction; same as for miter + anchors.push_back(pointA); + anchors.push_back(pointA); + normals.push_back(-segmentNormal); + normals.push_back(segmentNormal); + } + segment = newSegment; segmentLength = newSegmentLength; segmentNormal = newSegmentNormal; From f5b8117c19e95f05bfac6bf31752666d7eaed5c4 Mon Sep 17 00:00:00 2001 From: scurest Date: Tue, 21 Feb 2023 19:02:32 -0600 Subject: [PATCH 7/8] graphics: fix typo in comment --- src/modules/graphics/Polyline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/graphics/Polyline.cpp b/src/modules/graphics/Polyline.cpp index 5ce7e2a2f..e14e0fe3e 100644 --- a/src/modules/graphics/Polyline.cpp +++ b/src/modules/graphics/Polyline.cpp @@ -157,7 +157,7 @@ void NoneJoinPolyline::renderEdge(std::vector &anchors, std::vector Date: Thu, 13 Apr 2023 22:32:54 -0300 Subject: [PATCH 8/8] Updated changelog --- changes.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/changes.txt b/changes.txt index 7f4830b70..406fb8d27 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,22 @@ +LOVE 11.5 [Mysterious Mysteries] +-------------------------------- + +Released: N/A + +* Fixed love.threaderror not being called if the error message is an empty string. +* Fixed a race condition when a Thread is destroyed immediately after Thread:start. +* Fixed unexpectedly slow first frames on macOS. +* Fixed time drift in Source:tell after a Source loops. +* Fixed audio not always pausing when the app is minimized on Android. +* Fixed the original window size not always being restored when exiting fullscreen on Linux. +* Fixed some cases of framerate hitches in Windows when vsync is enabled in windowed mode. +* Fixed colors appearing over-saturated on P3 displays in macOS. +* Fixed textures looking washed out when gamma-correct rendering is used on some Android devices. +* Fixed images with mipmaps when ANGLE is used with an AMD GPU. +* Fixed line rendering when duplicate points are used in the line. +* Fixed line rendering with miter and bevel line join modes when antiparallel lines are formed. +* Fixed a crash when calling Text:add with an empty string parameter. + LOVE 11.4 [Mysterious Mysteries] --------------------------------