web-dev-qa-db-ja.com

glDrawArraysがGL_INVALID_OPERATIONエラーを生成する原因は何ですか?

私は、OpenGLとGLSLを使用して、GPU Gems 3の最初の章で詳述したものと同様に、マーチングキューブアルゴリズムの2パスGPU実装を記述しようと試みました。ただし、最初のパスでのglDrawArraysの呼び出しは、一貫して_GL_INVALID_OPERATION_で失敗します。

私が見つけることができるすべてのドキュメントを検索し、glDrawArraysがそのエラーをスローできる条件を見つけました。

  1. ゼロ以外のバッファオブジェクト名が有効な配列または_GL_INVALID_OPERATION_バインディングにバインドされ、バッファオブジェクトのデータストアが現在マップされている場合、_GL_DRAW_INDIRECT_BUFFER_が生成されます。
  2. glDrawArraysと対応するglBeginの実行の間にglEndが実行されると、_GL_INVALID_OPERATION_が生成されます。
  3. _GL_INVALID_OPERATION_は、現在のプログラムオブジェクト内の2つのアクティブサンプラーのタイプが異なるが、同じテクスチャイメージユニットを参照している場合、glDrawArraysまたはglDrawElementsによって生成されます。
  4. ジオメトリシェーダーがアクティブで、モードが現在インストールされているプログラムオブジェクトのジオメトリシェーダーの入力プリミティブタイプと互換性がない場合、_GL_INVALID_OPERATION_が生成されます。
  5. モードが_GL_INVALID_OPERATION_で、テッセレーションコントロールシェーダーがアクティブでない場合、_GL_PATCHES_が生成されます。
  6. _GL_INVALID_OPERATION_は、プリミティブの頂点を変換フィードバック目的で使用されているバッファーオブジェクトに記録すると、バッファーオブジェクトのサイズの制限を超えるか、終了位置のオフセット+サイズ-1を超える場合に生成されますglBindBufferRangeによって設定されます。
  7. _GL_INVALID_OPERATION_はglDrawArrays()によって生成されます。ジオメトリシェーダーが存在しない場合、変換フィードバックはアクティブであり、モードは許可されたモードの1つではありません。
  8. _GL_INVALID_OPERATION_は、glDrawArrays()によって生成されます。ジオメトリシェーダーが存在し、変換フィードバックがアクティブで、ジオメトリシェーダーの出力プリミティブタイプが変換フィードバックPrimitiveModeと一致しない場合。
  9. バインドされたシェーダープログラムが無効な場合は、_GL_INVALID_OPERATION_が生成されます。
  10. EDIT 10/10/12:_GL_INVALID_OPERATION_は、変換フィードバックが使用されており、変換フィードバックバインディングポイントにバインドされたバッファーがまた、配列バッファのバインディングポイントにバインドされます。 これは私が抱えていた問題で、バッファがバインドされているタイプミスによるものです。仕様にはこれは違法であると記載されていますが、私が見つけたドキュメントでは、エラーをスローする理由の1つとしてglDrawArraysの下にリストされていません。

残念ながら、私が見つけることができる公式文書のどれも、これらの3つ以上をカバーしていません。多数のソースからこのリストを収集する必要がありました。ポイント7と8は、実際にはglBeginTransformFeedbackのドキュメントに由来し、ポイント9はまったくドキュメント化されていないようです。どこかのフォーラムの投稿で言及されていることがわかりました。ただし、これらのどれもが私が得ているエラーを説明していないように見えるので、私はまだこのリストが完全だとは思わない。

  1. 私のプログラムでは、どこにでもバッファーをマッピングしていません。
  2. Coreプロファイルを使用しているため、glBeginglEndは使用できません。
  3. 2つのサンプラーがあり、種類は異なりますが、異なるテクスチャに確実にマッピングされます。
  4. ジオメトリシェーダーはアクティブですが、その入力レイアウトはlayout (points) inであり、glDrawArraysが_GL_POINTS_で呼び出されています。
  5. _GL_PATCHES_または任意の種類のテッセレーションシェーダーを使用していません。
  6. ジオメトリシェーダーが出力できるスペースの最大量を割り当てていることを確認しました。それから、4倍にしようとしました。助けなかった。
  7. ジオメトリシェーダーがあります。次のポイントを参照してください。
  8. 変換フィードバックが使用されており、ジオメトリシェーダーがありますが、出力レイアウトはlayout (points) outであり、glBeginTransformFeedbackが_GL_POINTS_で呼び出されます。
  9. glValidateProgramへの呼び出しの直前にglDrawArraysへの呼び出しを挿入しようとすると、_GL_TRUE_が返されました。

