1

私は数か月間 C++ で OpenGL アプリケーションに取り組んでいますが、これまでこの問題に直面したことはありません。

Visual Studio 2012でのコーディングIDE 内でアプリケーションを実行するか、実行可能ファイルを手動で起動することができます。どちらの場合も、デバッグ ビルドとリリース ビルドのどちらかを選択できます。次の問題は、リリース ビルドの実行可能ファイルを自分で起動した場合にのみ発生します。そうでなければ、すべて正常に動作します。

その場で新しいフォームをシーンに追加すると、アプリケーションがクラッシュすることがあります。私のオペレーティング システムWindows 8 64bitではDebug the program、実行時に利用できるデバッグ情報が少ないリリース ビルドであるため、実際にはあまり役に立ちませんでしたが、少なくとも描画呼び出しの近くでアプリケーションがクラッシュすることがわかりましたglDrawTriangles()。私のコードでは、ループ内に描画呼び出しが 1 つしかありません。

クラッシュが不規則にしか発生しないことに気が狂いそうです。アプリケーションが数分間問題なく動作する場合もあれば、すぐにクラッシュする場合もあれば、数秒実行される場合もあります。しかし、最初に別のスレッドで生成し、次にメインスレッドで OpenGL バッファを作成する新しいフォームがシーンに挿入された直後にのみ、アプリケーションがクラッシュすることを知っておくことが重要だと思います。

以下は、Windows クラッシュ ダイアログに表示された問題の詳細です。ビデオカードATI Radeon 7870のドライバがクラッシュしたようです。

問題の署名:
  問題イベント名: APPCRASH
  アプリケーション名: Application.exe
  アプリケーション バージョン: 0.0.0.0
  アプリケーション タイムスタンプ: 50f1491a
  障害モジュール名: atioglxx.dll
  障害モジュール バージョン: 6.14.10.11931
  障害モジュール タイムスタンプ: 50650037
  例外コード: c0000005
  例外オフセット: 001108ef
  OS バージョン: 6.2.9200.2.0.0.256.27
  ロケール ID: 1031
  追加情報 1: 5861
  追加情報 2: 5861822e1919d7c014bbb064c64908b2
  追加情報 3: dac6
  追加情報 4: dac6c2650fa14dd558bd9fd418e

プライバシーに関する声明をオンラインで読む:
  http://go.microsoft.com/fwlink/?linkid=190175

オンラインのプライバシーに関する声明が利用できない場合は、オフラインでプライバシーに関する声明をお読みください:
  C:\Windows\system32\en-US\erofflps.txt

これまでに行ったことは、ビデオ カード ドライバーの更新とアプリケーションのデバッグです。クラッシュが自然に発生するため、結果が信頼できないことに気付いたからです。多くのソース ファイルがあり、正直なところ、どれがバグに影響を与えているのかわかりません。というファイルかもしれないので、terrain.cppここにコードを貼り付けます。

#pragma once

#include "system.h"
#include "debug.h"

#include <vector>
#include <cstdlib>
#include <future>
using namespace std;
#include <GLEW/glew.h>
#include <SFML/OpenGL.hpp>
#include <SFML/Graphics/Image.hpp>
using namespace sf;
#include <GLM/glm.hpp>
#include <GLM/gtc/noise.hpp>
using namespace glm;

#include "settings.h"
#include "camera.h"
#include "form.h"
#include "transform.h"
#include "terrain.h"
#include "shader.h"
#include "movement.h"


typedef detail::tvec3<int> vec3i;

class ComponentTerrain : public Component
{
    void Init()
    {
        auto wld = Global->Add<StorageTerrain>("terrain");

        tasking = false;

        Texture();

        Listeners();
    }

