Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,51 @@
project(drape_tests)
set(DRAPE_TESTABLE_SRC_PREFIXED ${DRAPE_TESTABLE_SRC})
list(TRANSFORM DRAPE_TESTABLE_SRC_PREFIXED PREPEND "../")
set(SRC
${DRAPE_TESTABLE_SRC_PREFIXED}
attribute_provides_tests.cpp
batcher_tests.cpp
bingind_info_tests.cpp
buffer_tests.cpp
dummy_texture.hpp
font_texture_tests.cpp
gl_functions.cpp
gl_mock_functions.cpp
gl_mock_functions.hpp
glyph_mng_tests.cpp
glyph_packer_test.cpp
harfbuzz_shaping_test.cpp
img.cpp
img.hpp
memory_comparer.hpp
object_pool_tests.cpp
pointers_tests.cpp
static_texture_tests.cpp
stipple_pen_tests.cpp
texture_of_colors_tests.cpp
testing_graphics_context.hpp
uniform_value_tests.cpp
vertex_buffer_tests.cpp
)
omim_add_test(${PROJECT_NAME} ${SRC})
target_compile_definitions(${PROJECT_NAME}
PRIVATE GTEST_DONT_DEFINE_TEST WITH_GL_MOCK
)
if (WITH_SYSTEM_PROVIDED_3PARTY)
find_package(GTest REQUIRED)
target_link_libraries(${PROJECT_NAME} GTest::gtest)
else()
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../../../3party/googletest" "${CMAKE_CURRENT_BINARY_DIR}/googletest")
target_include_directories(${PROJECT_NAME} PUBLIC ${OMIM_ROOT}/3party/glm)
endif()
target_link_libraries(${PROJECT_NAME}
qt_tstfrm
gmock
${DRAPE_LINK_LIBRARIES}
)

View file

@ -0,0 +1,201 @@
#include "testing/testing.hpp"
#include "drape/attribute_provider.hpp"
#include <gmock/gmock.h>
using namespace dp;
UNIT_TEST(InitStreamsTest)
{
int const VERTEX_COUNT = 10;
AttributeProvider provider(3, VERTEX_COUNT);
float positions[2 * VERTEX_COUNT];
float depth[VERTEX_COUNT];
float normals[2 * VERTEX_COUNT];
for (int i = 0; i < VERTEX_COUNT; ++i)
{
positions[2 * i] = (float)i;
positions[(2 * i) + 1] = 0.0f;
depth[i] = (float)i;
normals[2 * i] = (float)i;
normals[(2 * i) + 1] = 0.0;
}
{
BindingInfo zeroStreamBinding(1);
BindingDecl & decl = zeroStreamBinding.GetBindingDecl(0);
decl.m_attributeName = "position";
decl.m_componentCount = 2;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 0;
decl.m_stride = 0;
provider.InitStream(0, zeroStreamBinding, make_ref(positions));
}
{
BindingInfo firstStreamBinding(1);
BindingDecl & decl = firstStreamBinding.GetBindingDecl(0);
decl.m_attributeName = "depth";
decl.m_componentCount = 1;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 0;
decl.m_stride = 0;
provider.InitStream(1, firstStreamBinding, make_ref(depth));
}
{
BindingInfo secondStreamBinding(1);
BindingDecl & decl = secondStreamBinding.GetBindingDecl(0);
decl.m_attributeName = "normal";
decl.m_componentCount = 2;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 0;
decl.m_stride = 0;
provider.InitStream(2, secondStreamBinding, make_ref(normals));
}
TEST_EQUAL(provider.IsDataExists(), true, ());
TEST_EQUAL(provider.GetVertexCount(), 10, ());
TEST_EQUAL(provider.GetRawPointer(0), (void *)positions, ());
TEST_EQUAL(provider.GetRawPointer(1), (void *)depth, ());
TEST_EQUAL(provider.GetRawPointer(2), (void *)normals, ());
provider.Advance(1);
TEST_EQUAL(provider.IsDataExists(), true, ());
TEST_EQUAL(provider.GetVertexCount(), 9, ());
TEST_EQUAL(provider.GetRawPointer(0), (void *)(&positions[2]), ());
TEST_EQUAL(provider.GetRawPointer(1), (void *)(&depth[1]), ());
TEST_EQUAL(provider.GetRawPointer(2), (void *)(&normals[2]), ());
provider.Advance(9);
TEST_EQUAL(provider.IsDataExists(), false, ());
TEST_EQUAL(provider.GetVertexCount(), 0, ());
}
UNIT_TEST(InterleavedStreamTest)
{
int const VERTEX_COUNT = 10;
AttributeProvider provider(1, 10);
float data[5 * VERTEX_COUNT];
for (int i = 0; i < VERTEX_COUNT; ++i)
{
data[(5 * i)] = (float)i;
data[(5 * i) + 1] = 0.0;
data[(5 * i) + 2] = (float)i;
data[(5 * i) + 3] = (float)i;
data[(5 * i) + 4] = 0.0;
}
BindingInfo binding(3);
{
BindingDecl & decl = binding.GetBindingDecl(0);
decl.m_attributeName = "position";
decl.m_componentCount = 2;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 0;
decl.m_stride = 5 * sizeof(float);
}
{
BindingDecl & decl = binding.GetBindingDecl(1);
decl.m_attributeName = "depth";
decl.m_componentCount = 1;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 2 * sizeof(float);
decl.m_stride = 5 * sizeof(float);
}
{
BindingDecl & decl = binding.GetBindingDecl(2);
decl.m_attributeName = "normal";
decl.m_componentCount = 2;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 3 * sizeof(float);
decl.m_stride = 5 * sizeof(float);
}
provider.InitStream(0, binding, make_ref(data));
TEST_EQUAL(provider.IsDataExists(), true, ());
TEST_EQUAL(provider.GetVertexCount(), 10, ());
TEST_EQUAL(provider.GetRawPointer(0), (void *)data, ());
provider.Advance(1);
TEST_EQUAL(provider.IsDataExists(), true, ());
TEST_EQUAL(provider.GetVertexCount(), 9, ());
TEST_EQUAL(provider.GetRawPointer(0), (void *)(&data[5]), ());
provider.Advance(9);
TEST_EQUAL(provider.IsDataExists(), false, ());
TEST_EQUAL(provider.GetVertexCount(), 0, ());
}
UNIT_TEST(MixedStreamsTest)
{
int const VERTEX_COUNT = 10;
AttributeProvider provider(2, 10);
float position[3 * VERTEX_COUNT];
float normal[2 * VERTEX_COUNT];
for (int i = 0; i < VERTEX_COUNT; ++i)
{
position[3 * i] = (float)i; // x
position[(3 * i) + 1] = 0.0; // y
position[(3 * i) + 2] = (float)i; // z
position[2 * i] = (float)i; // Nx
position[(2 * i) + 1] = 0.0; // Ny
}
{
BindingInfo binding(2);
{
BindingDecl & decl = binding.GetBindingDecl(0);
decl.m_attributeName = "position";
decl.m_componentCount = 2;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 0;
decl.m_stride = 3 * sizeof(float);
}
{
BindingDecl & decl = binding.GetBindingDecl(1);
decl.m_attributeName = "depth";
decl.m_componentCount = 1;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 2 * sizeof(float);
decl.m_stride = 3 * sizeof(float);
}
provider.InitStream(0, binding, make_ref(position));
}
{
BindingInfo binding(1);
BindingDecl & decl = binding.GetBindingDecl(0);
decl.m_attributeName = "normal";
decl.m_componentCount = 2;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 0;
decl.m_stride = 0;
provider.InitStream(1, binding, make_ref(normal));
}
TEST_EQUAL(provider.IsDataExists(), true, ());
TEST_EQUAL(provider.GetVertexCount(), 10, ());
TEST_EQUAL(provider.GetRawPointer(0), (void *)position, ());
TEST_EQUAL(provider.GetRawPointer(1), (void *)normal, ());
provider.Advance(1);
TEST_EQUAL(provider.IsDataExists(), true, ());
TEST_EQUAL(provider.GetVertexCount(), 9, ());
TEST_EQUAL(provider.GetRawPointer(0), (void *)(&position[3]), ());
TEST_EQUAL(provider.GetRawPointer(1), (void *)(&normal[2]), ());
provider.Advance(9);
TEST_EQUAL(provider.IsDataExists(), false, ());
TEST_EQUAL(provider.GetVertexCount(), 0, ());
}

View file

