[英]Why does this innocent function cause a segfault?
我正在尝试用 C++/SFML 编写元球,我的程序在单个线程中运行良好。 我试图写一个 MRE 来找到问题,这就是我得到的:
主文件
// main
#include <iostream>
#include "threader.h"
float func(float x, float y, float a, float b, float r) {
return 1.f / sqrt((x - a)*(x - a) + (y - b)*(x - b));
}
int main () {
sf::RenderWindow window(sf::VideoMode(2800, 1800), "");
window.setFramerateLimit(20);
sf::Event event{};
threader tf(window);
while (window.isOpen()) {
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed: {
window.close();
}
}
}
window.clear();
tf.parallel(func);
window.display();
}
}
线程器.h
//threader_H
#pragma once
#include <array>
#include <thread>
#include <cmath>
#include <functional>
#include <SFML/Graphics.hpp>
class threader {
public:
int threadCount = 4;
std::array<std::thread, 4> threads;
sf::RenderWindow& window;
public:
explicit threader(sf::RenderWindow& w) : window(w) {};
void strip(int start, int end, const std::function<float(float, float, float, float, float)>& func);
void parallel(const std::function<float(float, float, float, float, float)>& func);
};
线程器.cpp
// threader_CPP
#include "threader.h"
#include <iostream>
void threader::strip (int start, int end, const std::function<float (float, float, float, float, float)> &func) {
for (int X = start; X < end; X += 10) {
for (int Y = 0; Y < window.getSize().y; Y += 10) {
auto x = static_cast<float>(X);
auto y = static_cast<float>(Y);
x = x / 2800.f * 4 + 0 - 4 / 2.f;
y = -((1800.f - y) / 1800 * 4 + 0 - 4 / 2.f);
if (func(x, y, 1, 2, 3) > 1) {
sf::CircleShape circle(20);
circle.setPointCount(3);
circle.setFillColor(sf::Color::Cyan);
circle.setPosition(X, Y);
window.draw(circle);
}
}
}
}
void threader::parallel (const std::function<float (float, float, float, float, float)> &func) {
int start = 0;
int end = window.getSize().x / threadCount;
for (auto& t : threads) {
t = std::thread(&threader::strip, this, start, end, func);
start = end;
end += window.getSize().x / threadCount;
}
for (auto& t : threads) {
t.join();
}
}
现在解释一下。 threader
is a class which has two methods: strip
that calculates a function for a given strip of the window
and parallel
that creates threads to separately calculate my function for every strip. 此代码不起作用:
但这里有个问题:如果我在 main 中调整 function void func(...)
以返回1.f / sqrt((x - a) + (y - b))
,一切正常。 怎么了? 一个简单的计算如何导致段错误? 请帮忙...
编辑 1:用 CLion 编写,C++ 20。
与其尝试在多个线程上绘制到 window,不如使用std
算法过滤点以绘制圆,并将它们全部绘制在主线程上。
std::vector<std::pair<int, int>> getPoints(const sf::RenderWindow& window) {
std::vector<std::pair<int, int>> points;
for (int X = 0; X < window.getSize().x; X += 10) {
for (int Y = 0; Y < window.getSize().y; Y += 10) {
points.emplace_back(X, Y);
}
}
return points;
}
template<typename F>
auto filter(F f) {
return [f](const std::pair<int, int> & point) {
auto x = static_cast<float>(point.first);
auto y = static_cast<float>(point.second);
x = x / 2800.f * 4 + 0 - 4 / 2.f;
y = -((1800.f - y) / 1800 * 4 + 0 - 4 / 2.f);
return (func(x, y, 1, 2, 3) > 1);
}
}
sf::CircleShape toCircle(int X, int Y) {
sf::CircleShape circle(20);
circle.setPointCount(3);
circle.setFillColor(sf::Color::Cyan);
circle.setPosition(X, Y);
return circle;
}
template <typename F>
void drawCircles(sf::RenderWindow& window, F f) {
auto points = getPoints(window);
auto end = std::remove_if(std::execution::par, points.begin(), points.end(), filter(f));
points.erase(end, points.end());
for (auto & [X, Y] : points) {
window.draw(toCircle(X, Y));
}
}
float func(float x, float y, float a, float b, float r) {
return 1.f / sqrt((x - a)*(x - a) + (y - b)*(x - b));
}
int main () {
sf::RenderWindow window(sf::VideoMode(2800, 1800), "");
window.setFramerateLimit(20);
sf::Event event{};
threader tf(window);
while (window.isOpen()) {
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed: {
window.close();
}
}
}
window.clear();
drawCircles(window, func);
window.display();
}
}
在不正确切换 GL 上下文的情况下尝试从多个线程进行渲染会出现问题。 由于您是在 CPU 上执行此操作,因此这里有一些选项:
制作多个 sf::RenderTexture 对象,在单独的线程中渲染到每个对象(不要忘记在最后调用 display() ),然后在一些 sprite 中将它们绘制在主线程上。 (比较难,不确定是不是go的路,但不知道你到底想做什么)
制作多个 sf::Image 对象,在单独的线程中渲染它们中的每一个,然后在一些精灵中将它们绘制在主线程上。
只制作一个 sf::Image object,每个线程在图像的一个单独区域中渲染,然后在某个 sprite 中在主线程上绘制图像。
选项 2 和 3 使其在像素级别上更容易使用,也更快。 确保以缓存友好的方式遍历图像(最有可能逐行)
我会 go 与 2 或 3 取决于您认为它对您的应用程序更好。
此外,使用 sf::Transform 从屏幕空间(图像空间)转换到元球所在的世界空间。
https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Image.php
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.