実際のOpenGLコードは次のとおりです。

_    const int SECTOR_SIZE = 32;
    const int SECTOR_SIZE_CUBED = SECTOR_SIZE * SECTOR_SIZE * SECTOR_SIZE;
    const int CACHE_SIZE = SECTOR_SIZE + 3;
    const int CACHE_SIZE_CUBED = CACHE_SIZE * CACHE_SIZE * CACHE_SIZE;

    MarchingCubesDoublePass::MarchingCubesDoublePass(ServiceProvider* svc, DensityMap* sourceData) {
        this->sourceData = sourceData;
        densityCache = new float[CACHE_SIZE_CUBED];
    }

    MarchingCubesDoublePass::~MarchingCubesDoublePass() {
        delete densityCache;
    }

    void MarchingCubesDoublePass::InitShaders() {
        ShaderInfo vertShader, geoShader, fragShader;

        vertShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass1.vert", GL_VERTEX_SHADER);
        svc->shader->Compile(vertShader);
        geoShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass1.geo", GL_GEOMETRY_SHADER);
        svc->shader->Compile(geoShader);
        shaderPass1 = glCreateProgram();
        static const char* outputVaryings[] = { "triangle" };
        glTransformFeedbackVaryings(shaderPass1, 1, outputVaryings, GL_SEPARATE_ATTRIBS);
        assert(svc->shader->Link(shaderPass1, vertShader, geoShader));

        uniPass1DensityMap = glGetUniformLocation(shaderPass1, "densityMap");
        uniPass1TriTable = glGetUniformLocation(shaderPass1, "triangleTable");
        uniPass1Size = glGetUniformLocation(shaderPass1, "size");
        attribPass1VertPosition = glGetAttribLocation(shaderPass1, "vertPosition");

        vertShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.vert", GL_VERTEX_SHADER);
        svc->shader->Compile(vertShader);
        geoShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.geo", GL_GEOMETRY_SHADER);
        svc->shader->Compile(geoShader);
        fragShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.frag", GL_FRAGMENT_SHADER);
        svc->shader->Compile(fragShader);
        shaderPass2 = glCreateProgram();
        assert(svc->shader->Link(shaderPass2, vertShader, geoShader, fragShader));

        uniPass2DensityMap = glGetUniformLocation(shaderPass2, "densityMap");
        uniPass2Size = glGetUniformLocation(shaderPass2, "size");
        uniPass2Offset = glGetUniformLocation(shaderPass2, "offset");
        uniPass2Matrix = glGetUniformLocation(shaderPass2, "matrix");
        attribPass2Triangle = glGetAttribLocation(shaderPass2, "triangle");
    }

    void MarchingCubesDoublePass::InitTextures() {
        for (int x = 0; x < CACHE_SIZE; x++) {
            for (int y = 0; y < CACHE_SIZE; y++) {
                for (int z = 0; z < CACHE_SIZE; z++) {
                    densityCache[x + y*CACHE_SIZE + z*CACHE_SIZE*CACHE_SIZE] = sourceData->GetDensity(Vector3(x-1, y-1, z-1));
                }
            }
        }
        glGenTextures(1, &densityTex);
        glBindTexture(GL_TEXTURE_3D, densityTex);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_Edge);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_Edge);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_Edge);
        glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, CACHE_SIZE, CACHE_SIZE, CACHE_SIZE, 0, GL_RED, GL_FLOAT, densityCache);

        glGenTextures(1, &triTableTex);
        glBindTexture(GL_TEXTURE_RECTANGLE, triTableTex);
        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_Edge);
        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_Edge);
        glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_R16I, 16, 256, 0, GL_RED_INTEGER, GL_INT, triTable);
    }

    void MarchingCubesDoublePass::InitBuffers() {
        float* voxelGrid = new float[SECTOR_SIZE_CUBED*3];
        unsigned int index = 0;
        for (int x = 0; x < SECTOR_SIZE; x++) {
            for (int y = 0; y < SECTOR_SIZE; y++) {
                for (int z = 0; z < SECTOR_SIZE; z++) {
                    voxelGrid[index*3 + 0] = x;
                    voxelGrid[index*3 + 1] = y;
                    voxelGrid[index*3 + 2] = z;
                    index++;
                }
            }
        }

        glGenBuffers(1, &bufferPass1);
        glBindBuffer(GL_ARRAY_BUFFER, bufferPass1);
        glBufferData(GL_ARRAY_BUFFER, SECTOR_SIZE_CUBED*3*sizeof(float), voxelGrid, GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glGenBuffers(1, &bufferPass2);
        glBindBuffer(GL_ARRAY_BUFFER, bufferPass2);
        glBufferData(GL_ARRAY_BUFFER, SECTOR_SIZE_CUBED*5*sizeof(int), NULL, GL_DYNAMIC_COPY);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glGenVertexArrays(1, &vaoPass1);
        glBindVertexArray(vaoPass1);
        glBindBuffer(GL_ARRAY_BUFFER, bufferPass1);
        glVertexAttribPointer(attribPass1VertPosition, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glEnableVertexAttribArray(attribPass1VertPosition);
        glBindVertexArray(0);

        glGenVertexArrays(1, &vaoPass2);
        glBindVertexArray(vaoPass2);
        glBindBuffer(GL_ARRAY_BUFFER, bufferPass2);
        glVertexAttribIPointer(attribPass2Triangle, 1, GL_INT, 0, (void*)0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glEnableVertexAttribArray(attribPass2Triangle);
        glBindVertexArray(0);

        glGenQueries(1, &queryNumTriangles);
    }

    void MarchingCubesDoublePass::Register(Genesis::ServiceProvider* svc, Genesis::Entity* ent) {
        this->svc = svc;
        this->ent = ent;
        svc->scene->RegisterEntity(ent);

        InitShaders();
        InitTextures();
        InitBuffers();
    }

    void MarchingCubesDoublePass::Unregister() {
        if (!ent->GetBehavior<Genesis::Render>()) {
            svc->scene->UnregisterEntity(ent);
        }
    }

    void MarchingCubesDoublePass::RenderPass1() {
        glEnable(GL_RASTERIZER_DISCARD);

        glUseProgram(shaderPass1);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, densityTex);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_RECTANGLE, triTableTex);
        glUniform1i(uniPass1DensityMap, 0);
        glUniform1i(uniPass1TriTable, 1);
        glUniform1i(uniPass1Size, SECTOR_SIZE);

        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bufferPass2);

        glBindVertexArray(vaoPass2);
        glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, queryNumTriangles);
        glBeginTransformFeedback(GL_POINTS);
            GLenum error = glGetError();
            glDrawArrays(GL_POINTS, 0, SECTOR_SIZE_CUBED);
            error = glGetError();
        glEndTransformFeedback();
        glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
        glBindVertexArray(0);

        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);

        glUseProgram(0);

        glDisable(GL_RASTERIZER_DISCARD);

        glGetQueryObjectuiv(queryNumTriangles, GL_QUERY_RESULT, &numTriangles);
    }

    void MarchingCubesDoublePass::RenderPass2(Matrix mat) {
        glUseProgram(shaderPass2);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, densityTex);

        glUniform1i(uniPass2DensityMap, 0);
        glUniform1i(uniPass2Size, SECTOR_SIZE);
        glUniform3f(uniPass2Offset, 0, 0, 0);
        mat.UniformMatrix(uniPass2Matrix);

        glBindVertexArray(vaoPass2);
        glDrawArrays(GL_POINTS, 0, numTriangles);
        glBindVertexArray(0);

        glUseProgram(0);
    }

    void MarchingCubesDoublePass::OnRender(Matrix mat) {
        RenderPass1();
        RenderPass2(mat);
    }