@ -0,0 +1,355 @@
#include "testing/testing.hpp"
#include "drape/drape_tests/gl_mock_functions.hpp"
#include "drape/drape_tests/memory_comparer.hpp"
#include "drape/drape_tests/testing_graphics_context.hpp"
#include "drape/batcher.hpp"
#include "drape/gl_constants.hpp"
#include "drape/index_storage.hpp"
#include "drape/vertex_array_buffer.hpp"
#include "base/stl_helpers.hpp"
#include <cstring>
#include <functional>
#include <vector>
#include <gmock/gmock.h>
using testing::_;
using testing::AnyOf;
using testing::IgnoreResult;
using testing::InSequence;
using testing::Invoke;
using testing::Return;
using namespace dp;
using namespace std::placeholders;
namespace
{
struct VAOAcceptor
{
virtual void FlushFullBucket(RenderState const & /* state */, drape_ptr<RenderBucket> && bucket)
{
m_vao.push_back(std::move(bucket));
}
std::vector<drape_ptr<RenderBucket>> m_vao;
};
class TestExtension : public dp::BaseRenderStateExtension
{
public:
bool Less(ref_ptr<dp::BaseRenderStateExtension> other) const override { return false; }
bool Equal(ref_ptr<dp::BaseRenderStateExtension> other) const override { return true; }
};
class BatcherExpectations
{
public:
BatcherExpectations() : m_indexBufferID(1), m_dataBufferID(2) {}
template <typename TBatcherCall>
void RunTest(float * vertexes, void * indexes, uint32_t vertexCount, uint8_t vertexComponentCount,
uint32_t indexCount, TBatcherCall const & fn)
{
uint32_t const vertexSize = vertexCount * vertexComponentCount;
MemoryComparer const dataCmp(vertexes, vertexSize * sizeof(float));
MemoryComparer const indexCmp(indexes, indexCount * dp::IndexStorage::SizeOfIndex());
ExpectBufferCreation(vertexSize, indexCount, indexCmp, dataCmp);
auto renderState = make_unique_dp<TestExtension>();
auto state = RenderState(0, make_ref(renderState));
BindingInfo binding(1);
BindingDecl & decl = binding.GetBindingDecl(0);
decl.m_attributeName = "position";
decl.m_componentCount = vertexComponentCount;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 0;
decl.m_stride = 0;
AttributeProvider provider(1, vertexCount);
provider.InitStream(0, binding, make_ref(vertexes));
TestingGraphicsContext context;
VAOAcceptor vaoAcceptor;
Batcher batcher(65000, 65000);
batcher.StartSession(std::bind(&VAOAcceptor::FlushFullBucket, &vaoAcceptor, _1, _2));
fn(&batcher, state, make_ref(&provider));
batcher.EndSession(make_ref(&context));
ExpectBufferDeletion();
for (size_t i = 0; i < vaoAcceptor.m_vao.size(); ++i)
vaoAcceptor.m_vao[i].reset();
}
void ExpectBufferCreation(uint32_t vertexCount, uint32_t indexCount, MemoryComparer const & indexCmp,
MemoryComparer const & vertexCmp)
{
InSequence seq;
// data buffer creation
EXPECTGL(glGenBuffer()).WillOnce(Return(m_dataBufferID));
EXPECTGL(glBindBuffer(m_dataBufferID, gl_const::GLArrayBuffer));
EXPECTGL(glBufferData(gl_const::GLArrayBuffer, vertexCount * sizeof(float), _, gl_const::GLDynamicDraw))
.WillOnce(Invoke(&vertexCmp, &MemoryComparer::cmpSubBuffer));
// Index buffer creation
EXPECTGL(glGenBuffer()).WillOnce(Return(m_indexBufferID));
EXPECTGL(glBindBuffer(m_indexBufferID, gl_const::GLElementArrayBuffer));
EXPECTGL(glBufferData(gl_const::GLElementArrayBuffer, indexCount * dp::IndexStorage::SizeOfIndex(), _,
gl_const::GLDynamicDraw))
.WillOnce(Invoke(&indexCmp, &MemoryComparer::cmpSubBuffer));
EXPECTGL(glBindBuffer(0, gl_const::GLElementArrayBuffer));
EXPECTGL(glBindBuffer(0, gl_const::GLArrayBuffer));
}
void ExpectBufferDeletion()
{
InSequence seq;
EXPECTGL(glBindBuffer(0, gl_const::GLElementArrayBuffer));
EXPECTGL(glDeleteBuffer(m_indexBufferID));
EXPECTGL(glBindBuffer(0, gl_const::GLArrayBuffer));
EXPECTGL(glDeleteBuffer(m_dataBufferID));
}
private:
uint32_t m_indexBufferID = 0;
uint32_t m_dataBufferID = 0;
};
} // namespace
UNIT_TEST(BatchLists_Test)
{
TestingGraphicsContext context;
uint32_t const kVerticesCount = 12;
uint32_t const kFloatsCount = 3 * 12; // 3 component on each vertex.
float data[kFloatsCount];
for (uint32_t i = 0; i < kFloatsCount; ++i)
data[i] = static_cast<float>(i);
std::vector<uint32_t> indexesRaw(kVerticesCount);
for (uint32_t i = 0; i < kVerticesCount; ++i)
indexesRaw[i] = i;
dp::IndexStorage indexes(std::move(indexesRaw));
BatcherExpectations expectations;
auto fn = [&context](Batcher * batcher, RenderState const & state, ref_ptr<AttributeProvider> p)
{ batcher->InsertTriangleList(make_ref(&context), state, p); };
expectations.RunTest(data, indexes.GetRaw(), kVerticesCount, 3, kVerticesCount, fn);
}
UNIT_TEST(BatchListOfStript_4stride)
{
TestingGraphicsContext context;
uint32_t const kVerticesCount = 12;
uint32_t const kIndicesCount = 18;
float data[3 * kVerticesCount];
for (uint32_t i = 0; i < kVerticesCount * 3; ++i)
data[i] = static_cast<float>(i);
std::vector<uint32_t> indexesRaw = {0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7, 6, 8, 9, 10, 9, 11, 10};
dp::IndexStorage indexes(std::move(indexesRaw));
BatcherExpectations expectations;
auto fn = [&context](Batcher * batcher, RenderState const & state, ref_ptr<AttributeProvider> p)
{ batcher->InsertListOfStrip(make_ref(&context), state, p, dp::Batcher::VertexPerQuad); };
expectations.RunTest(data, indexes.GetRaw(), kVerticesCount, 3, kIndicesCount, fn);
}
UNIT_TEST(BatchListOfStript_5stride)
{
TestingGraphicsContext context;
uint32_t const kVerticesCount = 15;
uint32_t const kIndicesCount = 27;
float data[3 * kVerticesCount];
for (uint32_t i = 0; i < kVerticesCount * 3; ++i)
data[i] = static_cast<float>(i);
std::vector<uint32_t> indexesRaw = {0, 1, 2, 1, 3, 2, 2, 3, 4, 5, 6, 7, 6, 8,
7, 7, 8, 9, 10, 11, 12, 11, 13, 12, 12, 13, 14};
dp::IndexStorage indexes(std::move(indexesRaw));
BatcherExpectations expectations;
auto fn = [&context](Batcher * batcher, RenderState const & state, ref_ptr<AttributeProvider> p)
{ batcher->InsertListOfStrip(make_ref(&context), state, p, 5); };
expectations.RunTest(data, indexes.GetRaw(), kVerticesCount, 3, kIndicesCount, fn);
}
UNIT_TEST(BatchListOfStript_6stride)
{
TestingGraphicsContext context;
uint32_t const kVerticesCount = 18;
uint32_t const kIndicesCount = 36;
float data[3 * kVerticesCount];
for (uint32_t i = 0; i < kVerticesCount * 3; ++i)
data[i] = static_cast<float>(i);
std::vector<uint32_t> indexesRaw = {0, 1, 2, 1, 3, 2, 2, 3, 4, 3, 5, 4, 6, 7, 8, 7, 9, 8,
8, 9, 10, 9, 11, 10, 12, 13, 14, 13, 15, 14, 14, 15, 16, 15, 17, 16};
dp::IndexStorage indexes(std::move(indexesRaw));
BatcherExpectations expectations;
auto fn = [&context](Batcher * batcher, RenderState const & state, ref_ptr<AttributeProvider> p)
{ batcher->InsertListOfStrip(make_ref(&context), state, p, 6); };
expectations.RunTest(data, indexes.GetRaw(), kVerticesCount, 3, kIndicesCount, fn);
}
namespace
{
class PartialBatcherTest
{
public:
struct BufferNode
{
BufferNode(uint32_t indexByteCount, uint32_t vertexByteCount, void * indexData, void * vertexData)
: m_indexByteCount(indexByteCount)
, m_vertexByteCount(vertexByteCount)
, m_indexData(indexData)
, m_vertexData(vertexData)
, m_indexBufferID(0)
, m_vertexBufferID(0)
{}
uint32_t m_indexByteCount;
uint32_t m_vertexByteCount;
void * m_indexData;
void * m_vertexData;
uint32_t m_indexBufferID;
uint32_t m_vertexBufferID;
};
PartialBatcherTest() = default;
~PartialBatcherTest() { std::for_each(m_comparators.begin(), m_comparators.end(), base::DeleteFunctor()); }
void AddBufferNode(BufferNode const & node)
{
m_nodes.push_back(node);
BufferNode & currentNode = m_nodes.back();
currentNode.m_indexBufferID = m_bufferIDCounter++;
currentNode.m_vertexBufferID = m_bufferIDCounter++;
// data buffer creation
EXPECTGL(glGenBuffer()).WillOnce(Return(currentNode.m_vertexBufferID));
EXPECTGL(glBindBuffer(currentNode.m_vertexBufferID, gl_const::GLArrayBuffer));
m_comparators.push_back(new MemoryComparer(currentNode.m_vertexData, currentNode.m_vertexByteCount));
MemoryComparer * vertexComparer = m_comparators.back();
EXPECTGL(glBufferData(gl_const::GLArrayBuffer, currentNode.m_vertexByteCount, _, gl_const::GLDynamicDraw))
.WillOnce(Invoke(vertexComparer, &MemoryComparer::cmpSubBuffer));
// Index buffer creation
EXPECTGL(glGenBuffer()).WillOnce(Return(currentNode.m_indexBufferID));
EXPECTGL(glBindBuffer(currentNode.m_indexBufferID, gl_const::GLElementArrayBuffer));
m_comparators.push_back(new MemoryComparer(currentNode.m_indexData, currentNode.m_indexByteCount));
MemoryComparer * indexComparer = m_comparators.back();
EXPECTGL(glBufferData(gl_const::GLElementArrayBuffer, currentNode.m_indexByteCount, _, gl_const::GLDynamicDraw))
.WillOnce(Invoke(indexComparer, &MemoryComparer::cmpSubBuffer));
EXPECTGL(glBindBuffer(0, gl_const::GLElementArrayBuffer));
EXPECTGL(glBindBuffer(0, gl_const::GLArrayBuffer));
}
void CloseExpection()
{
for (auto const & node : m_nodes)
{
EXPECTGL(glBindBuffer(0, gl_const::GLElementArrayBuffer));
EXPECTGL(glDeleteBuffer(node.m_indexBufferID));
EXPECTGL(glBindBuffer(0, gl_const::GLArrayBuffer));
EXPECTGL(glDeleteBuffer(node.m_vertexBufferID));
}
}
private:
uint32_t m_bufferIDCounter = 1;
std::vector<BufferNode> m_nodes;
std::vector<MemoryComparer *> m_comparators;
};
} // namespace
UNIT_TEST(BatchListOfStript_partial)
{
TestingGraphicsContext context;
uint32_t const VertexCount = 16;
uint32_t const ComponentCount = 3;
uint32_t const VertexArraySize = VertexCount * ComponentCount;
uint32_t const IndexCount = 24;
uint32_t const FirstBufferVertexPortion = 12;
uint32_t const SecondBufferVertexPortion = VertexCount - FirstBufferVertexPortion;
uint32_t const FirstBufferIndexPortion = 18;
uint32_t const SecondBufferIndexPortion = IndexCount - FirstBufferIndexPortion;
float vertexData[VertexArraySize];
for (uint32_t i = 0; i < VertexArraySize; ++i)
vertexData[i] = static_cast<float>(i);
std::vector<uint32_t> indexDataRaw = {0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7,
6, 8, 9, 10, 9, 11, 10, 0, 1, 2, // start new buffer
1, 3, 2};
dp::IndexStorage indexData(std::move(indexDataRaw));
PartialBatcherTest::BufferNode node1(FirstBufferIndexPortion * dp::IndexStorage::SizeOfIndex(),
FirstBufferVertexPortion * ComponentCount * sizeof(float), indexData.GetRaw(),
vertexData);
PartialBatcherTest::BufferNode node2(SecondBufferIndexPortion * dp::IndexStorage::SizeOfIndex(),
SecondBufferVertexPortion * ComponentCount * sizeof(float),
indexData.GetRaw(FirstBufferIndexPortion),
vertexData + FirstBufferVertexPortion * ComponentCount);
using IndexVertexCount = std::pair<uint32_t, uint32_t>;
std::vector<IndexVertexCount> srcData;
srcData.emplace_back(30, 12);
srcData.emplace_back(30, 13);
srcData.emplace_back(18, 30);
srcData.emplace_back(19, 30);
for (size_t i = 0; i < srcData.size(); ++i)
{
InSequence seq;
PartialBatcherTest test;
test.AddBufferNode(node1);
test.AddBufferNode(node2);
test.CloseExpection();
auto renderState = make_unique_dp<TestExtension>();
auto state = RenderState(0, make_ref(renderState));
BindingInfo binding(1);
BindingDecl & decl = binding.GetBindingDecl(0);
decl.m_attributeName = "position";
decl.m_componentCount = ComponentCount;
decl.m_componentType = gl_const::GLFloatType;
decl.m_offset = 0;
decl.m_stride = 0;
AttributeProvider provider(1, VertexCount);
provider.InitStream(0, binding, make_ref(vertexData));
VAOAcceptor vaoAcceptor;
Batcher batcher(srcData[i].first, srcData[i].second);
batcher.StartSession(std::bind(&VAOAcceptor::FlushFullBucket, &vaoAcceptor, _1, _2));
batcher.InsertListOfStrip(make_ref(&context), state, make_ref(&provider), 4);
batcher.EndSession(make_ref(&context));
for (size_t i = 0; i < vaoAcceptor.m_vao.size(); ++i)
vaoAcceptor.m_vao[i].reset();
}
}

View file

@ -0,0 +1,31 @@
#include "testing/testing.hpp"
#include "drape/binding_info.hpp"
using namespace dp;
UNIT_TEST(BindingInfoIDTest)
{
{
BindingInfo info(1, 1);
TEST_EQUAL(info.GetID(), 1, ());
}
{
BindingInfo info(1);
TEST_EQUAL(info.GetID(), 0, ());
}
}
UNIT_TEST(DynamicHandlingTest)
{
{
BindingInfo info(1);
TEST_EQUAL(info.IsDynamic(), false, ());
}
{
BindingInfo info(1, 1);
TEST_EQUAL(info.IsDynamic(), true, ());
}
}

View file

