chromium/third_party/angle/src/tests/gl_tests/BufferDataTest.cpp

//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"

#include "util/random_utils.h"

#include <stdint.h>
#include <thread>

usingnamespaceangle;

class BufferDataTest : public ANGLETest<>
{};

// If glBufferData was not called yet the capturing must not try to
// read the data. http://anglebug.com/42264622
TEST_P(BufferDataTest, Uninitialized)
{}

TEST_P(BufferDataTest, ZeroNonNULLData)
{}

TEST_P(BufferDataTest, NULLResolvedData)
{}

// Internally in D3D, we promote dynamic data to static after many draw loops. This code tests
// path.
TEST_P(BufferDataTest, RepeatedDrawWithDynamic)
{}

// Tests for a bug where vertex attribute translation was not being invalidated when switching to
// DYNAMIC
TEST_P(BufferDataTest, RepeatedDrawDynamicBug)
{}

BufferSubDataTestParams;

std::string BufferSubDataTestPrint(
    const ::testing::TestParamInfo<BufferSubDataTestParams> &paramsInfo)
{}

class BufferSubDataTest : public ANGLETest<BufferSubDataTestParams>
{};

// Test that updating a small index buffer after drawing with it works.
// In the Vulkan backend, the CPU may be used to perform this copy.
TEST_P(BufferSubDataTest, SmallIndexBufferUpdateAfterDraw)
{}

// Test that updating a small index buffer after drawing with it works.
// In the Vulkan backend, the CPU may be used to perform this copy.
TEST_P(BufferSubDataTest, SmallVertexDataUpdateAfterDraw)
{}
class IndexedBufferCopyTest : public ANGLETest<>
{};

// The following test covers an ANGLE bug where our index ranges
// weren't updated from CopyBufferSubData calls
// https://code.google.com/p/angleproject/issues/detail?id=709
TEST_P(IndexedBufferCopyTest, IndexRangeBug)
{}

class BufferDataTestES3 : public BufferDataTest
{};

// The following test covers an ANGLE bug where the buffer storage
// is not resized by Buffer11::getLatestBufferStorage when needed.
// https://code.google.com/p/angleproject/issues/detail?id=897
TEST_P(BufferDataTestES3, BufferResizing)
{}

// Test to verify mapping a buffer after copying to it contains flushed/updated data
TEST_P(BufferDataTestES3, CopyBufferSubDataMapReadTest)
{}

// Test to verify mapping a buffer after copying to it contains expected data
// with GL_MAP_UNSYNCHRONIZED_BIT
TEST_P(BufferDataTestES3, MapBufferUnsynchronizedReadTest)
{}

// Verify the functionality of glMapBufferRange()'s GL_MAP_UNSYNCHRONIZED_BIT
// NOTE: On Vulkan, if we ever use memory that's not `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`, then
// this could incorrectly pass.
TEST_P(BufferDataTestES3, MapBufferRangeUnsynchronizedBit)
{}

// Verify OES_mapbuffer is present if EXT_map_buffer_range is.
TEST_P(BufferDataTest, ExtensionDependency)
{}

// Test mapping with the OES extension.
TEST_P(BufferDataTest, MapBufferOES)
{}

// Test to verify mapping a dynamic buffer with GL_MAP_UNSYNCHRONIZED_BIT to modify a portion
// won't affect draw calls using other portions.
TEST_P(BufferDataTest, MapDynamicBufferUnsynchronizedEXTTest)
{}

// Verify that we can map and write the buffer between draws and the second draw sees the new buffer
// data, using drawQuad().
TEST_P(BufferDataTest, MapWriteArrayBufferDataDrawQuad)
{}

// Verify that we can map and write the buffer between draws and the second draw sees the new buffer
// data, calling glDrawArrays() directly.
TEST_P(BufferDataTest, MapWriteArrayBufferDataDrawArrays)
{}

// Verify that buffer sub data uploads are properly validated within the buffer size range on 32-bit
// systems.
TEST_P(BufferDataTest, BufferSizeValidation32Bit)
{}

// Some drivers generate errors when array buffer bindings are left mapped during draw calls.
// crbug.com/1345777
TEST_P(BufferDataTestES3, GLDriverErrorWhenMappingArrayBuffersDuringDraw)
{}

// Tests a null crash bug caused by copying from null back-end buffer pointer
// when calling bufferData again after drawing without calling bufferData in D3D11.
TEST_P(BufferDataTestES3, DrawWithNotCallingBufferData)
{}

// Tests a bug where copying buffer data immediately after creation hit a nullptr in D3D11.
TEST_P(BufferDataTestES3, NoBufferInitDataCopyBug)
{}