    void Update()
    {
        auto wld = Global->Get<StorageTerrain>("terrain");
        auto stg = Global->Get<StorageSettings>("settings");
        auto cam = Global->Get<StorageCamera>("camera");
        auto cks = Entity->Get<StorageChunk>();

        int Distance = (int)(.5f * stg->Viewdistance / CHUNK_X / 2);
        Debug::Info("Terrain chunk distance " + to_string(Distance));
        for(int X = -Distance; X <= Distance; ++X)
        for(int Z = -Distance; Z <= Distance; ++Z)
        {
            addChunk(X + (int)cam->Position.x / CHUNK_X, 0, Z + (int)cam->Position.z / CHUNK_Z);
        }
        for(auto chunk : wld->chunks)
        {
            auto chk = cks.find(chunk.second);
            float distance = (float)vec3(chunk.first[0] * CHUNK_X - cam->Position.x, chunk.first[1] * CHUNK_Y - cam->Position.y, chunk.first[2] * CHUNK_Z - cam->Position.z).length();
            if(distance > stg->Viewdistance)
                deleteChunk(chunk.first[0], chunk.first[1], chunk.first[2]);
        }

        if(tasking)
        {
            if(task.wait_for(chrono::milliseconds(0)) == future_status::ready)
            {
                tasking = false;
                Data data = task.get();
                Buffers(data);
            }
        }
        else
        {
            for(auto chunk : cks)
            if(chunk.second->changed)
            {
                tasking = true;
                chunk.second->changed = false;
                task = async(launch::async, &ComponentTerrain::Mesh, this, Data(chunk.first));
                break;
            }
        }
    }

    struct Data
    {
        Data() {}
        Data(unsigned int id) : id(id) {}
        unsigned int id;
        vector<float> Vertices, Normals, Texcoords;
        vector<int> Elements;
    };

    future<Data> task;
    bool tasking;
    Image texture;

    void Listeners()
    {
        Event->Listen("SystemInitialized", [=]{
            auto cam = Global->Get<StorageCamera>("camera");
            cam->Position = vec3(0, CHUNK_Y, 0);
            cam->Angles = vec2(0.75, -0.25);
        });

        Event->Listen("InputBindChunk", [=]{
            addChunk(rand() % 5, 0, rand() % 5);
            addChunk(rand() % 5, 0, rand() % 5);
            addChunk(rand() % 5, 0, rand() % 5);
        });
    }

    unsigned int getChunk(int X, int Y, int Z)
    {
        auto wld = Global->Get<StorageTerrain>("terrain");
        array<int, 3> key = {X, Y, Z};
        auto i = wld->chunks.find(key);
        return (i != wld->chunks.end()) ? i->second : 0;
    }

    int addChunk(int X, int Y, int Z)
    {
        auto wld = Global->Get<StorageTerrain>("terrain");
        auto shd = Global->Get<StorageShader>("shader"); // moved this line

        unsigned int id = getChunk(X, Y, Z);
        if(!id)
        {
            id = Entity->New();
            Entity->Add<StorageChunk>(id);
            auto frm = Entity->Add<StorageForm>(id); // moved this line
            auto tsf = Entity->Add<StorageTransform>(id);

            frm->Program = shd->Program; // moved this line
            tsf->Position = vec3(X * CHUNK_X, Y * CHUNK_Y, Z * CHUNK_Z);

            Generate(id, X, Y, Z);

            array<int, 3> key = {X, Y, Z};
            wld->chunks.insert(make_pair(key, id));
        }
        return id;
    }

    void deleteChunk(int X, int Y, int Z)
    {
        auto wld = Global->Get<StorageTerrain>("terrain");

        unsigned int id = getChunk(X, Y, Z);
        if(id < 1) return;

        array<int, 3> key = {X, Y, Z};
        wld->chunks.erase(key);

        Entity->Delete<StorageChunk>(id);
        Entity->Delete<StorageForm>(id);
        Entity->Delete<StorageTransform>(id);

        // free buffers
    }

    void Generate(unsigned int id, int X, int Y, int Z)
    {
        auto cnk = Entity->Get<StorageChunk>(id);
        cnk->changed = true;

        for(int x = 0; x < CHUNK_X; ++x) {
        const float i = X + (float)x / CHUNK_X;
        for(int z = 0; z < CHUNK_Z; ++z) {
        const float j = Z + (float)z / CHUNK_Z;
                double height_bias = 0.30;
                double height_base = 0.50 * (simplex(0.2f * vec2(i, j)) + 1) / 2;
                double height_fine = 0.20 * (simplex(1.5f * vec2(i, j)) + 1) / 2;
                int height = (int)((height_bias + height_base + height_fine) * CHUNK_Y);
                for(int y = 0; y < height; ++y) cnk->blocks[x][y][z] = true;
        } }
    }

    #define TILES_U 4
    #define TILES_V 4

