From cc6ac25e95ba23e50f768d65ce88adb4b74af41e Mon Sep 17 00:00:00 2001 From: Jamie Madill Date: Wed, 25 Jan 2017 12:57:21 -0800 Subject: [PATCH] GLES: Expose OES_mapbuffer in GLES2 on GLES3. This extension is mandatory for EXT_map_buffer_range support. We can emulate it using GLES 3.0 core map functionality. BUG=angleproject:1751 Change-Id: Idba09ce7276603d5556039f4a49aa0b87cae22aa Reviewed-on: https://chromium-review.googlesource.com/431826 Reviewed-by: Jamie Madill Commit-Queue: Jamie Madill Commit-Queue: Geoff Lang --- src/libANGLE/renderer/gl/BufferGL.cpp | 9 ++- src/libANGLE/renderer/gl/renderergl_utils.cpp | 1 + src/libANGLE/validationES.cpp | 6 +- src/tests/gl_tests/BufferDataTest.cpp | 50 +++++++++++++++++ src/tests/gl_tests/SimpleOperationTest.cpp | 56 +++++++++++++------ util/random_utils.h | 10 ++++ 6 files changed, 112 insertions(+), 20 deletions(-) diff --git a/src/libANGLE/renderer/gl/BufferGL.cpp b/src/libANGLE/renderer/gl/BufferGL.cpp index 9dfd07614..2de2b3e8a 100644 --- a/src/libANGLE/renderer/gl/BufferGL.cpp +++ b/src/libANGLE/renderer/gl/BufferGL.cpp @@ -115,11 +115,18 @@ gl::Error BufferGL::map(GLenum access, GLvoid **mapPtr) { *mapPtr = mShadowCopy.data(); } - else + else if (mFunctions->mapBuffer) { mStateManager->bindBuffer(DestBufferOperationTarget, mBufferID); *mapPtr = mFunctions->mapBuffer(DestBufferOperationTarget, access); } + else + { + ASSERT(mFunctions->mapBufferRange && access == GL_WRITE_ONLY_OES); + mStateManager->bindBuffer(DestBufferOperationTarget, mBufferID); + *mapPtr = + mFunctions->mapBufferRange(DestBufferOperationTarget, 0, mBufferSize, GL_MAP_WRITE_BIT); + } mIsMapped = true; mMapOffset = 0; diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp index 26f44daae..217ac77fe 100644 --- a/src/libANGLE/renderer/gl/renderergl_utils.cpp +++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp @@ -788,6 +788,7 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM extensions->readFormatBGRA = functions->isAtLeastGL(gl::Version(1, 2)) || functions->hasGLExtension("GL_EXT_bgra") || functions->hasGLESExtension("GL_EXT_read_format_bgra"); extensions->mapBuffer = functions->isAtLeastGL(gl::Version(1, 5)) || + functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_OES_mapbuffer"); extensions->mapBufferRange = functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_ARB_map_buffer_range") || functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_map_buffer_range"); diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp index 3d9dba387..3607de643 100644 --- a/src/libANGLE/validationES.cpp +++ b/src/libANGLE/validationES.cpp @@ -1230,7 +1230,7 @@ bool ValidateGetBufferParameterBase(ValidationContext *context, if (!extensions.mapBuffer) { context->handleError( - Error(GL_INVALID_ENUM, "pname requires OpenGL ES 3.0 or GL_OES_map_buffer.")); + Error(GL_INVALID_ENUM, "pname requires OpenGL ES 3.0 or GL_OES_mapbuffer.")); return false; } break; @@ -1242,7 +1242,7 @@ bool ValidateGetBufferParameterBase(ValidationContext *context, { context->handleError(Error( GL_INVALID_ENUM, - "pname requires OpenGL ES 3.0, GL_OES_map_buffer or GL_EXT_map_buffer_range.")); + "pname requires OpenGL ES 3.0, GL_OES_mapbuffer or GL_EXT_map_buffer_range.")); return false; } break; @@ -4366,7 +4366,7 @@ bool ValidateGetBufferPointervBase(Context *context, { context->handleError( Error(GL_INVALID_OPERATION, - "Context does not support OpenGL ES 3.0 or GL_OES_map_buffer is not enabled.")); + "Context does not support OpenGL ES 3.0 or GL_OES_mapbuffer is not enabled.")); return false; } diff --git a/src/tests/gl_tests/BufferDataTest.cpp b/src/tests/gl_tests/BufferDataTest.cpp index 7707a4a56..5ba705dd2 100644 --- a/src/tests/gl_tests/BufferDataTest.cpp +++ b/src/tests/gl_tests/BufferDataTest.cpp @@ -7,6 +7,8 @@ #include "test_utils/ANGLETest.h" #include "test_utils/gl_raii.h" +#include "random_utils.h" + #include using namespace angle; @@ -433,6 +435,54 @@ TEST_P(BufferDataTestES3, BufferResizing) EXPECT_GL_NO_ERROR(); } +// Verify OES_mapbuffer is present if EXT_map_buffer_range is. +TEST_P(BufferDataTest, ExtensionDependency) +{ + if (extensionEnabled("GL_EXT_map_buffer_range")) + { + ASSERT_TRUE(extensionEnabled("GL_OES_mapbuffer")); + } +} + +// Test mapping with the OES extension. +TEST_P(BufferDataTest, MapBufferOES) +{ + if (!extensionEnabled("GL_EXT_map_buffer_range")) + { + // Needed for test validation. + return; + } + + std::vector data(1024); + FillVectorWithRandomUBytes(&data); + + GLBuffer buffer; + glBindBuffer(GL_ARRAY_BUFFER, buffer.get()); + glBufferData(GL_ARRAY_BUFFER, data.size(), nullptr, GL_STATIC_DRAW); + + // Validate that other map flags don't work. + void *badMapPtr = glMapBufferOES(GL_ARRAY_BUFFER, GL_MAP_READ_BIT); + EXPECT_EQ(nullptr, badMapPtr); + EXPECT_GL_ERROR(GL_INVALID_ENUM); + + // Map and write. + void *mapPtr = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES); + ASSERT_NE(nullptr, mapPtr); + ASSERT_GL_NO_ERROR(); + memcpy(mapPtr, data.data(), data.size()); + glUnmapBufferOES(GL_ARRAY_BUFFER); + + // Validate data with EXT_map_buffer_range + void *readMapPtr = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, data.size(), GL_MAP_READ_BIT_EXT); + ASSERT_NE(nullptr, readMapPtr); + ASSERT_GL_NO_ERROR(); + std::vector actualData(data.size()); + memcpy(actualData.data(), readMapPtr, data.size()); + glUnmapBufferOES(GL_ARRAY_BUFFER); + + EXPECT_EQ(data, actualData); +} + // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. ANGLE_INSTANTIATE_TEST(BufferDataTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES()); ANGLE_INSTANTIATE_TEST(BufferDataTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); diff --git a/src/tests/gl_tests/SimpleOperationTest.cpp b/src/tests/gl_tests/SimpleOperationTest.cpp index b11fb9fe9..4806abd07 100644 --- a/src/tests/gl_tests/SimpleOperationTest.cpp +++ b/src/tests/gl_tests/SimpleOperationTest.cpp @@ -10,6 +10,9 @@ #include +#include "random_utils.h" +#include "test_utils/gl_raii.h" + using namespace angle; namespace @@ -27,8 +30,28 @@ class SimpleOperationTest : public ANGLETest setConfigBlueBits(8); setConfigAlphaBits(8); } + + void verifyBuffer(const std::vector &data, GLenum binding); }; +void SimpleOperationTest::verifyBuffer(const std::vector &data, GLenum binding) +{ + if (!extensionEnabled("GL_EXT_map_buffer_range")) + { + return; + } + + uint8_t *mapPointer = + static_cast(glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 1024, GL_MAP_READ_BIT)); + ASSERT_GL_NO_ERROR(); + + std::vector readbackData(data.size()); + memcpy(readbackData.data(), mapPointer, data.size()); + glUnmapBufferOES(GL_ARRAY_BUFFER); + + EXPECT_EQ(data, readbackData); +} + TEST_P(SimpleOperationTest, CompileVertexShader) { const std::string source = SHADER_SOURCE @@ -154,46 +177,47 @@ TEST_P(SimpleOperationTest, LinkProgramWithAttributes) TEST_P(SimpleOperationTest, BufferDataWithData) { - GLuint buffer; - glGenBuffers(1, &buffer); - glBindBuffer(GL_ARRAY_BUFFER, buffer); + GLBuffer buffer; + glBindBuffer(GL_ARRAY_BUFFER, buffer.get()); std::vector data(1024); + FillVectorWithRandomUBytes(&data); glBufferData(GL_ARRAY_BUFFER, data.size(), &data[0], GL_STATIC_DRAW); - glDeleteBuffers(1, &buffer); + verifyBuffer(data, GL_ARRAY_BUFFER); EXPECT_GL_NO_ERROR(); } TEST_P(SimpleOperationTest, BufferDataWithNoData) { - GLuint buffer; - glGenBuffers(1, &buffer); - glBindBuffer(GL_ARRAY_BUFFER, buffer); + GLBuffer buffer; + glBindBuffer(GL_ARRAY_BUFFER, buffer.get()); glBufferData(GL_ARRAY_BUFFER, 1024, nullptr, GL_STATIC_DRAW); - glDeleteBuffers(1, &buffer); EXPECT_GL_NO_ERROR(); } TEST_P(SimpleOperationTest, BufferSubData) { - GLuint buffer; - glGenBuffers(1, &buffer); - glBindBuffer(GL_ARRAY_BUFFER, buffer); + GLBuffer buffer; + glBindBuffer(GL_ARRAY_BUFFER, buffer.get()); + + constexpr size_t bufferSize = 1024; + std::vector data(bufferSize); + FillVectorWithRandomUBytes(&data); - const size_t bufferSize = 1024; glBufferData(GL_ARRAY_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW); - const size_t subDataCount = 16; - std::vector data(bufferSize / subDataCount); + constexpr size_t subDataCount = 16; + constexpr size_t sliceSize = bufferSize / subDataCount; for (size_t i = 0; i < subDataCount; i++) { - glBufferSubData(GL_ARRAY_BUFFER, data.size() * i, data.size(), &data[0]); + size_t offset = i * sliceSize; + glBufferSubData(GL_ARRAY_BUFFER, offset, sliceSize, &data[offset]); } - glDeleteBuffers(1, &buffer); + verifyBuffer(data, GL_ARRAY_BUFFER); EXPECT_GL_NO_ERROR(); } diff --git a/util/random_utils.h b/util/random_utils.h index 39f9044ee..1128e0332 100644 --- a/util/random_utils.h +++ b/util/random_utils.h @@ -40,6 +40,16 @@ class ANGLE_EXPORT RNG std::default_random_engine mGenerator; }; +// Implemented htis way because of cross-module allocation issues. +inline void FillVectorWithRandomUBytes(std::vector *data) +{ + RNG rng; + for (size_t i = 0; i < data->size(); ++i) + { + (*data)[i] = static_cast(rng.randomIntBetween(0, 255)); + } +} + } // namespace angle #endif // UTIL_RANDOM_UTILS_H