@ -0,0 +1,111 @@
#include "testing/testing.hpp"
#include "drape/drape_tests/gl_mock_functions.hpp"
#include "drape/drape_tests/testing_graphics_context.hpp"
#include "drape/data_buffer.hpp"
#include "drape/gpu_buffer.hpp"
#include "drape/index_buffer.hpp"
#include "drape/index_storage.hpp"
#include <cstdlib>
#include <memory>
#include <gmock/gmock.h>
using namespace emul;
using namespace dp;
using ::testing::_;
using ::testing::InSequence;
using ::testing::Return;
UNIT_TEST(CreateDestroyDataBufferTest)
{
TestingGraphicsContext context;
InSequence s;
EXPECTGL(glGenBuffer()).WillOnce(Return(1));
EXPECTGL(glBindBuffer(1, gl_const::GLArrayBuffer));
EXPECTGL(glBufferData(gl_const::GLArrayBuffer, 3 * 100 * sizeof(float), nullptr, gl_const::GLDynamicDraw));
EXPECTGL(glBindBuffer(0, gl_const::GLArrayBuffer));
EXPECTGL(glDeleteBuffer(1));
std::unique_ptr<DataBuffer> buffer(new DataBuffer(3 * sizeof(float), 100));
buffer->MoveToGPU(make_ref(&context), GPUBuffer::ElementBuffer, 0);
}
UNIT_TEST(CreateDestroyIndexBufferTest)
{
TestingGraphicsContext context;
InSequence s;
EXPECTGL(glGenBuffer()).WillOnce(Return(1));
EXPECTGL(glBindBuffer(1, gl_const::GLElementArrayBuffer));
EXPECTGL(glBufferData(gl_const::GLElementArrayBuffer, 100 * dp::IndexStorage::SizeOfIndex(), nullptr,
gl_const::GLDynamicDraw));
EXPECTGL(glBindBuffer(0, gl_const::GLElementArrayBuffer));
EXPECTGL(glDeleteBuffer(1));
std::unique_ptr<IndexBuffer> buffer(new IndexBuffer(100));
buffer->MoveToGPU(make_ref(&context), GPUBuffer::IndexBuffer, 0);
}
UNIT_TEST(UploadDataTest)
{
float data[3 * 100];
for (int i = 0; i < 3 * 100; ++i)
data[i] = (float)i;
std::unique_ptr<DataBuffer> buffer(new DataBuffer(3 * sizeof(float), 100));
TestingGraphicsContext context;
InSequence s;
EXPECTGL(glGenBuffer()).WillOnce(Return(1));
EXPECTGL(glBindBuffer(1, gl_const::GLArrayBuffer));
EXPECTGL(glBufferData(gl_const::GLArrayBuffer, 3 * 100 * sizeof(float), buffer->GetBuffer()->Data(),
gl_const::GLDynamicDraw));
EXPECTGL(glBindBuffer(0, gl_const::GLArrayBuffer));
EXPECTGL(glDeleteBuffer(1));
buffer->GetBuffer()->UploadData(make_ref(&context), data, 100);
buffer->MoveToGPU(make_ref(&context), GPUBuffer::ElementBuffer, 0);
}
UNIT_TEST(ParticalUploadDataTest)
{
size_t const kPart1Size = 3 * 30;
float part1Data[kPart1Size];
for (size_t i = 0; i < kPart1Size; ++i)
part1Data[i] = (float)i;
size_t const kPart2Size = 3 * 100;
float part2Data[kPart2Size];
for (size_t i = 0; i < kPart2Size; ++i)
part2Data[i] = (float)i;
std::unique_ptr<DataBuffer> buffer(new DataBuffer(3 * sizeof(float), 100));
TestingGraphicsContext context;
InSequence s;
EXPECTGL(glGenBuffer()).WillOnce(Return(1));
EXPECTGL(glBindBuffer(1, gl_const::GLArrayBuffer));
EXPECTGL(glBufferData(gl_const::GLArrayBuffer, 3 * 100 * sizeof(float), buffer->GetBuffer()->Data(),
gl_const::GLDynamicDraw));
EXPECTGL(glBindBuffer(0, gl_const::GLArrayBuffer));
EXPECTGL(glDeleteBuffer(1));
TEST_EQUAL(buffer->GetBuffer()->GetCapacity(), 100, ());
TEST_EQUAL(buffer->GetBuffer()->GetAvailableSize(), 100, ());
TEST_EQUAL(buffer->GetBuffer()->GetCurrentSize(), 0, ());
buffer->GetBuffer()->UploadData(make_ref(&context), part1Data, 30);
TEST_EQUAL(buffer->GetBuffer()->GetCapacity(), 100, ());
TEST_EQUAL(buffer->GetBuffer()->GetAvailableSize(), 70, ());
TEST_EQUAL(buffer->GetBuffer()->GetCurrentSize(), 30, ());
buffer->GetBuffer()->UploadData(make_ref(&context), part2Data, 70);
TEST_EQUAL(buffer->GetBuffer()->GetCapacity(), 100, ());
TEST_EQUAL(buffer->GetBuffer()->GetAvailableSize(), 0, ());
TEST_EQUAL(buffer->GetBuffer()->GetCurrentSize(), 100, ());
buffer->MoveToGPU(make_ref(&context), GPUBuffer::ElementBuffer, 0);
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "drape/pointers.hpp"
#include "drape/texture.hpp"
class DummyTexture : public dp::Texture
{
public:
ref_ptr<ResourceInfo> FindResource(Key const & key)
{
bool dummy = false;
return FindResource(key, dummy);
}
virtual ref_ptr<ResourceInfo> FindResource(Key const & /*key*/, bool & /*newResource*/) { return nullptr; }
};

View file

@ -0,0 +1,137 @@
/* This test crashes with ASSERT_NOT_EQUAL(CurrentApiVersion, dp::ApiVersion::Invalid, ()); in gl_functions.cpp
#include "drape/drape_tests/dummy_texture.hpp"
#include "drape/drape_tests/gl_mock_functions.hpp"
#include "drape/drape_tests/img.hpp"
#include "drape/drape_tests/testing_graphics_context.hpp"
#include "base/file_name_utils.hpp"
#include "platform/platform.hpp"
#include "qt_tstfrm/test_main_loop.hpp"
#include "testing/testing.hpp"
#include "drape/drape_routine.hpp"
#include "drape/font_constants.hpp"
#include "drape/font_texture.hpp"
#include "drape/glyph_manager.hpp"
#include <functional>
#include <QtCore/QPoint>
#include <QtGui/QPainter>
#include <gmock/gmock.h>
using ::testing::_;
using ::testing::Return;
using ::testing::InSequence;
using ::testing::AnyNumber;
using ::testing::Invoke;
using namespace dp;
using namespace std::placeholders;
namespace
{
class UploadedRender
{
public:
explicit UploadedRender(QPoint const & pen) : m_pen(pen) {}
void glMemoryToQImage(int x, int y, int w, int h, glConst f, glConst t, void const * memory)
{
TEST(f == gl_const::GLAlpha || f == gl_const::GLAlpha8 || f == gl_const::GLRed, ());
TEST(t == gl_const::GLUnsignedByteType, ());
uint8_t const * image = reinterpret_cast<uint8_t const *>(memory);
QPoint p(m_pen);
p.rx() += x;
m_images.push_back(qMakePair(p, CreateImage(w, h, image)));
m_pen.ry() += h;
}
void Render(QPaintDevice * device)
{
QPainter p(device);
for (auto const & d : m_images)
p.drawImage(d.first, d.second);
}
private:
QPoint m_pen;
QVector<QPair<QPoint, QImage>> m_images;
};
class DummyGlyphIndex : public GlyphIndex
{
public:
DummyGlyphIndex(m2::PointU size, ref_ptr<GlyphManager> mng)
: GlyphIndex(size, mng)
{}
ref_ptr<Texture::ResourceInfo> MapResource(GlyphKey const & key)
{
bool dummy = false;
return GlyphIndex::MapResource(key, dummy);
}
};
} // namespace
UNIT_TEST(UploadingGlyphs)
{
// Set QT_QPA_PLATFORM=offscreen env var to avoid running GUI on Linux
DrapeRoutine::Init();
EXPECTGL(glHasExtension(_)).Times(AnyNumber());
EXPECTGL(glBindTexture(_)).Times(AnyNumber());
EXPECTGL(glDeleteTexture(_)).Times(AnyNumber());
EXPECTGL(glTexParameter(_, _)).Times(AnyNumber());
EXPECTGL(glTexImage2D(_, _, _, _, _)).Times(AnyNumber());
EXPECTGL(glGenTexture()).Times(AnyNumber());
UploadedRender r(QPoint(10, 10));
dp::GlyphManager::Params args;
args.m_uniBlocks = base::JoinPath("fonts", "unicode_blocks.txt");
args.m_whitelist = base::JoinPath("fonts", "whitelist.txt");
args.m_blacklist = base::JoinPath("fonts", "blacklist.txt");
GetPlatform().GetFontNames(args.m_fonts);
uint32_t constexpr kTextureSize = 1024;
GlyphManager mng(args);
DummyGlyphIndex index(m2::PointU(kTextureSize, kTextureSize), make_ref(&mng));
size_t count = 1; // invalid symbol glyph has been mapped internally.
count += (index.MapResource(GlyphKey(0x58)) != nullptr) ? 1 : 0;
count += (index.MapResource(GlyphKey(0x59)) != nullptr) ? 1 : 0;
count += (index.MapResource(GlyphKey(0x61)) != nullptr) ? 1 : 0;
while (index.GetPendingNodesCount() < count)
;
TestingGraphicsContext context;
Texture::Params p;
p.m_allocator = GetDefaultAllocator(make_ref(&context));
p.m_format = dp::TextureFormat::Red;
p.m_width = p.m_height = kTextureSize;
DummyTexture tex;
tex.Create(make_ref(&context), p);
EXPECTGL(glTexSubImage2D(_, _, _, _, _, _, _))
.WillRepeatedly(Invoke(&r, &UploadedRender::glMemoryToQImage));
index.UploadResources(make_ref(&context), make_ref(&tex));
count = 0;
count += (index.MapResource(GlyphKey(0x68)) != nullptr) ? 1 : 0;
count += (index.MapResource(GlyphKey(0x30)) != nullptr) ? 1 : 0;
count += (index.MapResource(GlyphKey(0x62)) != nullptr) ? 1 : 0;
count += (index.MapResource(GlyphKey(0x65)) != nullptr) ? 1 : 0;
count += (index.MapResource(GlyphKey(0x400)) != nullptr) ? 1 : 0;
count += (index.MapResource(GlyphKey(0x401)) != nullptr) ? 1 : 0;
// TODO: Fix this condition
//while (index.GetPendingNodesCount() < count)
// ;
EXPECTGL(glTexSubImage2D(_, _, _, _, _, _, _))
.WillRepeatedly(Invoke(&r, &UploadedRender::glMemoryToQImage));
index.UploadResources(make_ref(&context), make_ref(&tex));
RunTestLoop("UploadingGlyphs", std::bind(&UploadedRender::Render, &r, _1));
DrapeRoutine::Shutdown();
}
*/

View file

@ -0,0 +1,374 @@
#include "drape/gl_functions.hpp"
#include "drape/drape_tests/gl_mock_functions.hpp"
#include "base/assert.hpp"
using namespace emul;
dp::ApiVersion GLFunctions::CurrentApiVersion = dp::ApiVersion::Invalid;
dp::GLExtensionsList GLFunctions::ExtensionsList;
#define MOCK_CALL(f) GLMockFunctions::Instance().f;
void GLFunctions::Init(dp::ApiVersion apiVersion)
{
MOCK_CALL(Init(apiVersion));
}
void GLFunctions::glFlush() {}
void GLFunctions::glFinish() {}
uint32_t GLFunctions::glGenVertexArray()
{
return MOCK_CALL(glGenVertexArray());
}
void GLFunctions::glBindVertexArray(uint32_t vao)
{
MOCK_CALL(glBindVertexArray(vao));
}
void GLFunctions::glDeleteVertexArray(uint32_t vao)
{
MOCK_CALL(glDeleteVertexArray(vao));
}
uint32_t GLFunctions::glGenBuffer()
{
return MOCK_CALL(glGenBuffer());
}
void GLFunctions::glBindBuffer(uint32_t vbo, glConst target)
{
MOCK_CALL(glBindBuffer(vbo, target));
}
void GLFunctions::glDeleteBuffer(uint32_t vbo)
{
MOCK_CALL(glDeleteBuffer(vbo));
}
void GLFunctions::glBufferData(glConst target, uint32_t size, void const * data, glConst usage)
{
MOCK_CALL(glBufferData(target, size, data, usage));
}
void GLFunctions::glBufferSubData(glConst target, uint32_t size, void const * data, uint32_t offset)
{
MOCK_CALL(glBufferSubData(target, size, data, offset));
}
uint32_t GLFunctions::glCreateShader(glConst type)
{
return MOCK_CALL(glCreateShader(type));
}
void GLFunctions::glShaderSource(uint32_t shaderID, std::string const & src, std::string const & defines)
{
MOCK_CALL(glShaderSource(shaderID, src));
}
bool GLFunctions::glCompileShader(uint32_t shaderID, std::string & errorLog)
{
return MOCK_CALL(glCompileShader(shaderID, errorLog));
}
void GLFunctions::glDeleteShader(uint32_t shaderID)
{
MOCK_CALL(glDeleteShader(shaderID));
}
uint32_t GLFunctions::glCreateProgram()
{
return MOCK_CALL(glCreateProgram());
}
void GLFunctions::glAttachShader(uint32_t programID, uint32_t shaderID)
{
MOCK_CALL(glAttachShader(programID, shaderID));
}
void GLFunctions::glDetachShader(uint32_t programID, uint32_t shaderID)
{
MOCK_CALL(glDetachShader(programID, shaderID));
}
bool GLFunctions::glLinkProgram(uint32_t programID, std::string & errorLog)
{
return MOCK_CALL(glLinkProgram(programID, errorLog));
}
void GLFunctions::glDeleteProgram(uint32_t programID)
{
MOCK_CALL(glDeleteProgram(programID));
}
void GLFunctions::glUseProgram(uint32_t programID)
{
MOCK_CALL(glUseProgram(programID));
}
int8_t GLFunctions::glGetAttribLocation(uint32_t programID, std::string const & name)
{
return MOCK_CALL(glGetAttribLocation(programID, name));
}
void GLFunctions::glBindAttribLocation(uint32_t programID, uint8_t index, std::string const & name) {}
// Enable vertex attribute binding. To get attributeLocation need to call glGetAttributeLocation.
void GLFunctions::glEnableVertexAttribute(int32_t attributeLocation)
{
MOCK_CALL(glEnableVertexAttribute(attributeLocation));
}
void GLFunctions::glVertexAttributePointer(int32_t attrLocation, uint32_t count, glConst type, bool needNormalize,
uint32_t stride, uint32_t offset)
{
MOCK_CALL(glVertexAttributePointer(attrLocation, count, type, needNormalize, stride, offset));
}
int8_t GLFunctions::glGetUniformLocation(uint32_t programID, std::string const & name)
{
return MOCK_CALL(glGetUniformLocation(programID, name));
}
void GLFunctions::glUniformValuei(int8_t location, int32_t v)
{
MOCK_CALL(glUniformValuei(location, v));
}
void GLFunctions::glUniformValuei(int8_t location, int32_t v1, int32_t v2)
{
MOCK_CALL(glUniformValuei(location, v1, v2));
}
void GLFunctions::glUniformValuei(int8_t location, int32_t v1, int32_t v2, int32_t v3)
{
MOCK_CALL(glUniformValuei(location, v1, v2, v3));
}
void GLFunctions::glUniformValuei(int8_t location, int32_t v1, int32_t v2, int32_t v3, int32_t v4)
{
MOCK_CALL(glUniformValuei(location, v1, v2, v3, v4));
}
void GLFunctions::glUniformValuef(int8_t location, float v)
{
MOCK_CALL(glUniformValuef(location, v));
}
void GLFunctions::glUniformValuef(int8_t location, float v1, float v2)
{
MOCK_CALL(glUniformValuef(location, v1, v2));
}
void GLFunctions::glUniformValuef(int8_t location, float v1, float v2, float v3)
{
MOCK_CALL(glUniformValuef(location, v1, v2, v3));
}
void GLFunctions::glUniformValuef(int8_t location, float v1, float v2, float v3, float v4)
{
MOCK_CALL(glUniformValuef(location, v1, v2, v3, v4));
}
void GLFunctions::glUniformMatrix4x4Value(int8_t location, float const * values)
{
MOCK_CALL(glUniformMatrix4x4Value(location, values));
}
uint32_t GLFunctions::glGetCurrentProgram()
{
return MOCK_CALL(glGetCurrentProgram());
}
bool GLFunctions::glHasExtension(std::string const & extName)
{
return MOCK_CALL(glHasExtension(extName));
}
int32_t GLFunctions::glGetProgramiv(uint32_t program, glConst paramName)
{
return MOCK_CALL(glGetProgramiv(program, paramName));
}
void GLFunctions::glGetActiveUniform(uint32_t programID, uint32_t uniformIndex, int32_t * uniformSize, glConst * type,
std::string & name)
{
MOCK_CALL(glGetActiveUniform(programID, uniformIndex, uniformSize, type, name));
}
void GLFunctions::glActiveTexture(glConst texBlock)
{
MOCK_CALL(glActiveTexture(texBlock));
}
uint32_t GLFunctions::glGenTexture()
{
MOCK_CALL(glGenTexture());
return 1;
}
void GLFunctions::glDeleteTexture(uint32_t id)
{
MOCK_CALL(glDeleteTexture(id));
}
void GLFunctions::glBindTexture(uint32_t textureID)
{
MOCK_CALL(glBindTexture(textureID));
}
void GLFunctions::glTexImage2D(int width, int height, glConst layout, glConst pixelType, void const * data)
{
MOCK_CALL(glTexImage2D(width, height, layout, pixelType, data));
}
void GLFunctions::glTexSubImage2D(int x, int y, int width, int height, glConst layout, glConst pixelType,
void const * data)
{
MOCK_CALL(glTexSubImage2D(x, y, width, height, layout, pixelType, data));
}
void GLFunctions::glTexParameter(glConst param, glConst value)
{
MOCK_CALL(glTexParameter(param, value));
}
int32_t GLFunctions::glGetInteger(glConst pname)
{
return MOCK_CALL(glGetInteger(pname));
}
std::string GLFunctions::glGetString(glConst pname)
{
return MOCK_CALL(glGetString(pname));
}
int32_t GLFunctions::glGetMaxLineWidth()
{
return MOCK_CALL(glGetMaxLineWidth());
}
void GLFunctions::glLineWidth(uint32_t value)
{
return MOCK_CALL(glLineWidth(value));
}
void GLFunctions::glViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
return MOCK_CALL(glViewport(x, y, w, h));
}
void GLFunctions::glScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
return MOCK_CALL(glScissor(x, y, w, h));
}
void GLFunctions::glGenFramebuffer(uint32_t * fbo)
{
return MOCK_CALL(glGenFramebuffer(fbo));
}
void GLFunctions::glBindFramebuffer(uint32_t fbo)
{
return MOCK_CALL(glBindFramebuffer(fbo));
}
void GLFunctions::glDeleteFramebuffer(uint32_t * fbo)
{
return MOCK_CALL(glDeleteFramebuffer(fbo));
}
void GLFunctions::glFramebufferTexture2D(glConst attachment, glConst texture)
{
return MOCK_CALL(glFramebufferTexture2D(attachment, texture));
}
uint32_t GLFunctions::glCheckFramebufferStatus()
{
return MOCK_CALL(glCheckFramebufferStatus());
}
void CheckGLError(base::SrcPoint const & /*srcPt*/) {}
void GLFunctions::glEnable(glConst mode) {}
void GLFunctions::glBlendEquation(glConst function) {}
void GLFunctions::glBlendFunc(glConst srcFactor, glConst dstFactor) {}
bool GLFunctions::CanEnableDebugMessages()
{
return false;
}
void GLFunctions::glDebugMessageCallback(TglDebugProc messageCallback, void * userParam) {}
void GLFunctions::glDebugMessageControl(glConst source, glConst type, glConst severity, int32_t count,
uint32_t const * ids, uint8_t enabled)
{}
void GLFunctions::glDisable(glConst mode) {}
void GLFunctions::glDepthFunc(glConst depthFunc) {}
void GLFunctions::glUniformValueiv(int8_t location, int32_t * v, uint32_t size) {}
void * GLFunctions::glMapBuffer(glConst, glConst)
{
return 0;
}
void * GLFunctions::glMapBufferRange(glConst, uint32_t, uint32_t, glConst)
{
return 0;
}
void GLFunctions::glUnmapBuffer(glConst target) {}
void GLFunctions::glDrawElements(glConst primitive, uint32_t sizeOfIndex, uint32_t indexCount, uint32_t startIndex) {}
void GLFunctions::glDrawArrays(glConst mode, int32_t first, uint32_t count) {}
void GLFunctions::glPixelStore(glConst name, uint32_t value) {}
void GLFunctions::glStencilOpSeparate(glConst face, glConst sfail, glConst dpfail, glConst dppass) {}
void GLFunctions::glStencilFuncSeparate(glConst face, glConst func, int ref, uint32_t mask) {}
int32_t GLFunctions::glGetBufferParameter(glConst target, glConst name)
{
return MOCK_CALL(glGetBufferParameter(target, name));
}
void GLFunctions::glCullFace(glConst face)
{
MOCK_CALL(glCullFace(face));
}
void GLFunctions::glFrontFace(glConst mode)
{
MOCK_CALL(glFrontFace(mode));
}
void GLFunctions::glDepthMask(bool needWriteToDepthBuffer)
{
MOCK_CALL(glDepthMask(needWriteToDepthBuffer));
}
void GLFunctions::glClear(uint32_t clearBits)
{
MOCK_CALL(glClear(clearBits));
}
void GLFunctions::glClearColor(float r, float g, float b, float a)
{
MOCK_CALL(glClearColor(r, g, b, a));
}
void GLFunctions::glClearDepthValue(double depth)
{
MOCK_CALL(glClearDepthValue(depth));
}