_

実際のエラーは、_RenderPass1_のglDrawArraysの呼び出しです。 glBeginTransformFeedbackglEndTransformFeedbackの呼び出しをコメントアウトすると、glDrawArraysがエラーの生成を停止することに注意してください。間違っているものは何でも、それはおそらく何らかの形でフィードバックを変換することに関連しています。

編集8/18/12、午後9時:

GDEBuggerでNVIDIA GLExpert機能を見つけましたが、これは以前はよく知らなかったものです。これをオンにしたとき、_GL_INVALID_OPERATION_、特に_The current operation is illegal in the current state: Buffer is mapped._に関して、より実質的な情報が得られました。そのため、上記のポイント1に到達しています。方法はわかりませんが。

コードのどこにでもglMapBufferまたは関連する関数を呼び出していません。 glMapBufferglMapBufferARBglMapBufferRangeglUnmapBuffer、およびglUnmapBufferARBの呼び出しでブレークするようにgDEBuggerを設定しましたが、どこでもブレークしませんでした。次に、RenderPass1_の先頭にコードを追加して、わざわざバッファを明示的にマップ解除しました。エラーが解消されなかっただけでなく、glUnmapBufferへの呼び出しによって_The current operation is illegal in the current state: Buffer is unbound or is already unmapped._が生成されるようになりました。使用しているバッファのどちらもマッピングされていない場合、エラーはどこから発生しますか?