// This a shortened version of dEQP functional.buffer.copy.basic.array_copy_read. It provoked
// a bug in copyBufferSubData. The bug appeared to be that conversion buffers were not marked
// as dirty and therefore after copyBufferSubData the next draw call using the buffer that
// just had data copied to it was not re-converted. It's not clear to me how this ever worked
// or why changes to bufferSubData from
// https://chromium-review.googlesource.com/c/angle/angle/+/3842641 made this issue appear and
// why it wasn't already broken.
TEST_P(BufferDataTestES3, CopyBufferSubDataDraw)
{}

// Ensures that calling glBufferData on a mapped buffer results in an unmapped buffer
TEST_P(BufferDataTestES3, BufferDataUnmap)
{}

// Ensures that mapping buffer with GL_MAP_INVALIDATE_BUFFER_BIT followed by glBufferSubData calls
// works.  Regression test for the Vulkan backend where that flag caused use after free.
TEST_P(BufferSubDataTest, MapInvalidateThenBufferSubData)
{}

// Verify that previous draws are not affected when a buffer is respecified with null data
// and updated by calling map.
TEST_P(BufferDataTestES3, BufferDataWithNullFollowedByMap)
{}

// Test glFenceSync call breaks renderPass followed by glCopyBufferSubData that read access the same
// buffer that renderPass reads. There was a bug that this triggers assertion angleproject.com/7903.
TEST_P(BufferDataTestES3, bufferReadFromRenderPassAndOutsideRenderPassWithFenceSyncInBetween)
{}

class BufferStorageTestES3 : public BufferDataTest
{};

// Tests that proper error value is returned when bad size is passed in
TEST_P(BufferStorageTestES3, BufferStorageInvalidSize)
{}

// Tests that buffer storage can be allocated with the GL_MAP_PERSISTENT_BIT_EXT and
// GL_MAP_COHERENT_BIT_EXT flags
TEST_P(BufferStorageTestES3, BufferStorageFlagsPersistentCoherentWrite)
{}

// Verify that glBufferStorage makes a buffer immutable
TEST_P(BufferStorageTestES3, StorageBufferBufferData)
{}

// Verify that glBufferStorageEXT can be called after glBufferData
TEST_P(BufferStorageTestES3, BufferDataStorageBuffer)
{}

// Verify that consecutive BufferStorage calls don't clobber data
// This is a regression test for an AllocateNonZeroMemory bug, where the offset
// of the suballocation wasn't being used properly
TEST_P(BufferStorageTestES3, BufferStorageClobber)
{}

// Verify that we can perform subdata updates to a buffer marked with GL_DYNAMIC_STORAGE_BIT_EXT
// usage flag
TEST_P(BufferStorageTestES3, StorageBufferSubData)
{}

// Test interaction between GL_OES_mapbuffer and GL_EXT_buffer_storage extensions.
TEST_P(BufferStorageTestES3, StorageBufferMapBufferOES)
{}

// Verify persistently mapped buffers can use glCopyBufferSubData
// Tests a pattern used by Fortnite's GLES backend
TEST_P(BufferStorageTestES3, StorageCopyBufferSubDataMapped)
{}

// Verify persistently mapped element array buffers can use glDrawElements
TEST_P(BufferStorageTestES3, DrawElementsElementArrayBufferMapped)
{}

// Test that maps a coherent buffer storage and does not call glUnmapBuffer.
TEST_P(BufferStorageTestES3, NoUnmap)
{}

// Test that we are able to perform glTex*D calls while a pixel unpack buffer is bound
// and persistently mapped.
TEST_P(BufferStorageTestES3, TexImage2DPixelUnpackBufferMappedPersistently)
{}

// Verify persistently mapped buffers can use glBufferSubData
TEST_P(BufferStorageTestES3, StorageBufferSubDataMapped)
{}

// Verify that persistently mapped coherent buffers can be used as uniform buffers,
// and written to by using the pointer from glMapBufferRange.
TEST_P(BufferStorageTestES3, UniformBufferMapped)
{}

// Verify that persistently mapped coherent buffers can be used as vertex array buffers,
// and written to by using the pointer from glMapBufferRange.
TEST_P(BufferStorageTestES3, VertexBufferMapped)
{}

void TestPageSharingBuffers(std::function<void(void)> swapCallback,
                            size_t bufferSize,
                            const std::array<Vector3, 6> &quadVertices,
                            GLint positionLocation)
{}

// Create multiple persistently mapped coherent buffers of different sizes that will likely share a
// page. Map all buffers together and unmap each buffer after writing to it and using it for a draw.
// This tests the behaviour of the coherent buffer tracker in frame capture when buffers that share
// a page are written to after the other one is removed.
TEST_P(BufferStorageTestES3, PageSharingBuffers)
{}

class BufferStorageTestES3Threaded : public ANGLETest<>
{};

// Test using a large buffer storage for a color vertex array buffer, which is
// off set every iteration step via glVertexAttribPointer.
// Write to the buffer storage map pointer from multiple threads for the next iteration,
// while drawing the current one.
TEST_P(BufferStorageTestES3Threaded, VertexBuffer)
{}