View file

@ -0,0 +1,28 @@
#include "drape/drape_tests/gl_mock_functions.hpp"
namespace emul
{
void GLMockFunctions::Init(int * argc, char ** argv)
{
::testing::InitGoogleMock(argc, argv);
m_mock = new GLMockFunctions();
}
void GLMockFunctions::Teardown()
{
delete m_mock;
m_mock = NULL;
}
GLMockFunctions & GLMockFunctions::Instance()
{
return *m_mock;
}
void GLMockFunctions::ValidateAndClear()
{
::testing::Mock::VerifyAndClear(m_mock);
}
GLMockFunctions * GLMockFunctions::m_mock;
} // namespace emul

View file

@ -0,0 +1,109 @@
#pragma once
#include "drape/drape_global.hpp"
#include "drape/gl_constants.hpp"
#include <gmock/gmock.h>
#include <string>
namespace emul
{
class GLMockFunctions
{
public:
static void Init(int * argc, char ** argv);
static void Teardown();
static GLMockFunctions & Instance();
static void ValidateAndClear();
MOCK_METHOD1(Init, void(dp::ApiVersion));
// VAO
MOCK_METHOD0(glGenVertexArray, uint32_t());
MOCK_METHOD1(glBindVertexArray, void(uint32_t vao));
MOCK_METHOD1(glDeleteVertexArray, void(uint32_t vao));
// VBO
MOCK_METHOD0(glGenBuffer, uint32_t());
MOCK_METHOD2(glBindBuffer, void(uint32_t vbo, glConst target));
MOCK_METHOD1(glDeleteBuffer, void(uint32_t vbo));
MOCK_METHOD4(glBufferData, void(glConst target, uint32_t size, void const * data, glConst usage));
MOCK_METHOD4(glBufferSubData, void(glConst target, uint32_t size, void const * data, uint32_t offset));
MOCK_METHOD2(glGetBufferParameter, int32_t(glConst target, glConst name));
MOCK_METHOD2(glGetUniformLocation, int8_t(uint32_t programID, std::string const & name));
MOCK_METHOD2(glUniformValuei, void(int8_t location, int32_t v));
MOCK_METHOD3(glUniformValuei, void(int8_t location, int32_t v1, int32_t v2));
MOCK_METHOD4(glUniformValuei, void(int8_t location, int32_t v1, int32_t v2, int32_t v3));
MOCK_METHOD5(glUniformValuei, void(int8_t location, int32_t v1, int32_t v2, int32_t v3, int32_t v4));
MOCK_METHOD2(glUniformValuef, void(int8_t location, float v));
MOCK_METHOD3(glUniformValuef, void(int8_t location, float v1, float v2));
MOCK_METHOD4(glUniformValuef, void(int8_t location, float v1, float v2, float v3));
MOCK_METHOD5(glUniformValuef, void(int8_t location, float v1, float v2, float v3, float v4));
MOCK_METHOD2(glUniformMatrix4x4Value, void(int8_t location, float const * values));
MOCK_METHOD0(glGetCurrentProgram, uint32_t());
MOCK_METHOD1(glCreateShader, uint32_t(glConst type));
MOCK_METHOD2(glShaderSource, void(uint32_t shaderID, std::string const & src));
MOCK_METHOD2(glCompileShader, bool(uint32_t shaderID, std::string & errorLog));
MOCK_METHOD1(glDeleteShader, void(uint32_t shaderID));
MOCK_METHOD0(glCreateProgram, uint32_t());
MOCK_METHOD2(glAttachShader, void(uint32_t programID, uint32_t shaderID));
MOCK_METHOD2(glDetachShader, void(uint32_t programID, uint32_t shaderID));
MOCK_METHOD2(glLinkProgram, bool(uint32_t programID, std::string & errorLog));
MOCK_METHOD1(glDeleteProgram, void(uint32_t programID));
MOCK_METHOD2(glGetAttribLocation, int32_t(uint32_t programID, std::string const & name));
MOCK_METHOD1(glEnableVertexAttribute, void(int32_t attributeLocation));
MOCK_METHOD6(glVertexAttributePointer, void(int32_t attrLocation, uint32_t count, glConst type, bool needNormalize,
uint32_t stride, uint32_t offset));
MOCK_METHOD1(glUseProgram, void(uint32_t programID));
MOCK_METHOD1(glHasExtension, bool(std::string const & extName));
MOCK_METHOD2(glGetProgramiv, int32_t(uint32_t, glConst));
MOCK_METHOD5(glGetActiveUniform, void(uint32_t, uint32_t, int32_t *, glConst *, std::string &));
// Texture functions
MOCK_METHOD1(glActiveTexture, void(glConst));
MOCK_METHOD0(glGenTexture, uint32_t());
MOCK_METHOD1(glDeleteTexture, void(uint32_t));
MOCK_METHOD1(glBindTexture, void(uint32_t));
MOCK_METHOD5(glTexImage2D, void(int, int, glConst, glConst, void const *));
MOCK_METHOD7(glTexSubImage2D, void(int, int, int, int, glConst, glConst, void const *));
MOCK_METHOD2(glTexParameter, void(glConst, glConst));
MOCK_METHOD1(glGetInteger, int32_t(glConst));
MOCK_METHOD1(glGetString, std::string(glConst));
MOCK_METHOD0(glGetMaxLineWidth, int32_t());
MOCK_METHOD1(glLineWidth, void(uint32_t value));
MOCK_METHOD4(glViewport, void(uint32_t x, uint32_t y, uint32_t w, uint32_t h));
MOCK_METHOD4(glScissor, void(uint32_t x, uint32_t y, uint32_t w, uint32_t h));
// FBO
MOCK_METHOD1(glGenFramebuffer, void(uint32_t * fbo));
MOCK_METHOD1(glBindFramebuffer, void(uint32_t fbo));
MOCK_METHOD1(glDeleteFramebuffer, void(uint32_t * fbo));
MOCK_METHOD2(glFramebufferTexture2D, void(glConst attachment, glConst texture));
MOCK_METHOD0(glCheckFramebufferStatus, uint32_t());
MOCK_METHOD1(glCullFace, void(glConst));
MOCK_METHOD1(glFrontFace, void(glConst));
MOCK_METHOD1(glDepthMask, void(bool));
MOCK_METHOD1(glClear, void(uint32_t));
MOCK_METHOD4(glClearColor, void(float, float, float, float));
MOCK_METHOD1(glClearDepthValue, void(double));
private:
static GLMockFunctions * m_mock;
};
} // namespace emul
#define EXPECTGL(x) EXPECT_CALL(emul::GLMockFunctions::Instance(), x)

View file