編集8/12/12、午前12時:

GDEBuggerのGLExpertから取得しているエラーメッセージに基づいて、glBeginTransformFeedbackを呼び出すと、_GL_TRANSFORM_FEEDBACK_BUFFER_にバインドされたバッファーがマップされているようです。具体的には、「テクスチャ、バッファ、および画像ビューア」でバッファをクリックすると、メッセージ_The current operation is illegal in the current state: Buffer must be bound and not mapped._が出力されます。ただし、glBeginTransformFeedbackglEndTransformFeedbackの間にこれを追加すると:

_int bufferBinding;
glGetBufferParameteriv(GL_TRANSFORM_FEEDBACK_BUFFER, GL_BUFFER_MAPPED, &bufferBinding);
printf("Transform feedback buffer binding: %d\n", bufferBinding);
_

0を出力します。これは、_GL_TRANSFORM_FEEDBACK_BUFFER_がマップされていないことを示します。このバッファーが別のバインディングポイントにマップされている場合、これはまだ0を返しますか?なぜglBeginTransformFeedbackがバッファーをマップして、変換フィードバックに使用できなくなるのでしょうか?

ここで学べば学ぶほど、混乱してしまいます。

10/10/12を編集:

ニコルボーラスのソリューションへの私の返信で示されているように、私は問題を見つけました。それは彼が見つけたものと同じです。

おそらく質問を投稿してから2週間後に発見しました。私はしばらくフラストレーションをあきらめ、最終的に戻ってきて、基本的にすべてをゼロから再実装し、古くて機能していないものを定期的に比較しました。完了したとき、新しいバージョンは機能し、間違ったバッファーをバインドしていたことがわかった違いを検索しました。

47
Michael Powell

私はあなたの問題を理解しました:あなたは頂点データをソースしているのと同じバッファにレンダリングしています。

glBindVertexArray(vaoPass2);

vaoPass1

仕様から:

バッファーをバインドしたり、変換フィードバックとGLの他の目的の両方に使用しないでください。具体的には、バッファオブジェクトが変換フィードバックバッファバインディングポイントとGLの別の場所に同時にバインドされている場合、バッファへの書き込みまたはバッファからの読み取りは未定義の値を生成します。このようなバインディングの例には、ピクセルバッファーオブジェクトバインディングポイントへのReadPixelsや、MapBufferでマップされたバッファーへのクライアントアクセスが含まれます。

ここで、未定義の値を取得する必要があります。 GLエラーが該当するかどうかはわかりませんが、おそらくエラーであるはずです。

18
Nicol Bolas

glDrawArraysおよびglDrawElementsが_GL_INVALID_OPERATION_で失敗する別の(明らかに文書化されていない)ケース:

  • サンプラーのユニフォームが無効なテクスチャユニット識別子に設定されている場合、_GL_INVALID_OPERATION_が生成されます。 (glUniform1i(location, GL_TEXTURE0);を意味するときに、誤ってglUniform1i(location, 0);を実行していました。)
7
smokris

glDraw*()呼び出しが_GL_INVALID_OPERATION_で失敗する可能性がある別の(文書化されていない)ケース:

  • _GL_INVALID_OPERATION_は、サンプラーのユニフォームが正しくないタイプのテクスチャにバインドされたテクスチャユニットに設定されている場合に生成されます。たとえば、_uniform sampler2D_がglUniform1i(location, 0);に設定されているが、_GL_TEXTURE0_に_GL_TEXTURE_2D_ARRAY_テクスチャバウンドがある場合。
1
hacker64