    Data Mesh(Data data)
    {
        auto cnk = Entity->Get<StorageChunk>(data.id);
        auto *Vertices = &data.Vertices, *Normals = &data.Normals, *Texcoords = &data.Texcoords;
        auto *Elements = &data.Elements;

        const vec2 grid(1.f / TILES_U, 1.f / TILES_V);

        int n = 0;
        for(int X = 0; X < CHUNK_X; ++X)
        for(int Y = 0; Y < CHUNK_Y; ++Y)
        for(int Z = 0; Z < CHUNK_Z; ++Z)
            if(cnk->blocks[X][Y][Z])
            {
                int Tile = Clamp(rand() % 2 + 1, 0, TILES_U * TILES_V - 1);

                for(int dim = 0; dim < 3; ++dim) { int dir = -1; do {
                    vec3i neigh = Shift(dim, vec3i(dir, 0, 0)) + vec3i(X, Y, Z);

                    if(Inside(neigh, vec3i(0), vec3i(CHUNK_X, CHUNK_Y, CHUNK_Z) - 1))
                        if(cnk->blocks[neigh.x][neigh.y][neigh.z])
                            { dir *= -1; continue; }

                    for(float i = 0; i <= 1; ++i)
                    for(float j = 0; j <= 1; ++j)
                    {
                        vec3 vertex = vec3(X, Y, Z) + floatify(Shift(dim, vec3i((dir+1)/2, i, j)));
                        Vertices->push_back(vertex.x); Vertices->push_back(vertex.y); Vertices->push_back(vertex.z);
                    }

                    vec3 normal = normalize(floatify(Shift(dim, vec3i(dir, 0, 0))));
                    for(int i = 0; i < 4; ++i)
                    {
                        Normals->push_back(normal.x); Normals->push_back(normal.y); Normals->push_back(normal.z);
                    }

                    vec2 position = (vec2(Tile % TILES_U, Tile / TILES_U) + .25f) * grid;
                    Texcoords->push_back(position.x);            Texcoords->push_back(position.y);
                    Texcoords->push_back(position.x + grid.x/2); Texcoords->push_back(position.y);
                    Texcoords->push_back(position.x);            Texcoords->push_back(position.y + grid.y/2);
                    Texcoords->push_back(position.x + grid.x/2); Texcoords->push_back(position.y + grid.y/2);

                    if(dir == -1) {
                        Elements->push_back(n+0); Elements->push_back(n+1); Elements->push_back(n+2);
                        Elements->push_back(n+1); Elements->push_back(n+3); Elements->push_back(n+2);
                    } else {
                        Elements->push_back(n+0); Elements->push_back(n+2); Elements->push_back(n+1);
                        Elements->push_back(n+1); Elements->push_back(n+2); Elements->push_back(n+3);
                    }
                    n += 4;

                dir *= -1; } while(dir > 0); }
            }

        return data;
    }