@ -0,0 +1,321 @@
#include "testing/testing.hpp"
#include "drape/drape_tests/img.hpp"
#include "drape/font_constants.hpp"
#include "drape/glyph_manager.hpp"
#include "drape/harfbuzz_shaping.hpp"
#include "base/file_name_utils.hpp"
#include "qt_tstfrm/test_main_loop.hpp"
#include "platform/platform.hpp"
#include <QtGui/QPainter>
#include <functional>
#include <iostream>
#include <vector>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_MODULE_H
#include <hb-ft.h>
namespace glyph_mng_tests
{
class GlyphRenderer
{
FT_Library m_freetypeLibrary;
std::string m_utf8;
int m_fontPixelSize;
char const * m_lang;
static constexpr FT_Int kSdfSpread{dp::kSdfBorder};
public:
GlyphRenderer()
{
// Initialize FreeType
TEST_EQUAL(0, FT_Init_FreeType(&m_freetypeLibrary), ("Can't initialize FreeType"));
for (auto const module : {"sdf", "bsdf"})
TEST_EQUAL(0, FT_Property_Set(m_freetypeLibrary, module, "spread", &kSdfSpread), ());
dp::GlyphManager::Params args;
args.m_uniBlocks = base::JoinPath("fonts", "unicode_blocks.txt");
args.m_whitelist = base::JoinPath("fonts", "whitelist.txt");
args.m_blacklist = base::JoinPath("fonts", "blacklist.txt");
GetPlatform().GetFontNames(args.m_fonts);
m_mng = std::make_unique<dp::GlyphManager>(args);
}
~GlyphRenderer() { FT_Done_FreeType(m_freetypeLibrary); }
void SetString(std::string const & s, int fontPixelSize, char const * lang)
{
m_utf8 = s;
m_fontPixelSize = fontPixelSize;
m_lang = lang;
}
static float Smoothstep(float edge0, float edge1, float x)
{
x = std::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
return x * x * (3 - 2 * x);
}
static float PixelColorFromDistance(float distance)
{
// float const normalizedDistance = (distance - 128.f) / 128.f;
float const normalizedDistance = distance / 255.f;
static constexpr float kFontScale = 1.f;
static constexpr float kSmoothing = 0.25f / (kSdfSpread * kFontScale);
float const alpha = Smoothstep(0.5f - kSmoothing, 0.5f + kSmoothing, normalizedDistance);
return 255.f * alpha;
}
void RenderGlyphs(QPaintDevice * device) const
{
QPainter painter(device);
painter.fillRect(QRectF(0.0, 0.0, device->width(), device->height()), Qt::white);
auto const shapedText = m_mng->ShapeText(m_utf8, m_fontPixelSize, m_lang);
std::cout << "Total width: " << shapedText.m_lineWidthInPixels << '\n';
std::cout << "Max height: " << shapedText.m_maxLineHeightInPixels << '\n';
int constexpr kLineStartX = 10;
int constexpr kLineMarginY = 50;
QPoint pen(kLineStartX, kLineMarginY);
for (auto const & glyph : shapedText.m_glyphs)
{
constexpr bool kUseSdfBitmap = false;
dp::GlyphImage img = m_mng->GetGlyphImage(glyph.m_key, m_fontPixelSize, kUseSdfBitmap);
auto const w = img.m_width;
auto const h = img.m_height;
// Spaces do not have images.
if (w && h)
{
QPoint currentPen = pen;
currentPen.rx() += glyph.m_xOffset;
// Image is drawn at the top left origin, text metrics returns bottom left origin.
currentPen.ry() -= glyph.m_yOffset + h;
painter.drawImage(currentPen, CreateImage(w, h, img.m_data->data()), QRect(0, 0, w, h));
}
pen += QPoint(glyph.m_xAdvance, glyph.m_yAdvance /* 0 for horizontal texts */);
img.Destroy();
}
pen.rx() = kLineStartX;
pen.ry() += kLineMarginY;
for (auto const & glyph : shapedText.m_glyphs)
{
constexpr bool kUseSdfBitmap = true;
auto img = m_mng->GetGlyphImage(glyph.m_key, m_fontPixelSize, kUseSdfBitmap);
auto const w = img.m_width;
auto const h = img.m_height;
// Spaces do not have images.
if (w && h)
{
QPoint currentPen = pen;
currentPen.rx() += glyph.m_xOffset;
currentPen.ry() -= glyph.m_yOffset + h;
painter.drawImage(currentPen, CreateImage(w, h, img.m_data->data()),
QRect(dp::kSdfBorder, dp::kSdfBorder, w - 2 * dp::kSdfBorder, h - 2 * dp::kSdfBorder));
}
pen += QPoint(glyph.m_xAdvance, glyph.m_yAdvance /* 0 for horizontal texts */);
img.Destroy();
}
////////////////////////////////////////////////////////////////////////////
// Manual rendering using HB functions.
{
pen.rx() = kLineStartX;
pen.ry() += kLineMarginY;
auto const hbLanguage = hb_language_from_string(m_lang, -1);
auto const runs = harfbuzz_shaping::GetTextSegments(m_utf8);
for (auto const & segment : runs.m_segments)
{
hb_buffer_t * buf = hb_buffer_create();
hb_buffer_add_utf16(buf, reinterpret_cast<uint16_t const *>(runs.m_text.data()), runs.m_text.size(),
segment.m_start, segment.m_length);
hb_buffer_set_direction(buf, segment.m_direction);
hb_buffer_set_script(buf, segment.m_script);
hb_buffer_set_language(buf, hbLanguage);
// If direction, script, and language are not known.
// hb_buffer_guess_segment_properties(buf);
std::string const lang = m_lang;
std::string const fontFileName = lang == "ar" ? "00_NotoNaskhArabic-Regular.ttf" : "07_roboto_medium.ttf";
auto reader = GetPlatform().GetReader("fonts/" + fontFileName);
auto fontFile = reader->GetName();
FT_Face face;
if (FT_New_Face(m_freetypeLibrary, fontFile.c_str(), 0, &face))
{
std::cerr << "Can't load font " << fontFile << '\n';
return;
}
// Set character size
FT_Set_Pixel_Sizes(face, 0, m_fontPixelSize);
// This also works.
// if (FT_Set_Char_Size(face, 0, m_fontPixelSize << 6, 0, 0)) {
// std::cerr << "Can't set character size\n";
// return;
// }
// Set no transform (identity)
// FT_Set_Transform(face, nullptr, nullptr);
// Load font into HarfBuzz
hb_font_t * font = hb_ft_font_create(face, nullptr);
// Shape!
hb_shape(font, buf, nullptr, 0);
// Get the glyph and position information.
unsigned int glyph_count;
hb_glyph_info_t * glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
hb_glyph_position_t * glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
for (unsigned int i = 0; i < glyph_count; i++)
{
hb_codepoint_t const glyphid = glyph_info[i].codepoint;
FT_Int32 const flags = FT_LOAD_RENDER;
FT_Load_Glyph(face, glyphid, flags);
FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF);
FT_GlyphSlot slot = face->glyph;
FT_Bitmap const & ftBitmap = slot->bitmap;
auto const buffer = ftBitmap.buffer;
auto const width = ftBitmap.width;
auto const height = ftBitmap.rows;
for (unsigned h = 0; h < height; ++h)
{
for (unsigned w = 0; w < width; ++w)
{
auto curPixelAddr = buffer + h * width + w;
float currPixel = *curPixelAddr;
currPixel = PixelColorFromDistance(currPixel);
*curPixelAddr = static_cast<unsigned char>(currPixel);
}
}
auto const bearing_x = slot->metrics.horiBearingX; // slot->bitmap_left;
auto const bearing_y = slot->metrics.horiBearingY; // slot->bitmap_top;
auto const & glyphPos = glyph_pos[i];
hb_position_t const x_offset = (glyphPos.x_offset + bearing_x) >> 6;
hb_position_t const y_offset = (glyphPos.y_offset + bearing_y) >> 6;
hb_position_t const x_advance = glyphPos.x_advance >> 6;
hb_position_t const y_advance = glyphPos.y_advance >> 6;
// Empty images are possible for space characters.
if (width != 0 && height != 0)
{
QPoint currentPen = pen;
currentPen.rx() += x_offset;
currentPen.ry() -= y_offset;
painter.drawImage(currentPen, CreateImage(width, height, buffer),
QRect(kSdfSpread, kSdfSpread, width - 2 * kSdfSpread, height - 2 * kSdfSpread));
}
pen += QPoint(x_advance, y_advance);
}
// Tidy up.
hb_buffer_destroy(buf);
hb_font_destroy(font);
FT_Done_Face(face);
}
}
//////////////////////////////////////////////////////////////////
// QT text renderer.
{
pen.rx() = kLineStartX;
pen.ry() += kLineMarginY;
// QFont font("Noto Naskh Arabic");
QFont font("Roboto");
font.setPixelSize(m_fontPixelSize);
// font.setWeight(QFont::Weight::Normal);
painter.setFont(font);
painter.drawText(pen, QString::fromUtf8(m_utf8.c_str(), m_utf8.size()));
}
}
private:
std::unique_ptr<dp::GlyphManager> m_mng;
};
// This unit test creates a window so can't be run in GUI-less Linux machine.
// Make sure that the QT_QPA_PLATFORM=offscreen environment variable is set.
UNIT_TEST(GlyphLoadingTest)
{
GlyphRenderer renderer;
using namespace std::placeholders;
constexpr int fontSize = 27;
renderer.SetString("Muḩāfaz̧at", fontSize, "en");
RunTestLoop("Latin Extended", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
renderer.SetString("Строка", fontSize, "ru");
RunTestLoop("ru", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
renderer.SetString("ØŒÆ", fontSize, "en");
RunTestLoop("en", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
renderer.SetString("𫝚 𫝛 𫝜", fontSize, "zh");
RunTestLoop("CJK Surrogates", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
renderer.SetString(
"الحلّة گلها"
" كسول الزنجبيل القط"
"56"
"عين علي (الحربية)"
"123"
" اَلْعَرَبِيَّةُ",
fontSize, "ar");
RunTestLoop("Arabic1", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
renderer.SetString(
"12345"
"گُلها"
"12345"
"گُلها"
"12345",
fontSize, "ar");
RunTestLoop("Arabic2", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
renderer.SetString("മനക്കലപ്പടി", fontSize, "ml");
RunTestLoop("Malay", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
renderer.SetString(
"Test 12 345 "
"گُلها"
"678 9000 Test",
fontSize, "ar");
RunTestLoop("Arabic Mixed", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
renderer.SetString("NFKC Razdoĺny NFKD Razdoĺny", fontSize, "be");
RunTestLoop("Polish", std::bind(&GlyphRenderer::RenderGlyphs, &renderer, _1));
}
} // namespace glyph_mng_tests

View file

@ -0,0 +1,26 @@
#include "drape/font_texture.hpp"
#include "testing/testing.hpp"
UNIT_TEST(SimplePackTest)
{
dp::GlyphPacker packer(m2::PointU(32, 32));
m2::RectU r;
TEST(packer.PackGlyph(10, 13, r), ());
TEST_EQUAL(r, m2::RectU(0, 0, 10, 13), ());
TEST(packer.PackGlyph(18, 8, r), ());
TEST_EQUAL(r, m2::RectU(10, 0, 28, 8), ());
TEST(packer.PackGlyph(4, 15, r), ());
TEST_EQUAL(r, m2::RectU(28, 0, 32, 15), ());
TEST(packer.PackGlyph(7, 10, r), ());
TEST(!packer.IsFull(), ());
TEST_EQUAL(r, m2::RectU(0, 15, 7, 25), ());
TEST(!packer.PackGlyph(12, 18, r), ());
TEST(packer.IsFull(), ());
TEST_EQUAL(r, m2::RectU(0, 15, 7, 25), ());
}

View file

@ -0,0 +1,26 @@
#include "drape/harfbuzz_shaping.hpp"
#include "testing/testing.hpp"
namespace harfbuzz_shaping
{
bool operator==(TextSegment const & s1, TextSegment const & s2)
{
return s1.m_start == s2.m_start && s1.m_length == s2.m_length && s1.m_script == s2.m_script &&
s1.m_direction == s2.m_direction;
}
UNIT_TEST(GetTextSegments)
{
auto const [text, segments] = GetTextSegments("Map data © OpenStreetMap");
TEST(text == u"Map data © OpenStreetMap", ());
std::vector<TextSegment> const expected{
{0, 3, HB_SCRIPT_LATIN, HB_DIRECTION_LTR}, {3, 1, HB_SCRIPT_COMMON, HB_DIRECTION_LTR},
{4, 4, HB_SCRIPT_LATIN, HB_DIRECTION_LTR}, {8, 3, HB_SCRIPT_COMMON, HB_DIRECTION_LTR},
{11, 13, HB_SCRIPT_LATIN, HB_DIRECTION_LTR},
};
TEST_EQUAL(segments, expected, ());
}
} // namespace harfbuzz_shaping

View file

@ -0,0 +1,24 @@
#include "drape/drape_tests/img.hpp"
void cleanUpQImageMemory(void * mem)
{
free(mem);
}
QImage CreateImage(uint32_t w, uint32_t h, uint8_t const * mem)
{
int pitch = 32 * (((w - 1) / 32) + 1);
int byteCount = pitch * h;
unsigned char * buf = (unsigned char *)malloc(byteCount);
memset(buf, 0, byteCount);
for (uint32_t i = 0; i < h; ++i)
memcpy(buf + pitch * i, mem + w * i, w);
QImage img = QImage(buf, pitch, h, QImage::Format_Indexed8, &cleanUpQImageMemory, buf);
img.setColorCount(0xFF);
for (int i = 0; i < 256; ++i)
img.setColor(i, qRgb(255 - i, 255 - i, 255 - i));
return img;
}

View file

@ -0,0 +1,5 @@
#pragma once
#include <QtGui/QImage>
QImage CreateImage(uint32_t w, uint32_t h, uint8_t const * mem);

View file

@ -0,0 +1,49 @@
#pragma once
#include "base/logging.hpp"
#include "drape/gl_constants.hpp"
#include "testing/testing.hpp"
#include <cstring>
namespace dp
{
struct MemoryComparer
{
void * m_mem;
int m_size;
MemoryComparer(void * memory, int size) : m_mem(memory), m_size(size) {}
void cmpSubBuffer(glConst /*type*/, uint32_t size, void const * data, uint32_t /*offset*/) const
{
TEST_EQUAL(size, static_cast<uint32_t>(m_size), ());
TEST_EQUAL(memcmp(m_mem, data, size), 0, ());
}
void cmpSubImage(uint32_t /*x*/, uint32_t /*y*/, uint32_t width, uint32_t height, glConst layout, glConst pixelFormat,
void const * data) const
{
uint32_t channelCount = 0;
if (layout == gl_const::GLRGBA || layout == gl_const::GLRGBA8 || layout == gl_const::GLRGBA4)
channelCount = 4;
else if (layout == gl_const::GLRGB)
channelCount = 3;
else if (layout == gl_const::GLAlpha || layout == gl_const::GLAlpha8 || layout == gl_const::GLLuminance ||
layout == gl_const::GLLuminance8 || layout == gl_const::GLAlphaLuminance || layout == gl_const::GLRed)
{
channelCount = 1;
}
else
ASSERT(false, ());
ASSERT(gl_const::GL8BitOnChannel == pixelFormat, ());
TEST_EQUAL(static_cast<uint32_t>(m_size), width * height * channelCount, ());
uint8_t * member = (uint8_t *)m_mem;
uint8_t * input = (uint8_t *)data;
for (int i = 0; i < m_size; ++i)
TEST_EQUAL(member[i], input[i], (i));
}
};
} // namespace dp

View file

@ -0,0 +1,75 @@
#include "testing/testing.hpp"
#include "drape/object_pool.hpp"
class vec2
{
public:
class vec2Factory
{
public:
vec2 * GetNew() const { return new vec2(); }
};
public:
float m_x, m_y;
static int m_counter;
vec2() : m_x(0.0f), m_y(0.0f) { m_counter++; }
~vec2() { m_counter--; }
};
int vec2::m_counter = 0;
UNIT_TEST(ObjectPoolFilling)
{
vec2::vec2Factory factory;
dp::ObjectPool<vec2, vec2::vec2Factory> pool(1, factory);
vec2 *pt1, *pt2, *pt3;
pt1 = pool.Get();
pool.Return(pt1);
pt2 = pool.Get();
TEST_EQUAL(pt1, pt2, ());
pt3 = pool.Get();
pool.Return(pt2);
pool.Return(pt3);
}
UNIT_TEST(ObjectPoolClearing_1)
{
vec2::m_counter = 0;
vec2::vec2Factory factory;
auto pool = new dp::ObjectPool<vec2, vec2::vec2Factory>(1, factory);
vec2 *pt1, *pt2, *pt3;
pt1 = pool->Get();
pool->Return(pt1);
pt2 = pool->Get();
pt3 = pool->Get();
pool->Return(pt2);
pool->Return(pt3);
delete pool;
TEST_EQUAL(vec2::m_counter, 0, ());
}
UNIT_TEST(ObjectPoolClearing_2)
{
vec2::m_counter = 0;
vec2::vec2Factory factory;
auto pool = new dp::ObjectPool<vec2, vec2::vec2Factory>(100, factory);
vec2 *pt1, *pt2, *pt3;
pt1 = pool->Get();
pool->Return(pt1);
pt2 = pool->Get();
pt3 = pool->Get();
pool->Return(pt2);
pool->Return(pt3);
delete pool;
TEST_EQUAL(vec2::m_counter, 0, ());
}

View file

@ -0,0 +1,97 @@
#include "drape/pointers.hpp"
#include "testing/testing.hpp"
#include "base/base.hpp"
#include <algorithm>
#include <string>
#include <utility>
namespace
{
class Tester
{
public:
Tester() = default;
};
#if defined(TRACK_POINTERS)
bool g_assertRaised = false;
bool OnAssertRaised(base::SrcPoint const & /* srcPoint */, std::string const & /* msg */)
{
g_assertRaised = true;
return false;
}
#endif
} // namespace
UNIT_TEST(PointersTrackingTest)
{
#if defined(TRACK_POINTERS)
DpPointerTracker::TAlivePointers const & alivePointers = DpPointerTracker::Instance().GetAlivePointers();
drape_ptr<Tester> ptr = make_unique_dp<Tester>();
void * ptrAddress = ptr.get();
std::string const ptrTypeName = typeid(Tester *).name();
// no references
TEST(alivePointers.find(ptrAddress) == alivePointers.end(), ());
// create a reference
ref_ptr<Tester> refPtr = make_ref(ptr);
DpPointerTracker::TAlivePointers::const_iterator found = alivePointers.find(ptrAddress);
TEST(found != alivePointers.end(), ());
TEST_EQUAL(found->second.first, 1, ());
TEST_EQUAL(found->second.second, ptrTypeName, ());
// copy reference
ref_ptr<Tester> refPtr2 = refPtr;
found = alivePointers.find(ptrAddress);
TEST_EQUAL(found->second.first, 2, ());
// remove reference
{
ref_ptr<Tester> refPtrInScope = refPtr2;
TEST_EQUAL(found->second.first, 3, ());
}
TEST_EQUAL(found->second.first, 2, ());
// move reference
ref_ptr<Tester> refPtr3 = std::move(refPtr2);
TEST_EQUAL(found->second.first, 2, ());
// assign reference
ref_ptr<Tester> refPtr4;
refPtr4 = refPtr3;
TEST_EQUAL(found->second.first, 3, ());
// move-assign reference
refPtr4 = std::move(refPtr3);
TEST_EQUAL(found->second.first, 2, ());
// create another reference
ref_ptr<Tester> refPtr5 = make_ref(ptr);
TEST_EQUAL(found->second.first, 3, ());
#endif
}
UNIT_TEST(RefPointerExpiringTest)
{
#if defined(TRACK_POINTERS)
g_assertRaised = false;
base::AssertFailedFn prevFn = base::SetAssertFunction(OnAssertRaised);
drape_ptr<Tester> ptr = make_unique_dp<Tester>();
ref_ptr<Tester> refPtr1 = make_ref(ptr);
ref_ptr<Tester> refPtr2 = make_ref(ptr);
ptr.reset();
base::SetAssertFunction(prevFn);
TEST(g_assertRaised, ());
#endif
}

View file

@ -0,0 +1,31 @@
#include "testing/testing.hpp"
#include "indexer/map_style.hpp"
#include "indexer/map_style_reader.hpp"
#include "drape/drape_tests/testing_graphics_context.hpp"
#include "drape/static_texture.hpp"
#include <string>
#include <vector>
UNIT_TEST(CheckTrafficArrowTextures)
{
static std::vector<std::string> skinPaths = {"6plus", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"};
static std::vector<MapStyle> styles = {MapStyle::MapStyleDefaultLight, MapStyle::MapStyleDefaultDark,
MapStyle::MapStyleVehicleLight, MapStyle::MapStyleVehicleDark};
TestingGraphicsContext context;
for (auto const & style : styles)
{
GetStyleReader().SetCurrentStyle(style);
for (auto const & skinPath : skinPaths)
{
dp::StaticTexture texture(make_ref(&context), "traffic-arrow.png", skinPath, dp::TextureFormat::RGBA8, nullptr);
TEST(texture.IsLoadingCorrect(), ());
dp::StaticTexture texture2(make_ref(&context), "area-hatching.png", skinPath, dp::TextureFormat::RGBA8, nullptr);
TEST(texture2.IsLoadingCorrect(), ());
}
}
}

View file

@ -0,0 +1,77 @@
#include "testing/testing.hpp"
#include "drape/drape_tests/dummy_texture.hpp"
#include "drape/drape_tests/memory_comparer.hpp"
#include "drape/stipple_pen_resource.hpp"
#include "drape/texture.hpp"
#include "drape/tm_read_resources.hpp"
namespace stipple_pen_tests
{
using namespace dp;
namespace
{
void TestPacker(StipplePenPacker & packer, uint32_t width, m2::RectU const & expect)
{
TEST_EQUAL(packer.PackResource({width, 1}), expect, ());
}
bool IsRectsEqual(m2::RectF const & r1, m2::RectF const & r2)
{
return AlmostEqualULPs(r1.minX(), r2.minX()) && AlmostEqualULPs(r1.minY(), r2.minY()) &&
AlmostEqualULPs(r1.maxX(), r2.maxX()) && AlmostEqualULPs(r1.maxY(), r2.maxY());
}
} // namespace
UNIT_TEST(StippleTest_Pack)
{
StipplePenPacker packer(m2::PointU(512, 8));
TestPacker(packer, 30, m2::RectU(0, 0, 30, 1));
TestPacker(packer, 254, m2::RectU(0, 1, 254, 2));
TestPacker(packer, 1, m2::RectU(0, 2, 1, 3));
TestPacker(packer, 250, m2::RectU(0, 3, 250, 4));
TestPacker(packer, 249, m2::RectU(0, 4, 249, 5));
m2::RectF mapped = packer.MapTextureCoords(m2::RectU(0, 0, 256, 1));
TEST(IsRectsEqual(mapped, m2::RectF(0.5f / 512.0f, 0.5f / 8.0f, 255.5f / 512.0f, 0.5f / 8.0f)), ());
}
UNIT_TEST(StippleTest_EqualPatterns)
{
using PatternT = std::array<double, 2>;
std::vector<PatternT> patterns;
using namespace dp::impl;
ParsePatternsList("./data/patterns.txt", [&patterns](buffer_vector<double, 8> const & p)
{
if (p.size() == 2)
patterns.push_back({p[0], p[1]});
});
auto const IsEqualPatterns = [](PatternT const & p1, PatternT const & p2)
{
for (double scale : {1, 2, 3})
if ((PatternFloat2Pixel(scale * p1[0]) != PatternFloat2Pixel(scale * p2[0])) ||
(PatternFloat2Pixel(scale * p1[1]) != PatternFloat2Pixel(scale * p2[1])))
return false;
return true;
};
auto const IsAlmostEqualPatterns = [](PatternT const & p1, PatternT const & p2)
{
double const scale = 3;
return (fabs(scale * p1[0] - scale * p2[0]) + fabs(scale * p1[1] - scale * p2[1])) < 1;
};
size_t const count = patterns.size();
for (size_t i = 0; i < count - 1; ++i)
{
for (size_t j = i + 1; j < count; ++j)
if (IsEqualPatterns(patterns[i], patterns[j]))
LOG(LINFO, ("Equal:", patterns[i], patterns[j]));
else if (IsAlmostEqualPatterns(patterns[i], patterns[j]))
LOG(LINFO, ("Almost equal:", patterns[i], patterns[j]));
}
}
} // namespace stipple_pen_tests

View file

@ -0,0 +1,43 @@
#pragma once
#include "drape/graphics_context.hpp"
// Testing context simulates OpenGLES3 API version.
class TestingGraphicsContext : public dp::GraphicsContext
{
public:
TestingGraphicsContext() = default;
explicit TestingGraphicsContext(dp::ApiVersion apiVersion) : m_apiVersion(apiVersion) {}
void Present() override {}
void MakeCurrent() override {}
void SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer) override {}
void ForgetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer) override {}
void ApplyFramebuffer(std::string const & framebufferLabel) override {}
void Init(dp::ApiVersion apiVersion) override {}
dp::ApiVersion GetApiVersion() const override { return m_apiVersion; }
std::string GetRendererName() const override { return {}; }
std::string GetRendererVersion() const override { return {}; }
void PushDebugLabel(std::string const & label) override {}
void PopDebugLabel() override {}
void SetClearColor(dp::Color const & color) override {}
void Clear(uint32_t clearBits, uint32_t storeBits) override {}
void Flush() override {}
void SetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override {}
void SetScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override {}
void SetDepthTestEnabled(bool enabled) override {}
void SetDepthTestFunction(dp::TestFunction depthFunction) override {}
void SetStencilTestEnabled(bool enabled) override {}
void SetStencilFunction(dp::StencilFace face, dp::TestFunction stencilFunction) override {}
void SetStencilActions(dp::StencilFace face, dp::StencilAction stencilFailAction, dp::StencilAction depthFailAction,
dp::StencilAction passAction) override
{}
void SetStencilReferenceValue(uint32_t stencilReferenceValue) override {}
void SetCullingEnabled(bool enabled) override {}
private:
dp::ApiVersion m_apiVersion = dp::ApiVersion::OpenGLES3;
};

View file

@ -0,0 +1,346 @@
#include "testing/testing.hpp"
#include "drape/drape_tests/dummy_texture.hpp"
#include "drape/drape_tests/memory_comparer.hpp"
#include "drape/drape_tests/testing_graphics_context.hpp"
#include "drape/gl_constants.hpp"
#include "drape/texture.hpp"
#include "drape/texture_of_colors.hpp"
#include "drape/drape_tests/gl_mock_functions.hpp"
#include <gmock/gmock.h>
using testing::_;
using testing::AnyOf;
using testing::IgnoreResult;
using testing::InSequence;
using testing::Invoke;
using testing::Return;
using namespace dp;
namespace
{
void TestRects(m2::RectF const & a, m2::RectF const & b)
{
TEST_ALMOST_EQUAL_ULPS(a.minX(), b.minX(), ());
TEST_ALMOST_EQUAL_ULPS(a.maxX(), b.maxX(), ());
TEST_ALMOST_EQUAL_ULPS(a.minY(), b.minY(), ());
TEST_ALMOST_EQUAL_ULPS(a.maxY(), b.maxY(), ());
}
void InitOpenGLTextures(int const w, int const h)
{
InSequence seq;
EXPECTGL(glHasExtension(_)).WillRepeatedly(Return(true));
EXPECTGL(glGenTexture()).WillOnce(Return(1));
EXPECTGL(glBindTexture(1)).WillOnce(Return());
EXPECTGL(glTexImage2D(w, h, AnyOf(gl_const::GLRGBA, gl_const::GLRGBA8), gl_const::GL8BitOnChannel, NULL));
EXPECTGL(glTexParameter(gl_const::GLMinFilter, gl_const::GLLinear));
EXPECTGL(glTexParameter(gl_const::GLMagFilter, gl_const::GLLinear));
EXPECTGL(glTexParameter(gl_const::GLWrapS, gl_const::GLClampToEdge));
EXPECTGL(glTexParameter(gl_const::GLWrapT, gl_const::GLClampToEdge));
EXPECTGL(glBindTexture(0)).WillOnce(Return());
}
class DummyColorPallete : public ColorPalette
{
typedef ColorPalette TBase;
public:
explicit DummyColorPallete(m2::PointU const & size) : TBase(size) {}
ref_ptr<Texture::ResourceInfo> MapResource(ColorKey const & key)
{
bool dummy = false;
return TBase::MapResource(key, dummy);
}
};
} // namespace
UNIT_TEST(ColorPalleteMappingTests)
{
DummyColorPallete cp(m2::PointU(32, 16));
ref_ptr<Texture::ResourceInfo> info1 = cp.MapResource(dp::ColorKey(dp::Color(0, 0, 0, 0)));
ref_ptr<Texture::ResourceInfo> info2 = cp.MapResource(dp::ColorKey(dp::Color(1, 1, 1, 1)));
ref_ptr<Texture::ResourceInfo> info3 = cp.MapResource(dp::ColorKey(dp::Color(0, 0, 0, 0)));
TEST_NOT_EQUAL(info1, info2, ());
TEST_EQUAL(info1, info3, ());
TestRects(info1->GetTexRect(), m2::RectF(1.0f / 32.0f, 1.0f / 16, 1.0f / 32.0f, 1.0f / 16));
TestRects(info2->GetTexRect(), m2::RectF(3.0f / 32.0f, 1.0f / 16, 3.0f / 32.0f, 1.0f / 16));
TestRects(info3->GetTexRect(), m2::RectF(1.0f / 32.0f, 1.0f / 16, 1.0f / 32.0f, 1.0f / 16));
for (int i = 2; i < 100; ++i)
cp.MapResource(dp::ColorKey(dp::Color(i, i, i, i)));
TestRects(cp.MapResource(dp::ColorKey(dp::Color(54, 54, 54, 54)))->GetTexRect(),
m2::RectF(13.0f / 32.0f, 7.0f / 16.0f, 13.0f / 32.0f, 7.0f / 16.0f));
}
UNIT_TEST(ColorPalleteUploadingSingleRow)
{
int const width = 32;
int const height = 16;
InitOpenGLTextures(width, height);
TestingGraphicsContext context;
Texture::Params p;
p.m_allocator = GetDefaultAllocator(make_ref(&context));
p.m_format = dp::TextureFormat::RGBA8;
p.m_width = width;
p.m_height = height;
DummyTexture texture;
texture.Create(make_ref(&context), p);
DummyColorPallete cp(m2::PointU(width, height));
cp.UploadResources(make_ref(&context), make_ref(&texture));
{
cp.MapResource(dp::ColorKey(dp::Color(0xFF, 0, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0, 0xFF, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0, 0, 0xFF, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0, 0, 0, 0xFF)));
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0xBB, 0xCC, 0xDD)));
uint8_t memoryEtalon[] = {
0xFF, 0x00, 0x00, 0x00, // 1 pixel (1st row)
0xFF, 0x00, 0x00, 0x00, // 1 pixel (1st row)
0x00, 0xFF, 0x00, 0x00, // 2 pixel (1st row)
0x00, 0xFF, 0x00, 0x00, // 2 pixel (1st row)
0x00, 0x00, 0xFF, 0x00, // 3 pixel (1st row)
0x00, 0x00, 0xFF, 0x00, // 3 pixel (1st row)
0x00, 0x00, 0x00, 0xFF, // 4 pixel (1st row)
0x00, 0x00, 0x00, 0xFF, // 4 pixel (1st row)
0xAA, 0xBB, 0xCC, 0xDD, // 5 pixel (1st row)
0xAA, 0xBB, 0xCC, 0xDD, // 5 pixel (1st row)
0xFF, 0x00, 0x00, 0x00, // 1 pixel (2nd row)
0xFF, 0x00, 0x00, 0x00, // 1 pixel (2nd row)
0x00, 0xFF, 0x00, 0x00, // 2 pixel (2nd row)
0x00, 0xFF, 0x00, 0x00, // 2 pixel (2nd row)
0x00, 0x00, 0xFF, 0x00, // 3 pixel (2nd row)
0x00, 0x00, 0xFF, 0x00, // 3 pixel (2nd row)
0x00, 0x00, 0x00, 0xFF, // 4 pixel (2nd row)
0x00, 0x00, 0x00, 0xFF, // 4 pixel (2nd row)
0xAA, 0xBB, 0xCC, 0xDD, // 5 pixel (2nd row)
0xAA, 0xBB, 0xCC, 0xDD // 5 pixel (2nd row)
};
MemoryComparer cmp(memoryEtalon, ARRAY_SIZE(memoryEtalon));
EXPECTGL(glTexSubImage2D(0, 0, 10, 2, AnyOf(gl_const::GLRGBA, gl_const::GLRGBA8), gl_const::GL8BitOnChannel, _))
.WillOnce(Invoke(&cmp, &MemoryComparer::cmpSubImage));
cp.UploadResources(make_ref(&context), make_ref(&texture));
}
{
cp.MapResource(dp::ColorKey(dp::Color(0xFF, 0xAA, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0xFF, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0, 0xFF, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0, 0, 0xFF)));
cp.MapResource(dp::ColorKey(dp::Color(0x00, 0xBB, 0xCC, 0xDD)));
uint8_t memoryEtalon[] = {
0xFF, 0xAA, 0x00, 0x00, // 1 pixel (1st row)
0xFF, 0xAA, 0x00, 0x00, // 1 pixel (1st row)
0xAA, 0xFF, 0x00, 0x00, // 2 pixel (1st row)
0xAA, 0xFF, 0x00, 0x00, // 2 pixel (1st row)
0xAA, 0x00, 0xFF, 0x00, // 3 pixel (1st row)
0xAA, 0x00, 0xFF, 0x00, // 3 pixel (1st row)
0xAA, 0x00, 0x00, 0xFF, // 4 pixel (1st row)
0xAA, 0x00, 0x00, 0xFF, // 4 pixel (1st row)
0x00, 0xBB, 0xCC, 0xDD, // 5 pixel (1st row)
0x00, 0xBB, 0xCC, 0xDD, // 5 pixel (1st row)
0xFF, 0xAA, 0x00, 0x00, // 1 pixel (2nd row)
0xFF, 0xAA, 0x00, 0x00, // 1 pixel (2nd row)
0xAA, 0xFF, 0x00, 0x00, // 2 pixel (2nd row)
0xAA, 0xFF, 0x00, 0x00, // 2 pixel (2nd row)
0xAA, 0x00, 0xFF, 0x00, // 3 pixel (2nd row)
0xAA, 0x00, 0xFF, 0x00, // 3 pixel (2nd row)
0xAA, 0x00, 0x00, 0xFF, // 4 pixel (2nd row)
0xAA, 0x00, 0x00, 0xFF, // 4 pixel (2nd row)
0x00, 0xBB, 0xCC, 0xDD, // 5 pixel (2nd row)
0x00, 0xBB, 0xCC, 0xDD // 5 pixel (2nd row)
};
MemoryComparer cmp(memoryEtalon, ARRAY_SIZE(memoryEtalon));
EXPECTGL(glTexSubImage2D(10, 0, 10, 2, AnyOf(gl_const::GLRGBA, gl_const::GLRGBA8), gl_const::GL8BitOnChannel, _))
.WillOnce(Invoke(&cmp, &MemoryComparer::cmpSubImage));
cp.UploadResources(make_ref(&context), make_ref(&texture));
}
EXPECTGL(glDeleteTexture(1));
}
UNIT_TEST(ColorPalleteUploadingPartialyRow)
{
int const width = 8;
int const height = 8;
InitOpenGLTextures(width, height);
TestingGraphicsContext context;
Texture::Params p;
p.m_allocator = GetDefaultAllocator(make_ref(&context));
p.m_format = dp::TextureFormat::RGBA8;
p.m_width = width;
p.m_height = height;
DummyTexture texture;
texture.Create(make_ref(&context), p);
DummyColorPallete cp(m2::PointU(width, height));
{
cp.MapResource(dp::ColorKey(dp::Color(0xFF, 0, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xFF, 0xFF, 0, 0)));
uint8_t memoryEtalon[] = {
0xFF, 0x00, 0x00, 0x00, // 1 pixel
0xFF, 0x00, 0x00, 0x00, // 1 pixel
0xFF, 0xFF, 0x00, 0x00, // 2 pixel
0xFF, 0xFF, 0x00, 0x00, // 2 pixel
0xFF, 0x00, 0x00, 0x00, // 1 pixel
0xFF, 0x00, 0x00, 0x00, // 1 pixel
0xFF, 0xFF, 0x00, 0x00, // 2 pixel
0xFF, 0xFF, 0x00, 0x00 // 2 pixel
};
MemoryComparer cmp(memoryEtalon, ARRAY_SIZE(memoryEtalon));
EXPECTGL(glTexSubImage2D(0, 0, 4, 2, AnyOf(gl_const::GLRGBA, gl_const::GLRGBA8), gl_const::GL8BitOnChannel, _))
.WillOnce(Invoke(&cmp, &MemoryComparer::cmpSubImage));
cp.UploadResources(make_ref(&context), make_ref(&texture));
}
{
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0xAA, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0xAA, 0xAA, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0xAA, 0xAA, 0xAA)));
uint8_t memoryEtalon1[] = {
0xAA, 0x00, 0x00, 0x00, // 1 pixel
0xAA, 0x00, 0x00, 0x00, // 1 pixel
0xAA, 0xAA, 0x00, 0x00, // 2 pixel
0xAA, 0xAA, 0x00, 0x00, // 2 pixel
0xAA, 0x00, 0x00, 0x00, // 1 pixel
0xAA, 0x00, 0x00, 0x00, // 1 pixel
0xAA, 0xAA, 0x00, 0x00, // 2 pixel
0xAA, 0xAA, 0x00, 0x00 // 2 pixel
};
uint8_t memoryEtalon2[] = {
0xAA, 0xAA, 0xAA, 0x00, // 1 pixel
0xAA, 0xAA, 0xAA, 0x00, // 1 pixel
0xAA, 0xAA, 0xAA, 0xAA, // 2 pixel
0xAA, 0xAA, 0xAA, 0xAA, // 2 pixel
0xAA, 0xAA, 0xAA, 0x00, // 1 pixel
0xAA, 0xAA, 0xAA, 0x00, // 1 pixel
0xAA, 0xAA, 0xAA, 0xAA, // 2 pixel
0xAA, 0xAA, 0xAA, 0xAA // 2 pixel
};
MemoryComparer cmp1(memoryEtalon1, ARRAY_SIZE(memoryEtalon1));
EXPECTGL(glTexSubImage2D(4, 0, 4, 2, AnyOf(gl_const::GLRGBA, gl_const::GLRGBA8), gl_const::GL8BitOnChannel, _))
.WillOnce(Invoke(&cmp1, &MemoryComparer::cmpSubImage));
MemoryComparer cmp2(memoryEtalon2, ARRAY_SIZE(memoryEtalon2));
EXPECTGL(glTexSubImage2D(0, 2, 4, 2, AnyOf(gl_const::GLRGBA, gl_const::GLRGBA8), gl_const::GL8BitOnChannel, _))
.WillOnce(Invoke(&cmp2, &MemoryComparer::cmpSubImage));
cp.UploadResources(make_ref(&context), make_ref(&texture));
}
EXPECTGL(glDeleteTexture(1));
}
UNIT_TEST(ColorPalleteUploadingMultiplyRow)
{
int const width = 4;
int const height = 8;
InitOpenGLTextures(width, height);
TestingGraphicsContext context;
Texture::Params p;
p.m_allocator = GetDefaultAllocator(make_ref(&context));
p.m_format = dp::TextureFormat::RGBA8;
p.m_width = width;
p.m_height = height;
DummyTexture texture;
texture.Create(make_ref(&context), p);
DummyColorPallete cp(m2::PointU(width, height));
cp.SetIsDebug(true);
{
cp.MapResource(dp::ColorKey(dp::Color(0xFF, 0, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xAA, 0, 0, 0)));
uint8_t memoryEtalon[] = {
0xFF, 0x00, 0x00, 0x00, // 1 pixel
0xFF, 0x00, 0x00, 0x00, // 1 pixel
0xAA, 0x00, 0x00, 0x00, // 2 pixel
0xAA, 0x00, 0x00, 0x00, // 2 pixel
0xFF, 0x00, 0x00, 0x00, // 1 pixel
0xFF, 0x00, 0x00, 0x00, // 1 pixel
0xAA, 0x00, 0x00, 0x00, // 2 pixel
0xAA, 0x00, 0x00, 0x00, // 2 pixel
};
MemoryComparer cmp(memoryEtalon, ARRAY_SIZE(memoryEtalon));
EXPECTGL(glTexSubImage2D(0, 0, 4, 2, AnyOf(gl_const::GLRGBA, gl_const::GLRGBA8), gl_const::GL8BitOnChannel, _))
.WillOnce(Invoke(&cmp, &MemoryComparer::cmpSubImage));
cp.UploadResources(make_ref(&context), make_ref(&texture));
}
{
cp.MapResource(dp::ColorKey(dp::Color(0xCC, 0, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xFF, 0xFF, 0, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xFF, 0xFF, 0xFF, 0)));
cp.MapResource(dp::ColorKey(dp::Color(0xFF, 0xFF, 0xFF, 0xFF)));
uint8_t memoryEtalon1[] = {
0xCC, 0x00, 0x00, 0x00, // 1 pixel
0xCC, 0x00, 0x00, 0x00, // 1 pixel
0xFF, 0xFF, 0x00, 0x00, // 2 pixel
0xFF, 0xFF, 0x00, 0x00, // 2 pixel
0xCC, 0x00, 0x00, 0x00, // 1 pixel
0xCC, 0x00, 0x00, 0x00, // 1 pixel
0xFF, 0xFF, 0x00, 0x00, // 2 pixel
0xFF, 0xFF, 0x00, 0x00, // 2 pixel
0xFF, 0xFF, 0xFF, 0x00, // 1 pixel
0xFF, 0xFF, 0xFF, 0x00, // 1 pixel
0xFF, 0xFF, 0xFF, 0xFF, // 2 pixel
0xFF, 0xFF, 0xFF, 0xFF, // 2 pixel
0xFF, 0xFF, 0xFF, 0x00, // 1 pixel
0xFF, 0xFF, 0xFF, 0x00, // 1 pixel
0xFF, 0xFF, 0xFF, 0xFF, // 2 pixel
0xFF, 0xFF, 0xFF, 0xFF, // 2 pixel
};
MemoryComparer cmp1(memoryEtalon1, ARRAY_SIZE(memoryEtalon1));
EXPECTGL(glTexSubImage2D(0, 2, 4, 4, AnyOf(gl_const::GLRGBA, gl_const::GLRGBA8), gl_const::GL8BitOnChannel, _))
.WillOnce(Invoke(&cmp1, &MemoryComparer::cmpSubImage));
cp.UploadResources(make_ref(&context), make_ref(&texture));
}
EXPECTGL(glDeleteTexture(1));
}