// Test that buffer self-copy works when buffer is used as UBO
TEST_P(BufferDataTestES3, CopyBufferSubDataSelfDependency)
{}

ANGLE_INSTANTIATE_TEST_ES2();

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST_COMBINE_1();

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST_ES3_AND();

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST_ES3_AND();

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST_ES3();

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST();
ANGLE_INSTANTIATE_TEST_ES3();

#ifdef _WIN64

// Test a bug where an integer overflow bug could trigger a crash in D3D.
// The test uses 8 buffers with a size just under 0x2000000 to overflow max uint
// (with the internal D3D rounding to 16-byte values) and trigger the bug.
// Only handle this bug on 64-bit Windows for now. Harder to repro on 32-bit.
class BufferDataOverflowTest : public ANGLETest<>
{
  protected:
    BufferDataOverflowTest() {}
};

// See description above.
TEST_P(BufferDataOverflowTest, VertexBufferIntegerOverflow)
{
    // These values are special, to trigger the rounding bug.
    unsigned int numItems       = 0x7FFFFFE;
    constexpr GLsizei bufferCnt = 8;

    std::vector<GLBuffer> buffers(bufferCnt);

    std::stringstream vertexShaderStr;

    for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex)
    {
        vertexShaderStr << "attribute float attrib" << bufferIndex << ";\n";
    }

    vertexShaderStr << "attribute vec2 position;\n"
                       "varying float v_attrib;\n"
                       "void main() {\n"
                       "  gl_Position = vec4(position, 0, 1);\n"
                       "  v_attrib = 0.0;\n";

    for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex)
    {
        vertexShaderStr << "v_attrib += attrib" << bufferIndex << ";\n";
    }

    vertexShaderStr << "}";

    constexpr char kFS[] =
        "varying highp float v_attrib;\n"
        "void main() {\n"
        "  gl_FragColor = vec4(v_attrib, 0, 0, 1);\n"
        "}";

    ANGLE_GL_PROGRAM(program, vertexShaderStr.str().c_str(), kFS);
    glUseProgram(program);

    std::vector<GLfloat> data(numItems, 1.0f);

    for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex)
    {
        glBindBuffer(GL_ARRAY_BUFFER, buffers[bufferIndex]);
        glBufferData(GL_ARRAY_BUFFER, numItems * sizeof(float), &data[0], GL_DYNAMIC_DRAW);

        std::stringstream attribNameStr;
        attribNameStr << "attrib" << bufferIndex;

        GLint attribLocation = glGetAttribLocation(program, attribNameStr.str().c_str());
        ASSERT_NE(-1, attribLocation);

        glVertexAttribPointer(attribLocation, 1, GL_FLOAT, GL_FALSE, 4, nullptr);
        glEnableVertexAttribArray(attribLocation);
    }

    GLint positionLocation = glGetAttribLocation(program, "position");
    ASSERT_NE(-1, positionLocation);
    glDisableVertexAttribArray(positionLocation);
    glVertexAttrib2f(positionLocation, 1.0f, 1.0f);

    EXPECT_GL_NO_ERROR();
    glDrawArrays(GL_TRIANGLES, 0, numItems);
    EXPECT_GL_ERROR(GL_OUT_OF_MEMORY);

    // Test that a small draw still works.
    for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex)
    {
        std::stringstream attribNameStr;
        attribNameStr << "attrib" << bufferIndex;
        GLint attribLocation = glGetAttribLocation(program, attribNameStr.str().c_str());
        ASSERT_NE(-1, attribLocation);
        glDisableVertexAttribArray(attribLocation);
    }

    glDrawArrays(GL_TRIANGLES, 0, 3);
    EXPECT_GL_ERROR(GL_NO_ERROR);
}

// Tests a security bug in our CopyBufferSubData validation (integer overflow).
TEST_P(BufferDataOverflowTest, CopySubDataValidation)
{
    GLBuffer readBuffer, writeBuffer;

    glBindBuffer(GL_COPY_READ_BUFFER, readBuffer);
    glBindBuffer(GL_COPY_WRITE_BUFFER, writeBuffer);

    constexpr int bufSize = 100;

    glBufferData(GL_COPY_READ_BUFFER, bufSize, nullptr, GL_STATIC_DRAW);
    glBufferData(GL_COPY_WRITE_BUFFER, bufSize, nullptr, GL_STATIC_DRAW);

    GLintptr big = std::numeric_limits<GLintptr>::max() - bufSize + 90;

    glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, big, 0, 50);
    EXPECT_GL_ERROR(GL_INVALID_VALUE);

    glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, big, 50);
    EXPECT_GL_ERROR(GL_INVALID_VALUE);
}

ANGLE_INSTANTIATE_TEST_ES3(BufferDataOverflowTest);

#endif  // _WIN64