    void Buffers(Data data)
    {
        auto frm = Entity->Get<StorageForm>(data.id);

        glGenBuffers(1, &frm->Positions);
        glBindBuffer(GL_ARRAY_BUFFER, frm->Positions);
        glBufferData(GL_ARRAY_BUFFER, data.Vertices.size() * sizeof(float), &(data.Vertices[0]), GL_STATIC_DRAW);

        glGenBuffers(1, &frm->Normals);
        glBindBuffer(GL_ARRAY_BUFFER, frm->Normals);
        glBufferData(GL_ARRAY_BUFFER, data.Normals.size() * sizeof(float), &(data.Normals[0]), GL_STATIC_DRAW);

        glGenBuffers(1, &frm->Texcoords);
        glBindBuffer(GL_ARRAY_BUFFER, frm->Texcoords);
        glBufferData(GL_ARRAY_BUFFER, data.Texcoords.size() * sizeof(float), &(data.Texcoords[0]), GL_STATIC_DRAW);

        glGenBuffers(1, &frm->Elements);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, frm->Elements);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.Elements.size() * sizeof(int), &data.Elements[0], GL_STATIC_DRAW);

        glGenTextures(1, &frm->Texture);
        glBindTexture(GL_TEXTURE_2D, frm->Texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.getSize().x, texture.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.getPixelsPtr());
        glGenerateMipmap(GL_TEXTURE_2D);
    }

    void Texture()
    {
        Image image;
        bool result = image.loadFromFile("forms/textures/terrain.png");
        if(!result){ Debug::Fail("Terrain texture loading fail"); return; }

        Vector2u size = Vector2u(image.getSize().x / TILES_U, image.getSize().y / TILES_V);
        texture.create(image.getSize().x * 2, image.getSize().y * 2, Color());
        for(int u = 0; u < TILES_U; ++u)
        for(int v = 0; v < TILES_V; ++v)
        {
            Image tile, quarter;
            tile.create(size.x, size.y, Color());
            tile.copy(image, 0, 0, IntRect(size.x * u, size.y * v, size.x, size.y), true);
            quarter.create(size.x, size.y, Color());
            quarter.copy(tile, 0,          0,          IntRect(size.x / 2, size.y / 2, size.x / 2, size.y / 2), true);
            quarter.copy(tile, size.x / 2, 0,          IntRect(0,          size.y / 2, size.x / 2, size.y / 2), true);
            quarter.copy(tile, 0,          size.y / 2, IntRect(size.x / 2, 0,          size.x / 2, size.y / 2), true);
            quarter.copy(tile, size.x / 2, size.y / 2, IntRect(0,          0,          size.x / 2, size.y / 2), true);
            texture.copy(quarter, (u * 2    ) * size.x, (v * 2    ) * size.y, IntRect(0, 0, 0, 0), true);
            texture.copy(quarter, (u * 2 + 1) * size.x, (v * 2    ) * size.y, IntRect(0, 0, 0, 0), true);
            texture.copy(quarter, (u * 2    ) * size.x, (v * 2 + 1) * size.y, IntRect(0, 0, 0, 0), true);
            texture.copy(quarter, (u * 2 + 1) * size.x, (v * 2 + 1) * size.y, IntRect(0, 0, 0, 0), true);
        }
    }

    template <typename T>
    inline T Clamp(T Value, T Min, T Max)
    {
        if(Value < Min) return Min;
        if(Value > Max) return Max;
        return Value;
    }

    bool Inside(vec3i Position, vec3i Min, vec3i Max)
    {
        if(Position.x < Min.x || Position.y < Min.y || Position.z < Min.z) return false;
        if(Position.x > Max.x || Position.y > Max.y || Position.z > Max.z) return false;
        return true;
    }

    inline vec3i Shift(int Dimension, vec3i Vector)
    {
        if      (Dimension % 3 == 1) return vec3i(Vector.z, Vector.x, Vector.y);
        else if (Dimension % 3 == 2) return vec3i(Vector.y, Vector.z, Vector.x);
        else                         return Vector;
    }

    vec3 floatify(vec3i Value)
    {
        return vec3(Value.x, Value.y, Value.z);
    }
};

ただし、コード全体は Github で見つけることができます。

クラッシュの原因やバグを見つける方法を知っていますか? さらに詳しい情報が必要な場合は、どのような情報が必要かお知らせください。

@doomster のおかげで、バグを見つけて修正できました。

レンダラー コンポーネントは、フォームのベクターをループして描画します。非同期スレッドはそのベクターに新しいフォームを追加しましたが、それらのバッファーは、生成された頂点が返された後にメイン スレッドで作成されました。つまり、描画とフォームの追加が並行して実行されました。クラッシュは、レンダラーがバッファを作成せずにフォームをレンダリングしようとしたときに発生しました。

上記のコードにコメントを挿入して、問題を修正するためaddChunk()に移動した 3 行を強調表示しました。Buffers()リリース ビルドの実行可能ファイルだけがクラッシュした理由はわかりませんが、それはもはや問題ではありません。

4

1 に答える 1

4

ここで確認することが2つあります。

  • を使用していfuture<T>ます。よくわかりませんが、別のスレッドで実行すると、マルチスレッドの使用に敏感に動作する可能性があると聞いているOpenGLに悪影響を与える可能性があります。これを噂として扱ってください、私はそれについて本当によくわかりません、しかしシングルスレッドコードに変換しようとすることは試みる価値があります。
  • リリースビルドでデバッグシンボルをアクティブ化することもできます。これにより、少なくともクラッシュが発生したときに使用可能なバックトレースが得られるはずです。実際、VSのデバッグ/リリース設定は単なるデフォルト設定ですが、本質的な意味はないため、リリース設定と一致するまで、デバッグ設定を段階的に変更できます。これにより、デバッガーで使用可能であるにもかかわらず失敗するバリアントが得られるはずです。
于 2013-01-12T12:14:03.610 に答える