View file

@ -0,0 +1,157 @@
#include "testing/testing.hpp"
#include "drape/drape_global.hpp"
#include "drape/gl_gpu_program.hpp"
#include "drape/uniform_value.hpp"
#include "drape/drape_tests/gl_mock_functions.hpp"
#include <cstring>
#include <string>
#include <utility>
#include <gmock/gmock.h>
using ::testing::_;
using ::testing::AnyOf;
using ::testing::IgnoreResult;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Return;
using namespace dp;
namespace uniform_value_tests
{
template <typename T>
class MemoryComparer
{
public:
MemoryComparer(T const * memory, uint32_t size) : m_result(false), m_memory(memory), m_size(size) {}
void Compare(int32_t id, T const * memory) { m_result = memcmp(m_memory, memory, m_size) == 0; }
bool GetResult() const { return m_result; }
private:
bool m_result;
T const * m_memory;
uint32_t m_size;
};
void mock_glGetActiveUniform(uint32_t programID, uint32_t index, int32_t * size, glConst * type, std::string & name)
{
*size = 1;
if (index < 9)
{
static std::pair<std::string, glConst> mockUniforms[9] = {
{"position0", gl_const::GLIntType}, {"position1", gl_const::GLIntVec2},
{"position2", gl_const::GLIntVec3}, {"position3", gl_const::GLIntVec4},
{"position4", gl_const::GLFloatType}, {"position5", gl_const::GLFloatVec2},
{"position6", gl_const::GLFloatVec3}, {"position7", gl_const::GLFloatVec4},
{"viewModel", gl_const::GLFloatMat4}};
name = mockUniforms[index].first;
*type = mockUniforms[index].second;
}
else
{
ASSERT(false, ("Undefined index:", index));
}
}
UNIT_TEST(UniformValueTest)
{
uint32_t constexpr VertexShaderID = 1;
uint32_t constexpr FragmentShaderID = 2;
uint32_t constexpr ProgramID = 2;
int32_t constexpr positionLoc = 10;
int32_t constexpr modelViewLoc = 11;
float matrix[16] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f};
MemoryComparer<float> comparer(matrix, 16 * sizeof(float));
{
InSequence seq;
EXPECTGL(glCreateShader(gl_const::GLVertexShader)).WillOnce(Return(VertexShaderID));
EXPECTGL(glShaderSource(VertexShaderID, _)).Times(1);
EXPECTGL(glCompileShader(VertexShaderID, _)).WillOnce(Return(true));
EXPECTGL(glCreateShader(gl_const::GLFragmentShader)).WillOnce(Return(FragmentShaderID));
EXPECTGL(glShaderSource(FragmentShaderID, _)).Times(1);
EXPECTGL(glCompileShader(FragmentShaderID, _)).WillOnce(Return(true));
EXPECTGL(glCreateProgram()).WillOnce(Return(ProgramID));
EXPECTGL(glAttachShader(ProgramID, VertexShaderID));
EXPECTGL(glAttachShader(ProgramID, FragmentShaderID));
EXPECTGL(glLinkProgram(ProgramID, _)).WillOnce(Return(true));
EXPECTGL(glGetProgramiv(ProgramID, gl_const::GLActiveUniforms)).WillOnce(Return(9));
for (int i = 0; i < 9; i++)
{
EXPECTGL(glGetActiveUniform(ProgramID, _, _, _, _)).WillOnce(Invoke(mock_glGetActiveUniform));
EXPECTGL(glGetUniformLocation(ProgramID, _)).WillOnce(Return(i == 8 ? modelViewLoc : positionLoc));
}
EXPECTGL(glDetachShader(ProgramID, VertexShaderID));
EXPECTGL(glDetachShader(ProgramID, FragmentShaderID));
EXPECTGL(glUseProgram(ProgramID));
EXPECTGL(glUniformValuei(positionLoc, 1));
EXPECTGL(glUniformValuei(positionLoc, 1, 2));
EXPECTGL(glUniformValuei(positionLoc, 1, 2, 3));
EXPECTGL(glUniformValuei(positionLoc, 1, 2, 3, 4));
EXPECTGL(glUniformValuef(positionLoc, 1.0f));
EXPECTGL(glUniformValuef(positionLoc, 1.0f, 2.0f));
EXPECTGL(glUniformValuef(positionLoc, 1.0f, 2.0f, 3.0f));
EXPECTGL(glUniformValuef(positionLoc, 1.0f, 2.0f, 3.0f, 4.0f));
EXPECTGL(glUniformMatrix4x4Value(modelViewLoc, _)).WillOnce(Invoke(&comparer, &MemoryComparer<float>::Compare));
EXPECTGL(glUseProgram(0));
EXPECTGL(glDeleteProgram(ProgramID));
EXPECTGL(glDeleteShader(AnyOf(VertexShaderID, FragmentShaderID))).Times(2);
}
drape_ptr<Shader> vs = make_unique_dp<Shader>("", "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); }", "",
Shader::Type::VertexShader);
drape_ptr<Shader> fs = make_unique_dp<Shader>("", "void main() { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); }", "",
Shader::Type::FragmentShader);
drape_ptr<GLGpuProgram> program = make_unique_dp<GLGpuProgram>("", make_ref(vs), make_ref(fs));
program->Bind();
UniformValue::ApplyRaw(positionLoc, 1);
UniformValue::ApplyRaw(positionLoc, glsl::ivec2(1, 2));
UniformValue::ApplyRaw(positionLoc, glsl::ivec3(1, 2, 3));
UniformValue::ApplyRaw(positionLoc, glsl::ivec4(1, 2, 3, 4));
UniformValue::ApplyRaw(positionLoc, 1.0f);
UniformValue::ApplyRaw(positionLoc, glsl::vec2(1.0f, 2.0f));
UniformValue::ApplyRaw(positionLoc, glsl::vec3(1.0f, 2.0f, 3.0f));
UniformValue::ApplyRaw(positionLoc, glsl::vec4(1.0f, 2.0f, 3.0f, 4.0f));
UniformValue::ApplyRaw(modelViewLoc, glsl::make_mat4(matrix));
program->Unbind();
program.reset();
vs.reset();
fs.reset();
}
} // namespace uniform_value_tests

View file

@ -0,0 +1,97 @@
#include "testing/testing.hpp"
#include "drape/utils/vertex_decl.hpp"
#include "base/buffer_vector.hpp"
#include <boost/container/small_vector.hpp>
#include <random>
#include <vector>
namespace vertex_buffer_tests
{
#ifndef DEBUG
std::vector<uint32_t> GenerateSizes(uint32_t min, uint32_t max)
{
std::vector<uint32_t> res;
std::uniform_int_distribution<uint64_t> randDist(min, max);
std::random_device randDevice;
std::mt19937 randEngine(randDevice());
for (size_t i = 0; i < 1000000; ++i)
res.push_back(randDist(randEngine));
return res;
}
UNIT_TEST(VertexBuffer_Benchmark)
{
using ValueT = gpu::TextOutlinedStaticVertex;
// using ValueT = gpu::TextDynamicVertex
ValueT objs[] = {
{{1, 1}, {2, 2}, {3, 3}},
{{4, 4}, {5, 5}, {6, 6}},
{{7, 7}, {8, 8}, {9, 9}},
};
// Very comfortable for buffer_vector<ValueT, kUpperBound>.
uint32_t constexpr kUpperBound = 128;
std::vector<uint32_t> sizes = GenerateSizes(16, kUpperBound);
uint64_t t1, t2, t3, t4;
{
base::Timer timer;
for (uint32_t sz : sizes)
{
std::vector<ValueT> v;
for (size_t i = 0; i < sz; ++i)
v.push_back(objs[i % std::size(objs)]);
}
t1 = timer.ElapsedMilliseconds();
}
{
base::Timer timer;
for (uint32_t sz : sizes)
{
buffer_vector<ValueT, kUpperBound> v;
for (size_t i = 0; i < sz; ++i)
v.push_back(objs[i % std::size(objs)]);
}
t2 = timer.ElapsedMilliseconds();
}
{
base::Timer timer;
for (uint32_t sz : sizes)
{
boost::container::small_vector<ValueT, kUpperBound> v;
for (size_t i = 0; i < sz; ++i)
v.push_back(objs[i % std::size(objs)]);
}
t3 = timer.ElapsedMilliseconds();
}
{
base::Timer timer;
for (uint32_t sz : sizes)
{
std::vector<ValueT> v;
v.reserve(sz);
for (size_t i = 0; i < sz; ++i)
v.push_back(objs[i % std::size(objs)]);
}
t4 = timer.ElapsedMilliseconds();
}
LOG(LINFO,
("vector time:", t1, "buffer_vector time:", t2, "boost::small_vector time:", t3, "reserved vector time:", t4));
TEST_LESS(t2, t1, ());
TEST_LESS(t3, t2, ());
// TODO: Fix this condition
// TEST_LESS(t4, t3, ());
}
#endif
} // namespace vertex_buffer_tests