Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
199
libs/drape/CMakeLists.txt
Normal file
199
libs/drape/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
project(drape)
|
||||
|
||||
set(DRAPE_TESTABLE_SRC
|
||||
attribute_buffer_mutator.cpp
|
||||
attribute_buffer_mutator.hpp
|
||||
attribute_provider.cpp
|
||||
attribute_provider.hpp
|
||||
batcher.cpp
|
||||
batcher.hpp
|
||||
batcher_helpers.cpp
|
||||
batcher_helpers.hpp
|
||||
binding_info.cpp
|
||||
binding_info.hpp
|
||||
buffer_base.cpp
|
||||
buffer_base.hpp
|
||||
color.hpp
|
||||
constants.hpp
|
||||
cpu_buffer.cpp
|
||||
cpu_buffer.hpp
|
||||
data_buffer.cpp
|
||||
data_buffer.hpp
|
||||
data_buffer_impl.hpp
|
||||
debug_renderer.hpp
|
||||
drape_diagnostics.hpp
|
||||
drape_global.hpp
|
||||
drape_routine.hpp
|
||||
dynamic_texture.hpp
|
||||
font_constants.hpp
|
||||
font_texture.cpp
|
||||
font_texture.hpp
|
||||
framebuffer.cpp
|
||||
framebuffer.hpp
|
||||
gl_constants.cpp
|
||||
gl_constants.hpp
|
||||
gl_gpu_program.cpp
|
||||
gl_gpu_program.hpp
|
||||
gl_extensions_list.cpp
|
||||
gl_extensions_list.hpp
|
||||
gl_functions.hpp
|
||||
gl_includes.hpp
|
||||
glsl_func.hpp
|
||||
glsl_types.hpp
|
||||
glyph.hpp
|
||||
glyph_manager.cpp
|
||||
glyph_manager.hpp
|
||||
gpu_buffer.cpp
|
||||
gpu_buffer.hpp
|
||||
gpu_program.hpp
|
||||
graphics_context.hpp
|
||||
graphics_context_factory.cpp
|
||||
graphics_context_factory.hpp
|
||||
hw_texture.cpp
|
||||
hw_texture.hpp
|
||||
index_buffer.cpp
|
||||
index_buffer.hpp
|
||||
index_buffer_mutator.cpp
|
||||
index_buffer_mutator.hpp
|
||||
index_storage.cpp
|
||||
index_storage.hpp
|
||||
mesh_object.cpp
|
||||
mesh_object.hpp
|
||||
object_pool.hpp
|
||||
oglcontext.cpp
|
||||
oglcontext.hpp
|
||||
overlay_handle.cpp
|
||||
overlay_handle.hpp
|
||||
overlay_tree.cpp
|
||||
overlay_tree.hpp
|
||||
pointers.cpp
|
||||
pointers.hpp
|
||||
render_bucket.cpp
|
||||
render_bucket.hpp
|
||||
render_state.cpp
|
||||
render_state.hpp
|
||||
shader.cpp
|
||||
shader.hpp
|
||||
static_texture.cpp
|
||||
static_texture.hpp
|
||||
stipple_pen_resource.cpp
|
||||
stipple_pen_resource.hpp
|
||||
support_manager.cpp
|
||||
support_manager.hpp
|
||||
symbols_texture.cpp
|
||||
symbols_texture.hpp
|
||||
harfbuzz_shaping.cpp
|
||||
harfbuzz_shaping.hpp
|
||||
texture.cpp
|
||||
texture.hpp
|
||||
texture_manager.cpp
|
||||
texture_manager.hpp
|
||||
texture_of_colors.cpp
|
||||
texture_of_colors.hpp
|
||||
texture_types.hpp
|
||||
tm_read_resources.hpp
|
||||
uniform_value.cpp
|
||||
uniform_value.hpp
|
||||
utils/glyph_usage_tracker.cpp
|
||||
utils/glyph_usage_tracker.hpp
|
||||
utils/gpu_mem_tracker.cpp
|
||||
utils/gpu_mem_tracker.hpp
|
||||
utils/projection.cpp
|
||||
utils/projection.hpp
|
||||
utils/vertex_decl.cpp
|
||||
utils/vertex_decl.hpp
|
||||
vertex_array_buffer.cpp
|
||||
vertex_array_buffer.hpp
|
||||
viewport.cpp
|
||||
viewport.hpp
|
||||
visual_scale.hpp
|
||||
vulkan/vulkan_base_context.cpp
|
||||
vulkan/vulkan_base_context.hpp
|
||||
vulkan/vulkan_context_factory.cpp
|
||||
vulkan/vulkan_context_factory.hpp
|
||||
vulkan/vulkan_gpu_buffer_impl.cpp
|
||||
vulkan/vulkan_gpu_buffer_impl.hpp
|
||||
vulkan/vulkan_gpu_program.hpp
|
||||
vulkan/vulkan_layers.cpp
|
||||
vulkan/vulkan_layers.hpp
|
||||
vulkan/vulkan_memory_manager.cpp
|
||||
vulkan/vulkan_memory_manager.hpp
|
||||
vulkan/vulkan_mesh_object_impl.cpp
|
||||
vulkan/vulkan_object_manager.cpp
|
||||
vulkan/vulkan_object_manager.hpp
|
||||
vulkan/vulkan_param_descriptor.cpp
|
||||
vulkan/vulkan_param_descriptor.hpp
|
||||
vulkan/vulkan_pipeline.cpp
|
||||
vulkan/vulkan_pipeline.hpp
|
||||
vulkan/vulkan_staging_buffer.cpp
|
||||
vulkan/vulkan_staging_buffer.hpp
|
||||
vulkan/vulkan_texture.cpp
|
||||
vulkan/vulkan_texture.hpp
|
||||
vulkan/vulkan_utils.cpp
|
||||
vulkan/vulkan_utils.hpp
|
||||
vulkan/vulkan_vertex_array_buffer_impl.cpp
|
||||
)
|
||||
|
||||
if (PLATFORM_IPHONE OR PLATFORM_MAC)
|
||||
append(DRAPE_TESTABLE_SRC
|
||||
metal/metal_base_context.hpp
|
||||
metal/metal_base_context.mm
|
||||
metal/metal_cleaner.hpp
|
||||
metal/metal_cleaner.mm
|
||||
metal/metal_gpu_buffer_impl.mm
|
||||
metal/metal_gpu_program.hpp
|
||||
metal/metal_mesh_object_impl.mm
|
||||
metal/metal_states.hpp
|
||||
metal/metal_states.mm
|
||||
metal/metal_texture.hpp
|
||||
metal/metal_texture.mm
|
||||
metal/metal_vertex_array_buffer_impl.mm
|
||||
metal/render_state_metal.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
set(SRC ${DRAPE_TESTABLE_SRC} gl_functions.cpp)
|
||||
|
||||
if (PLATFORM_IPHONE)
|
||||
append(SRC
|
||||
hw_texture_ios.hpp
|
||||
hw_texture_ios.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
# Do not include glm's CMakeLists.txt, because it's outdated and not necessary.
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${OMIM_ROOT}/3party/glm)
|
||||
|
||||
if (PLATFORM_LINUX OR PLATFORM_WIN)
|
||||
find_package(OpenGL)
|
||||
endif()
|
||||
|
||||
if (PLATFORM_MAC)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
|
||||
endif()
|
||||
|
||||
set(DRAPE_LINK_LIBRARIES
|
||||
indexer
|
||||
platform
|
||||
geometry
|
||||
coding
|
||||
base
|
||||
Freetype::Freetype
|
||||
vulkan_wrapper
|
||||
stb_image
|
||||
harfbuzz::harfbuzz
|
||||
ICU::i18n
|
||||
expat::expat
|
||||
$<$<BOOL:${PLATFORM_MAC}>:-framework\ OpenGL>
|
||||
$<$<BOOL:${PLATFORM_MAC}>:-framework\ Metal>
|
||||
$<$<BOOL:${PLATFORM_LINUX}>:OpenGL::GL>
|
||||
$<$<BOOL:${PLATFORM_WIN}>:OpenGL::GL>
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} ${DRAPE_LINK_LIBRARIES})
|
||||
|
||||
omim_add_test_subdirectory(drape_tests)
|
||||
|
||||
omim_add_tool_subdirectory(fonts_tool)
|
||||
25
libs/drape/attribute_buffer_mutator.cpp
Normal file
25
libs/drape/attribute_buffer_mutator.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include "drape/attribute_buffer_mutator.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
AttributeBufferMutator::~AttributeBufferMutator()
|
||||
{
|
||||
SharedBufferManager & mng = SharedBufferManager::instance();
|
||||
for (size_t i = 0; i < m_array.size(); ++i)
|
||||
{
|
||||
TBufferNode & node = m_array[i];
|
||||
mng.freeSharedBuffer(node.second, node.first);
|
||||
}
|
||||
}
|
||||
|
||||
void AttributeBufferMutator::AddMutation(BindingInfo const & info, MutateNode const & node)
|
||||
{
|
||||
m_data[info].push_back(node);
|
||||
}
|
||||
|
||||
void * AttributeBufferMutator::AllocateMutationBuffer(size_t byteCount)
|
||||
{
|
||||
m_array.push_back(make_pair(SharedBufferManager::instance().reserveSharedBuffer(byteCount), byteCount));
|
||||
return &((*m_array.back().first)[0]);
|
||||
}
|
||||
} // namespace dp
|
||||
49
libs/drape/attribute_buffer_mutator.hpp
Normal file
49
libs/drape/attribute_buffer_mutator.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/binding_info.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "base/shared_buffer_manager.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
struct MutateRegion
|
||||
{
|
||||
MutateRegion() : m_offset(0), m_count(0) {}
|
||||
MutateRegion(uint32_t offset, uint32_t count) : m_offset(offset), m_count(count) {}
|
||||
|
||||
uint32_t m_offset; // Offset from buffer begin in "Elements" not in bytes.
|
||||
uint32_t m_count; // Count of "Elements".
|
||||
};
|
||||
|
||||
struct MutateNode
|
||||
{
|
||||
MutateRegion m_region;
|
||||
ref_ptr<void> m_data;
|
||||
};
|
||||
|
||||
class AttributeBufferMutator
|
||||
{
|
||||
using TBufferNode = std::pair<SharedBufferManager::shared_buffer_ptr_t, size_t>;
|
||||
using TBufferArray = std::vector<TBufferNode>;
|
||||
using TMutateNodes = std::vector<MutateNode>;
|
||||
using TMutateData = std::map<BindingInfo, TMutateNodes>;
|
||||
|
||||
public:
|
||||
~AttributeBufferMutator();
|
||||
void AddMutation(BindingInfo const & info, MutateNode const & node);
|
||||
void * AllocateMutationBuffer(size_t byteCount);
|
||||
|
||||
private:
|
||||
friend class VertexArrayBuffer;
|
||||
TMutateData const & GetMutateData() const { return m_data; }
|
||||
|
||||
TMutateData m_data;
|
||||
TBufferArray m_array;
|
||||
};
|
||||
} // namespace dp
|
||||
107
libs/drape/attribute_provider.cpp
Normal file
107
libs/drape/attribute_provider.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#include "drape/attribute_provider.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define INIT_CHECK_INFO(x) m_checkInfo = std::vector<bool>((std::vector<bool>::size_type)(x), false);
|
||||
#define CHECK_STREAMS CheckStreams()
|
||||
#define INIT_STREAM(x) InitCheckStream((x))
|
||||
#else
|
||||
#include "base/macros.hpp"
|
||||
#define INIT_CHECK_INFO(x) UNUSED_VALUE((x))
|
||||
#define CHECK_STREAMS
|
||||
#define INIT_STREAM(x) UNUSED_VALUE((x))
|
||||
#endif
|
||||
|
||||
namespace dp
|
||||
{
|
||||
AttributeProvider::AttributeProvider(uint8_t streamCount, uint32_t vertexCount) : m_vertexCount(vertexCount)
|
||||
{
|
||||
m_streams.resize(streamCount);
|
||||
INIT_CHECK_INFO(streamCount);
|
||||
}
|
||||
|
||||
bool AttributeProvider::IsDataExists() const
|
||||
{
|
||||
CHECK_STREAMS;
|
||||
return m_vertexCount > 0;
|
||||
}
|
||||
|
||||
uint32_t AttributeProvider::GetVertexCount() const
|
||||
{
|
||||
return m_vertexCount;
|
||||
}
|
||||
|
||||
uint8_t AttributeProvider::GetStreamCount() const
|
||||
{
|
||||
return m_streams.size();
|
||||
}
|
||||
|
||||
void const * AttributeProvider::GetRawPointer(uint8_t streamIndex)
|
||||
{
|
||||
ASSERT_LESS(streamIndex, GetStreamCount(), ());
|
||||
CHECK_STREAMS;
|
||||
return m_streams[streamIndex].m_data.get();
|
||||
}
|
||||
|
||||
BindingInfo const & AttributeProvider::GetBindingInfo(uint8_t streamIndex) const
|
||||
{
|
||||
ASSERT_LESS(streamIndex, GetStreamCount(), ());
|
||||
CHECK_STREAMS;
|
||||
return m_streams[streamIndex].m_binding;
|
||||
}
|
||||
|
||||
void AttributeProvider::Advance(uint32_t vertexCount)
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(vertexCount, m_vertexCount, ());
|
||||
CHECK_STREAMS;
|
||||
|
||||
if (m_vertexCount != vertexCount)
|
||||
{
|
||||
for (size_t i = 0; i < GetStreamCount(); ++i)
|
||||
{
|
||||
BindingInfo const & info = m_streams[i].m_binding;
|
||||
uint32_t offset = vertexCount * info.GetElementSize();
|
||||
void * rawPointer = m_streams[i].m_data.get();
|
||||
m_streams[i].m_data = make_ref((void *)(((uint8_t *)rawPointer) + offset));
|
||||
}
|
||||
}
|
||||
|
||||
m_vertexCount -= vertexCount;
|
||||
}
|
||||
|
||||
void AttributeProvider::InitStream(uint8_t streamIndex, BindingInfo const & bindingInfo, ref_ptr<void> data)
|
||||
{
|
||||
ASSERT_LESS(streamIndex, GetStreamCount(), ());
|
||||
AttributeStream s;
|
||||
s.m_binding = bindingInfo;
|
||||
s.m_data = data;
|
||||
m_streams[streamIndex] = s;
|
||||
INIT_STREAM(streamIndex);
|
||||
}
|
||||
|
||||
void AttributeProvider::Reset(uint32_t vertexCount)
|
||||
{
|
||||
m_vertexCount = vertexCount;
|
||||
}
|
||||
|
||||
void AttributeProvider::UpdateStream(uint8_t streamIndex, ref_ptr<void> data)
|
||||
{
|
||||
ASSERT_LESS(streamIndex, GetStreamCount(), ());
|
||||
m_streams[streamIndex].m_data = data;
|
||||
INIT_STREAM(streamIndex);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void AttributeProvider::CheckStreams() const
|
||||
{
|
||||
ASSERT(!base::IsExist(m_checkInfo, false), ("Not all streams initialized"));
|
||||
}
|
||||
|
||||
void AttributeProvider::InitCheckStream(uint8_t streamIndex)
|
||||
{
|
||||
m_checkInfo[streamIndex] = true;
|
||||
}
|
||||
#endif
|
||||
} // namespace dp
|
||||
44
libs/drape/attribute_provider.hpp
Normal file
44
libs/drape/attribute_provider.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/binding_info.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class AttributeProvider
|
||||
{
|
||||
public:
|
||||
AttributeProvider(uint8_t streamCount, uint32_t vertexCount);
|
||||
|
||||
bool IsDataExists() const;
|
||||
uint32_t GetVertexCount() const;
|
||||
|
||||
uint8_t GetStreamCount() const;
|
||||
void const * GetRawPointer(uint8_t streamIndex);
|
||||
BindingInfo const & GetBindingInfo(uint8_t streamIndex) const;
|
||||
|
||||
void Advance(uint32_t vertexCount);
|
||||
|
||||
void InitStream(uint8_t streamIndex, BindingInfo const & bindingInfo, ref_ptr<void> data);
|
||||
|
||||
void Reset(uint32_t vertexCount);
|
||||
void UpdateStream(uint8_t streamIndex, ref_ptr<void> data);
|
||||
|
||||
private:
|
||||
uint32_t m_vertexCount;
|
||||
|
||||
struct AttributeStream
|
||||
{
|
||||
BindingInfo m_binding;
|
||||
ref_ptr<void> m_data;
|
||||
};
|
||||
std::vector<AttributeStream> m_streams;
|
||||
#ifdef DEBUG
|
||||
void CheckStreams() const;
|
||||
void InitCheckStream(uint8_t streamIndex);
|
||||
std::vector<bool> m_checkInfo;
|
||||
#endif
|
||||
};
|
||||
} // namespace dp
|
||||
299
libs/drape/batcher.cpp
Normal file
299
libs/drape/batcher.cpp
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
#include "drape/batcher.hpp"
|
||||
#include "drape/batcher_helpers.hpp"
|
||||
#include "drape/cpu_buffer.hpp"
|
||||
#include "drape/index_storage.hpp"
|
||||
#include "drape/vertex_array_buffer.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class Batcher::CallbacksWrapper : public BatchCallbacks
|
||||
{
|
||||
public:
|
||||
CallbacksWrapper(RenderState const & state, ref_ptr<OverlayHandle> overlay, ref_ptr<Batcher> batcher)
|
||||
: m_state(state)
|
||||
, m_overlay(overlay)
|
||||
, m_batcher(batcher)
|
||||
{}
|
||||
|
||||
void SetVAO(ref_ptr<VertexArrayBuffer> buffer)
|
||||
{
|
||||
// Invocation with non-null VAO will cause to invalid range of indices.
|
||||
// It means that VAO has been changed during batching.
|
||||
if (m_buffer != nullptr)
|
||||
m_vaoChanged = true;
|
||||
|
||||
m_buffer = buffer;
|
||||
m_indicesRange.m_idxStart = m_buffer->GetIndexCount();
|
||||
}
|
||||
|
||||
void FlushData(ref_ptr<GraphicsContext> context, BindingInfo const & info, void const * data, uint32_t count) override
|
||||
{
|
||||
if (m_overlay != nullptr && info.IsDynamic())
|
||||
{
|
||||
uint32_t offset = m_buffer->GetDynamicBufferOffset(info);
|
||||
m_overlay->AddDynamicAttribute(info, offset, count);
|
||||
}
|
||||
m_buffer->UploadData(context, info, data, count);
|
||||
}
|
||||
|
||||
void * GetIndexStorage(uint32_t size, uint32_t & startIndex) override
|
||||
{
|
||||
startIndex = m_buffer->GetStartIndexValue();
|
||||
if (m_overlay == nullptr || !m_overlay->IndexesRequired())
|
||||
{
|
||||
m_indexStorage.Resize(size);
|
||||
return m_indexStorage.GetRaw();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_overlay->IndexStorage(size);
|
||||
}
|
||||
}
|
||||
|
||||
void SubmitIndices(ref_ptr<GraphicsContext> context) override
|
||||
{
|
||||
if (m_overlay == nullptr || !m_overlay->IndexesRequired())
|
||||
m_buffer->UploadIndices(context, m_indexStorage.GetRawConst(), m_indexStorage.Size());
|
||||
}
|
||||
|
||||
uint32_t GetAvailableVertexCount() const override { return m_buffer->GetAvailableVertexCount(); }
|
||||
|
||||
uint32_t GetAvailableIndexCount() const override { return m_buffer->GetAvailableIndexCount(); }
|
||||
|
||||
void ChangeBuffer(ref_ptr<GraphicsContext> context) override { m_batcher->ChangeBuffer(context, make_ref(this)); }
|
||||
|
||||
RenderState const & GetState() const { return m_state; }
|
||||
|
||||
IndicesRange const & Finish()
|
||||
{
|
||||
if (!m_vaoChanged)
|
||||
m_indicesRange.m_idxCount = m_buffer->GetIndexCount() - m_indicesRange.m_idxStart;
|
||||
else
|
||||
m_indicesRange = IndicesRange();
|
||||
|
||||
return m_indicesRange;
|
||||
}
|
||||
|
||||
private:
|
||||
RenderState const & m_state;
|
||||
ref_ptr<OverlayHandle> m_overlay;
|
||||
ref_ptr<Batcher> m_batcher;
|
||||
ref_ptr<VertexArrayBuffer> m_buffer;
|
||||
IndexStorage m_indexStorage;
|
||||
IndicesRange m_indicesRange;
|
||||
bool m_vaoChanged = false;
|
||||
};
|
||||
|
||||
Batcher::Batcher(uint32_t indexBufferSize, uint32_t vertexBufferSize)
|
||||
: m_indexBufferSize(indexBufferSize)
|
||||
, m_vertexBufferSize(vertexBufferSize)
|
||||
{}
|
||||
|
||||
Batcher::~Batcher()
|
||||
{
|
||||
m_buckets.clear();
|
||||
}
|
||||
|
||||
void Batcher::InsertTriangleList(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params)
|
||||
{
|
||||
InsertTriangleList(context, state, params, nullptr);
|
||||
}
|
||||
|
||||
IndicesRange Batcher::InsertTriangleList(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle)
|
||||
{
|
||||
return InsertPrimitives<TriangleListBatch>(context, state, params, std::move(handle), 0 /* vertexStride */);
|
||||
}
|
||||
|
||||
void Batcher::InsertTriangleStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params)
|
||||
{
|
||||
InsertTriangleStrip(context, state, params, nullptr);
|
||||
}
|
||||
|
||||
IndicesRange Batcher::InsertTriangleStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle)
|
||||
{
|
||||
return InsertPrimitives<TriangleStripBatch>(context, state, params, std::move(handle), 0 /* vertexStride */);
|
||||
}
|
||||
|
||||
void Batcher::InsertTriangleFan(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params)
|
||||
{
|
||||
InsertTriangleFan(context, state, params, nullptr);
|
||||
}
|
||||
|
||||
IndicesRange Batcher::InsertTriangleFan(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle)
|
||||
{
|
||||
return InsertPrimitives<TriangleFanBatch>(context, state, params, std::move(handle), 0 /* vertexStride */);
|
||||
}
|
||||
|
||||
void Batcher::InsertListOfStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, uint8_t vertexStride)
|
||||
{
|
||||
InsertListOfStrip(context, state, params, nullptr, vertexStride);
|
||||
}
|
||||
|
||||
IndicesRange Batcher::InsertListOfStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle,
|
||||
uint8_t vertexStride)
|
||||
{
|
||||
return InsertPrimitives<TriangleListOfStripBatch>(context, state, params, std::move(handle), vertexStride);
|
||||
}
|
||||
|
||||
void Batcher::InsertLineStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params)
|
||||
{
|
||||
InsertLineStrip(context, state, params, nullptr);
|
||||
}
|
||||
|
||||
IndicesRange Batcher::InsertLineStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle)
|
||||
{
|
||||
return InsertPrimitives<LineStripBatch>(context, state, params, std::move(handle), 0 /* vertexStride */);
|
||||
}
|
||||
|
||||
void Batcher::InsertLineRaw(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, std::vector<int> const & indices)
|
||||
{
|
||||
InsertLineRaw(context, state, params, indices, nullptr);
|
||||
}
|
||||
|
||||
IndicesRange Batcher::InsertLineRaw(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, std::vector<int> const & indices,
|
||||
drape_ptr<OverlayHandle> && handle)
|
||||
{
|
||||
return InsertPrimitives<LineRawBatch>(context, state, params, std::move(handle), 0 /* vertexStride */, indices);
|
||||
}
|
||||
|
||||
void Batcher::StartSession(TFlushFn && flusher)
|
||||
{
|
||||
m_flushInterface = std::move(flusher);
|
||||
}
|
||||
|
||||
void Batcher::EndSession(ref_ptr<GraphicsContext> context)
|
||||
{
|
||||
Flush(context);
|
||||
m_flushInterface = TFlushFn();
|
||||
}
|
||||
|
||||
void Batcher::ResetSession()
|
||||
{
|
||||
m_flushInterface = TFlushFn();
|
||||
m_buckets.clear();
|
||||
}
|
||||
|
||||
void Batcher::SetFeatureMinZoom(int minZoom)
|
||||
{
|
||||
m_featureMinZoom = minZoom;
|
||||
|
||||
for (auto const & bucket : m_buckets)
|
||||
bucket.second->SetFeatureMinZoom(m_featureMinZoom);
|
||||
}
|
||||
|
||||
void Batcher::SetBatcherHash(uint64_t batcherHash)
|
||||
{
|
||||
m_batcherHash = batcherHash;
|
||||
}
|
||||
|
||||
void Batcher::ChangeBuffer(ref_ptr<GraphicsContext> context, ref_ptr<CallbacksWrapper> wrapper)
|
||||
{
|
||||
RenderState const & state = wrapper->GetState();
|
||||
FinalizeBucket(context, state);
|
||||
|
||||
CHECK(m_buckets.find(state) == m_buckets.end(), ());
|
||||
ref_ptr<RenderBucket> bucket = GetBucket(state);
|
||||
wrapper->SetVAO(bucket->GetBuffer());
|
||||
}
|
||||
|
||||
ref_ptr<RenderBucket> Batcher::GetBucket(RenderState const & state)
|
||||
{
|
||||
auto res = m_buckets.insert({state, nullptr});
|
||||
if (res.second)
|
||||
{
|
||||
drape_ptr<VertexArrayBuffer> vao =
|
||||
make_unique_dp<VertexArrayBuffer>(m_indexBufferSize, m_vertexBufferSize, m_batcherHash);
|
||||
drape_ptr<RenderBucket> buffer = make_unique_dp<RenderBucket>(std::move(vao));
|
||||
buffer->SetFeatureMinZoom(m_featureMinZoom);
|
||||
res.first->second = std::move(buffer);
|
||||
}
|
||||
|
||||
return make_ref(res.first->second);
|
||||
}
|
||||
|
||||
void Batcher::FinalizeBucket(ref_ptr<GraphicsContext> context, RenderState const & state)
|
||||
{
|
||||
auto const it = m_buckets.find(state);
|
||||
CHECK(it != m_buckets.end(), ("Have no bucket for finalize with given state"));
|
||||
drape_ptr<RenderBucket> bucket = std::move(it->second);
|
||||
m_buckets.erase(it);
|
||||
|
||||
bucket->GetBuffer()->Preflush(context);
|
||||
m_flushInterface(state, std::move(bucket));
|
||||
}
|
||||
|
||||
void Batcher::Flush(ref_ptr<GraphicsContext> context)
|
||||
{
|
||||
ASSERT(m_flushInterface != NULL, ());
|
||||
std::for_each(m_buckets.begin(), m_buckets.end(), [this, context](TBuckets::value_type & bucket)
|
||||
{
|
||||
ASSERT(bucket.second != nullptr, ());
|
||||
bucket.second->GetBuffer()->Preflush(context);
|
||||
m_flushInterface(bucket.first, std::move(bucket.second));
|
||||
});
|
||||
|
||||
m_buckets.clear();
|
||||
}
|
||||
|
||||
template <typename TBatcher, typename... TArgs>
|
||||
IndicesRange Batcher::InsertPrimitives(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && transferHandle,
|
||||
uint8_t vertexStride, TArgs... batcherArgs)
|
||||
{
|
||||
ref_ptr<VertexArrayBuffer> vao = GetBucket(state)->GetBuffer();
|
||||
IndicesRange range;
|
||||
|
||||
drape_ptr<OverlayHandle> handle = std::move(transferHandle);
|
||||
|
||||
{
|
||||
Batcher::CallbacksWrapper wrapper(state, make_ref(handle), make_ref(this));
|
||||
wrapper.SetVAO(vao);
|
||||
|
||||
TBatcher batch(wrapper, batcherArgs...);
|
||||
batch.SetCanDivideStreams(handle == nullptr);
|
||||
batch.SetVertexStride(vertexStride);
|
||||
batch.BatchData(context, params);
|
||||
|
||||
range = wrapper.Finish();
|
||||
}
|
||||
|
||||
if (handle != nullptr)
|
||||
GetBucket(state)->AddOverlayHandle(std::move(handle));
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
Batcher * BatcherFactory::GetNew() const
|
||||
{
|
||||
return new Batcher(m_indexBufferSize, m_vertexBufferSize);
|
||||
}
|
||||
|
||||
SessionGuard::SessionGuard(ref_ptr<GraphicsContext> context, Batcher & batcher, Batcher::TFlushFn && flusher)
|
||||
: m_context(context)
|
||||
, m_batcher(batcher)
|
||||
{
|
||||
m_batcher.StartSession(std::move(flusher));
|
||||
}
|
||||
|
||||
SessionGuard::~SessionGuard()
|
||||
{
|
||||
m_batcher.EndSession(m_context);
|
||||
}
|
||||
} // namespace dp
|
||||
125
libs/drape/batcher.hpp
Normal file
125
libs/drape/batcher.hpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/attribute_provider.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/overlay_handle.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/render_bucket.hpp"
|
||||
#include "drape/render_state.hpp"
|
||||
#include "drape/vertex_array_buffer.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class RenderBucket;
|
||||
class AttributeProvider;
|
||||
class OverlayHandle;
|
||||
|
||||
class Batcher
|
||||
{
|
||||
public:
|
||||
static uint32_t const IndexPerTriangle = 3;
|
||||
static uint32_t const IndexPerQuad = 6;
|
||||
static uint32_t const VertexPerQuad = 4;
|
||||
|
||||
Batcher(uint32_t indexBufferSize, uint32_t vertexBufferSize);
|
||||
~Batcher();
|
||||
|
||||
void InsertTriangleList(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params);
|
||||
IndicesRange InsertTriangleList(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle);
|
||||
|
||||
void InsertTriangleStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params);
|
||||
IndicesRange InsertTriangleStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle);
|
||||
|
||||
void InsertTriangleFan(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params);
|
||||
IndicesRange InsertTriangleFan(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle);
|
||||
|
||||
void InsertListOfStrip(ref_ptr<GraphicsContext> context, RenderState const & state, ref_ptr<AttributeProvider> params,
|
||||
uint8_t vertexStride);
|
||||
IndicesRange InsertListOfStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle,
|
||||
uint8_t vertexStride);
|
||||
|
||||
void InsertLineStrip(ref_ptr<GraphicsContext> context, RenderState const & state, ref_ptr<AttributeProvider> params);
|
||||
IndicesRange InsertLineStrip(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && handle);
|
||||
|
||||
void InsertLineRaw(ref_ptr<GraphicsContext> context, RenderState const & state, ref_ptr<AttributeProvider> params,
|
||||
std::vector<int> const & indices);
|
||||
IndicesRange InsertLineRaw(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, std::vector<int> const & indices,
|
||||
drape_ptr<OverlayHandle> && handle);
|
||||
|
||||
using TFlushFn = std::function<void(RenderState const &, drape_ptr<RenderBucket> &&)>;
|
||||
void StartSession(TFlushFn && flusher);
|
||||
void EndSession(ref_ptr<GraphicsContext> context);
|
||||
void ResetSession();
|
||||
|
||||
void SetBatcherHash(uint64_t batcherHash);
|
||||
|
||||
void SetFeatureMinZoom(int minZoom);
|
||||
|
||||
private:
|
||||
template <typename TBatcher, typename... TArgs>
|
||||
IndicesRange InsertPrimitives(ref_ptr<GraphicsContext> context, RenderState const & state,
|
||||
ref_ptr<AttributeProvider> params, drape_ptr<OverlayHandle> && transferHandle,
|
||||
uint8_t vertexStride, TArgs... batcherArgs);
|
||||
|
||||
class CallbacksWrapper;
|
||||
void ChangeBuffer(ref_ptr<GraphicsContext> context, ref_ptr<CallbacksWrapper> wrapper);
|
||||
ref_ptr<RenderBucket> GetBucket(RenderState const & state);
|
||||
|
||||
void FinalizeBucket(ref_ptr<GraphicsContext> context, RenderState const & state);
|
||||
void Flush(ref_ptr<GraphicsContext> context);
|
||||
|
||||
uint32_t const m_indexBufferSize;
|
||||
uint32_t const m_vertexBufferSize;
|
||||
|
||||
uint64_t m_batcherHash = 0;
|
||||
|
||||
TFlushFn m_flushInterface;
|
||||
|
||||
using TBuckets = std::map<RenderState, drape_ptr<RenderBucket>>;
|
||||
TBuckets m_buckets;
|
||||
|
||||
int m_featureMinZoom = 0;
|
||||
};
|
||||
|
||||
class BatcherFactory
|
||||
{
|
||||
public:
|
||||
BatcherFactory(uint32_t indexBufferSize, uint32_t vertexBufferSize)
|
||||
: m_indexBufferSize(indexBufferSize)
|
||||
, m_vertexBufferSize(vertexBufferSize)
|
||||
{}
|
||||
|
||||
Batcher * GetNew() const;
|
||||
|
||||
private:
|
||||
uint32_t const m_indexBufferSize;
|
||||
uint32_t const m_vertexBufferSize;
|
||||
};
|
||||
|
||||
class SessionGuard
|
||||
{
|
||||
public:
|
||||
SessionGuard(ref_ptr<GraphicsContext> context, Batcher & batcher, Batcher::TFlushFn && flusher);
|
||||
~SessionGuard();
|
||||
|
||||
private:
|
||||
ref_ptr<GraphicsContext> m_context;
|
||||
Batcher & m_batcher;
|
||||
DISALLOW_COPY_AND_MOVE(SessionGuard);
|
||||
};
|
||||
} // namespace dp
|
||||
639
libs/drape/batcher_helpers.cpp
Normal file
639
libs/drape/batcher_helpers.cpp
Normal file
|
|
@ -0,0 +1,639 @@
|
|||
#include "drape/batcher_helpers.hpp"
|
||||
#include "drape/attribute_provider.hpp"
|
||||
#include "drape/cpu_buffer.hpp"
|
||||
#include "drape/index_storage.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
T CyclicClamp(T const x, T const xmin, T const xmax)
|
||||
{
|
||||
if (x > xmax)
|
||||
return xmin;
|
||||
if (x < xmin)
|
||||
return xmax;
|
||||
return x;
|
||||
}
|
||||
|
||||
bool IsEnoughMemory(uint32_t avVertex, uint32_t existVertex, uint32_t avIndex, uint32_t existIndex)
|
||||
{
|
||||
return avVertex >= existVertex && avIndex >= existIndex;
|
||||
}
|
||||
|
||||
template <typename TGenerator>
|
||||
void GenerateIndices(void * indexStorage, uint32_t count, uint32_t startIndex)
|
||||
{
|
||||
GenerateIndices<TGenerator>(indexStorage, count, TGenerator(startIndex));
|
||||
}
|
||||
|
||||
template <typename TGenerator>
|
||||
void GenerateIndices(void * indexStorage, uint32_t count, TGenerator const & generator)
|
||||
{
|
||||
if (dp::IndexStorage::IsSupported32bit())
|
||||
{
|
||||
auto pIndexStorage = static_cast<uint32_t *>(indexStorage);
|
||||
std::generate(pIndexStorage, pIndexStorage + count, generator);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pIndexStorage = static_cast<uint16_t *>(indexStorage);
|
||||
std::generate(pIndexStorage, pIndexStorage + count, generator);
|
||||
}
|
||||
}
|
||||
|
||||
class IndexGenerator
|
||||
{
|
||||
public:
|
||||
explicit IndexGenerator(uint32_t startIndex) : m_startIndex(startIndex), m_counter(0), m_minStripCounter(0) {}
|
||||
|
||||
protected:
|
||||
uint32_t GetCounter() { return m_counter++; }
|
||||
|
||||
void ResetCounter()
|
||||
{
|
||||
m_counter = 0;
|
||||
m_minStripCounter = 0;
|
||||
}
|
||||
|
||||
uint32_t const m_startIndex;
|
||||
|
||||
int16_t GetCWNormalizer()
|
||||
{
|
||||
int16_t tmp = m_minStripCounter;
|
||||
m_minStripCounter = static_cast<uint8_t>(CyclicClamp(m_minStripCounter + 1, 0, 5));
|
||||
switch (tmp)
|
||||
{
|
||||
case 4: return 1;
|
||||
case 5: return -1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_counter;
|
||||
uint8_t m_minStripCounter;
|
||||
};
|
||||
|
||||
class ListIndexGenerator : public IndexGenerator
|
||||
{
|
||||
public:
|
||||
explicit ListIndexGenerator(uint32_t startIndex) : IndexGenerator(startIndex) {}
|
||||
uint32_t operator()() { return m_startIndex + GetCounter(); }
|
||||
};
|
||||
|
||||
class StripIndexGenerator : public IndexGenerator
|
||||
{
|
||||
public:
|
||||
explicit StripIndexGenerator(uint32_t startIndex) : IndexGenerator(startIndex) {}
|
||||
|
||||
uint32_t operator()()
|
||||
{
|
||||
uint32_t const counter = GetCounter();
|
||||
return m_startIndex + counter - 2 * (counter / 3) + GetCWNormalizer();
|
||||
}
|
||||
};
|
||||
|
||||
class LineStripIndexGenerator : public IndexGenerator
|
||||
{
|
||||
public:
|
||||
explicit LineStripIndexGenerator(uint32_t startIndex) : IndexGenerator(startIndex) {}
|
||||
|
||||
uint32_t operator()()
|
||||
{
|
||||
uint32_t const counter = GetCounter();
|
||||
uint32_t const result = m_startIndex + m_counter;
|
||||
if (counter % 2 == 0)
|
||||
m_counter++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_counter = 0;
|
||||
};
|
||||
|
||||
class LineRawIndexGenerator : public IndexGenerator
|
||||
{
|
||||
public:
|
||||
LineRawIndexGenerator(uint32_t startIndex, std::vector<int> const & indices)
|
||||
: IndexGenerator(startIndex)
|
||||
, m_indices(indices)
|
||||
{}
|
||||
|
||||
uint32_t operator()()
|
||||
{
|
||||
uint32_t const counter = GetCounter();
|
||||
ASSERT_LESS(counter, m_indices.size(), ());
|
||||
return static_cast<uint32_t>(m_startIndex + m_indices[counter]);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int> const & m_indices;
|
||||
};
|
||||
|
||||
class FanIndexGenerator : public IndexGenerator
|
||||
{
|
||||
public:
|
||||
explicit FanIndexGenerator(uint32_t startIndex) : IndexGenerator(startIndex) {}
|
||||
|
||||
uint32_t operator()()
|
||||
{
|
||||
uint32_t const counter = GetCounter();
|
||||
if ((counter % 3) == 0)
|
||||
return m_startIndex;
|
||||
return m_startIndex + counter - 2 * (counter / 3);
|
||||
}
|
||||
};
|
||||
|
||||
class ListOfStripGenerator : public IndexGenerator
|
||||
{
|
||||
public:
|
||||
ListOfStripGenerator(uint32_t startIndex, uint32_t vertexStride, uint32_t indexPerStrip)
|
||||
: IndexGenerator(startIndex)
|
||||
, m_vertexStride(vertexStride)
|
||||
, m_indexPerStrip(indexPerStrip)
|
||||
, m_base(0)
|
||||
{}
|
||||
|
||||
uint32_t operator()()
|
||||
{
|
||||
uint32_t const counter = GetCounter();
|
||||
uint32_t const result = m_startIndex + m_base + counter - 2 * (counter / 3) + GetCWNormalizer();
|
||||
if (counter + 1 == m_indexPerStrip)
|
||||
{
|
||||
m_base += m_vertexStride;
|
||||
ResetCounter();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_vertexStride;
|
||||
uint32_t m_indexPerStrip;
|
||||
uint32_t m_base;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
UniversalBatch::UniversalBatch(BatchCallbacks & callbacks, uint8_t minVerticesCount, uint8_t minIndicesCount)
|
||||
: m_callbacks(callbacks)
|
||||
, m_canDivideStreams(true)
|
||||
, m_minVerticesCount(minVerticesCount)
|
||||
, m_minIndicesCount(minIndicesCount)
|
||||
{}
|
||||
|
||||
void UniversalBatch::SetCanDivideStreams(bool canDivide)
|
||||
{
|
||||
m_canDivideStreams = canDivide;
|
||||
}
|
||||
|
||||
bool UniversalBatch::CanDivideStreams() const
|
||||
{
|
||||
return m_canDivideStreams;
|
||||
}
|
||||
|
||||
void UniversalBatch::SetVertexStride(uint8_t vertexStride)
|
||||
{
|
||||
m_vertexStride = vertexStride;
|
||||
}
|
||||
|
||||
void UniversalBatch::FlushData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams,
|
||||
uint32_t vertexCount) const
|
||||
{
|
||||
for (uint8_t i = 0; i < streams->GetStreamCount(); ++i)
|
||||
FlushData(context, streams->GetBindingInfo(i), streams->GetRawPointer(i), vertexCount);
|
||||
}
|
||||
|
||||
void UniversalBatch::FlushData(ref_ptr<GraphicsContext> context, BindingInfo const & info, void const * data,
|
||||
uint32_t elementCount) const
|
||||
{
|
||||
m_callbacks.FlushData(context, info, data, elementCount);
|
||||
}
|
||||
|
||||
void * UniversalBatch::GetIndexStorage(uint32_t indexCount, uint32_t & startIndex)
|
||||
{
|
||||
return m_callbacks.GetIndexStorage(indexCount, startIndex);
|
||||
}
|
||||
|
||||
void UniversalBatch::SubmitIndices(ref_ptr<GraphicsContext> context)
|
||||
{
|
||||
m_callbacks.SubmitIndices(context);
|
||||
}
|
||||
|
||||
uint32_t UniversalBatch::GetAvailableVertexCount() const
|
||||
{
|
||||
return m_callbacks.GetAvailableVertexCount();
|
||||
}
|
||||
|
||||
uint32_t UniversalBatch::GetAvailableIndexCount() const
|
||||
{
|
||||
return m_callbacks.GetAvailableIndexCount();
|
||||
}
|
||||
|
||||
void UniversalBatch::ChangeBuffer(ref_ptr<GraphicsContext> context) const
|
||||
{
|
||||
return m_callbacks.ChangeBuffer(context);
|
||||
}
|
||||
|
||||
uint8_t UniversalBatch::GetVertexStride() const
|
||||
{
|
||||
return m_vertexStride;
|
||||
}
|
||||
|
||||
bool UniversalBatch::IsBufferFilled(uint32_t availableVerticesCount, uint32_t availableIndicesCount) const
|
||||
{
|
||||
return availableVerticesCount < m_minVerticesCount || availableIndicesCount < m_minIndicesCount;
|
||||
}
|
||||
|
||||
TriangleListBatch::TriangleListBatch(BatchCallbacks & callbacks)
|
||||
: TBase(callbacks, 3 /* minVerticesCount */, 3 /* minIndicesCount */)
|
||||
{}
|
||||
|
||||
void TriangleListBatch::BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams)
|
||||
{
|
||||
while (streams->IsDataExists())
|
||||
{
|
||||
if (IsBufferFilled(GetAvailableVertexCount(), GetAvailableIndexCount()))
|
||||
ChangeBuffer(context);
|
||||
|
||||
uint32_t avVertex = GetAvailableVertexCount();
|
||||
uint32_t avIndex = GetAvailableIndexCount();
|
||||
uint32_t vertexCount = streams->GetVertexCount();
|
||||
|
||||
if (CanDivideStreams())
|
||||
{
|
||||
vertexCount = std::min(vertexCount, avVertex);
|
||||
vertexCount = std::min(vertexCount, avIndex);
|
||||
ASSERT(vertexCount >= 3, ());
|
||||
vertexCount -= vertexCount % 3;
|
||||
}
|
||||
else if (!IsEnoughMemory(avVertex, vertexCount, avIndex, vertexCount))
|
||||
{
|
||||
ChangeBuffer(context);
|
||||
avVertex = GetAvailableVertexCount();
|
||||
avIndex = GetAvailableIndexCount();
|
||||
ASSERT(IsEnoughMemory(avVertex, vertexCount, avIndex, vertexCount), ());
|
||||
ASSERT(vertexCount % 3 == 0, ());
|
||||
}
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
void * indicesStorage = GetIndexStorage(vertexCount, startIndex);
|
||||
GenerateIndices<ListIndexGenerator>(indicesStorage, vertexCount, startIndex);
|
||||
SubmitIndices(context);
|
||||
|
||||
FlushData(context, streams, vertexCount);
|
||||
streams->Advance(vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
LineStripBatch::LineStripBatch(BatchCallbacks & callbacks)
|
||||
: TBase(callbacks, 2 /* minVerticesCount */, 2 /* minIndicesCount */)
|
||||
{}
|
||||
|
||||
void LineStripBatch::BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams)
|
||||
{
|
||||
while (streams->IsDataExists())
|
||||
{
|
||||
if (IsBufferFilled(GetAvailableVertexCount(), GetAvailableIndexCount()))
|
||||
ChangeBuffer(context);
|
||||
|
||||
uint32_t avVertex = GetAvailableVertexCount();
|
||||
uint32_t avIndex = GetAvailableIndexCount();
|
||||
uint32_t vertexCount = streams->GetVertexCount();
|
||||
ASSERT_GREATER_OR_EQUAL(vertexCount, 2, ());
|
||||
uint32_t indexCount = (vertexCount - 1) * 2;
|
||||
|
||||
if (!IsEnoughMemory(avVertex, vertexCount, avIndex, indexCount))
|
||||
{
|
||||
ChangeBuffer(context);
|
||||
avVertex = GetAvailableVertexCount();
|
||||
avIndex = GetAvailableIndexCount();
|
||||
ASSERT(IsEnoughMemory(avVertex, vertexCount, avIndex, indexCount), ());
|
||||
}
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
void * indicesStorage = GetIndexStorage(indexCount, startIndex);
|
||||
GenerateIndices<LineStripIndexGenerator>(indicesStorage, indexCount, startIndex);
|
||||
SubmitIndices(context);
|
||||
|
||||
FlushData(context, streams, vertexCount);
|
||||
streams->Advance(vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
LineRawBatch::LineRawBatch(BatchCallbacks & callbacks, std::vector<int> const & indices)
|
||||
: TBase(callbacks, 2 /* minVerticesCount */, 2 /* minIndicesCount */)
|
||||
, m_indices(indices)
|
||||
{}
|
||||
|
||||
void LineRawBatch::BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams)
|
||||
{
|
||||
while (streams->IsDataExists())
|
||||
{
|
||||
if (IsBufferFilled(GetAvailableVertexCount(), GetAvailableIndexCount()))
|
||||
ChangeBuffer(context);
|
||||
|
||||
uint32_t avVertex = GetAvailableVertexCount();
|
||||
uint32_t avIndex = GetAvailableIndexCount();
|
||||
uint32_t vertexCount = streams->GetVertexCount();
|
||||
CHECK_GREATER_OR_EQUAL(vertexCount, 2, (vertexCount));
|
||||
auto const indexCount = static_cast<uint32_t>(m_indices.size());
|
||||
|
||||
if (!IsEnoughMemory(avVertex, vertexCount, avIndex, indexCount))
|
||||
{
|
||||
ChangeBuffer(context);
|
||||
avVertex = GetAvailableVertexCount();
|
||||
avIndex = GetAvailableIndexCount();
|
||||
CHECK(IsEnoughMemory(avVertex, vertexCount, avIndex, indexCount), (avVertex, vertexCount, avIndex, indexCount));
|
||||
}
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
void * indicesStorage = GetIndexStorage(indexCount, startIndex);
|
||||
LineRawIndexGenerator generator(startIndex, m_indices);
|
||||
GenerateIndices(indicesStorage, indexCount, generator);
|
||||
SubmitIndices(context);
|
||||
|
||||
FlushData(context, streams, vertexCount);
|
||||
streams->Advance(vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
FanStripHelper::FanStripHelper(BatchCallbacks & callbacks)
|
||||
: TBase(callbacks, 3 /* minVerticesCount */, 3 /* minIndicesCount */)
|
||||
, m_isFullUploaded(false)
|
||||
{}
|
||||
|
||||
uint32_t FanStripHelper::BatchIndexes(ref_ptr<GraphicsContext> context, uint32_t vertexCount)
|
||||
{
|
||||
uint32_t avVertex = GetAvailableVertexCount();
|
||||
uint32_t avIndex = GetAvailableIndexCount();
|
||||
|
||||
uint32_t batchVertexCount = 0;
|
||||
uint32_t batchIndexCount = 0;
|
||||
CalcBatchPortion(vertexCount, avVertex, avIndex, batchVertexCount, batchIndexCount);
|
||||
|
||||
if (!IsFullUploaded() && !CanDivideStreams())
|
||||
{
|
||||
ChangeBuffer(context);
|
||||
avVertex = GetAvailableVertexCount();
|
||||
avIndex = GetAvailableIndexCount();
|
||||
CalcBatchPortion(vertexCount, avVertex, avIndex, batchVertexCount, batchIndexCount);
|
||||
ASSERT(IsFullUploaded(), ());
|
||||
}
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
void * pIndexStorage = GetIndexStorage(batchIndexCount, startIndex);
|
||||
GenerateIndexes(pIndexStorage, batchIndexCount, startIndex);
|
||||
SubmitIndices(context);
|
||||
|
||||
return batchVertexCount;
|
||||
}
|
||||
|
||||
void FanStripHelper::CalcBatchPortion(uint32_t vertexCount, uint32_t avVertex, uint32_t avIndex,
|
||||
uint32_t & batchVertexCount, uint32_t & batchIndexCount)
|
||||
{
|
||||
uint32_t const indexCount = VtoICount(vertexCount);
|
||||
batchVertexCount = vertexCount;
|
||||
batchIndexCount = indexCount;
|
||||
m_isFullUploaded = true;
|
||||
|
||||
if (vertexCount > avVertex || indexCount > avIndex)
|
||||
{
|
||||
uint32_t alignedAvVertex = AlignVCount(avVertex);
|
||||
uint32_t alignedAvIndex = AlignICount(avIndex);
|
||||
uint32_t indexCountForAvailableVertexCount = VtoICount(alignedAvVertex);
|
||||
if (indexCountForAvailableVertexCount <= alignedAvIndex)
|
||||
{
|
||||
batchVertexCount = alignedAvVertex;
|
||||
batchIndexCount = indexCountForAvailableVertexCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
batchIndexCount = alignedAvIndex;
|
||||
batchVertexCount = ItoVCount(batchIndexCount);
|
||||
}
|
||||
m_isFullUploaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FanStripHelper::IsFullUploaded() const
|
||||
{
|
||||
return m_isFullUploaded;
|
||||
}
|
||||
|
||||
uint32_t FanStripHelper::VtoICount(uint32_t vCount) const
|
||||
{
|
||||
return 3 * (vCount - 2);
|
||||
}
|
||||
|
||||
uint32_t FanStripHelper::ItoVCount(uint32_t iCount) const
|
||||
{
|
||||
return iCount / 3 + 2;
|
||||
}
|
||||
|
||||
uint32_t FanStripHelper::AlignVCount(uint32_t vCount) const
|
||||
{
|
||||
return vCount;
|
||||
}
|
||||
|
||||
uint32_t FanStripHelper::AlignICount(uint32_t iCount) const
|
||||
{
|
||||
return iCount - iCount % 3;
|
||||
}
|
||||
|
||||
TriangleStripBatch::TriangleStripBatch(BatchCallbacks & callbacks) : TBase(callbacks) {}
|
||||
|
||||
void TriangleStripBatch::BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams)
|
||||
{
|
||||
while (streams->IsDataExists())
|
||||
{
|
||||
if (IsBufferFilled(GetAvailableVertexCount(), GetAvailableIndexCount()))
|
||||
ChangeBuffer(context);
|
||||
|
||||
uint32_t const batchVertexCount = BatchIndexes(context, streams->GetVertexCount());
|
||||
FlushData(context, streams, batchVertexCount);
|
||||
|
||||
uint32_t const advanceCount = IsFullUploaded() ? batchVertexCount : (batchVertexCount - 2);
|
||||
streams->Advance(advanceCount);
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleStripBatch::GenerateIndexes(void * indexStorage, uint32_t count, uint32_t startIndex) const
|
||||
{
|
||||
GenerateIndices<StripIndexGenerator>(indexStorage, count, startIndex);
|
||||
}
|
||||
|
||||
TriangleFanBatch::TriangleFanBatch(BatchCallbacks & callbacks) : TBase(callbacks) {}
|
||||
|
||||
/*
|
||||
* What happens here
|
||||
*
|
||||
* We try to pack TriangleFan on GPU indexed like triangle list.
|
||||
* If we have enough memory in VertexArrayBuffer to store all data from params, we just copy it
|
||||
*
|
||||
* If we have not enough memory we broke data on parts.
|
||||
* On first iteration we create CPUBuffer for each separate atribute
|
||||
* in params and copy to it first vertex of fan. This vertex will be need
|
||||
* when we will upload second part of data.
|
||||
*
|
||||
* Than we copy vertex data on GPU as much as we can and move params cursor on
|
||||
* "uploaded vertex count" - 1. This last vertex will be used for uploading next part of data
|
||||
*
|
||||
* On second iteration we need upload first vertex of fan that stored in cpuBuffers and than upload
|
||||
* second part of data. But to avoid 2 separate call of glBufferSubData we at first do a copy of
|
||||
* data from params to cpuBuffer and than copy continuous block of memory from cpuBuffer
|
||||
*/
|
||||
void TriangleFanBatch::BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams)
|
||||
{
|
||||
std::vector<CPUBuffer> cpuBuffers;
|
||||
while (streams->IsDataExists())
|
||||
{
|
||||
if (IsBufferFilled(GetAvailableVertexCount(), GetAvailableIndexCount()))
|
||||
ChangeBuffer(context);
|
||||
|
||||
uint32_t vertexCount = streams->GetVertexCount();
|
||||
uint32_t batchVertexCount = BatchIndexes(context, vertexCount);
|
||||
|
||||
if (!cpuBuffers.empty())
|
||||
{
|
||||
// if cpuBuffers not empty than on previous iteration we not move data on gpu
|
||||
// and in cpuBuffers stored first vertex of fan.
|
||||
// To avoid two separate call of glBufferSubData
|
||||
// (for first vertex and for next part of data)
|
||||
// we at first copy next part of data into
|
||||
// cpuBuffers, and than copy it from cpuBuffers to GPU
|
||||
for (size_t i = 0; i < streams->GetStreamCount(); ++i)
|
||||
{
|
||||
CPUBuffer & cpuBuffer = cpuBuffers[i];
|
||||
ASSERT(cpuBuffer.GetCurrentElementNumber() == 1, ());
|
||||
cpuBuffer.UploadData(streams->GetRawPointer(i), batchVertexCount);
|
||||
|
||||
// now in cpuBuffer we have correct "fan" created from second part of data
|
||||
// first vertex of cpuBuffer if the first vertex of params, second vertex is
|
||||
// the last vertex of previous uploaded data. We copy this data on GPU.
|
||||
FlushData(context, streams->GetBindingInfo(i), cpuBuffer.Data(), batchVertexCount + 1);
|
||||
}
|
||||
|
||||
uint32_t advanceCount = batchVertexCount;
|
||||
if (!IsFullUploaded())
|
||||
{
|
||||
// not all data was moved on gpu and last vertex of fan
|
||||
// will need on second iteration
|
||||
advanceCount -= 1;
|
||||
}
|
||||
|
||||
streams->Advance(advanceCount);
|
||||
}
|
||||
else // if m_cpuBuffer empty than it's first iteration
|
||||
if (IsFullUploaded())
|
||||
{
|
||||
// We can upload all input data as one peace. For upload we need only one iteration
|
||||
FlushData(context, streams, batchVertexCount);
|
||||
streams->Advance(batchVertexCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// for each stream we must create CPU buffer.
|
||||
// Copy first vertex of fan into cpuBuffer for next iterations
|
||||
// Than move first part of data on GPU
|
||||
cpuBuffers.reserve(streams->GetStreamCount());
|
||||
for (size_t i = 0; i < streams->GetStreamCount(); ++i)
|
||||
{
|
||||
BindingInfo const & binding = streams->GetBindingInfo(i);
|
||||
void const * rawDataPointer = streams->GetRawPointer(i);
|
||||
FlushData(context, binding, rawDataPointer, batchVertexCount);
|
||||
|
||||
// "(vertexCount + 1) - batchVertexCount" we allocate CPUBuffer on all remaining data
|
||||
// + first vertex of fan, that must be duplicate in the next buffer
|
||||
// + last vertex of currently uploaded data.
|
||||
cpuBuffers.emplace_back(binding.GetElementSize(), (vertexCount + 2) - batchVertexCount);
|
||||
CPUBuffer & cpuBuffer = cpuBuffers.back();
|
||||
cpuBuffer.UploadData(rawDataPointer, 1);
|
||||
|
||||
// Move cpu buffer cursor on second element of buffer.
|
||||
// On next iteration first vertex of fan will be also available
|
||||
cpuBuffer.Seek(1);
|
||||
}
|
||||
|
||||
// advance on uploadVertexCount - 1 to copy last vertex also into next VAO with
|
||||
// first vertex of data from CPUBuffers
|
||||
streams->Advance(batchVertexCount - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleFanBatch::GenerateIndexes(void * indexStorage, uint32_t count, uint32_t startIndex) const
|
||||
{
|
||||
GenerateIndices<FanIndexGenerator>(indexStorage, count, startIndex);
|
||||
}
|
||||
|
||||
TriangleListOfStripBatch::TriangleListOfStripBatch(BatchCallbacks & callbacks) : TBase(callbacks) {}
|
||||
|
||||
void TriangleListOfStripBatch::BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams)
|
||||
{
|
||||
while (streams->IsDataExists())
|
||||
{
|
||||
if (IsBufferFilled(GetAvailableVertexCount(), GetAvailableIndexCount()))
|
||||
ChangeBuffer(context);
|
||||
|
||||
uint32_t const batchVertexCount = BatchIndexes(context, streams->GetVertexCount());
|
||||
FlushData(context, streams, batchVertexCount);
|
||||
streams->Advance(batchVertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
bool TriangleListOfStripBatch::IsBufferFilled(uint32_t availableVerticesCount, uint32_t availableIndicesCount) const
|
||||
{
|
||||
uint8_t const vertexStride = GetVertexStride();
|
||||
ASSERT_GREATER_OR_EQUAL(vertexStride, 4, ());
|
||||
|
||||
uint32_t const indicesPerStride = TBase::VtoICount(vertexStride);
|
||||
return availableVerticesCount < vertexStride || availableIndicesCount < indicesPerStride;
|
||||
}
|
||||
|
||||
uint32_t TriangleListOfStripBatch::VtoICount(uint32_t vCount) const
|
||||
{
|
||||
uint8_t const vertexStride = GetVertexStride();
|
||||
ASSERT_GREATER_OR_EQUAL(vertexStride, 4, ());
|
||||
ASSERT_EQUAL(vCount % vertexStride, 0, ());
|
||||
|
||||
uint32_t const striptCount = vCount / vertexStride;
|
||||
return striptCount * TBase::VtoICount(vertexStride);
|
||||
}
|
||||
|
||||
uint32_t TriangleListOfStripBatch::ItoVCount(uint32_t iCount) const
|
||||
{
|
||||
uint8_t const vertexStride = GetVertexStride();
|
||||
ASSERT_GREATER_OR_EQUAL(vertexStride, 4, ());
|
||||
ASSERT_EQUAL(iCount % 3, 0, ());
|
||||
|
||||
return vertexStride * iCount / TBase::VtoICount(vertexStride);
|
||||
}
|
||||
|
||||
uint32_t TriangleListOfStripBatch::AlignVCount(uint32_t vCount) const
|
||||
{
|
||||
return vCount - vCount % GetVertexStride();
|
||||
}
|
||||
|
||||
uint32_t TriangleListOfStripBatch::AlignICount(uint32_t iCount) const
|
||||
{
|
||||
uint8_t const vertexStride = GetVertexStride();
|
||||
ASSERT_GREATER_OR_EQUAL(vertexStride, 4, ());
|
||||
|
||||
uint32_t const indicesPerStride = TBase::VtoICount(vertexStride);
|
||||
return iCount - iCount % indicesPerStride;
|
||||
}
|
||||
|
||||
void TriangleListOfStripBatch::GenerateIndexes(void * indexStorage, uint32_t count, uint32_t startIndex) const
|
||||
{
|
||||
uint8_t const vertexStride = GetVertexStride();
|
||||
GenerateIndices(indexStorage, count, ListOfStripGenerator(startIndex, vertexStride, VtoICount(vertexStride)));
|
||||
}
|
||||
} // namespace dp
|
||||
157
libs/drape/batcher_helpers.hpp
Normal file
157
libs/drape/batcher_helpers.hpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class AttributeProvider;
|
||||
class BindingInfo;
|
||||
|
||||
class BatchCallbacks
|
||||
{
|
||||
public:
|
||||
virtual ~BatchCallbacks() = default;
|
||||
virtual void FlushData(ref_ptr<GraphicsContext> context, BindingInfo const & binding, void const * data,
|
||||
uint32_t count) = 0;
|
||||
virtual void * GetIndexStorage(uint32_t indexCount, uint32_t & startIndex) = 0;
|
||||
virtual void SubmitIndices(ref_ptr<GraphicsContext> context) = 0;
|
||||
virtual uint32_t GetAvailableVertexCount() const = 0;
|
||||
virtual uint32_t GetAvailableIndexCount() const = 0;
|
||||
virtual void ChangeBuffer(ref_ptr<GraphicsContext> context) = 0;
|
||||
};
|
||||
|
||||
class UniversalBatch
|
||||
{
|
||||
public:
|
||||
UniversalBatch(BatchCallbacks & callbacks, uint8_t minVerticesCount, uint8_t minIndicesCount);
|
||||
virtual ~UniversalBatch() = default;
|
||||
|
||||
virtual void BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams) = 0;
|
||||
void SetCanDivideStreams(bool canDivide);
|
||||
bool CanDivideStreams() const;
|
||||
void SetVertexStride(uint8_t vertexStride);
|
||||
|
||||
protected:
|
||||
void FlushData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams, uint32_t vertexCount) const;
|
||||
void FlushData(ref_ptr<GraphicsContext> context, BindingInfo const & info, void const * data,
|
||||
uint32_t elementCount) const;
|
||||
void * GetIndexStorage(uint32_t indexCount, uint32_t & startIndex);
|
||||
void SubmitIndices(ref_ptr<GraphicsContext> context);
|
||||
uint32_t GetAvailableVertexCount() const;
|
||||
uint32_t GetAvailableIndexCount() const;
|
||||
void ChangeBuffer(ref_ptr<GraphicsContext> context) const;
|
||||
uint8_t GetVertexStride() const;
|
||||
|
||||
virtual bool IsBufferFilled(uint32_t availableVerticesCount, uint32_t availableIndicesCount) const;
|
||||
|
||||
private:
|
||||
BatchCallbacks & m_callbacks;
|
||||
bool m_canDivideStreams;
|
||||
uint8_t m_vertexStride;
|
||||
uint8_t const m_minVerticesCount;
|
||||
uint8_t const m_minIndicesCount;
|
||||
};
|
||||
|
||||
class TriangleListBatch : public UniversalBatch
|
||||
{
|
||||
using TBase = UniversalBatch;
|
||||
|
||||
public:
|
||||
explicit TriangleListBatch(BatchCallbacks & callbacks);
|
||||
|
||||
void BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams) override;
|
||||
};
|
||||
|
||||
class LineStripBatch : public UniversalBatch
|
||||
{
|
||||
using TBase = UniversalBatch;
|
||||
|
||||
public:
|
||||
explicit LineStripBatch(BatchCallbacks & callbacks);
|
||||
|
||||
void BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams) override;
|
||||
};
|
||||
|
||||
class LineRawBatch : public UniversalBatch
|
||||
{
|
||||
using TBase = UniversalBatch;
|
||||
|
||||
public:
|
||||
LineRawBatch(BatchCallbacks & callbacks, std::vector<int> const & indices);
|
||||
|
||||
void BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams) override;
|
||||
|
||||
private:
|
||||
std::vector<int> const & m_indices;
|
||||
};
|
||||
|
||||
class FanStripHelper : public UniversalBatch
|
||||
{
|
||||
using TBase = UniversalBatch;
|
||||
|
||||
public:
|
||||
explicit FanStripHelper(BatchCallbacks & callbacks);
|
||||
|
||||
protected:
|
||||
uint32_t BatchIndexes(ref_ptr<GraphicsContext> context, uint32_t vertexCount);
|
||||
void CalcBatchPortion(uint32_t vertexCount, uint32_t avVertex, uint32_t avIndex, uint32_t & batchVertexCount,
|
||||
uint32_t & batchIndexCount);
|
||||
bool IsFullUploaded() const;
|
||||
|
||||
virtual uint32_t VtoICount(uint32_t vCount) const;
|
||||
virtual uint32_t ItoVCount(uint32_t iCount) const;
|
||||
virtual uint32_t AlignVCount(uint32_t vCount) const;
|
||||
virtual uint32_t AlignICount(uint32_t vCount) const;
|
||||
virtual void GenerateIndexes(void * indexStorage, uint32_t count, uint32_t startIndex) const = 0;
|
||||
|
||||
private:
|
||||
bool m_isFullUploaded;
|
||||
};
|
||||
|
||||
class TriangleStripBatch : public FanStripHelper
|
||||
{
|
||||
using TBase = FanStripHelper;
|
||||
|
||||
public:
|
||||
explicit TriangleStripBatch(BatchCallbacks & callbacks);
|
||||
|
||||
void BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams) override;
|
||||
|
||||
protected:
|
||||
void GenerateIndexes(void * indexStorage, uint32_t count, uint32_t startIndex) const override;
|
||||
};
|
||||
|
||||
class TriangleFanBatch : public FanStripHelper
|
||||
{
|
||||
using TBase = FanStripHelper;
|
||||
|
||||
public:
|
||||
explicit TriangleFanBatch(BatchCallbacks & callbacks);
|
||||
|
||||
void BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams) override;
|
||||
|
||||
protected:
|
||||
void GenerateIndexes(void * indexStorage, uint32_t count, uint32_t startIndex) const override;
|
||||
};
|
||||
|
||||
class TriangleListOfStripBatch : public FanStripHelper
|
||||
{
|
||||
using TBase = FanStripHelper;
|
||||
|
||||
public:
|
||||
explicit TriangleListOfStripBatch(BatchCallbacks & callbacks);
|
||||
|
||||
void BatchData(ref_ptr<GraphicsContext> context, ref_ptr<AttributeProvider> streams) override;
|
||||
|
||||
protected:
|
||||
bool IsBufferFilled(uint32_t availableVerticesCount, uint32_t availableIndicesCount) const override;
|
||||
uint32_t VtoICount(uint32_t vCount) const override;
|
||||
uint32_t ItoVCount(uint32_t iCount) const override;
|
||||
uint32_t AlignVCount(uint32_t vCount) const override;
|
||||
uint32_t AlignICount(uint32_t iCount) const override;
|
||||
void GenerateIndexes(void * indexStorage, uint32_t count, uint32_t startIndex) const override;
|
||||
};
|
||||
} // namespace dp
|
||||
134
libs/drape/binding_info.cpp
Normal file
134
libs/drape/binding_info.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#include "drape/binding_info.hpp"
|
||||
#include "drape/gl_includes.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
uint16_t SizeOfType(glConst type)
|
||||
{
|
||||
if (type == gl_const::GLByteType || type == gl_const::GLUnsignedByteType)
|
||||
return sizeof(GLbyte);
|
||||
else if (type == gl_const::GLShortType || type == gl_const::GLUnsignedShortType)
|
||||
return sizeof(GLshort);
|
||||
else if (type == gl_const::GLIntType || type == gl_const::GLUnsignedIntType)
|
||||
return sizeof(GLint);
|
||||
else if (type == gl_const::GLFloatType)
|
||||
return sizeof(GLfloat);
|
||||
|
||||
ASSERT(false, ());
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool BindingDecl::operator==(BindingDecl const & other) const
|
||||
{
|
||||
return m_attributeName == other.m_attributeName && m_componentCount == other.m_componentCount &&
|
||||
m_componentType == other.m_componentType && m_stride == other.m_stride && m_offset == other.m_offset;
|
||||
}
|
||||
|
||||
bool BindingDecl::operator!=(BindingDecl const & other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool BindingDecl::operator<(BindingDecl const & other) const
|
||||
{
|
||||
if (m_attributeName != other.m_attributeName)
|
||||
return m_attributeName < other.m_attributeName;
|
||||
if (m_componentCount != other.m_componentCount)
|
||||
return m_componentCount < other.m_componentCount;
|
||||
if (m_componentType != other.m_componentType)
|
||||
return m_componentType < other.m_componentType;
|
||||
if (m_stride != other.m_stride)
|
||||
return m_stride < other.m_stride;
|
||||
return m_offset < other.m_offset;
|
||||
}
|
||||
|
||||
BindingInfo::BindingInfo() : m_info(0) {}
|
||||
|
||||
BindingInfo::BindingInfo(uint8_t count, uint8_t id) : m_info((static_cast<uint16_t>(count) << 8) | id)
|
||||
{
|
||||
CHECK_LESS_OR_EQUAL(count, kMaxBindingDecl, ());
|
||||
}
|
||||
|
||||
uint8_t BindingInfo::GetCount() const
|
||||
{
|
||||
return static_cast<uint8_t>((m_info & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
uint8_t BindingInfo::GetID() const
|
||||
{
|
||||
return static_cast<uint8_t>(m_info & 0xFF);
|
||||
}
|
||||
|
||||
BindingDecl const & BindingInfo::GetBindingDecl(uint16_t index) const
|
||||
{
|
||||
ASSERT_LESS(index, GetCount(), ());
|
||||
return m_bindings[index];
|
||||
}
|
||||
|
||||
BindingDecl & BindingInfo::GetBindingDecl(uint16_t index)
|
||||
{
|
||||
ASSERT_LESS(index, GetCount(), ());
|
||||
return m_bindings[index];
|
||||
}
|
||||
|
||||
uint16_t BindingInfo::GetElementSize() const
|
||||
{
|
||||
if (GetCount() == 0)
|
||||
return 0;
|
||||
|
||||
uint8_t stride = m_bindings[0].m_stride;
|
||||
if (stride != 0)
|
||||
return stride;
|
||||
|
||||
int calcStride = 0;
|
||||
for (uint16_t i = 0; i < GetCount(); ++i)
|
||||
calcStride += (m_bindings[i].m_componentCount * SizeOfType(m_bindings[i].m_componentType));
|
||||
|
||||
return static_cast<uint16_t>(calcStride);
|
||||
}
|
||||
|
||||
bool BindingInfo::IsDynamic() const
|
||||
{
|
||||
return GetID() > 0;
|
||||
}
|
||||
|
||||
bool BindingInfo::operator==(BindingInfo const & other) const
|
||||
{
|
||||
if (m_info != other.m_info)
|
||||
return false;
|
||||
|
||||
for (uint16_t i = 0; i < GetCount(); ++i)
|
||||
{
|
||||
BindingDecl const & thisDecl = m_bindings[i];
|
||||
BindingDecl const & otherDecl = other.m_bindings[i];
|
||||
if (thisDecl != otherDecl)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BindingInfo::operator!=(BindingInfo const & other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool BindingInfo::operator<(BindingInfo const & other) const
|
||||
{
|
||||
if (m_info != other.m_info)
|
||||
return m_info < other.m_info;
|
||||
|
||||
for (uint16_t i = 0; i < GetCount(); ++i)
|
||||
{
|
||||
BindingDecl const & thisDecl = m_bindings[i];
|
||||
BindingDecl const & otherDecl = other.m_bindings[i];
|
||||
if (thisDecl != otherDecl)
|
||||
return thisDecl < otherDecl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace dp
|
||||
85
libs/drape/binding_info.hpp
Normal file
85
libs/drape/binding_info.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/gl_constants.hpp"
|
||||
#include "drape/glsl_func.hpp"
|
||||
#include "drape/glsl_types.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
size_t constexpr kMaxBindingDecl = 8;
|
||||
size_t constexpr kMaxBindingInfo = 3;
|
||||
|
||||
struct BindingDecl
|
||||
{
|
||||
std::string m_attributeName;
|
||||
uint8_t m_componentCount;
|
||||
glConst m_componentType;
|
||||
uint8_t m_stride;
|
||||
uint8_t m_offset;
|
||||
|
||||
bool operator==(BindingDecl const & other) const;
|
||||
bool operator!=(BindingDecl const & other) const;
|
||||
bool operator<(BindingDecl const & other) const;
|
||||
};
|
||||
|
||||
class BindingInfo
|
||||
{
|
||||
public:
|
||||
BindingInfo();
|
||||
explicit BindingInfo(uint8_t count, uint8_t id = 0);
|
||||
|
||||
uint8_t GetCount() const;
|
||||
uint8_t GetID() const;
|
||||
BindingDecl const & GetBindingDecl(uint16_t index) const;
|
||||
BindingDecl & GetBindingDecl(uint16_t index);
|
||||
uint16_t GetElementSize() const;
|
||||
bool IsDynamic() const;
|
||||
|
||||
bool operator==(BindingInfo const & other) const;
|
||||
bool operator!=(BindingInfo const & other) const;
|
||||
bool operator<(BindingInfo const & other) const;
|
||||
|
||||
protected:
|
||||
std::array<BindingDecl, kMaxBindingDecl> m_bindings;
|
||||
uint16_t m_info;
|
||||
};
|
||||
|
||||
template <typename TFieldType, typename TVertexType>
|
||||
uint8_t FillDecl(size_t index, std::string const & attrName, dp::BindingInfo & info, uint8_t offset)
|
||||
{
|
||||
dp::BindingDecl & decl = info.GetBindingDecl(static_cast<uint16_t>(index));
|
||||
decl.m_attributeName = attrName;
|
||||
decl.m_componentCount = glsl::GetComponentCount<TFieldType>();
|
||||
decl.m_componentType = gl_const::GLFloatType;
|
||||
decl.m_offset = offset;
|
||||
decl.m_stride = sizeof(TVertexType);
|
||||
|
||||
return sizeof(TFieldType);
|
||||
}
|
||||
|
||||
using BindingInfoArray = std::array<dp::BindingInfo, kMaxBindingInfo>;
|
||||
|
||||
template <typename TVertex>
|
||||
class BindingFiller
|
||||
{
|
||||
public:
|
||||
explicit BindingFiller(uint8_t count, uint8_t id = 0) : m_info(count, id) {}
|
||||
|
||||
template <typename TFieldType>
|
||||
void FillDecl(std::string const & attrName)
|
||||
{
|
||||
m_offset += dp::FillDecl<TFieldType, TVertex>(m_index, attrName, m_info, m_offset);
|
||||
++m_index;
|
||||
}
|
||||
|
||||
dp::BindingInfo m_info;
|
||||
|
||||
private:
|
||||
size_t m_index = 0;
|
||||
uint8_t m_offset = 0;
|
||||
};
|
||||
} // namespace dp
|
||||
57
libs/drape/buffer_base.cpp
Normal file
57
libs/drape/buffer_base.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#include "drape/buffer_base.hpp"
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
|
||||
BufferBase::BufferBase(uint8_t elementSize, uint32_t capacity)
|
||||
: m_elementSize(elementSize)
|
||||
, m_capacity(capacity)
|
||||
, m_size(0)
|
||||
{}
|
||||
|
||||
uint32_t BufferBase::GetCapacity() const
|
||||
{
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
uint32_t BufferBase::GetCurrentSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
uint32_t BufferBase::GetAvailableSize() const
|
||||
{
|
||||
return m_capacity - m_size;
|
||||
}
|
||||
|
||||
void BufferBase::Resize(uint32_t elementCount)
|
||||
{
|
||||
m_capacity = elementCount;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
uint8_t BufferBase::GetElementSize() const
|
||||
{
|
||||
return m_elementSize;
|
||||
}
|
||||
|
||||
void BufferBase::Seek(uint32_t elementNumber)
|
||||
{
|
||||
ASSERT(elementNumber <= m_capacity, ());
|
||||
m_size = elementNumber;
|
||||
}
|
||||
|
||||
void BufferBase::UploadData(uint32_t elementCount)
|
||||
{
|
||||
ASSERT(m_size + elementCount <= m_capacity, ());
|
||||
m_size += elementCount;
|
||||
}
|
||||
|
||||
void BufferBase::SetDataSize(uint32_t elementCount)
|
||||
{
|
||||
ASSERT(elementCount <= m_capacity, ());
|
||||
m_size = elementCount;
|
||||
}
|
||||
|
||||
} // namespace dp
|
||||
31
libs/drape/buffer_base.hpp
Normal file
31
libs/drape/buffer_base.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class BufferBase
|
||||
{
|
||||
public:
|
||||
BufferBase(uint8_t elementSize, uint32_t capacity);
|
||||
virtual ~BufferBase() = default;
|
||||
|
||||
uint32_t GetCapacity() const;
|
||||
uint32_t GetCurrentSize() const;
|
||||
uint32_t GetAvailableSize() const;
|
||||
|
||||
uint8_t GetElementSize() const;
|
||||
|
||||
virtual void Seek(uint32_t elementNumber);
|
||||
|
||||
protected:
|
||||
void Resize(uint32_t elementCount);
|
||||
void UploadData(uint32_t elementCount);
|
||||
void SetDataSize(uint32_t elementCount);
|
||||
|
||||
private:
|
||||
uint8_t m_elementSize;
|
||||
uint32_t m_capacity;
|
||||
uint32_t m_size;
|
||||
};
|
||||
} // namespace dp
|
||||
86
libs/drape/color.hpp
Normal file
86
libs/drape/color.hpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
struct Color
|
||||
{
|
||||
constexpr Color() : Color(0, 0, 0, kMaxChannelValue) {}
|
||||
constexpr explicit Color(uint32_t rgba) : m_rgba(rgba) {}
|
||||
constexpr Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
|
||||
: Color(red << 24 | green << 16 | blue << 8 | alpha)
|
||||
{}
|
||||
constexpr Color(uint8_t red, uint8_t green, uint8_t blue) : Color(red, green, blue, 255) {}
|
||||
constexpr Color(uint32_t rgb, uint8_t alpha)
|
||||
: Color(ExtractByte(rgb, 2), ExtractByte(rgb, 1), ExtractByte(rgb, 0), alpha)
|
||||
{}
|
||||
|
||||
constexpr uint8_t GetRed() const { return ExtractByte(m_rgba, 3); }
|
||||
constexpr uint8_t GetGreen() const { return ExtractByte(m_rgba, 2); }
|
||||
constexpr uint8_t GetBlue() const { return ExtractByte(m_rgba, 1); }
|
||||
constexpr uint8_t GetAlpha() const { return ExtractByte(m_rgba, 0); }
|
||||
constexpr uint32_t GetRGBA() const { return m_rgba; }
|
||||
constexpr uint32_t GetARGB() const
|
||||
{
|
||||
return (static_cast<uint32_t>(GetAlpha()) << 24) | (static_cast<uint32_t>(GetRed()) << 16) |
|
||||
(static_cast<uint32_t>(GetGreen()) << 8) | static_cast<uint32_t>(GetBlue());
|
||||
}
|
||||
|
||||
constexpr float GetRedF() const { return ChannelToFloat(GetRed()); }
|
||||
constexpr float GetGreenF() const { return ChannelToFloat(GetGreen()); }
|
||||
constexpr float GetBlueF() const { return ChannelToFloat(GetBlue()); }
|
||||
constexpr float GetAlphaF() const { return ChannelToFloat(GetAlpha()); }
|
||||
|
||||
constexpr bool operator==(Color const & other) const { return m_rgba == other.m_rgba; }
|
||||
constexpr bool operator!=(Color const & other) const { return m_rgba != other.m_rgba; }
|
||||
constexpr bool operator<(Color const & other) const { return m_rgba < other.m_rgba; }
|
||||
|
||||
constexpr Color operator*(float s) const
|
||||
{
|
||||
return {static_cast<uint8_t>(math::Clamp(GetRedF() * s, 0.0f, 1.0f) * 255.0f),
|
||||
static_cast<uint8_t>(math::Clamp(GetGreenF() * s, 0.0f, 1.0f) * 255.0f),
|
||||
static_cast<uint8_t>(math::Clamp(GetBlueF() * s, 0.0f, 1.0f) * 255.0f), GetAlpha()};
|
||||
}
|
||||
|
||||
constexpr static Color Black() { return {0, 0, 0, 255}; }
|
||||
constexpr static Color White() { return {255, 255, 255, 255}; }
|
||||
constexpr static Color Red() { return {255, 0, 0, 255}; }
|
||||
constexpr static Color Blue() { return {0, 0, 255, 255}; }
|
||||
constexpr static Color Green() { return {0, 255, 0, 255}; }
|
||||
constexpr static Color Yellow() { return {255, 255, 0, 255}; }
|
||||
constexpr static Color Transparent() { return {0, 0, 0, 0}; }
|
||||
|
||||
constexpr static Color FromARGB(uint32_t argb)
|
||||
{
|
||||
return {ExtractByte(argb, 2), ExtractByte(argb, 1), ExtractByte(argb, 0), ExtractByte(argb, 3)};
|
||||
}
|
||||
constexpr static Color FromRGBA(uint32_t rgba)
|
||||
{
|
||||
return {ExtractByte(rgba, 3), ExtractByte(rgba, 2), ExtractByte(rgba, 1), ExtractByte(rgba, 0)};
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static uint8_t ExtractByte(uint32_t number, uint8_t byteIdx) { return (number >> (8 * byteIdx)) & 0xFF; }
|
||||
|
||||
constexpr static float ChannelToFloat(uint8_t channelValue)
|
||||
{
|
||||
return static_cast<float>(channelValue) / kMaxChannelValue;
|
||||
}
|
||||
|
||||
constexpr static uint8_t kMaxChannelValue = 255;
|
||||
uint32_t m_rgba;
|
||||
};
|
||||
|
||||
inline std::string DebugPrint(Color const & c)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "[R = " << static_cast<uint32_t>(c.GetRed()) << ", G = " << static_cast<uint32_t>(c.GetGreen())
|
||||
<< ", B = " << static_cast<uint32_t>(c.GetBlue()) << ", A = " << static_cast<uint32_t>(c.GetAlpha()) << "]";
|
||||
return out.str();
|
||||
}
|
||||
} // namespace dp
|
||||
13
libs/drape/constants.hpp
Normal file
13
libs/drape/constants.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "utils/projection.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace depth
|
||||
{
|
||||
float constexpr kMyPositionMarkDepth = kMaxDepth - 1.0f;
|
||||
} // namespace depth
|
||||
|
||||
uint32_t constexpr kScreenPixelRectExtension = 75; // in pixels.
|
||||
} // namespace dp
|
||||
66
libs/drape/cpu_buffer.cpp
Normal file
66
libs/drape/cpu_buffer.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include "drape/cpu_buffer.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/math.hpp"
|
||||
#include "base/shared_buffer_manager.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
CPUBuffer::CPUBuffer(uint8_t elementSize, uint32_t capacity) : TBase(elementSize, capacity)
|
||||
{
|
||||
uint32_t memorySize = math::NextPowOf2(GetCapacity() * GetElementSize());
|
||||
m_memory = SharedBufferManager::instance().reserveSharedBuffer(memorySize);
|
||||
m_memoryCursor = NonConstData();
|
||||
}
|
||||
|
||||
CPUBuffer::~CPUBuffer()
|
||||
{
|
||||
m_memoryCursor = nullptr;
|
||||
SharedBufferManager::instance().freeSharedBuffer(m_memory->size(), m_memory);
|
||||
}
|
||||
|
||||
void CPUBuffer::UploadData(void const * data, uint32_t elementCount)
|
||||
{
|
||||
CHECK(elementCount == 0 || data != nullptr, (elementCount));
|
||||
uint32_t byteCountToCopy = GetElementSize() * elementCount;
|
||||
#ifdef DEBUG
|
||||
// Memory check
|
||||
ASSERT(GetCursor() + byteCountToCopy <= Data() + m_memory->size(), ());
|
||||
#endif
|
||||
|
||||
memcpy(GetCursor(), data, byteCountToCopy);
|
||||
TBase::UploadData(elementCount);
|
||||
}
|
||||
|
||||
void CPUBuffer::Seek(uint32_t elementNumber)
|
||||
{
|
||||
uint32_t offsetFromBegin = GetElementSize() * elementNumber;
|
||||
ASSERT(Data() + offsetFromBegin <= Data() + m_memory->size(), ());
|
||||
TBase::Seek(elementNumber);
|
||||
m_memoryCursor = NonConstData() + offsetFromBegin;
|
||||
}
|
||||
|
||||
uint32_t CPUBuffer::GetCurrentElementNumber() const
|
||||
{
|
||||
auto pointerDiff = static_cast<uint32_t>(GetCursor() - Data());
|
||||
ASSERT(pointerDiff % GetElementSize() == 0, ());
|
||||
return pointerDiff / GetElementSize();
|
||||
}
|
||||
|
||||
unsigned char const * CPUBuffer::Data() const
|
||||
{
|
||||
return &((*m_memory)[0]);
|
||||
}
|
||||
|
||||
unsigned char * CPUBuffer::NonConstData()
|
||||
{
|
||||
return &((*m_memory)[0]);
|
||||
}
|
||||
|
||||
unsigned char * CPUBuffer::GetCursor() const
|
||||
{
|
||||
return m_memoryCursor;
|
||||
}
|
||||
} // namespace dp
|
||||
33
libs/drape/cpu_buffer.hpp
Normal file
33
libs/drape/cpu_buffer.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/buffer_base.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class CPUBuffer : public BufferBase
|
||||
{
|
||||
using TBase = BufferBase;
|
||||
|
||||
public:
|
||||
CPUBuffer(uint8_t elementSize, uint32_t capacity);
|
||||
~CPUBuffer() override;
|
||||
|
||||
void UploadData(void const * data, uint32_t elementCount);
|
||||
// Set memory cursor on element with number == "elementNumber"
|
||||
// Element numbers start from 0
|
||||
void Seek(uint32_t elementNumber) override;
|
||||
// Check function. In real world use must use it only in assert
|
||||
uint32_t GetCurrentElementNumber() const;
|
||||
unsigned char const * Data() const;
|
||||
|
||||
private:
|
||||
unsigned char * NonConstData();
|
||||
unsigned char * GetCursor() const;
|
||||
|
||||
unsigned char * m_memoryCursor;
|
||||
std::shared_ptr<std::vector<unsigned char>> m_memory;
|
||||
};
|
||||
} // namespace dp
|
||||
76
libs/drape/data_buffer.cpp
Normal file
76
libs/drape/data_buffer.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#include "drape/data_buffer.hpp"
|
||||
#include "drape/data_buffer_impl.hpp"
|
||||
#include "drape/drape_global.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
DataBuffer::DataBuffer(uint8_t elementSize, uint32_t capacity)
|
||||
: m_impl(make_unique_dp<CpuBufferImpl>(elementSize, capacity))
|
||||
{}
|
||||
|
||||
ref_ptr<DataBufferBase> DataBuffer::GetBuffer() const
|
||||
{
|
||||
ASSERT(m_impl != nullptr, ());
|
||||
return make_ref(m_impl);
|
||||
}
|
||||
|
||||
void DataBuffer::MoveToGPU(ref_ptr<GraphicsContext> context, GPUBuffer::Target target, uint64_t batcherHash)
|
||||
{
|
||||
// If currentSize is 0 buffer hasn't been filled on preparation stage, let it be filled further.
|
||||
uint32_t const currentSize = m_impl->GetCurrentSize();
|
||||
|
||||
auto const apiVersion = context->GetApiVersion();
|
||||
if (apiVersion == dp::ApiVersion::OpenGLES3)
|
||||
{
|
||||
if (currentSize != 0)
|
||||
{
|
||||
m_impl =
|
||||
make_unique_dp<GpuBufferImpl>(target, m_impl->Data(), m_impl->GetElementSize(), currentSize, batcherHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_impl = make_unique_dp<GpuBufferImpl>(target, nullptr, m_impl->GetElementSize(), m_impl->GetAvailableSize(),
|
||||
batcherHash);
|
||||
}
|
||||
}
|
||||
else if (apiVersion == dp::ApiVersion::Metal)
|
||||
{
|
||||
#if defined(OMIM_METAL_AVAILABLE)
|
||||
if (currentSize != 0)
|
||||
m_impl = CreateImplForMetal(context, m_impl->Data(), m_impl->GetElementSize(), currentSize);
|
||||
else
|
||||
m_impl = CreateImplForMetal(context, nullptr, m_impl->GetElementSize(), m_impl->GetAvailableSize());
|
||||
#endif
|
||||
}
|
||||
else if (apiVersion == dp::ApiVersion::Vulkan)
|
||||
{
|
||||
if (currentSize != 0)
|
||||
m_impl = CreateImplForVulkan(context, m_impl->Data(), m_impl->GetElementSize(), currentSize, batcherHash);
|
||||
else
|
||||
m_impl = CreateImplForVulkan(context, nullptr, m_impl->GetElementSize(), m_impl->GetAvailableSize(), batcherHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(false, ("Unsupported API version."));
|
||||
}
|
||||
}
|
||||
|
||||
DataBufferMapper::DataBufferMapper(ref_ptr<GraphicsContext> context, ref_ptr<DataBuffer> buffer, uint32_t elementOffset,
|
||||
uint32_t elementCount)
|
||||
: m_context(context)
|
||||
, m_buffer(buffer)
|
||||
{
|
||||
m_buffer->GetBuffer()->Bind();
|
||||
m_ptr = m_buffer->GetBuffer()->Map(m_context, elementOffset, elementCount);
|
||||
}
|
||||
|
||||
DataBufferMapper::~DataBufferMapper()
|
||||
{
|
||||
m_buffer->GetBuffer()->Unmap(m_context);
|
||||
}
|
||||
|
||||
void DataBufferMapper::UpdateData(void const * data, uint32_t elementOffset, uint32_t elementCount)
|
||||
{
|
||||
m_buffer->GetBuffer()->UpdateData(m_ptr, data, elementOffset, elementCount);
|
||||
}
|
||||
} // namespace dp
|
||||
63
libs/drape/data_buffer.hpp
Normal file
63
libs/drape/data_buffer.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/gpu_buffer.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class DataBufferBase
|
||||
{
|
||||
public:
|
||||
virtual ~DataBufferBase() = default;
|
||||
virtual uint32_t GetCapacity() const = 0;
|
||||
virtual uint32_t GetCurrentSize() const = 0;
|
||||
virtual uint32_t GetAvailableSize() const = 0;
|
||||
virtual uint8_t GetElementSize() const = 0;
|
||||
virtual void Seek(uint32_t elementNumber) = 0;
|
||||
virtual void const * Data() const = 0;
|
||||
|
||||
virtual void UploadData(ref_ptr<GraphicsContext> context, void const * data, uint32_t elementCount) = 0;
|
||||
|
||||
virtual void * Map(ref_ptr<GraphicsContext> context, uint32_t elementOffset, uint32_t elementCount) = 0;
|
||||
virtual void UpdateData(void * destPtr, void const * srcPtr, uint32_t elementOffset, uint32_t elementCount) = 0;
|
||||
virtual void Unmap(ref_ptr<GraphicsContext> context) = 0;
|
||||
|
||||
virtual void Bind() = 0;
|
||||
};
|
||||
|
||||
class DataBuffer
|
||||
{
|
||||
public:
|
||||
DataBuffer(uint8_t elementSize, uint32_t capacity);
|
||||
|
||||
ref_ptr<DataBufferBase> GetBuffer() const;
|
||||
void MoveToGPU(ref_ptr<GraphicsContext> context, GPUBuffer::Target target, uint64_t batcherHash);
|
||||
|
||||
private:
|
||||
// Definition of this method is in a .mm-file.
|
||||
drape_ptr<DataBufferBase> CreateImplForMetal(ref_ptr<GraphicsContext> context, void const * data, uint8_t elementSize,
|
||||
uint32_t capacity);
|
||||
|
||||
// Definition of this method is in a separate .cpp-file.
|
||||
drape_ptr<DataBufferBase> CreateImplForVulkan(ref_ptr<GraphicsContext> context, void const * data,
|
||||
uint8_t elementSize, uint32_t capacity, uint64_t batcherHash);
|
||||
|
||||
drape_ptr<DataBufferBase> m_impl;
|
||||
};
|
||||
|
||||
class DataBufferMapper
|
||||
{
|
||||
public:
|
||||
DataBufferMapper(ref_ptr<GraphicsContext> context, ref_ptr<DataBuffer> buffer, uint32_t elementOffset,
|
||||
uint32_t elementCount);
|
||||
~DataBufferMapper();
|
||||
|
||||
void UpdateData(void const * data, uint32_t elementOffset, uint32_t elementCount);
|
||||
|
||||
private:
|
||||
ref_ptr<GraphicsContext> m_context;
|
||||
ref_ptr<DataBuffer> m_buffer;
|
||||
void * m_ptr;
|
||||
};
|
||||
} // namespace dp
|
||||
117
libs/drape/data_buffer_impl.hpp
Normal file
117
libs/drape/data_buffer_impl.hpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/cpu_buffer.hpp"
|
||||
#include "drape/data_buffer.hpp"
|
||||
#include "drape/gpu_buffer.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
// Generic implementation of data buffer.
|
||||
template <typename TBuffer>
|
||||
class DataBufferImpl : public DataBufferBase
|
||||
{
|
||||
public:
|
||||
template <typename... Args>
|
||||
DataBufferImpl(Args &&... params) : m_buffer(make_unique_dp<TBuffer>(std::forward<Args>(params)...))
|
||||
{}
|
||||
|
||||
uint32_t GetCapacity() const override { return m_buffer->GetCapacity(); }
|
||||
uint32_t GetCurrentSize() const override { return m_buffer->GetCurrentSize(); }
|
||||
uint32_t GetAvailableSize() const override { return m_buffer->GetAvailableSize(); }
|
||||
uint8_t GetElementSize() const override { return m_buffer->GetElementSize(); }
|
||||
void Seek(uint32_t elementNumber) override { m_buffer->Seek(elementNumber); }
|
||||
|
||||
protected:
|
||||
drape_ptr<TBuffer> m_buffer;
|
||||
};
|
||||
|
||||
// CPU implementation of data buffer.
|
||||
class CpuBufferImpl : public DataBufferImpl<CPUBuffer>
|
||||
{
|
||||
public:
|
||||
template <typename... Args>
|
||||
CpuBufferImpl(Args &&... params) : DataBufferImpl(std::forward<Args>(params)...)
|
||||
{}
|
||||
|
||||
void const * Data() const override { return m_buffer->Data(); }
|
||||
|
||||
void UploadData(ref_ptr<GraphicsContext> context, void const * data, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
m_buffer->UploadData(data, elementCount);
|
||||
uint32_t const newOffset = m_buffer->GetCurrentSize();
|
||||
m_buffer->Seek(newOffset);
|
||||
}
|
||||
|
||||
void * Map(ref_ptr<GraphicsContext> context, uint32_t elementOffset, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
UNUSED_VALUE(elementOffset);
|
||||
UNUSED_VALUE(elementCount);
|
||||
ASSERT(false, ("Mapping is unavailable for CPU buffer"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UpdateData(void * destPtr, void const * srcPtr, uint32_t elementOffset, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(destPtr);
|
||||
UNUSED_VALUE(srcPtr);
|
||||
UNUSED_VALUE(elementOffset);
|
||||
UNUSED_VALUE(elementCount);
|
||||
ASSERT(false, ("Data updating is unavailable for CPU buffer"));
|
||||
}
|
||||
|
||||
void Unmap(ref_ptr<GraphicsContext> context) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
ASSERT(false, ("Unmapping is unavailable for CPU buffer"));
|
||||
}
|
||||
|
||||
void Bind() override { ASSERT(false, ("Binding is unavailable for CPU buffer")); }
|
||||
};
|
||||
|
||||
// GPU implementation of data buffer for OpenGL.
|
||||
class GpuBufferImpl : public DataBufferImpl<GPUBuffer>
|
||||
{
|
||||
public:
|
||||
template <typename... Args>
|
||||
GpuBufferImpl(Args &&... params) : DataBufferImpl(std::forward<Args>(params)...)
|
||||
{}
|
||||
|
||||
void const * Data() const override
|
||||
{
|
||||
ASSERT(false, ("Retrieving of raw data is unavailable for GPU buffer"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UploadData(ref_ptr<GraphicsContext> context, void const * data, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
m_buffer->UploadData(data, elementCount);
|
||||
}
|
||||
|
||||
void * Map(ref_ptr<GraphicsContext> context, uint32_t elementOffset, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
return m_buffer->Map(elementOffset, elementCount);
|
||||
}
|
||||
|
||||
void UpdateData(void * destPtr, void const * srcPtr, uint32_t elementOffset, uint32_t elementCount) override
|
||||
{
|
||||
m_buffer->UpdateData(destPtr, srcPtr, elementOffset, elementCount);
|
||||
}
|
||||
|
||||
void Unmap(ref_ptr<GraphicsContext> context) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
m_buffer->Unmap();
|
||||
}
|
||||
|
||||
void Bind() override { m_buffer->Bind(); }
|
||||
};
|
||||
} // namespace dp
|
||||
20
libs/drape/debug_renderer.hpp
Normal file
20
libs/drape/debug_renderer.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/color.hpp"
|
||||
#include "drape/overlay_tree.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GraphicsContext;
|
||||
|
||||
class DebugRenderer
|
||||
{
|
||||
public:
|
||||
virtual ~DebugRenderer() = default;
|
||||
virtual bool IsEnabled() const = 0;
|
||||
virtual void DrawRect(ref_ptr<GraphicsContext> context, ScreenBase const & screen, m2::RectF const & rect,
|
||||
Color const & color) = 0;
|
||||
virtual void DrawArrow(ref_ptr<GraphicsContext> context, ScreenBase const & screen,
|
||||
OverlayTree::DisplacementData const & data) = 0;
|
||||
};
|
||||
} // namespace dp
|
||||
31
libs/drape/drape_diagnostics.hpp
Normal file
31
libs/drape/drape_diagnostics.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
// #define DRAW_TILE_NET
|
||||
// #define DEBUG_OVERLAYS_OUTPUT
|
||||
|
||||
// #define CHECK_VBO_BOUNDS
|
||||
// #define TRACK_POINTERS
|
||||
// #define DEBUG_ANIMATIONS
|
||||
// #define LINES_GENERATION_CALC_FILTERED_POINTS
|
||||
// #define GPS_TRACK_SHOW_RAW_POINTS
|
||||
// #define DEBUG_MESSAGE_QUEUE
|
||||
// #define RENDER_DEBUG_INFO_LABELS
|
||||
|
||||
// #define DRAPE_MEASURER_BENCHMARK
|
||||
// #define SCENARIO_ENABLE
|
||||
// #define SHOW_FRAMES_STATS
|
||||
|
||||
#ifdef DRAPE_MEASURER_BENCHMARK
|
||||
|
||||
// #define RENDER_STATISTIC
|
||||
// #define TILES_STATISTIC
|
||||
// #define GENERATING_STATISTIC
|
||||
|
||||
// #define TRACK_GPU_MEM
|
||||
// #define TRACK_GLYPH_USAGE
|
||||
|
||||
#endif
|
||||
|
||||
// #define ENABLE_VULKAN_DIAGNOSTICS
|
||||
// #define ENABLE_VULKAN_DEBUG_DIAGNOSTICS_MESSAGES
|
||||
// #define ENABLE_OPENGL_DIAGNOSTICS
|
||||
142
libs/drape/drape_global.hpp
Normal file
142
libs/drape/drape_global.hpp
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/color.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define OMIM_METAL_AVAILABLE
|
||||
#endif
|
||||
|
||||
namespace gpu
|
||||
{
|
||||
class ProgramManager;
|
||||
} // namespace gpu
|
||||
|
||||
namespace dp
|
||||
{
|
||||
enum class ApiVersion
|
||||
{
|
||||
Invalid = -1,
|
||||
OpenGLES3,
|
||||
Metal,
|
||||
Vulkan
|
||||
};
|
||||
|
||||
/// @todo We have in code: if (anchor & dp::Center) which is not consistent with Center == 0.
|
||||
/// Making Center == 1 breaks other defaults. Review this logic in future.
|
||||
enum Anchor
|
||||
{
|
||||
Center = 0,
|
||||
Left = 0x1,
|
||||
Right = Left << 1,
|
||||
Top = Right << 1,
|
||||
Bottom = Top << 1,
|
||||
LeftTop = Left | Top,
|
||||
RightTop = Right | Top,
|
||||
LeftBottom = Left | Bottom,
|
||||
RightBottom = Right | Bottom
|
||||
};
|
||||
|
||||
enum LineCap
|
||||
{
|
||||
SquareCap = -1,
|
||||
RoundCap = 0,
|
||||
ButtCap = 1,
|
||||
};
|
||||
|
||||
enum LineJoin
|
||||
{
|
||||
MiterJoin = -1,
|
||||
BevelJoin = 0,
|
||||
RoundJoin = 1,
|
||||
};
|
||||
|
||||
using DrapeID = uint64_t;
|
||||
|
||||
struct FontDecl
|
||||
{
|
||||
FontDecl() = default;
|
||||
FontDecl(Color const & color, float size, Color const & outlineColor = Color::Transparent())
|
||||
: m_color(color)
|
||||
, m_outlineColor(outlineColor)
|
||||
, m_size(size)
|
||||
{}
|
||||
|
||||
Color m_color = Color::Transparent();
|
||||
Color m_outlineColor = Color::Transparent();
|
||||
float m_size = 0;
|
||||
};
|
||||
|
||||
struct TitleDecl
|
||||
{
|
||||
dp::FontDecl m_primaryTextFont;
|
||||
std::string m_primaryText;
|
||||
dp::FontDecl m_secondaryTextFont;
|
||||
std::string m_secondaryText;
|
||||
dp::Anchor m_anchor = dp::Anchor::Center;
|
||||
bool m_forceNoWrap = false;
|
||||
m2::PointF m_primaryOffset = m2::PointF(0.0f, 0.0f);
|
||||
m2::PointF m_secondaryOffset = m2::PointF(0.0f, 0.0f);
|
||||
bool m_primaryOptional = false;
|
||||
bool m_secondaryOptional = false;
|
||||
};
|
||||
|
||||
class BaseFramebuffer
|
||||
{
|
||||
public:
|
||||
virtual ~BaseFramebuffer() = default;
|
||||
virtual void Bind() = 0;
|
||||
};
|
||||
|
||||
inline std::string DebugPrint(dp::ApiVersion apiVersion)
|
||||
{
|
||||
switch (apiVersion)
|
||||
{
|
||||
case dp::ApiVersion::Invalid: return "Invalid";
|
||||
case dp::ApiVersion::OpenGLES3: return "OpenGLES3";
|
||||
case dp::ApiVersion::Metal: return "Metal";
|
||||
case dp::ApiVersion::Vulkan: return "Vulkan";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
inline dp::ApiVersion ApiVersionFromString(std::string const & str)
|
||||
{
|
||||
#if defined(OMIM_METAL_AVAILABLE)
|
||||
if (str == "Metal")
|
||||
return dp::ApiVersion::Metal;
|
||||
#endif
|
||||
|
||||
#if defined(OMIM_OS_ANDROID)
|
||||
if (str == "Vulkan")
|
||||
return dp::ApiVersion::Vulkan;
|
||||
#endif
|
||||
|
||||
if (str == "OpenGLES3")
|
||||
return dp::ApiVersion::OpenGLES3;
|
||||
|
||||
// Default behavior for different OS. Appropriate fallback will be chosen
|
||||
// if default API is not supported.
|
||||
#if defined(OMIM_METAL_AVAILABLE)
|
||||
return dp::ApiVersion::Metal;
|
||||
#elif defined(OMIM_OS_ANDROID)
|
||||
return dp::ApiVersion::Vulkan;
|
||||
#else
|
||||
return dp::ApiVersion::OpenGLES3;
|
||||
#endif
|
||||
}
|
||||
|
||||
class GraphicsContext;
|
||||
class TextureManager;
|
||||
using RenderInjectionHandler = std::function<void(ref_ptr<dp::GraphicsContext>, ref_ptr<TextureManager>,
|
||||
ref_ptr<gpu::ProgramManager>, bool shutdown)>;
|
||||
} // namespace dp
|
||||
218
libs/drape/drape_routine.hpp
Normal file
218
libs/drape/drape_routine.hpp
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/thread_pool_delayed.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
// This class MUST NOT run OpenGL-related tasks (which invoke OpenGL or contain any
|
||||
// OpenGL data), use FR/BR threads for that.
|
||||
class DrapeRoutine
|
||||
{
|
||||
public:
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
void Wait()
|
||||
{
|
||||
if (m_isFinished)
|
||||
return;
|
||||
|
||||
DrapeRoutine::Instance().Wait(m_id);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DrapeRoutine;
|
||||
|
||||
explicit Result(uint64_t id) : m_id(id), m_isFinished(false) {}
|
||||
|
||||
uint64_t Finish()
|
||||
{
|
||||
m_isFinished = true;
|
||||
return m_id;
|
||||
}
|
||||
|
||||
uint64_t const m_id;
|
||||
std::atomic<bool> m_isFinished;
|
||||
};
|
||||
|
||||
using ResultPtr = std::shared_ptr<Result>;
|
||||
|
||||
static void Init() { Instance(true /* reinitialize*/); }
|
||||
|
||||
static void Shutdown() { Instance().FinishAll(); }
|
||||
|
||||
template <typename Task>
|
||||
static ResultPtr Run(Task && t)
|
||||
{
|
||||
ResultPtr result(new Result(Instance().GetNextId()));
|
||||
auto const pushResult = Instance().m_workerThread.Push([result, t = std::forward<Task>(t)]() mutable
|
||||
{
|
||||
t();
|
||||
Instance().Notify(result->Finish());
|
||||
});
|
||||
|
||||
if (!pushResult.m_isSuccess)
|
||||
return {};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Task>
|
||||
static ResultPtr RunDelayed(base::DelayedThreadPool::Duration const & duration, Task && t)
|
||||
{
|
||||
ResultPtr result(new Result(Instance().GetNextId()));
|
||||
auto const pushResult =
|
||||
Instance().m_workerThread.PushDelayed(duration, [result, t = std::forward<Task>(t)]() mutable
|
||||
{
|
||||
t();
|
||||
Instance().Notify(result->Finish());
|
||||
});
|
||||
|
||||
if (!pushResult.m_isSuccess)
|
||||
return {};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Asynchronous execution for tasks when execution order matters.
|
||||
template <typename Task>
|
||||
static ResultPtr RunSequential(Task && t)
|
||||
{
|
||||
ResultPtr result(new Result(Instance().GetNextId()));
|
||||
auto const pushResult = Instance().m_sequentialWorkerThread.Push([result, t = std::forward<Task>(t)]() mutable
|
||||
{
|
||||
t();
|
||||
Instance().Notify(result->Finish());
|
||||
});
|
||||
|
||||
if (!pushResult.m_isSuccess)
|
||||
return {};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static DrapeRoutine & Instance(bool reinitialize = false)
|
||||
{
|
||||
static std::unique_ptr<DrapeRoutine> instance;
|
||||
if (!instance || reinitialize)
|
||||
{
|
||||
if (instance)
|
||||
instance->FinishAll();
|
||||
instance = std::unique_ptr<DrapeRoutine>(new DrapeRoutine());
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
DrapeRoutine() : m_workerThread(4 /* threads count */) {}
|
||||
|
||||
uint64_t GetNextId()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_counter++;
|
||||
}
|
||||
|
||||
void Notify(uint64_t id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_finishedIds.insert(id);
|
||||
m_condition.notify_all();
|
||||
}
|
||||
|
||||
void Wait(uint64_t id)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (m_finished)
|
||||
return;
|
||||
m_condition.wait(lock, [this, id]() { return m_finished || m_finishedIds.find(id) != m_finishedIds.end(); });
|
||||
m_finishedIds.erase(id);
|
||||
}
|
||||
|
||||
void FinishAll()
|
||||
{
|
||||
m_workerThread.ShutdownAndJoin();
|
||||
m_sequentialWorkerThread.ShutdownAndJoin();
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_finished = true;
|
||||
m_condition.notify_all();
|
||||
}
|
||||
|
||||
std::unordered_set<uint64_t> m_finishedIds;
|
||||
uint64_t m_counter = 0;
|
||||
bool m_finished = false;
|
||||
std::condition_variable m_condition;
|
||||
std::mutex m_mutex;
|
||||
base::DelayedThreadPool m_workerThread;
|
||||
base::DelayedThreadPool m_sequentialWorkerThread;
|
||||
};
|
||||
|
||||
// This is a helper class, which aggregates logic of waiting for active
|
||||
// tasks completion. It must be used when we provide tasks completion
|
||||
// before subsystem shutting down.
|
||||
template <typename TaskType>
|
||||
class ActiveTasks
|
||||
{
|
||||
struct ActiveTask
|
||||
{
|
||||
std::shared_ptr<TaskType> m_task;
|
||||
DrapeRoutine::ResultPtr m_result;
|
||||
|
||||
ActiveTask(std::shared_ptr<TaskType> && task, DrapeRoutine::ResultPtr && result)
|
||||
: m_task(std::move(task))
|
||||
, m_result(std::move(result))
|
||||
{}
|
||||
};
|
||||
|
||||
public:
|
||||
~ActiveTasks() { FinishAll(); }
|
||||
|
||||
void Add(std::shared_ptr<TaskType> && task, DrapeRoutine::ResultPtr && result)
|
||||
{
|
||||
ASSERT(task != nullptr, ());
|
||||
ASSERT(result != nullptr, ());
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_tasks.emplace_back(std::move(task), std::move(result));
|
||||
}
|
||||
|
||||
void Remove(std::shared_ptr<TaskType> const & task)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_tasks.erase(
|
||||
std::remove_if(m_tasks.begin(), m_tasks.end(), [task](ActiveTask const & t) { return t.m_task == task; }),
|
||||
m_tasks.end());
|
||||
}
|
||||
|
||||
void FinishAll()
|
||||
{
|
||||
// Move tasks to a temporary vector, because m_tasks
|
||||
// can be modified during 'Cancel' calls.
|
||||
std::vector<ActiveTask> tasks;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
tasks.swap(m_tasks);
|
||||
}
|
||||
|
||||
// Cancel all tasks.
|
||||
for (auto & t : tasks)
|
||||
t.m_task->Cancel();
|
||||
|
||||
// Wait for completion of unfinished tasks.
|
||||
for (auto & t : tasks)
|
||||
t.m_result->Wait();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ActiveTask> m_tasks;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
} // namespace dp
|
||||
51
libs/drape/drape_tests/CMakeLists.txt
Normal file
51
libs/drape/drape_tests/CMakeLists.txt
Normal 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}
|
||||
)
|
||||
201
libs/drape/drape_tests/attribute_provides_tests.cpp
Normal file
201
libs/drape/drape_tests/attribute_provides_tests.cpp
Normal 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, ());
|
||||
}
|
||||
355
libs/drape/drape_tests/batcher_tests.cpp
Normal file
355
libs/drape/drape_tests/batcher_tests.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
31
libs/drape/drape_tests/bingind_info_tests.cpp
Normal file
31
libs/drape/drape_tests/bingind_info_tests.cpp
Normal 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, ());
|
||||
}
|
||||
}
|
||||
111
libs/drape/drape_tests/buffer_tests.cpp
Normal file
111
libs/drape/drape_tests/buffer_tests.cpp
Normal 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);
|
||||
}
|
||||
16
libs/drape/drape_tests/dummy_texture.hpp
Normal file
16
libs/drape/drape_tests/dummy_texture.hpp
Normal 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; }
|
||||
};
|
||||
137
libs/drape/drape_tests/font_texture_tests.cpp
Normal file
137
libs/drape/drape_tests/font_texture_tests.cpp
Normal 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();
|
||||
}
|
||||
*/
|
||||
374
libs/drape/drape_tests/gl_functions.cpp
Normal file
374
libs/drape/drape_tests/gl_functions.cpp
Normal 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));
|
||||
}
|
||||
28
libs/drape/drape_tests/gl_mock_functions.cpp
Normal file
28
libs/drape/drape_tests/gl_mock_functions.cpp
Normal 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
|
||||
109
libs/drape/drape_tests/gl_mock_functions.hpp
Normal file
109
libs/drape/drape_tests/gl_mock_functions.hpp
Normal 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)
|
||||
321
libs/drape/drape_tests/glyph_mng_tests.cpp
Normal file
321
libs/drape/drape_tests/glyph_mng_tests.cpp
Normal 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
|
||||
26
libs/drape/drape_tests/glyph_packer_test.cpp
Normal file
26
libs/drape/drape_tests/glyph_packer_test.cpp
Normal 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), ());
|
||||
}
|
||||
26
libs/drape/drape_tests/harfbuzz_shaping_test.cpp
Normal file
26
libs/drape/drape_tests/harfbuzz_shaping_test.cpp
Normal 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
|
||||
24
libs/drape/drape_tests/img.cpp
Normal file
24
libs/drape/drape_tests/img.cpp
Normal 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;
|
||||
}
|
||||
5
libs/drape/drape_tests/img.hpp
Normal file
5
libs/drape/drape_tests/img.hpp
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtGui/QImage>
|
||||
|
||||
QImage CreateImage(uint32_t w, uint32_t h, uint8_t const * mem);
|
||||
49
libs/drape/drape_tests/memory_comparer.hpp
Normal file
49
libs/drape/drape_tests/memory_comparer.hpp
Normal 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
|
||||
75
libs/drape/drape_tests/object_pool_tests.cpp
Normal file
75
libs/drape/drape_tests/object_pool_tests.cpp
Normal 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, ());
|
||||
}
|
||||
97
libs/drape/drape_tests/pointers_tests.cpp
Normal file
97
libs/drape/drape_tests/pointers_tests.cpp
Normal 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
|
||||
}
|
||||
31
libs/drape/drape_tests/static_texture_tests.cpp
Normal file
31
libs/drape/drape_tests/static_texture_tests.cpp
Normal 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(), ());
|
||||
}
|
||||
}
|
||||
}
|
||||
77
libs/drape/drape_tests/stipple_pen_tests.cpp
Normal file
77
libs/drape/drape_tests/stipple_pen_tests.cpp
Normal 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
|
||||
43
libs/drape/drape_tests/testing_graphics_context.hpp
Normal file
43
libs/drape/drape_tests/testing_graphics_context.hpp
Normal 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;
|
||||
};
|
||||
346
libs/drape/drape_tests/texture_of_colors_tests.cpp
Normal file
346
libs/drape/drape_tests/texture_of_colors_tests.cpp
Normal 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));
|
||||
}
|
||||
157
libs/drape/drape_tests/uniform_value_tests.cpp
Normal file
157
libs/drape/drape_tests/uniform_value_tests.cpp
Normal 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
|
||||
97
libs/drape/drape_tests/vertex_buffer_tests.cpp
Normal file
97
libs/drape/drape_tests/vertex_buffer_tests.cpp
Normal 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
|
||||
107
libs/drape/dynamic_texture.hpp
Normal file
107
libs/drape/dynamic_texture.hpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/texture.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
template <typename TIndexer, typename TResourceKey, Texture::ResourceType TResourceType>
|
||||
class DynamicTexture : public Texture
|
||||
{
|
||||
using Base = Texture;
|
||||
|
||||
public:
|
||||
~DynamicTexture() override { ASSERT(m_indexer == nullptr, ()); }
|
||||
|
||||
ref_ptr<ResourceInfo> FindResource(Key const & key, bool & newResource) override
|
||||
{
|
||||
ASSERT(m_indexer != nullptr, ());
|
||||
if (key.GetType() != TResourceType)
|
||||
return nullptr;
|
||||
|
||||
return m_indexer->MapResource(static_cast<TResourceKey const &>(key), newResource);
|
||||
}
|
||||
|
||||
void Create(ref_ptr<GraphicsContext> context, Params const & params) override
|
||||
{
|
||||
ASSERT(Base::IsPowerOfTwo(params.m_width, params.m_height), (params.m_width, params.m_height));
|
||||
Base::Create(context, params);
|
||||
}
|
||||
|
||||
void Create(ref_ptr<GraphicsContext> context, Params const & params, ref_ptr<void> data) override
|
||||
{
|
||||
ASSERT(Base::IsPowerOfTwo(params.m_width, params.m_height), (params.m_width, params.m_height));
|
||||
Base::Create(context, params, data);
|
||||
}
|
||||
|
||||
void UpdateState(ref_ptr<GraphicsContext> context) override
|
||||
{
|
||||
// Create texture before first uploading.
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
std::vector<uint8_t> initData(m_params.m_width * m_params.m_height * GetBytesPerPixel(m_params.m_format), 0);
|
||||
Create(context, m_params, initData.data());
|
||||
m_isInitialized = true;
|
||||
}
|
||||
|
||||
ASSERT(m_indexer != nullptr, ());
|
||||
Bind(context);
|
||||
m_indexer->UploadResources(context, make_ref(this));
|
||||
}
|
||||
|
||||
TextureFormat GetFormat() const override { return m_params.m_format; }
|
||||
|
||||
uint32_t GetWidth() const override { return m_params.m_width; }
|
||||
|
||||
uint32_t GetHeight() const override { return m_params.m_height; }
|
||||
|
||||
float GetS(uint32_t x) const override { return static_cast<float>(x) / m_params.m_width; }
|
||||
|
||||
float GetT(uint32_t y) const override { return static_cast<float>(y) / m_params.m_height; }
|
||||
|
||||
uint32_t GetID() const override { return m_isInitialized ? Texture::GetID() : 0; }
|
||||
|
||||
void Bind(ref_ptr<dp::GraphicsContext> context) const override
|
||||
{
|
||||
if (m_isInitialized)
|
||||
Texture::Bind(context);
|
||||
}
|
||||
|
||||
void SetFilter(TextureFilter filter) override
|
||||
{
|
||||
if (m_isInitialized)
|
||||
Texture::SetFilter(filter);
|
||||
}
|
||||
|
||||
protected:
|
||||
DynamicTexture() : m_isInitialized(false) {}
|
||||
|
||||
struct DynamicTextureParams
|
||||
{
|
||||
m2::PointU m_size;
|
||||
dp::TextureFormat m_format = dp::TextureFormat::Unspecified;
|
||||
TextureFilter m_filter = dp::TextureFilter::Nearest;
|
||||
bool m_usePixelBuffer = false;
|
||||
};
|
||||
|
||||
void Init(ref_ptr<HWTextureAllocator> allocator, ref_ptr<TIndexer> indexer, DynamicTextureParams const & params)
|
||||
{
|
||||
m_indexer = indexer;
|
||||
m_params.m_allocator = allocator;
|
||||
m_params.m_width = params.m_size.x;
|
||||
m_params.m_height = params.m_size.y;
|
||||
m_params.m_format = params.m_format;
|
||||
m_params.m_filter = params.m_filter;
|
||||
m_params.m_usePixelBuffer = params.m_usePixelBuffer;
|
||||
m_params.m_isMutable = true;
|
||||
}
|
||||
|
||||
void Reset() { m_indexer = nullptr; }
|
||||
|
||||
ref_ptr<TIndexer> m_indexer;
|
||||
Texture::Params m_params;
|
||||
std::atomic<bool> m_isInitialized;
|
||||
};
|
||||
} // namespace dp
|
||||
7
libs/drape/font_constants.hpp
Normal file
7
libs/drape/font_constants.hpp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
namespace dp
|
||||
{
|
||||
int constexpr kSdfBorder = 4;
|
||||
int constexpr kBaseFontSizePixels = 22;
|
||||
} // namespace dp
|
||||
211
libs/drape/font_texture.cpp
Normal file
211
libs/drape/font_texture.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
#include "drape/font_texture.hpp"
|
||||
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
|
||||
#include "font_constants.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
GlyphPacker::GlyphPacker(m2::PointU const & size) : m_size(size) {}
|
||||
|
||||
bool GlyphPacker::PackGlyph(uint32_t width, uint32_t height, m2::RectU & rect)
|
||||
{
|
||||
ASSERT_LESS(width, m_size.x, ());
|
||||
ASSERT_LESS(height, m_size.y, ());
|
||||
|
||||
if (m_cursor.x + width > m_size.x)
|
||||
{
|
||||
m_cursor.x = 0;
|
||||
m_cursor.y += m_yStep;
|
||||
m_yStep = 0;
|
||||
}
|
||||
|
||||
if (m_cursor.y + height > m_size.y)
|
||||
{
|
||||
m_isFull = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
rect = m2::RectU(m_cursor.x, m_cursor.y, m_cursor.x + width, m_cursor.y + height);
|
||||
|
||||
m_cursor.x += width;
|
||||
m_yStep = std::max(height, m_yStep);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GlyphPacker::CanBePacked(uint32_t glyphsCount, uint32_t width, uint32_t height) const
|
||||
{
|
||||
uint32_t x = m_cursor.x;
|
||||
uint32_t y = m_cursor.y;
|
||||
uint32_t step = m_yStep;
|
||||
for (uint32_t i = 0; i < glyphsCount; i++)
|
||||
{
|
||||
if (x + width > m_size.x)
|
||||
{
|
||||
x = 0;
|
||||
y += step;
|
||||
}
|
||||
|
||||
if (y + height > m_size.y)
|
||||
return false;
|
||||
|
||||
x += width;
|
||||
step = std::max(height, step);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
m2::RectF GlyphPacker::MapTextureCoords(m2::RectU const & pixelRect) const
|
||||
{
|
||||
auto const width = static_cast<float>(m_size.x);
|
||||
auto const height = static_cast<float>(m_size.y);
|
||||
|
||||
// Half-pixel offset to eliminate artefacts on fetching from texture.
|
||||
float offset = 0.0f;
|
||||
if (pixelRect.SizeX() != 0 && pixelRect.SizeY() != 0)
|
||||
offset = 0.5f;
|
||||
|
||||
return {(pixelRect.minX() + offset) / width, (pixelRect.minY() + offset) / height,
|
||||
(pixelRect.maxX() - offset) / width, (pixelRect.maxY() - offset) / height};
|
||||
}
|
||||
|
||||
bool GlyphPacker::IsFull() const
|
||||
{
|
||||
return m_isFull;
|
||||
}
|
||||
|
||||
GlyphIndex::GlyphIndex(m2::PointU const & size, ref_ptr<GlyphManager> mng) : m_packer(size), m_mng(mng)
|
||||
{
|
||||
ASSERT(m_mng != nullptr, ());
|
||||
}
|
||||
|
||||
GlyphIndex::~GlyphIndex()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (auto & [rect, glyph] : m_pendingNodes)
|
||||
glyph.m_image.Destroy();
|
||||
m_pendingNodes.clear();
|
||||
}
|
||||
|
||||
std::vector<ref_ptr<Texture::ResourceInfo>> GlyphIndex::MapResources(TGlyphs const & keys, bool & hasNewResources)
|
||||
{
|
||||
std::vector<ref_ptr<Texture::ResourceInfo>> info;
|
||||
info.reserve(keys.size());
|
||||
|
||||
hasNewResources = false;
|
||||
for (auto const & glyphKey : keys)
|
||||
{
|
||||
bool newResource = false;
|
||||
auto result = MapResource(glyphKey, newResource);
|
||||
hasNewResources |= newResource;
|
||||
info.push_back(std::move(result));
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
ref_ptr<Texture::ResourceInfo> GlyphIndex::MapResource(GlyphFontAndId const & key, bool & newResource)
|
||||
{
|
||||
newResource = false;
|
||||
if (auto const found = m_index.find(key); found != m_index.end())
|
||||
return make_ref(&found->second);
|
||||
|
||||
newResource = true;
|
||||
|
||||
constexpr bool kUseSdf = true;
|
||||
GlyphImage glyphImage = m_mng->GetGlyphImage(key, dp::kBaseFontSizePixels, kUseSdf);
|
||||
m2::RectU r;
|
||||
if (!m_packer.PackGlyph(glyphImage.m_width, glyphImage.m_height, r))
|
||||
{
|
||||
glyphImage.Destroy();
|
||||
|
||||
LOG(LWARNING, ("Glyphs packer could not pack a glyph with fontIndex =", key.m_fontIndex, "glyphId =", key.m_glyphId,
|
||||
"w =", glyphImage.m_width, "h =", glyphImage.m_height, "packerSize =", m_packer.GetSize()));
|
||||
|
||||
// TODO(AB): Is it a valid invalid glyph?
|
||||
GlyphFontAndId constexpr kInvalidGlyphKey{0, 0};
|
||||
if (auto const found = m_index.find(kInvalidGlyphKey); found != m_index.end())
|
||||
{
|
||||
newResource = false;
|
||||
return make_ref(&found->second);
|
||||
}
|
||||
|
||||
LOG(LERROR, ("Invalid glyph was not found in the index"));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto res = m_index.emplace(key, GlyphInfo{m_packer.MapTextureCoords(r)});
|
||||
ASSERT(res.second, ());
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_pendingNodes.emplace_back(r, Glyph{std::move(glyphImage), key});
|
||||
}
|
||||
|
||||
return make_ref(&res.first->second);
|
||||
}
|
||||
|
||||
bool GlyphIndex::CanBeGlyphPacked(uint32_t glyphsCount) const
|
||||
{
|
||||
if (glyphsCount == 0)
|
||||
return true;
|
||||
|
||||
if (m_packer.IsFull())
|
||||
return false;
|
||||
|
||||
float constexpr kGlyphScalar = 1.5f;
|
||||
auto constexpr baseSizePixels = static_cast<uint32_t>(kBaseFontSizePixels * kGlyphScalar);
|
||||
return m_packer.CanBePacked(glyphsCount, baseSizePixels, baseSizePixels);
|
||||
}
|
||||
|
||||
size_t GlyphIndex::GetPendingNodesCount()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
return m_pendingNodes.size();
|
||||
}
|
||||
|
||||
void GlyphIndex::UploadResources(ref_ptr<GraphicsContext> context, ref_ptr<Texture> texture)
|
||||
{
|
||||
PendingNodes pendingNodes;
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
if (m_pendingNodes.empty())
|
||||
return;
|
||||
m_pendingNodes.swap(pendingNodes);
|
||||
}
|
||||
|
||||
for (auto it = pendingNodes.begin(); it != pendingNodes.end();)
|
||||
{
|
||||
// TODO(AB): Is it possible to mark and check if there is no image before uploading?
|
||||
m_mng->MarkGlyphReady(it->second.m_key);
|
||||
|
||||
if (it->second.m_image.m_data)
|
||||
++it;
|
||||
else
|
||||
it = pendingNodes.erase(it);
|
||||
}
|
||||
|
||||
if (pendingNodes.empty())
|
||||
return;
|
||||
|
||||
for (auto & [rect, glyph] : pendingNodes)
|
||||
{
|
||||
ASSERT(glyph.m_image.m_width != 0 && glyph.m_image.m_height != 0 && rect.SizeX() != 0 && rect.SizeY() != 0, ());
|
||||
ASSERT_EQUAL(glyph.m_image.m_width, rect.SizeX(), ());
|
||||
ASSERT_EQUAL(glyph.m_image.m_height, rect.SizeY(), ());
|
||||
|
||||
m2::PointU const zeroPoint = rect.LeftBottom();
|
||||
uint8_t * srcMemory = SharedBufferManager::GetRawPointer(glyph.m_image.m_data);
|
||||
texture->UploadData(context, zeroPoint.x, zeroPoint.y, rect.SizeX(), rect.SizeY(), make_ref(srcMemory));
|
||||
|
||||
glyph.m_image.Destroy();
|
||||
}
|
||||
}
|
||||
} // namespace dp
|
||||
101
libs/drape/font_texture.hpp
Normal file
101
libs/drape/font_texture.hpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/dynamic_texture.hpp"
|
||||
#include "drape/glyph_manager.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <utility> // std::tie
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GlyphPacker
|
||||
{
|
||||
public:
|
||||
explicit GlyphPacker(m2::PointU const & size);
|
||||
|
||||
bool PackGlyph(uint32_t width, uint32_t height, m2::RectU & rect);
|
||||
bool CanBePacked(uint32_t glyphsCount, uint32_t width, uint32_t height) const;
|
||||
m2::RectF MapTextureCoords(m2::RectU const & pixelRect) const;
|
||||
bool IsFull() const;
|
||||
m2::PointU const & GetSize() const { return m_size; }
|
||||
|
||||
private:
|
||||
m2::PointU m_size = m2::PointU(0, 0);
|
||||
m2::PointU m_cursor = m2::PointU(0, 0);
|
||||
uint32_t m_yStep = 0;
|
||||
bool m_isFull = false;
|
||||
};
|
||||
|
||||
class GlyphKey
|
||||
: public GlyphFontAndId
|
||||
, public Texture::Key
|
||||
{
|
||||
public:
|
||||
Texture::ResourceType GetType() const override { return Texture::ResourceType::Glyph; }
|
||||
};
|
||||
|
||||
// TODO(AB): Make Texture::ResourceInfo non-abstract and use it here directly.
|
||||
class GlyphInfo : public Texture::ResourceInfo
|
||||
{
|
||||
public:
|
||||
explicit GlyphInfo(m2::RectF const & texRect) : ResourceInfo(texRect) {}
|
||||
|
||||
Texture::ResourceType GetType() const override { return Texture::ResourceType::Glyph; }
|
||||
};
|
||||
|
||||
class GlyphIndex
|
||||
{
|
||||
public:
|
||||
GlyphIndex(m2::PointU const & size, ref_ptr<GlyphManager> mng);
|
||||
~GlyphIndex();
|
||||
|
||||
// This function can return nullptr.
|
||||
ref_ptr<Texture::ResourceInfo> MapResource(GlyphFontAndId const & key, bool & newResource);
|
||||
std::vector<ref_ptr<Texture::ResourceInfo>> MapResources(TGlyphs const & keys, bool & hasNewResources);
|
||||
void UploadResources(ref_ptr<dp::GraphicsContext> context, ref_ptr<Texture> texture);
|
||||
|
||||
bool CanBeGlyphPacked(uint32_t glyphsCount) const;
|
||||
|
||||
// ONLY for unit-tests. DO NOT use this function anywhere else.
|
||||
size_t GetPendingNodesCount();
|
||||
|
||||
private:
|
||||
GlyphPacker m_packer;
|
||||
ref_ptr<GlyphManager> m_mng;
|
||||
|
||||
using ResourceMapping = std::map<GlyphFontAndId, GlyphInfo>;
|
||||
using PendingNode = std::pair<m2::RectU, Glyph>;
|
||||
using PendingNodes = std::vector<PendingNode>;
|
||||
|
||||
ResourceMapping m_index;
|
||||
PendingNodes m_pendingNodes;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
class FontTexture : public DynamicTexture<GlyphIndex, GlyphKey, Texture::ResourceType::Glyph>
|
||||
{
|
||||
public:
|
||||
FontTexture(m2::PointU const & size, ref_ptr<GlyphManager> glyphMng, ref_ptr<HWTextureAllocator> allocator)
|
||||
: m_index(size, glyphMng)
|
||||
{
|
||||
DynamicTextureParams const params{size, TextureFormat::Red, TextureFilter::Linear, true /* m_usePixelBuffer */};
|
||||
Init(allocator, make_ref(&m_index), params);
|
||||
}
|
||||
|
||||
~FontTexture() override { Reset(); }
|
||||
|
||||
ref_ptr<ResourceInfo> MapResource(GlyphFontAndId const & key, bool & hasNewResources) const
|
||||
{
|
||||
ASSERT(m_indexer != nullptr, ());
|
||||
return m_indexer->MapResource(key, hasNewResources);
|
||||
}
|
||||
|
||||
bool HasEnoughSpace(uint32_t newKeysCount) const override { return m_index.CanBeGlyphPacked(newKeysCount); }
|
||||
|
||||
private:
|
||||
GlyphIndex m_index;
|
||||
};
|
||||
} // namespace dp
|
||||
9
libs/drape/fonts_tool/CMakeLists.txt
Normal file
9
libs/drape/fonts_tool/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
project(fonts_tool)
|
||||
|
||||
set(SRC
|
||||
fonts_tool.cpp
|
||||
)
|
||||
|
||||
omim_add_executable(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} drape)
|
||||
62
libs/drape/fonts_tool/fonts_tool.cpp
Normal file
62
libs/drape/fonts_tool/fonts_tool.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#include "drape/glyph_manager.hpp"
|
||||
#include "drape/harfbuzz_shaping.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <numeric> // std::accumulate
|
||||
|
||||
void ItemizeLine(std::string & str, dp::GlyphManager & glyphManager)
|
||||
{
|
||||
strings::Trim(str);
|
||||
if (str.empty())
|
||||
return;
|
||||
|
||||
auto const segments = harfbuzz_shaping::GetTextSegments(str);
|
||||
for (auto const & run : segments.m_segments)
|
||||
{
|
||||
std::u16string_view sv{segments.m_text.data() + run.m_start, static_cast<size_t>(run.m_length)};
|
||||
std::cout << DebugPrint(sv) << '|';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cout << "Debugging tool to experiment with text segmentation\n";
|
||||
std::cout << "Usage: " << argv[0] << " [text file with utf8 strings or any arbitrary text string]\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
dp::GlyphManager::Params params;
|
||||
params.m_uniBlocks = "unicode_blocks.txt";
|
||||
params.m_whitelist = "fonts_whitelist.txt";
|
||||
params.m_blacklist = "fonts_blacklist.txt";
|
||||
GetPlatform().GetFontNames(params.m_fonts);
|
||||
|
||||
dp::GlyphManager glyphManager(params);
|
||||
|
||||
if (Platform::IsFileExistsByFullPath(argv[1]))
|
||||
{
|
||||
std::ifstream file(argv[1]);
|
||||
std::string line;
|
||||
while (file.good())
|
||||
{
|
||||
std::getline(file, line);
|
||||
ItemizeLine(line, glyphManager);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get all args as one string.
|
||||
std::vector<std::string> const args(argv + 1, argv + argc);
|
||||
auto line = std::accumulate(args.begin(), args.end(), std::string{});
|
||||
ItemizeLine(line, glyphManager);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
204
libs/drape/framebuffer.cpp
Normal file
204
libs/drape/framebuffer.cpp
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
#include "drape/framebuffer.hpp"
|
||||
#include "drape/gl_functions.hpp"
|
||||
#include "drape/texture.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
Framebuffer::DepthStencil::DepthStencil(bool depthEnabled, bool stencilEnabled)
|
||||
: m_depthEnabled(depthEnabled)
|
||||
, m_stencilEnabled(stencilEnabled)
|
||||
{}
|
||||
|
||||
Framebuffer::DepthStencil::~DepthStencil()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Framebuffer::DepthStencil::SetSize(ref_ptr<dp::GraphicsContext> context, uint32_t width, uint32_t height)
|
||||
{
|
||||
if (m_texture && m_texture->GetWidth() == width && m_texture->GetHeight() == height)
|
||||
return;
|
||||
|
||||
Destroy();
|
||||
|
||||
Texture::Params params;
|
||||
params.m_width = width;
|
||||
params.m_height = height;
|
||||
if (m_depthEnabled && m_stencilEnabled)
|
||||
params.m_format = TextureFormat::DepthStencil;
|
||||
else if (m_depthEnabled)
|
||||
params.m_format = TextureFormat::Depth;
|
||||
else
|
||||
CHECK(false, ("Unsupported depth-stencil combination."));
|
||||
params.m_allocator = GetDefaultAllocator(context);
|
||||
params.m_isRenderTarget = true;
|
||||
|
||||
m_texture = make_unique_dp<FramebufferTexture>();
|
||||
m_texture->Create(context, params);
|
||||
}
|
||||
|
||||
void Framebuffer::DepthStencil::Destroy()
|
||||
{
|
||||
m_texture.reset();
|
||||
}
|
||||
|
||||
uint32_t Framebuffer::DepthStencil::GetDepthAttachmentId() const
|
||||
{
|
||||
ASSERT(m_texture != nullptr, ());
|
||||
return m_texture->GetID();
|
||||
}
|
||||
|
||||
uint32_t Framebuffer::DepthStencil::GetStencilAttachmentId() const
|
||||
{
|
||||
ASSERT(m_stencilEnabled ? m_texture != nullptr : true, ());
|
||||
return m_stencilEnabled ? m_texture->GetID() : 0;
|
||||
}
|
||||
|
||||
ref_ptr<FramebufferTexture> Framebuffer::DepthStencil::GetTexture() const
|
||||
{
|
||||
return make_ref(m_texture);
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer() : m_colorFormat(TextureFormat::RGBA8)
|
||||
{
|
||||
ApplyOwnDepthStencil();
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(TextureFormat colorFormat) : m_colorFormat(colorFormat)
|
||||
{
|
||||
ApplyOwnDepthStencil();
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(TextureFormat colorFormat, bool depthEnabled, bool stencilEnabled)
|
||||
: m_depthStencil(make_unique_dp<dp::Framebuffer::DepthStencil>(depthEnabled, stencilEnabled))
|
||||
, m_colorFormat(colorFormat)
|
||||
{
|
||||
ApplyOwnDepthStencil();
|
||||
}
|
||||
|
||||
Framebuffer::~Framebuffer()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Framebuffer::Destroy()
|
||||
{
|
||||
m_colorTexture.reset();
|
||||
|
||||
if (m_depthStencil != nullptr)
|
||||
m_depthStencil->Destroy();
|
||||
|
||||
if (m_framebufferId != 0)
|
||||
{
|
||||
GLFunctions::glDeleteFramebuffer(&m_framebufferId);
|
||||
m_framebufferId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::SetFramebufferFallback(FramebufferFallback && fallback)
|
||||
{
|
||||
m_framebufferFallback = std::move(fallback);
|
||||
}
|
||||
|
||||
void Framebuffer::SetSize(ref_ptr<dp::GraphicsContext> context, uint32_t width, uint32_t height)
|
||||
{
|
||||
if (!m_isSupported)
|
||||
return;
|
||||
|
||||
if (m_width == width && m_height == height)
|
||||
return;
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
Destroy();
|
||||
|
||||
Texture::Params params;
|
||||
params.m_width = width;
|
||||
params.m_height = height;
|
||||
params.m_format = m_colorFormat;
|
||||
params.m_allocator = GetDefaultAllocator(context);
|
||||
params.m_isRenderTarget = true;
|
||||
|
||||
m_colorTexture = make_unique_dp<FramebufferTexture>();
|
||||
m_colorTexture->Create(context, params);
|
||||
|
||||
if (m_depthStencilRef != nullptr && m_depthStencilRef == m_depthStencil.get())
|
||||
m_depthStencilRef->SetSize(context, m_width, m_height);
|
||||
|
||||
auto const apiVersion = context->GetApiVersion();
|
||||
if (apiVersion == dp::ApiVersion::OpenGLES3)
|
||||
{
|
||||
glConst depthAttachmentId = 0;
|
||||
glConst stencilAttachmentId = 0;
|
||||
if (m_depthStencilRef != nullptr)
|
||||
{
|
||||
depthAttachmentId = m_depthStencilRef->GetDepthAttachmentId();
|
||||
stencilAttachmentId = m_depthStencilRef->GetStencilAttachmentId();
|
||||
}
|
||||
|
||||
GLFunctions::glGenFramebuffer(&m_framebufferId);
|
||||
GLFunctions::glBindFramebuffer(m_framebufferId);
|
||||
|
||||
GLFunctions::glFramebufferTexture2D(gl_const::GLColorAttachment, m_colorTexture->GetID());
|
||||
if (depthAttachmentId != stencilAttachmentId)
|
||||
{
|
||||
GLFunctions::glFramebufferTexture2D(gl_const::GLDepthAttachment, depthAttachmentId);
|
||||
GLFunctions::glFramebufferTexture2D(gl_const::GLStencilAttachment, stencilAttachmentId);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLFunctions::glFramebufferTexture2D(gl_const::GLDepthStencilAttachment, depthAttachmentId);
|
||||
}
|
||||
|
||||
uint32_t const status = GLFunctions::glCheckFramebufferStatus();
|
||||
if (status != gl_const::GLFramebufferComplete)
|
||||
{
|
||||
m_isSupported = false;
|
||||
Destroy();
|
||||
LOG(LWARNING, ("Framebuffer is unsupported. Framebuffer status =", status));
|
||||
}
|
||||
|
||||
if (m_framebufferFallback != nullptr)
|
||||
m_framebufferFallback();
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::SetDepthStencilRef(ref_ptr<DepthStencil> depthStencilRef)
|
||||
{
|
||||
m_depthStencilRef = std::move(depthStencilRef);
|
||||
}
|
||||
|
||||
void Framebuffer::ApplyOwnDepthStencil()
|
||||
{
|
||||
m_depthStencilRef = make_ref(m_depthStencil);
|
||||
}
|
||||
|
||||
void Framebuffer::Bind()
|
||||
{
|
||||
ASSERT(m_isSupported, ());
|
||||
ASSERT_NOT_EQUAL(m_framebufferId, 0, ());
|
||||
GLFunctions::glBindFramebuffer(m_framebufferId);
|
||||
}
|
||||
|
||||
void Framebuffer::ApplyFallback()
|
||||
{
|
||||
ASSERT(m_isSupported, ());
|
||||
if (m_framebufferFallback != nullptr)
|
||||
m_framebufferFallback();
|
||||
}
|
||||
|
||||
ref_ptr<Texture> Framebuffer::GetTexture() const
|
||||
{
|
||||
return make_ref(m_colorTexture);
|
||||
}
|
||||
|
||||
ref_ptr<Framebuffer::DepthStencil> Framebuffer::GetDepthStencilRef() const
|
||||
{
|
||||
return m_depthStencilRef;
|
||||
}
|
||||
} // namespace dp
|
||||
72
libs/drape/framebuffer.hpp
Normal file
72
libs/drape/framebuffer.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/drape_global.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/texture.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class FramebufferTexture : public Texture
|
||||
{
|
||||
public:
|
||||
ref_ptr<ResourceInfo> FindResource(Key const & key, bool & newResource) override { return nullptr; }
|
||||
};
|
||||
|
||||
using FramebufferFallback = std::function<bool()>;
|
||||
|
||||
class Framebuffer : public BaseFramebuffer
|
||||
{
|
||||
public:
|
||||
class DepthStencil
|
||||
{
|
||||
public:
|
||||
DepthStencil(bool depthEnabled, bool stencilEnabled);
|
||||
~DepthStencil();
|
||||
void SetSize(ref_ptr<dp::GraphicsContext> context, uint32_t width, uint32_t height);
|
||||
void Destroy();
|
||||
uint32_t GetDepthAttachmentId() const;
|
||||
uint32_t GetStencilAttachmentId() const;
|
||||
ref_ptr<FramebufferTexture> GetTexture() const;
|
||||
|
||||
private:
|
||||
bool const m_depthEnabled = false;
|
||||
bool const m_stencilEnabled = false;
|
||||
drape_ptr<FramebufferTexture> m_texture;
|
||||
};
|
||||
|
||||
Framebuffer();
|
||||
explicit Framebuffer(TextureFormat colorFormat);
|
||||
Framebuffer(TextureFormat colorFormat, bool depthEnabled, bool stencilEnabled);
|
||||
~Framebuffer() override;
|
||||
|
||||
void SetFramebufferFallback(FramebufferFallback && fallback);
|
||||
void SetSize(ref_ptr<dp::GraphicsContext> context, uint32_t width, uint32_t height);
|
||||
void SetDepthStencilRef(ref_ptr<DepthStencil> depthStencilRef);
|
||||
void ApplyOwnDepthStencil();
|
||||
|
||||
void Bind() override;
|
||||
void ApplyFallback();
|
||||
|
||||
ref_ptr<Texture> GetTexture() const;
|
||||
ref_ptr<DepthStencil> GetDepthStencilRef() const;
|
||||
|
||||
bool IsSupported() const { return m_isSupported; }
|
||||
|
||||
private:
|
||||
void Destroy();
|
||||
|
||||
drape_ptr<DepthStencil> m_depthStencil;
|
||||
ref_ptr<DepthStencil> m_depthStencilRef;
|
||||
drape_ptr<FramebufferTexture> m_colorTexture;
|
||||
uint32_t m_width = 0;
|
||||
uint32_t m_height = 0;
|
||||
uint32_t m_framebufferId = 0;
|
||||
TextureFormat m_colorFormat;
|
||||
FramebufferFallback m_framebufferFallback;
|
||||
bool m_isSupported = true;
|
||||
};
|
||||
} // namespace dp
|
||||
314
libs/drape/gl_constants.cpp
Normal file
314
libs/drape/gl_constants.cpp
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
#include "drape/gl_constants.hpp"
|
||||
#include "drape/gl_includes.hpp"
|
||||
|
||||
#if !defined(GL_RGBA8_OES)
|
||||
#define GL_RGBA8_OES 0x8058
|
||||
#endif
|
||||
|
||||
#if !defined(GL_RGBA4_OES)
|
||||
#define GL_RGBA4_OES 0x8056
|
||||
#endif
|
||||
|
||||
#if !defined(GL_ALPHA8_OES)
|
||||
#define GL_ALPHA8_OES 0x803C
|
||||
#endif
|
||||
|
||||
#if !defined(GL_LUMINANCE8_OES)
|
||||
#define GL_LUMINANCE8_OES 0x8040
|
||||
#endif
|
||||
|
||||
#if !defined(GL_LUMINANCE8_ALPHA8_OES)
|
||||
#define GL_LUMINANCE8_ALPHA8_OES 0x8045
|
||||
#endif
|
||||
|
||||
#if !defined(GL_LUMINANCE8_ALPHA4_OES)
|
||||
#define GL_LUMINANCE8_ALPHA4_OES 0x8043
|
||||
#endif
|
||||
|
||||
#if !defined(GL_LUMINANCE)
|
||||
#define GL_LUMINANCE 0x1909
|
||||
#endif
|
||||
|
||||
#if !defined(GL_LUMINANCE_ALPHA)
|
||||
#define GL_LUMINANCE_ALPHA 0x190A
|
||||
#endif
|
||||
|
||||
#if defined(GL_WRITE_ONLY)
|
||||
#define WRITE_ONLY_DEF GL_WRITE_ONLY
|
||||
#elif defined(GL_WRITE_ONLY_OES)
|
||||
#define WRITE_ONLY_DEF GL_WRITE_ONLY_OES
|
||||
#else
|
||||
#define WRITE_ONLY_DEF 0x88B9
|
||||
#endif
|
||||
|
||||
#if defined(GL_READ_ONLY)
|
||||
#define READ_ONLY_DEF GL_READ_ONLY
|
||||
#else
|
||||
#define READ_ONLY_DEF 0x88B8
|
||||
#endif
|
||||
|
||||
#if defined(GL_MAP_READ_BIT_EXT)
|
||||
#define READ_BIT_DEF GL_MAP_READ_BIT_EXT
|
||||
#else
|
||||
#define READ_BIT_DEF 0x0001
|
||||
#endif
|
||||
|
||||
#if defined(GL_MAP_WRITE_BIT_EXT)
|
||||
#define WRITE_BIT_DEF GL_MAP_WRITE_BIT_EXT
|
||||
#else
|
||||
#define WRITE_BIT_DEF 0x0002
|
||||
#endif
|
||||
|
||||
#if defined(GL_MAP_INVALIDATE_RANGE_BIT_EXT)
|
||||
#define INVALIDATE_RANGE_BIT_DEF GL_MAP_INVALIDATE_RANGE_BIT_EXT
|
||||
#else
|
||||
#define INVALIDATE_RANGE_BIT_DEF 0x0004
|
||||
#endif
|
||||
|
||||
#if defined(GL_MAP_INVALIDATE_BUFFER_BIT_EXT)
|
||||
#define INVALIDATE_BUFFER_BIT_DEF GL_MAP_INVALIDATE_BUFFER_BIT_EXT
|
||||
#else
|
||||
#define INVALIDATE_BUFFER_BIT_DEF 0x0008
|
||||
#endif
|
||||
|
||||
#if defined(GL_MAP_FLUSH_EXPLICIT_BIT_EXT)
|
||||
#define FLUSH_EXPLICIT_BIT_DEF GL_MAP_FLUSH_EXPLICIT_BIT_EXT
|
||||
#else
|
||||
#define FLUSH_EXPLICIT_BIT_DEF 0x0010
|
||||
#endif
|
||||
|
||||
#if defined(GL_MAP_UNSYNCHRONIZED_BIT_EXT)
|
||||
#define UNSYNCHRONIZED_BIT_DEF GL_MAP_UNSYNCHRONIZED_BIT_EXT
|
||||
#else
|
||||
#define UNSYNCHRONIZED_BIT_DEF 0x0020
|
||||
#endif
|
||||
|
||||
#if !defined(GL_FUNC_ADD)
|
||||
#define GL_FUNC_ADD 0x8006
|
||||
#endif
|
||||
|
||||
#if !defined(GL_FUNC_SUBTRACT)
|
||||
#define GL_FUNC_SUBTRACT 0x800A
|
||||
#endif
|
||||
|
||||
#if !defined(GL_FUNC_REVERSE_SUBTRACT)
|
||||
#define GL_FUNC_REVERSE_SUBTRACT 0x800B
|
||||
#endif
|
||||
|
||||
namespace gl_const
|
||||
{
|
||||
|
||||
constexpr glConst GLUnpackAlignment = GL_UNPACK_ALIGNMENT;
|
||||
|
||||
constexpr glConst GLRenderer = GL_RENDERER;
|
||||
constexpr glConst GLVendor = GL_VENDOR;
|
||||
constexpr glConst GLVersion = GL_VERSION;
|
||||
|
||||
#ifdef GL_VERSION_3_0
|
||||
constexpr glConst glContextFlags = GL_CONTEXT_FLAGS;
|
||||
#else
|
||||
constexpr glConst glContextFlags = 0;
|
||||
#endif
|
||||
|
||||
constexpr glConst GLColorBit = GL_COLOR_BUFFER_BIT;
|
||||
constexpr glConst GLDepthBit = GL_DEPTH_BUFFER_BIT;
|
||||
constexpr glConst GLStencilBit = GL_STENCIL_BUFFER_BIT;
|
||||
|
||||
constexpr glConst GLMaxFragmentTextures = GL_MAX_TEXTURE_IMAGE_UNITS;
|
||||
constexpr glConst GLMaxVertexTextures = GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;
|
||||
constexpr glConst GLMaxTextureSize = GL_MAX_TEXTURE_SIZE;
|
||||
|
||||
constexpr glConst GLArrayBuffer = GL_ARRAY_BUFFER;
|
||||
constexpr glConst GLElementArrayBuffer = GL_ELEMENT_ARRAY_BUFFER;
|
||||
constexpr glConst GLPixelBufferWrite = GL_PIXEL_UNPACK_BUFFER;
|
||||
|
||||
constexpr glConst GLBufferSize = GL_BUFFER_SIZE;
|
||||
constexpr glConst GLBufferUsage = GL_BUFFER_USAGE;
|
||||
|
||||
constexpr glConst GLWriteOnly = WRITE_ONLY_DEF;
|
||||
constexpr glConst GLReadOnly = READ_ONLY_DEF;
|
||||
|
||||
constexpr glConst GLReadBufferBit = READ_BIT_DEF;
|
||||
constexpr glConst GLWriteBufferBit = WRITE_BIT_DEF;
|
||||
constexpr glConst GLInvalidateRange = INVALIDATE_RANGE_BIT_DEF;
|
||||
constexpr glConst GLInvalidateBuffer = INVALIDATE_BUFFER_BIT_DEF;
|
||||
constexpr glConst GLFlushExplicit = FLUSH_EXPLICIT_BIT_DEF;
|
||||
constexpr glConst GLUnsynchronized = UNSYNCHRONIZED_BIT_DEF;
|
||||
|
||||
constexpr glConst GLStaticDraw = GL_STATIC_DRAW;
|
||||
constexpr glConst GLStreamDraw = GL_STREAM_DRAW;
|
||||
constexpr glConst GLDynamicDraw = GL_DYNAMIC_DRAW;
|
||||
|
||||
constexpr glConst GLVertexShader = GL_VERTEX_SHADER;
|
||||
constexpr glConst GLFragmentShader = GL_FRAGMENT_SHADER;
|
||||
constexpr glConst GLCurrentProgram = GL_CURRENT_PROGRAM;
|
||||
|
||||
constexpr glConst GLRGBA = GL_RGBA;
|
||||
constexpr glConst GLRGB = GL_RGB;
|
||||
constexpr glConst GLAlpha = GL_ALPHA;
|
||||
constexpr glConst GLLuminance = GL_LUMINANCE;
|
||||
constexpr glConst GLAlphaLuminance = GL_LUMINANCE_ALPHA;
|
||||
constexpr glConst GLDepthComponent = GL_DEPTH_COMPONENT;
|
||||
constexpr glConst GLDepthStencil = GL_DEPTH_STENCIL;
|
||||
|
||||
constexpr glConst GLRGBA8 = GL_RGBA8_OES;
|
||||
constexpr glConst GLRGBA4 = GL_RGBA4_OES;
|
||||
constexpr glConst GLAlpha8 = GL_ALPHA8_OES;
|
||||
constexpr glConst GLLuminance8 = GL_LUMINANCE8_OES;
|
||||
constexpr glConst GLAlphaLuminance8 = GL_LUMINANCE8_ALPHA8_OES;
|
||||
constexpr glConst GLAlphaLuminance4 = GL_LUMINANCE8_ALPHA4_OES;
|
||||
constexpr glConst GLRed = GL_RED;
|
||||
constexpr glConst GLRedGreen = GL_RG;
|
||||
|
||||
constexpr glConst GL8BitOnChannel = GL_UNSIGNED_BYTE;
|
||||
constexpr glConst GL4BitOnChannel = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
|
||||
constexpr glConst GLTexture2D = GL_TEXTURE_2D;
|
||||
|
||||
constexpr glConst GLTexture0 = GL_TEXTURE0;
|
||||
|
||||
constexpr glConst GLMinFilter = GL_TEXTURE_MIN_FILTER;
|
||||
constexpr glConst GLMagFilter = GL_TEXTURE_MAG_FILTER;
|
||||
constexpr glConst GLWrapS = GL_TEXTURE_WRAP_S;
|
||||
constexpr glConst GLWrapT = GL_TEXTURE_WRAP_T;
|
||||
|
||||
constexpr glConst GLRepeat = GL_REPEAT;
|
||||
constexpr glConst GLMirroredRepeat = GL_MIRRORED_REPEAT;
|
||||
constexpr glConst GLClampToEdge = GL_CLAMP_TO_EDGE;
|
||||
|
||||
constexpr glConst GLLinear = GL_LINEAR;
|
||||
constexpr glConst GLNearest = GL_NEAREST;
|
||||
|
||||
constexpr glConst GLByteType = GL_BYTE;
|
||||
constexpr glConst GLUnsignedByteType = GL_UNSIGNED_BYTE;
|
||||
constexpr glConst GLShortType = GL_SHORT;
|
||||
constexpr glConst GLUnsignedShortType = GL_UNSIGNED_SHORT;
|
||||
constexpr glConst GLIntType = GL_INT;
|
||||
constexpr glConst GLUnsignedIntType = GL_UNSIGNED_INT;
|
||||
constexpr glConst GLFloatType = GL_FLOAT;
|
||||
constexpr glConst GLUnsignedInt24_8Type = GL_UNSIGNED_INT_24_8;
|
||||
|
||||
constexpr glConst GLFloatVec2 = GL_FLOAT_VEC2;
|
||||
constexpr glConst GLFloatVec3 = GL_FLOAT_VEC3;
|
||||
constexpr glConst GLFloatVec4 = GL_FLOAT_VEC4;
|
||||
|
||||
constexpr glConst GLIntVec2 = GL_INT_VEC2;
|
||||
constexpr glConst GLIntVec3 = GL_INT_VEC3;
|
||||
constexpr glConst GLIntVec4 = GL_INT_VEC4;
|
||||
|
||||
constexpr glConst GLFloatMat4 = GL_FLOAT_MAT4;
|
||||
|
||||
constexpr glConst GLSampler2D = GL_SAMPLER_2D;
|
||||
|
||||
constexpr glConst GLAddBlend = GL_FUNC_ADD;
|
||||
constexpr glConst GLSubstractBlend = GL_FUNC_SUBTRACT;
|
||||
constexpr glConst GLReverseSubstrBlend = GL_FUNC_REVERSE_SUBTRACT;
|
||||
|
||||
constexpr glConst GLZero = GL_ZERO;
|
||||
constexpr glConst GLOne = GL_ONE;
|
||||
constexpr glConst GLSrcColor = GL_SRC_COLOR;
|
||||
constexpr glConst GLOneMinusSrcColor = GL_ONE_MINUS_SRC_COLOR;
|
||||
constexpr glConst GLDstColor = GL_DST_COLOR;
|
||||
constexpr glConst GLOneMinusDstColor = GL_ONE_MINUS_DST_COLOR;
|
||||
constexpr glConst GLSrcAlpha = GL_SRC_ALPHA;
|
||||
constexpr glConst GLOneMinusSrcAlpha = GL_ONE_MINUS_SRC_ALPHA;
|
||||
constexpr glConst GLDstAlpha = GL_DST_ALPHA;
|
||||
constexpr glConst GLOneMinusDstAlpha = GL_ONE_MINUS_DST_ALPHA;
|
||||
|
||||
constexpr glConst GLDepthTest = GL_DEPTH_TEST;
|
||||
constexpr glConst GLBlending = GL_BLEND;
|
||||
constexpr glConst GLCullFace = GL_CULL_FACE;
|
||||
constexpr glConst GLScissorTest = GL_SCISSOR_TEST;
|
||||
constexpr glConst GLStencilTest = GL_STENCIL_TEST;
|
||||
|
||||
constexpr glConst GLDontCare = GL_DONT_CARE;
|
||||
constexpr glConst GLTrue = GL_TRUE;
|
||||
constexpr glConst GLFalse = GL_FALSE;
|
||||
|
||||
#ifdef GL_VERSION_4_3
|
||||
constexpr glConst GLDebugOutput = GL_DEBUG_OUTPUT;
|
||||
constexpr glConst GLDebugOutputSynchronous = GL_DEBUG_OUTPUT_SYNCHRONOUS;
|
||||
|
||||
constexpr glConst GLDebugSourceApi = GL_DEBUG_SOURCE_API;
|
||||
constexpr glConst GLDebugSourceShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER;
|
||||
constexpr glConst GLDebugSourceThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY;
|
||||
constexpr glConst GLDebugSourceApplication = GL_DEBUG_SOURCE_APPLICATION;
|
||||
constexpr glConst GLDebugSourceOther = GL_DEBUG_SOURCE_OTHER;
|
||||
|
||||
constexpr glConst GLDebugTypeError = GL_DEBUG_TYPE_ERROR;
|
||||
constexpr glConst GLDebugDeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR;
|
||||
constexpr glConst GLDebugUndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR;
|
||||
constexpr glConst GLDebugPortability = GL_DEBUG_TYPE_PORTABILITY;
|
||||
constexpr glConst GLDebugPerformance = GL_DEBUG_TYPE_PERFORMANCE;
|
||||
constexpr glConst GLDebugOther = GL_DEBUG_TYPE_OTHER;
|
||||
|
||||
constexpr glConst GLDebugSeverityLow = GL_DEBUG_SEVERITY_LOW;
|
||||
constexpr glConst GLDebugSeverityMedium = GL_DEBUG_SEVERITY_MEDIUM;
|
||||
constexpr glConst GLDebugSeverityHigh = GL_DEBUG_SEVERITY_HIGH;
|
||||
constexpr glConst GLDebugSeverityNotification = GL_DEBUG_SEVERITY_NOTIFICATION;
|
||||
|
||||
constexpr glConst glContextFlagDebugBit = GL_CONTEXT_FLAG_DEBUG_BIT;
|
||||
#else
|
||||
constexpr glConst GLDebugOutput = 0;
|
||||
constexpr glConst GLDebugOutputSynchronous = 0;
|
||||
|
||||
constexpr glConst GLDebugSourceApi = 0;
|
||||
constexpr glConst GLDebugSourceShaderCompiler = 0;
|
||||
constexpr glConst GLDebugSourceThirdParty = 0;
|
||||
constexpr glConst GLDebugSourceApplication = 0;
|
||||
constexpr glConst GLDebugSourceOther = 0;
|
||||
|
||||
constexpr glConst GLDebugTypeError = 0;
|
||||
constexpr glConst GLDebugDeprecatedBehavior = 0;
|
||||
constexpr glConst GLDebugUndefinedBehavior = 0;
|
||||
constexpr glConst GLDebugPortability = 0;
|
||||
constexpr glConst GLDebugPerformance = 0;
|
||||
constexpr glConst GLDebugOther = 0;
|
||||
|
||||
constexpr glConst GLDebugSeverityLow = 0;
|
||||
constexpr glConst GLDebugSeverityMedium = 0;
|
||||
constexpr glConst GLDebugSeverityHigh = 0;
|
||||
constexpr glConst GLDebugSeverityNotification = 0;
|
||||
|
||||
constexpr glConst glContextFlagDebugBit = 0;
|
||||
#endif
|
||||
|
||||
constexpr glConst GLClockwise = GL_CW;
|
||||
constexpr glConst GLCounterClockwise = GL_CCW;
|
||||
|
||||
constexpr glConst GLFront = GL_FRONT;
|
||||
constexpr glConst GLBack = GL_BACK;
|
||||
constexpr glConst GLFrontAndBack = GL_FRONT_AND_BACK;
|
||||
|
||||
constexpr glConst GLNever = GL_NEVER;
|
||||
constexpr glConst GLLess = GL_LESS;
|
||||
constexpr glConst GLEqual = GL_EQUAL;
|
||||
constexpr glConst GLLessOrEqual = GL_LEQUAL;
|
||||
constexpr glConst GLGreat = GL_GREATER;
|
||||
constexpr glConst GLNotEqual = GL_NOTEQUAL;
|
||||
constexpr glConst GLGreatOrEqual = GL_GEQUAL;
|
||||
constexpr glConst GLAlways = GL_ALWAYS;
|
||||
|
||||
constexpr glConst GLKeep = GL_KEEP;
|
||||
constexpr glConst GLIncr = GL_INCR;
|
||||
constexpr glConst GLDecr = GL_DECR;
|
||||
constexpr glConst GLInvert = GL_INVERT;
|
||||
constexpr glConst GLReplace = GL_REPLACE;
|
||||
constexpr glConst GLIncrWrap = GL_INCR_WRAP;
|
||||
constexpr glConst GLDecrWrap = GL_DECR_WRAP;
|
||||
|
||||
constexpr glConst GLActiveUniforms = GL_ACTIVE_UNIFORMS;
|
||||
|
||||
constexpr glConst GLLines = GL_LINES;
|
||||
constexpr glConst GLLineStrip = GL_LINE_STRIP;
|
||||
constexpr glConst GLTriangles = GL_TRIANGLES;
|
||||
constexpr glConst GLTriangleStrip = GL_TRIANGLE_STRIP;
|
||||
|
||||
constexpr glConst GLColorAttachment = GL_COLOR_ATTACHMENT0;
|
||||
constexpr glConst GLDepthAttachment = GL_DEPTH_ATTACHMENT;
|
||||
constexpr glConst GLStencilAttachment = GL_STENCIL_ATTACHMENT;
|
||||
constexpr glConst GLDepthStencilAttachment = GL_DEPTH_STENCIL_ATTACHMENT;
|
||||
|
||||
constexpr glConst GLFramebufferComplete = GL_FRAMEBUFFER_COMPLETE;
|
||||
|
||||
} // namespace gl_const
|
||||
222
libs/drape/gl_constants.hpp
Normal file
222
libs/drape/gl_constants.hpp
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
using glConst = uint32_t;
|
||||
|
||||
namespace gl_const
|
||||
{
|
||||
extern glConst const GLUnpackAlignment;
|
||||
|
||||
extern glConst const GLRenderer;
|
||||
extern glConst const GLVendor;
|
||||
extern glConst const GLVersion;
|
||||
|
||||
extern glConst const glContextFlags;
|
||||
extern glConst const glContextFlagDebugBit;
|
||||
|
||||
/// Clear bits
|
||||
extern glConst const GLColorBit;
|
||||
extern glConst const GLDepthBit;
|
||||
extern glConst const GLStencilBit;
|
||||
|
||||
/// Hardware specific params
|
||||
extern glConst const GLMaxFragmentTextures;
|
||||
extern glConst const GLMaxVertexTextures;
|
||||
extern glConst const GLMaxTextureSize;
|
||||
|
||||
/// Buffer targets
|
||||
extern glConst const GLArrayBuffer;
|
||||
extern glConst const GLElementArrayBuffer;
|
||||
extern glConst const GLPixelBufferWrite;
|
||||
|
||||
/// Buffer params
|
||||
extern glConst const GLBufferSize;
|
||||
extern glConst const GLBufferUsage;
|
||||
|
||||
/// VBO Access
|
||||
extern glConst const GLWriteOnly;
|
||||
extern glConst const GLReadOnly;
|
||||
|
||||
/// MapBufferRange
|
||||
extern glConst const GLWriteBufferBit;
|
||||
extern glConst const GLReadBufferBit;
|
||||
extern glConst const GLInvalidateRange;
|
||||
extern glConst const GLInvalidateBuffer;
|
||||
extern glConst const GLFlushExplicit;
|
||||
extern glConst const GLUnsynchronized;
|
||||
|
||||
/// BufferUsage
|
||||
extern glConst const GLStaticDraw;
|
||||
extern glConst const GLStreamDraw;
|
||||
extern glConst const GLDynamicDraw;
|
||||
|
||||
/// ShaderType
|
||||
extern glConst const GLVertexShader;
|
||||
extern glConst const GLFragmentShader;
|
||||
extern glConst const GLCurrentProgram;
|
||||
|
||||
/// Texture layouts
|
||||
extern glConst const GLRGBA;
|
||||
extern glConst const GLRGB;
|
||||
extern glConst const GLAlpha;
|
||||
extern glConst const GLLuminance;
|
||||
extern glConst const GLAlphaLuminance;
|
||||
extern glConst const GLDepthComponent;
|
||||
extern glConst const GLDepthStencil;
|
||||
|
||||
/// Texture layout size
|
||||
extern glConst const GLRGBA8;
|
||||
extern glConst const GLRGBA4;
|
||||
extern glConst const GLAlpha8;
|
||||
extern glConst const GLLuminance8;
|
||||
extern glConst const GLAlphaLuminance8;
|
||||
extern glConst const GLAlphaLuminance4;
|
||||
extern glConst const GLRed;
|
||||
extern glConst const GLRedGreen;
|
||||
|
||||
/// Pixel type for texture upload
|
||||
extern glConst const GL8BitOnChannel;
|
||||
extern glConst const GL4BitOnChannel;
|
||||
|
||||
/// Texture targets
|
||||
extern glConst const GLTexture2D;
|
||||
|
||||
/// Texture uniform blocks
|
||||
extern glConst const GLTexture0;
|
||||
|
||||
/// Texture param names
|
||||
extern glConst const GLMinFilter;
|
||||
extern glConst const GLMagFilter;
|
||||
extern glConst const GLWrapS;
|
||||
extern glConst const GLWrapT;
|
||||
|
||||
/// Texture Wrap Modes
|
||||
extern glConst const GLRepeat;
|
||||
extern glConst const GLMirroredRepeat;
|
||||
extern glConst const GLClampToEdge;
|
||||
|
||||
/// Texture Filter Modes
|
||||
extern glConst const GLLinear;
|
||||
extern glConst const GLNearest;
|
||||
|
||||
/// OpenGL types
|
||||
extern glConst const GLByteType;
|
||||
extern glConst const GLUnsignedByteType;
|
||||
extern glConst const GLShortType;
|
||||
extern glConst const GLUnsignedShortType;
|
||||
extern glConst const GLIntType;
|
||||
extern glConst const GLUnsignedIntType;
|
||||
extern glConst const GLFloatType;
|
||||
extern glConst const GLUnsignedInt24_8Type;
|
||||
|
||||
extern glConst const GLFloatVec2;
|
||||
extern glConst const GLFloatVec3;
|
||||
extern glConst const GLFloatVec4;
|
||||
|
||||
extern glConst const GLIntVec2;
|
||||
extern glConst const GLIntVec3;
|
||||
extern glConst const GLIntVec4;
|
||||
|
||||
extern glConst const GLFloatMat4;
|
||||
|
||||
extern glConst const GLSampler2D;
|
||||
|
||||
/// Blend Functions
|
||||
extern glConst const GLAddBlend;
|
||||
extern glConst const GLSubstractBlend;
|
||||
extern glConst const GLReverseSubstrBlend;
|
||||
|
||||
/// Blend Factors
|
||||
extern glConst const GLZero;
|
||||
extern glConst const GLOne;
|
||||
extern glConst const GLSrcColor;
|
||||
extern glConst const GLOneMinusSrcColor;
|
||||
extern glConst const GLDstColor;
|
||||
extern glConst const GLOneMinusDstColor;
|
||||
extern glConst const GLSrcAlpha;
|
||||
extern glConst const GLOneMinusSrcAlpha;
|
||||
extern glConst const GLDstAlpha;
|
||||
extern glConst const GLOneMinusDstAlpha;
|
||||
|
||||
/// OpenGL states
|
||||
extern glConst const GLDepthTest;
|
||||
extern glConst const GLBlending;
|
||||
extern glConst const GLCullFace;
|
||||
extern glConst const GLScissorTest;
|
||||
extern glConst const GLStencilTest;
|
||||
extern glConst const GLDebugOutput;
|
||||
extern glConst const GLDebugOutputSynchronous;
|
||||
|
||||
extern glConst const GLDontCare;
|
||||
extern glConst const GLDontCare;
|
||||
extern glConst const GLTrue;
|
||||
extern glConst const GLFalse;
|
||||
|
||||
// OpenGL source type
|
||||
extern glConst const GLDebugSourceApi;
|
||||
extern glConst const GLDebugSourceShaderCompiler;
|
||||
extern glConst const GLDebugSourceThirdParty;
|
||||
extern glConst const GLDebugSourceApplication;
|
||||
extern glConst const GLDebugSourceOther;
|
||||
|
||||
// OpenGL debug type
|
||||
extern glConst const GLDebugTypeError;
|
||||
extern glConst const GLDebugDeprecatedBehavior;
|
||||
extern glConst const GLDebugUndefinedBehavior;
|
||||
extern glConst const GLDebugPortability;
|
||||
extern glConst const GLDebugPerformance;
|
||||
extern glConst const GLDebugOther;
|
||||
|
||||
// OpenGL debug severity
|
||||
extern glConst const GLDebugSeverityLow;
|
||||
extern glConst const GLDebugSeverityMedium;
|
||||
extern glConst const GLDebugSeverityHigh;
|
||||
extern glConst const GLDebugSeverityNotification;
|
||||
|
||||
/// Triangle faces order
|
||||
extern glConst const GLClockwise;
|
||||
extern glConst const GLCounterClockwise;
|
||||
|
||||
/// Triangle face
|
||||
extern glConst const GLFront;
|
||||
extern glConst const GLBack;
|
||||
extern glConst const GLFrontAndBack;
|
||||
|
||||
/// OpenGL depth functions
|
||||
extern glConst const GLNever;
|
||||
extern glConst const GLLess;
|
||||
extern glConst const GLEqual;
|
||||
extern glConst const GLLessOrEqual;
|
||||
extern glConst const GLGreat;
|
||||
extern glConst const GLNotEqual;
|
||||
extern glConst const GLGreatOrEqual;
|
||||
extern glConst const GLAlways;
|
||||
|
||||
/// OpenGL stencil functions
|
||||
extern glConst const GLKeep;
|
||||
extern glConst const GLIncr;
|
||||
extern glConst const GLDecr;
|
||||
extern glConst const GLInvert;
|
||||
extern glConst const GLReplace;
|
||||
extern glConst const GLIncrWrap;
|
||||
extern glConst const GLDecrWrap;
|
||||
|
||||
/// Program object parameter names
|
||||
extern glConst const GLActiveUniforms;
|
||||
|
||||
/// Draw primitives
|
||||
extern glConst const GLLines;
|
||||
extern glConst const GLLineStrip;
|
||||
extern glConst const GLTriangles;
|
||||
extern glConst const GLTriangleStrip;
|
||||
|
||||
/// Framebuffer attachment points
|
||||
extern glConst const GLColorAttachment;
|
||||
extern glConst const GLDepthAttachment;
|
||||
extern glConst const GLStencilAttachment;
|
||||
extern glConst const GLDepthStencilAttachment;
|
||||
|
||||
/// Framebuffer status
|
||||
extern glConst const GLFramebufferComplete;
|
||||
} // namespace gl_const
|
||||
40
libs/drape/gl_extensions_list.cpp
Normal file
40
libs/drape/gl_extensions_list.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#include "drape/gl_extensions_list.hpp"
|
||||
#include "drape/gl_functions.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
void GLExtensionsList::Init()
|
||||
{
|
||||
#ifdef OMIM_OS_ANDROID
|
||||
// NOTE: MapBuffer/MapBufferRange are disabled by performance reasons according to
|
||||
// https://github.com/organicmaps/organicmaps/commit/d72ab7c8cd8be0eb5a622d9d33ae943b391d5707
|
||||
SetExtension(MapBuffer, false);
|
||||
#else
|
||||
SetExtension(MapBuffer, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GLExtensionsList::IsSupported(ExtensionName extName) const
|
||||
{
|
||||
auto const it = m_supportedMap.find(extName);
|
||||
if (it != m_supportedMap.end())
|
||||
return it->second;
|
||||
|
||||
ASSERT(false, ("Not all used extensions are checked"));
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLExtensionsList::CheckExtension(ExtensionName enumName, std::string const & extName)
|
||||
{
|
||||
m_supportedMap[enumName] = GLFunctions::glHasExtension(extName);
|
||||
}
|
||||
|
||||
void GLExtensionsList::SetExtension(ExtensionName enumName, bool isSupported)
|
||||
{
|
||||
m_supportedMap[enumName] = isSupported;
|
||||
}
|
||||
} // namespace dp
|
||||
32
libs/drape/gl_extensions_list.hpp
Normal file
32
libs/drape/gl_extensions_list.hpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/drape_global.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GLExtensionsList
|
||||
{
|
||||
public:
|
||||
enum ExtensionName
|
||||
{
|
||||
MapBuffer,
|
||||
};
|
||||
|
||||
GLExtensionsList() = default;
|
||||
void Init();
|
||||
bool IsSupported(ExtensionName extName) const;
|
||||
|
||||
private:
|
||||
void CheckExtension(ExtensionName enumName, std::string const & extName);
|
||||
void SetExtension(ExtensionName enumName, bool isSupported);
|
||||
|
||||
std::map<ExtensionName, bool> m_supportedMap;
|
||||
|
||||
DISALLOW_COPY_AND_MOVE(GLExtensionsList);
|
||||
};
|
||||
} // namespace dp
|
||||
1070
libs/drape/gl_functions.cpp
Normal file
1070
libs/drape/gl_functions.cpp
Normal file
File diff suppressed because it is too large
Load diff
177
libs/drape/gl_functions.hpp
Normal file
177
libs/drape/gl_functions.hpp
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/drape_global.hpp"
|
||||
#include "drape/gl_constants.hpp"
|
||||
#include "drape/gl_extensions_list.hpp"
|
||||
|
||||
#include "base/src_point.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
class GLFunctions
|
||||
{
|
||||
public:
|
||||
static dp::ApiVersion CurrentApiVersion;
|
||||
static dp::GLExtensionsList ExtensionsList;
|
||||
|
||||
static void Init(dp::ApiVersion apiVersion);
|
||||
|
||||
static bool glHasExtension(std::string const & name);
|
||||
static void glClearColor(float r, float g, float b, float a);
|
||||
static void glClear(uint32_t clearBits);
|
||||
static void glViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h);
|
||||
static void glScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h);
|
||||
static void glFlush();
|
||||
static void glFinish();
|
||||
|
||||
static void glFrontFace(glConst mode);
|
||||
static void glCullFace(glConst face);
|
||||
|
||||
static void glStencilOpSeparate(glConst face, glConst sfail, glConst dpfail, glConst dppass);
|
||||
static void glStencilFuncSeparate(glConst face, glConst func, int ref, uint32_t mask);
|
||||
|
||||
static void glPixelStore(glConst name, uint32_t value);
|
||||
|
||||
static int32_t glGetInteger(glConst pname);
|
||||
/// target = { gl_const::GLArrayBuffer, gl_const::GLElementArrayBuffer }
|
||||
/// name = { gl_const::GLBufferSize, gl_const::GLBufferUsage }
|
||||
static int32_t glGetBufferParameter(glConst target, glConst name);
|
||||
|
||||
static std::string glGetString(glConst pname);
|
||||
|
||||
static int32_t glGetMaxLineWidth();
|
||||
|
||||
static void glEnable(glConst mode);
|
||||
static void glDisable(glConst mode);
|
||||
static void glClearDepthValue(double depth);
|
||||
static void glDepthMask(bool needWriteToDepthBuffer);
|
||||
static void glDepthFunc(glConst depthFunc);
|
||||
static void glBlendEquation(glConst function);
|
||||
static void glBlendFunc(glConst srcFactor, glConst dstFactor);
|
||||
|
||||
/// Debug Output
|
||||
static bool CanEnableDebugMessages();
|
||||
using TglDebugProc = void (*)(glConst source, glConst type, uint32_t id, glConst severity, int32_t length,
|
||||
char const * message, void * userParam);
|
||||
static void glDebugMessageCallback(TglDebugProc messageCallback, void * userParam);
|
||||
static void glDebugMessageControl(glConst source, glConst type, glConst severity, int32_t count, uint32_t const * ids,
|
||||
uint8_t enabled);
|
||||
|
||||
static void glLineWidth(uint32_t value);
|
||||
|
||||
/// VAO support
|
||||
static uint32_t glGenVertexArray();
|
||||
static void glBindVertexArray(uint32_t vao);
|
||||
static void glDeleteVertexArray(uint32_t vao);
|
||||
|
||||
/// VBO support
|
||||
static uint32_t glGenBuffer();
|
||||
/// target - type of buffer to bind. Look GLConst
|
||||
static void glBindBuffer(uint32_t vbo, glConst target);
|
||||
static void glDeleteBuffer(uint32_t vbo);
|
||||
/// usage - Look GLConst
|
||||
static void glBufferData(glConst target, uint32_t size, void const * data, glConst usage);
|
||||
static void glBufferSubData(glConst target, uint32_t size, void const * data, uint32_t offset);
|
||||
|
||||
static void * glMapBuffer(glConst target, glConst access = gl_const::GLWriteOnly);
|
||||
static void glUnmapBuffer(glConst target);
|
||||
|
||||
static void * glMapBufferRange(glConst target, uint32_t offset, uint32_t length, glConst access);
|
||||
static void glFlushMappedBufferRange(glConst target, uint32_t offset, uint32_t length);
|
||||
|
||||
/// Shaders support
|
||||
static uint32_t glCreateShader(glConst type);
|
||||
static void glShaderSource(uint32_t shaderID, std::string const & src, std::string const & defines);
|
||||
static bool glCompileShader(uint32_t shaderID, std::string & errorLog);
|
||||
static void glDeleteShader(uint32_t shaderID);
|
||||
|
||||
static uint32_t glCreateProgram();
|
||||
static void glAttachShader(uint32_t programID, uint32_t shaderID);
|
||||
static void glDetachShader(uint32_t programID, uint32_t shaderID);
|
||||
static bool glLinkProgram(uint32_t programID, std::string & errorLog);
|
||||
static void glDeleteProgram(uint32_t programID);
|
||||
|
||||
static void glUseProgram(uint32_t programID);
|
||||
static int8_t glGetAttribLocation(uint32_t programID, std::string const & name);
|
||||
static void glBindAttribLocation(uint32_t programID, uint8_t index, std::string const & name);
|
||||
|
||||
/// enable vertex attribute binding. To get attributeLocation need to call glGetAttributeLocation
|
||||
static void glEnableVertexAttribute(int32_t attributeLocation);
|
||||
/// Configure vertex attribute binding.
|
||||
/// attrLocation - attribute location in shader program
|
||||
/// count - specify number of components with "type" for generic attribute
|
||||
/// needNormalize - if "true" then OGL will map integer value on [-1 : 1] for signed of on [0 : 1]
|
||||
/// for unsigned
|
||||
/// if "false" it will direct convert to float type
|
||||
/// if "type" == GLFloat this parameter have no sense
|
||||
/// stride - how much bytes need to seek from current attribute value to get the second value
|
||||
/// offset - how much bytes need to seek from begin of currenct buffer to get first attribute
|
||||
/// value
|
||||
static void glVertexAttributePointer(int32_t attrLocation, uint32_t count, glConst type, bool needNormalize,
|
||||
uint32_t stride, uint32_t offset);
|
||||
|
||||
static void glGetActiveUniform(uint32_t programID, uint32_t uniformIndex, int32_t * uniformSize, glConst * type,
|
||||
std::string & name);
|
||||
|
||||
static int8_t glGetUniformLocation(uint32_t programID, std::string const & name);
|
||||
static void glUniformValuei(int8_t location, int32_t v);
|
||||
static void glUniformValuei(int8_t location, int32_t v1, int32_t v2);
|
||||
static void glUniformValuei(int8_t location, int32_t v1, int32_t v2, int32_t v3);
|
||||
static void glUniformValuei(int8_t location, int32_t v1, int32_t v2, int32_t v3, int32_t v4);
|
||||
static void glUniformValueiv(int8_t location, int32_t * v, uint32_t size);
|
||||
|
||||
static void glUniformValuef(int8_t location, float v);
|
||||
static void glUniformValuef(int8_t location, float v1, float v2);
|
||||
static void glUniformValuef(int8_t location, float v1, float v2, float v3);
|
||||
static void glUniformValuef(int8_t location, float v1, float v2, float v3, float v4);
|
||||
static void glUniformValuefv(int8_t location, float * v, uint32_t size);
|
||||
|
||||
static void glUniformMatrix4x4Value(int8_t location, float const * values);
|
||||
|
||||
static uint32_t glGetCurrentProgram();
|
||||
|
||||
static int32_t glGetProgramiv(uint32_t program, glConst paramName);
|
||||
|
||||
// Textures support
|
||||
static void glActiveTexture(glConst texBlock);
|
||||
static uint32_t glGenTexture();
|
||||
static void glDeleteTexture(uint32_t id);
|
||||
static void glBindTexture(uint32_t textureID);
|
||||
static void glTexImage2D(int width, int height, glConst layout, glConst pixelType, void const * data);
|
||||
static void glTexSubImage2D(int x, int y, int width, int height, glConst layout, glConst pixelType,
|
||||
void const * data);
|
||||
static void glTexParameter(glConst param, glConst value);
|
||||
|
||||
// Draw support
|
||||
static void glDrawElements(glConst primitive, uint32_t sizeOfIndex, uint32_t indexCount, uint32_t startIndex = 0);
|
||||
static void glDrawArrays(glConst mode, int32_t first, uint32_t count);
|
||||
|
||||
// FBO support
|
||||
static void glGenFramebuffer(uint32_t * fbo);
|
||||
static void glDeleteFramebuffer(uint32_t * fbo);
|
||||
static void glBindFramebuffer(uint32_t fbo);
|
||||
static void glFramebufferTexture2D(glConst attachment, glConst texture);
|
||||
static uint32_t glCheckFramebufferStatus();
|
||||
};
|
||||
|
||||
void CheckGLError(base::SrcPoint const & src);
|
||||
|
||||
#ifdef DEBUG
|
||||
#define GLCHECK(x) \
|
||||
do \
|
||||
{ \
|
||||
(x); \
|
||||
CheckGLError(SRC()); \
|
||||
} \
|
||||
while (false)
|
||||
#define GLCHECKCALL() \
|
||||
do \
|
||||
{ \
|
||||
CheckGLError(SRC()); \
|
||||
} \
|
||||
while (false)
|
||||
#else
|
||||
#define GLCHECK(x) (x)
|
||||
#define GLCHECKCALL()
|
||||
#endif
|
||||
115
libs/drape/gl_gpu_program.cpp
Normal file
115
libs/drape/gl_gpu_program.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#include "drape/gl_gpu_program.hpp"
|
||||
#include "drape/gl_functions.hpp"
|
||||
#include "drape/render_state.hpp"
|
||||
#include "drape/support_manager.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
GLGpuProgram::GLGpuProgram(std::string const & programName, ref_ptr<Shader> vertexShader,
|
||||
ref_ptr<Shader> fragmentShader)
|
||||
: GpuProgram(programName)
|
||||
, m_vertexShader(vertexShader)
|
||||
, m_fragmentShader(fragmentShader)
|
||||
{
|
||||
m_programID = GLFunctions::glCreateProgram();
|
||||
GLFunctions::glAttachShader(m_programID, m_vertexShader->GetID());
|
||||
GLFunctions::glAttachShader(m_programID, m_fragmentShader->GetID());
|
||||
|
||||
std::string errorLog;
|
||||
if (!GLFunctions::glLinkProgram(m_programID, errorLog))
|
||||
LOG(LERROR, ("Program ", programName, " link error = ", errorLog));
|
||||
|
||||
LoadUniformLocations();
|
||||
|
||||
GLFunctions::glDetachShader(m_programID, m_vertexShader->GetID());
|
||||
GLFunctions::glDetachShader(m_programID, m_fragmentShader->GetID());
|
||||
}
|
||||
|
||||
GLGpuProgram::~GLGpuProgram()
|
||||
{
|
||||
GLFunctions::glDeleteProgram(m_programID);
|
||||
}
|
||||
|
||||
void GLGpuProgram::Bind()
|
||||
{
|
||||
// Deactivate all unused textures.
|
||||
uint8_t const usedSlots = TextureState::GetLastUsedSlots();
|
||||
for (uint8_t i = m_textureSlotsCount; i < usedSlots; i++)
|
||||
{
|
||||
GLFunctions::glActiveTexture(gl_const::GLTexture0 + i);
|
||||
GLFunctions::glBindTexture(0);
|
||||
}
|
||||
|
||||
GLFunctions::glUseProgram(m_programID);
|
||||
}
|
||||
|
||||
void GLGpuProgram::Unbind()
|
||||
{
|
||||
GLFunctions::glUseProgram(0);
|
||||
}
|
||||
|
||||
int8_t GLGpuProgram::GetAttributeLocation(std::string const & attributeName) const
|
||||
{
|
||||
return GLFunctions::glGetAttribLocation(m_programID, attributeName);
|
||||
}
|
||||
|
||||
int8_t GLGpuProgram::GetUniformLocation(std::string const & uniformName) const
|
||||
{
|
||||
auto const it = m_uniforms.find(uniformName);
|
||||
if (it == m_uniforms.end())
|
||||
return -1;
|
||||
|
||||
return it->second.m_location;
|
||||
}
|
||||
|
||||
glConst GLGpuProgram::GetUniformType(std::string const & uniformName) const
|
||||
{
|
||||
auto const it = m_uniforms.find(uniformName);
|
||||
if (it == m_uniforms.end())
|
||||
return -1;
|
||||
|
||||
return it->second.m_type;
|
||||
}
|
||||
|
||||
GLGpuProgram::UniformsInfo const & GLGpuProgram::GetUniformsInfo() const
|
||||
{
|
||||
return m_uniforms;
|
||||
}
|
||||
|
||||
void GLGpuProgram::LoadUniformLocations()
|
||||
{
|
||||
static std::set<glConst> const kSupportedTypes = {gl_const::GLFloatType, gl_const::GLFloatVec2, gl_const::GLFloatVec3,
|
||||
gl_const::GLFloatVec4, gl_const::GLIntType, gl_const::GLIntVec2,
|
||||
gl_const::GLIntVec3, gl_const::GLIntVec4, gl_const::GLFloatMat4,
|
||||
gl_const::GLSampler2D};
|
||||
|
||||
auto const uniformsCount = GLFunctions::glGetProgramiv(m_programID, gl_const::GLActiveUniforms);
|
||||
for (int i = 0; i < uniformsCount; ++i)
|
||||
{
|
||||
int32_t size = 0;
|
||||
UniformInfo info;
|
||||
std::string name;
|
||||
GLFunctions::glGetActiveUniform(m_programID, static_cast<uint32_t>(i), &size, &info.m_type, name);
|
||||
CHECK(kSupportedTypes.find(info.m_type) != kSupportedTypes.cend(),
|
||||
("Used uniform has unsupported type. Program =", m_programName, "; Type =", info.m_type, "; Name =", name));
|
||||
|
||||
info.m_location = GLFunctions::glGetUniformLocation(m_programID, name);
|
||||
m_uniforms[name] = std::move(info);
|
||||
}
|
||||
m_numericUniformsCount = CalculateNumericUniformsCount();
|
||||
m_textureSlotsCount = static_cast<uint8_t>(m_uniforms.size() - m_numericUniformsCount);
|
||||
}
|
||||
|
||||
uint32_t GLGpuProgram::CalculateNumericUniformsCount() const
|
||||
{
|
||||
uint32_t counter = 0;
|
||||
for (auto const & u : m_uniforms)
|
||||
if (u.second.m_type != gl_const::GLSampler2D)
|
||||
counter++;
|
||||
return counter;
|
||||
}
|
||||
} // namespace dp
|
||||
50
libs/drape/gl_gpu_program.hpp
Normal file
50
libs/drape/gl_gpu_program.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/gl_constants.hpp"
|
||||
#include "drape/gpu_program.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/shader.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GLGpuProgram : public GpuProgram
|
||||
{
|
||||
public:
|
||||
GLGpuProgram(std::string const & programName, ref_ptr<Shader> vertexShader, ref_ptr<Shader> fragmentShader);
|
||||
~GLGpuProgram() override;
|
||||
|
||||
void Bind() override;
|
||||
void Unbind() override;
|
||||
|
||||
int8_t GetAttributeLocation(std::string const & attributeName) const;
|
||||
int8_t GetUniformLocation(std::string const & uniformName) const;
|
||||
glConst GetUniformType(std::string const & uniformName) const;
|
||||
|
||||
struct UniformInfo
|
||||
{
|
||||
int8_t m_location = -1;
|
||||
glConst m_type = gl_const::GLFloatType;
|
||||
};
|
||||
|
||||
using UniformsInfo = std::map<std::string, UniformInfo>;
|
||||
UniformsInfo const & GetUniformsInfo() const;
|
||||
uint32_t GetNumericUniformsCount() const { return m_numericUniformsCount; }
|
||||
|
||||
private:
|
||||
void LoadUniformLocations();
|
||||
uint32_t CalculateNumericUniformsCount() const;
|
||||
|
||||
uint32_t m_programID;
|
||||
|
||||
ref_ptr<Shader> m_vertexShader;
|
||||
ref_ptr<Shader> m_fragmentShader;
|
||||
|
||||
UniformsInfo m_uniforms;
|
||||
uint8_t m_textureSlotsCount = 0;
|
||||
uint32_t m_numericUniformsCount = 0;
|
||||
};
|
||||
} // namespace dp
|
||||
33
libs/drape/gl_includes.hpp
Normal file
33
libs/drape/gl_includes.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#if defined(OMIM_OS_IPHONE)
|
||||
#define GL_SILENCE_DEPRECATION
|
||||
#include <OpenGLES/ES2/glext.h>
|
||||
#include <OpenGLES/ES3/gl.h>
|
||||
#elif defined(OMIM_OS_MAC)
|
||||
#define GL_SILENCE_DEPRECATION
|
||||
#include <OpenGL/gl3.h>
|
||||
#include <OpenGL/glext.h>
|
||||
#elif defined(OMIM_OS_WINDOWS)
|
||||
#include "std/windows.hpp"
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include "3party/GL/glext.h"
|
||||
#elif defined(OMIM_OS_ANDROID)
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include "android/sdk/src/main/cpp/app/organicmaps/sdk/opengl/gl3stub.h"
|
||||
#elif defined(OMIM_OS_LINUX)
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <dlfcn.h>
|
||||
#else
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
20
libs/drape/glsl_func.hpp
Normal file
20
libs/drape/glsl_func.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/geometric.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL // TODO: Remove this line after upgrading glm to the latest version.
|
||||
#include "std/glm_gtx_rotate_vector.hpp"
|
||||
|
||||
namespace glsl
|
||||
{
|
||||
using glm::cross;
|
||||
using glm::distance;
|
||||
using glm::dot;
|
||||
using glm::length;
|
||||
using glm::normalize;
|
||||
|
||||
using glm::rotate;
|
||||
using glm::scale;
|
||||
using glm::translate;
|
||||
using glm::transpose;
|
||||
} // namespace glsl
|
||||
119
libs/drape/glsl_types.hpp
Normal file
119
libs/drape/glsl_types.hpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/color.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/mat3x3.hpp>
|
||||
#include <glm/mat4x2.hpp>
|
||||
#include <glm/mat4x3.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace glsl
|
||||
{
|
||||
using glm::vec2;
|
||||
using glm::vec3;
|
||||
using glm::vec4;
|
||||
|
||||
using glm::dvec2;
|
||||
using glm::dvec3;
|
||||
using glm::dvec4;
|
||||
|
||||
using glm::ivec2;
|
||||
using glm::ivec3;
|
||||
using glm::ivec4;
|
||||
|
||||
using glm::uvec2;
|
||||
using glm::uvec3;
|
||||
using glm::uvec4;
|
||||
|
||||
using glm::mat3;
|
||||
using glm::mat4;
|
||||
using glm::mat4x2;
|
||||
using glm::mat4x3;
|
||||
|
||||
using glm::dmat3;
|
||||
using glm::dmat4;
|
||||
using glm::dmat4x2;
|
||||
using glm::dmat4x3;
|
||||
|
||||
using glm::value_ptr;
|
||||
|
||||
using glm::make_mat4;
|
||||
using glm::make_vec2;
|
||||
using glm::make_vec3;
|
||||
using glm::make_vec4;
|
||||
|
||||
inline m2::PointF ToPoint(vec2 const & v)
|
||||
{
|
||||
return m2::PointF(v.x, v.y);
|
||||
}
|
||||
|
||||
inline vec2 ToVec2(m2::PointF const & pt)
|
||||
{
|
||||
return glsl::vec2(pt.x, pt.y);
|
||||
}
|
||||
|
||||
inline vec2 ToVec2(m2::PointD const & pt)
|
||||
{
|
||||
return glsl::vec2(pt.x, pt.y);
|
||||
}
|
||||
|
||||
inline m2::PointD FromVec2(glsl::vec2 const & pt)
|
||||
{
|
||||
return m2::PointD(pt.x, pt.y);
|
||||
}
|
||||
|
||||
inline vec3 ToVec3(dp::Color const & color)
|
||||
{
|
||||
return glsl::vec3(static_cast<float>(color.GetRed()) / 255, static_cast<float>(color.GetGreen()) / 255,
|
||||
static_cast<float>(color.GetBlue()) / 255);
|
||||
}
|
||||
|
||||
inline vec4 ToVec4(dp::Color const & color)
|
||||
{
|
||||
return glsl::vec4(static_cast<float>(color.GetRed()) / 255, static_cast<float>(color.GetGreen()) / 255,
|
||||
static_cast<float>(color.GetBlue()) / 255, static_cast<float>(color.GetAlpha()) / 255);
|
||||
}
|
||||
|
||||
inline vec4 ToVec4(m2::PointD const & pt1, m2::PointD const & pt2)
|
||||
{
|
||||
return glsl::vec4(pt1.x, pt1.y, pt2.x, pt2.y);
|
||||
}
|
||||
|
||||
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value || std::is_floating_point<T>::value>>
|
||||
inline uint8_t GetArithmeticComponentCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline uint8_t GetComponentCount()
|
||||
{
|
||||
return GetArithmeticComponentCount<T>();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint8_t GetComponentCount<vec2>()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint8_t GetComponentCount<vec3>()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint8_t GetComponentCount<vec4>()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
} // namespace glsl
|
||||
62
libs/drape/glyph.hpp
Normal file
62
libs/drape/glyph.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/buffer_vector.hpp"
|
||||
#include "base/shared_buffer_manager.hpp"
|
||||
|
||||
#include <tuple> // std::tie
|
||||
|
||||
namespace dp
|
||||
{
|
||||
struct GlyphImage
|
||||
{
|
||||
~GlyphImage() { ASSERT_NOT_EQUAL(m_data.use_count(), 1, ("Probably you forgot to call Destroy()")); }
|
||||
|
||||
// TODO(AB): Get rid of manual call to Destroy.
|
||||
void Destroy()
|
||||
{
|
||||
if (m_data != nullptr)
|
||||
{
|
||||
SharedBufferManager::instance().freeSharedBuffer(m_data->size(), m_data);
|
||||
m_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t m_width;
|
||||
uint32_t m_height;
|
||||
|
||||
SharedBufferManager::shared_buffer_ptr_t m_data;
|
||||
};
|
||||
|
||||
struct GlyphFontAndId
|
||||
{
|
||||
int16_t m_fontIndex;
|
||||
uint16_t m_glyphId;
|
||||
|
||||
// Required only for buffer_vector's internal T m_static[N];
|
||||
GlyphFontAndId() = default;
|
||||
|
||||
constexpr GlyphFontAndId(int16_t fontIndex, uint16_t glyphId) : m_fontIndex(fontIndex), m_glyphId(glyphId) {}
|
||||
|
||||
bool operator==(GlyphFontAndId const & other) const
|
||||
{
|
||||
return m_fontIndex == other.m_fontIndex && m_glyphId == other.m_glyphId;
|
||||
}
|
||||
|
||||
bool operator<(GlyphFontAndId const & other) const
|
||||
{
|
||||
return std::tie(m_fontIndex, m_glyphId) < std::tie(other.m_fontIndex, other.m_glyphId);
|
||||
}
|
||||
};
|
||||
|
||||
// 50 glyphs should fit most of the strings based on tests in Switzerland and China.
|
||||
using TGlyphs = buffer_vector<GlyphFontAndId, 50>;
|
||||
|
||||
struct Glyph
|
||||
{
|
||||
Glyph(GlyphImage && image, GlyphFontAndId key) : m_image(image), m_key(key) {}
|
||||
|
||||
GlyphImage m_image;
|
||||
GlyphFontAndId m_key;
|
||||
};
|
||||
} // namespace dp
|
||||
655
libs/drape/glyph_manager.cpp
Normal file
655
libs/drape/glyph_manager.cpp
Normal file
|
|
@ -0,0 +1,655 @@
|
|||
#include "drape/glyph_manager.hpp"
|
||||
|
||||
#include "drape/font_constants.hpp"
|
||||
#include "drape/glyph.hpp"
|
||||
#include "drape/harfbuzz_shaping.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "coding/hex.hpp"
|
||||
#include "coding/reader.hpp"
|
||||
#include "coding/string_utf8_multilang.hpp"
|
||||
|
||||
#include "base/internal/message.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/macros.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <ft2build.h>
|
||||
#include <hb-ft.h>
|
||||
#include <unicode/unistr.h>
|
||||
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_MODULE_H
|
||||
#include FT_SYSTEM_H
|
||||
#include FT_SIZES_H
|
||||
#include FT_TYPES_H
|
||||
|
||||
#undef __FTERRORS_H__
|
||||
#define FT_ERRORDEF(e, v, s) {e, s},
|
||||
#define FT_ERROR_START_LIST {
|
||||
#define FT_ERROR_END_LIST {0, 0}} \
|
||||
;
|
||||
struct FreetypeError
|
||||
{
|
||||
int m_code;
|
||||
char const * const m_message;
|
||||
};
|
||||
|
||||
FreetypeError constexpr g_FT_Errors[] =
|
||||
#include FT_ERRORS_H
|
||||
|
||||
#ifdef DEBUG
|
||||
#define FREETYPE_CHECK(x) \
|
||||
do \
|
||||
{ \
|
||||
FT_Error const err = (x); \
|
||||
if (err) \
|
||||
LOG(LERROR, ("Freetype:", g_FT_Errors[err].m_code, g_FT_Errors[err].m_message)); \
|
||||
} \
|
||||
while (false)
|
||||
#else
|
||||
#define FREETYPE_CHECK(x) x
|
||||
#endif
|
||||
|
||||
namespace dp
|
||||
{
|
||||
int constexpr kInvalidFont = -1;
|
||||
|
||||
template <typename ToDo>
|
||||
void ParseUniBlocks(std::string const & uniBlocksFile, ToDo toDo)
|
||||
{
|
||||
std::string uniBlocks;
|
||||
try
|
||||
{
|
||||
ReaderPtr<Reader>(GetPlatform().GetReader(uniBlocksFile)).ReadAsString(uniBlocks);
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LCRITICAL, ("Error reading uniblock description: ", e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream fin(uniBlocks);
|
||||
while (true)
|
||||
{
|
||||
std::string name;
|
||||
uint32_t start, end;
|
||||
fin >> name >> std::hex >> start >> std::hex >> end;
|
||||
if (!fin)
|
||||
break;
|
||||
|
||||
toDo(name, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ToDo>
|
||||
void ParseFontList(std::string const & fontListFile, ToDo toDo)
|
||||
{
|
||||
std::string fontList;
|
||||
try
|
||||
{
|
||||
ReaderPtr<Reader>(GetPlatform().GetReader(fontListFile)).ReadAsString(fontList);
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LWARNING, ("Error reading font list ", fontListFile, " : ", e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream fin(fontList);
|
||||
while (true)
|
||||
{
|
||||
std::string ubName;
|
||||
std::string fontName;
|
||||
fin >> ubName >> fontName;
|
||||
if (!fin)
|
||||
break;
|
||||
|
||||
toDo(ubName, fontName);
|
||||
}
|
||||
}
|
||||
|
||||
class Font
|
||||
{
|
||||
public:
|
||||
DECLARE_EXCEPTION(InvalidFontException, RootException);
|
||||
DISALLOW_COPY_AND_MOVE(Font);
|
||||
|
||||
Font(ReaderPtr<Reader> && fontReader, FT_Library lib) : m_fontReader(std::move(fontReader)), m_fontFace(nullptr)
|
||||
{
|
||||
std::memset(&m_stream, 0, sizeof(m_stream));
|
||||
m_stream.size = static_cast<unsigned long>(m_fontReader.Size());
|
||||
m_stream.descriptor.pointer = &m_fontReader;
|
||||
m_stream.read = &Font::Read;
|
||||
m_stream.close = &Font::Close;
|
||||
|
||||
FT_Open_Args args = {};
|
||||
args.flags = FT_OPEN_STREAM;
|
||||
args.stream = &m_stream;
|
||||
|
||||
FT_Error const err = FT_Open_Face(lib, &args, 0, &m_fontFace);
|
||||
if (err || !IsValid())
|
||||
MYTHROW(InvalidFontException, (g_FT_Errors[err].m_code, g_FT_Errors[err].m_message));
|
||||
|
||||
// The same font size is used to render all glyphs to textures and to shape them.
|
||||
FT_Set_Pixel_Sizes(m_fontFace, kBaseFontSizePixels, kBaseFontSizePixels);
|
||||
FT_Activate_Size(m_fontFace->size);
|
||||
}
|
||||
|
||||
~Font()
|
||||
{
|
||||
ASSERT(m_fontFace, ());
|
||||
if (m_harfbuzzFont)
|
||||
hb_font_destroy(m_harfbuzzFont);
|
||||
|
||||
FREETYPE_CHECK(FT_Done_Face(m_fontFace));
|
||||
}
|
||||
|
||||
bool IsValid() const { return m_fontFace && m_fontFace->num_glyphs > 0; }
|
||||
|
||||
bool HasGlyph(strings::UniChar unicodePoint) const { return FT_Get_Char_Index(m_fontFace, unicodePoint) != 0; }
|
||||
|
||||
GlyphImage GetGlyphImage(uint16_t glyphId, int pixelHeight, bool sdf) const
|
||||
{
|
||||
FREETYPE_CHECK(FT_Set_Pixel_Sizes(m_fontFace, 0, pixelHeight));
|
||||
// FT_LOAD_RENDER with FT_RENDER_MODE_SDF uses bsdf driver that is around 3x times faster
|
||||
// than sdf driver, activated by FT_LOAD_DEFAULT + FT_RENDER_MODE_SDF
|
||||
FREETYPE_CHECK(FT_Load_Glyph(m_fontFace, glyphId, FT_LOAD_RENDER));
|
||||
if (sdf)
|
||||
FREETYPE_CHECK(FT_Render_Glyph(m_fontFace->glyph, FT_RENDER_MODE_SDF));
|
||||
|
||||
FT_Bitmap const & bitmap = m_fontFace->glyph->bitmap;
|
||||
|
||||
SharedBufferManager::shared_buffer_ptr_t data;
|
||||
if (bitmap.buffer != nullptr)
|
||||
{
|
||||
// Bitmap is stored without a padding.
|
||||
data = SharedBufferManager::instance().reserveSharedBuffer(bitmap.width * bitmap.rows);
|
||||
auto ptr = data->data();
|
||||
|
||||
for (unsigned int row = 0; row < bitmap.rows; ++row)
|
||||
{
|
||||
unsigned int const dstBaseIndex = row * bitmap.width;
|
||||
int const srcBaseIndex = static_cast<int>(row) * bitmap.pitch;
|
||||
for (unsigned int column = 0; column < bitmap.width; ++column)
|
||||
ptr[dstBaseIndex + column] = bitmap.buffer[srcBaseIndex + column];
|
||||
}
|
||||
}
|
||||
return {bitmap.width, bitmap.rows, data};
|
||||
}
|
||||
|
||||
void GetCharcodes(std::vector<FT_ULong> & charcodes) const
|
||||
{
|
||||
FT_UInt gindex;
|
||||
charcodes.push_back(FT_Get_First_Char(m_fontFace, &gindex));
|
||||
while (gindex)
|
||||
charcodes.push_back(FT_Get_Next_Char(m_fontFace, charcodes.back(), &gindex));
|
||||
|
||||
base::SortUnique(charcodes);
|
||||
}
|
||||
|
||||
static unsigned long Read(FT_Stream stream, unsigned long offset, unsigned char * buffer, unsigned long count)
|
||||
{
|
||||
if (count != 0)
|
||||
{
|
||||
auto * reader = reinterpret_cast<ReaderPtr<Reader> *>(stream->descriptor.pointer);
|
||||
reader->Read(offset, buffer, count);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void Close(FT_Stream) {}
|
||||
|
||||
void MarkGlyphReady(uint16_t glyphId) { m_readyGlyphs.emplace(glyphId); }
|
||||
|
||||
bool IsGlyphReady(uint16_t glyphId) const { return m_readyGlyphs.find(glyphId) != m_readyGlyphs.end(); }
|
||||
|
||||
std::string GetName() const { return std::string(m_fontFace->family_name) + ':' + m_fontFace->style_name; }
|
||||
|
||||
// This code is not thread safe.
|
||||
void Shape(hb_buffer_t * hbBuffer, int fontPixelSize, int fontIndex, text::TextMetrics & outMetrics)
|
||||
{
|
||||
// TODO(AB): Do not set the same font size every time.
|
||||
// TODO(AB): Use hb_font_set_scale to scale the same size font in HB instead of changing it in Freetype.
|
||||
FREETYPE_CHECK(FT_Set_Pixel_Sizes(m_fontFace, 0 /* pixel_width */, fontPixelSize /* pixel_height */));
|
||||
if (!m_harfbuzzFont)
|
||||
m_harfbuzzFont = hb_ft_font_create(m_fontFace, nullptr);
|
||||
// else
|
||||
// Call on each font size change.
|
||||
// hb_ft_font_changed(m_harfbuzzFont);
|
||||
|
||||
// Shape!
|
||||
hb_shape(m_harfbuzzFont, hbBuffer, nullptr, 0);
|
||||
|
||||
// Get the glyph and position information.
|
||||
unsigned int glyphCount;
|
||||
hb_glyph_info_t const * glyphInfo = hb_buffer_get_glyph_infos(hbBuffer, &glyphCount);
|
||||
hb_glyph_position_t const * glyphPos = hb_buffer_get_glyph_positions(hbBuffer, &glyphCount);
|
||||
|
||||
for (unsigned int i = 0; i < glyphCount; ++i)
|
||||
{
|
||||
// TODO(AB): Check for missing glyph ID?
|
||||
auto const glyphId = static_cast<uint16_t>(glyphInfo[i].codepoint);
|
||||
|
||||
FT_Int32 constexpr flags = FT_LOAD_DEFAULT;
|
||||
FREETYPE_CHECK(FT_Load_Glyph(m_fontFace, glyphId, flags));
|
||||
|
||||
auto const & currPos = glyphPos[i];
|
||||
|
||||
auto const & metrics = m_fontFace->glyph->metrics;
|
||||
auto const xOffset = static_cast<int32_t>((currPos.x_offset + metrics.horiBearingX) >> 6);
|
||||
// The original Drape code expects a bottom, not a top offset in its calculations.
|
||||
auto const yOffset = static_cast<int32_t>((currPos.y_offset + metrics.horiBearingY - metrics.height) >> 6);
|
||||
int32_t const xAdvance = currPos.x_advance >> 6;
|
||||
// yAdvance is always zero for horizontal text layouts.
|
||||
|
||||
outMetrics.AddGlyphMetrics(static_cast<int16_t>(fontIndex), glyphId, xOffset, yOffset, xAdvance, fontPixelSize);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ReaderPtr<Reader> m_fontReader;
|
||||
FT_StreamRec_ m_stream;
|
||||
FT_Face m_fontFace;
|
||||
|
||||
std::set<uint16_t> m_readyGlyphs;
|
||||
|
||||
hb_font_t * m_harfbuzzFont{nullptr};
|
||||
};
|
||||
|
||||
// Information about single unicode block.
|
||||
struct UnicodeBlock
|
||||
{
|
||||
std::string m_name;
|
||||
|
||||
strings::UniChar m_start;
|
||||
strings::UniChar m_end;
|
||||
std::vector<int> m_fontsWeight;
|
||||
|
||||
UnicodeBlock(std::string const & name, strings::UniChar start, strings::UniChar end)
|
||||
: m_name(name)
|
||||
, m_start(start)
|
||||
, m_end(end)
|
||||
{}
|
||||
|
||||
int GetFontOffset(int idx) const
|
||||
{
|
||||
if (m_fontsWeight.empty())
|
||||
return kInvalidFont;
|
||||
|
||||
int maxWeight = 0;
|
||||
int upperBoundWeight = std::numeric_limits<int>::max();
|
||||
if (idx != kInvalidFont)
|
||||
upperBoundWeight = m_fontsWeight[idx];
|
||||
|
||||
int index = kInvalidFont;
|
||||
ASSERT_LESS(m_fontsWeight.size(), static_cast<size_t>(std::numeric_limits<int>::max()), ());
|
||||
for (size_t i = 0; i < m_fontsWeight.size(); ++i)
|
||||
{
|
||||
int const w = m_fontsWeight[i];
|
||||
if (w < upperBoundWeight && w > maxWeight)
|
||||
{
|
||||
maxWeight = w;
|
||||
index = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
bool HasSymbol(strings::UniChar sym) const { return (m_start <= sym) && (m_end >= sym); }
|
||||
};
|
||||
|
||||
using TUniBlocks = std::vector<UnicodeBlock>;
|
||||
using TUniBlockIter = TUniBlocks::const_iterator;
|
||||
|
||||
struct GlyphManager::Impl
|
||||
{
|
||||
DISALLOW_COPY_AND_MOVE(Impl);
|
||||
|
||||
Impl() { m_harfbuzzBuffer = hb_buffer_create(); }
|
||||
|
||||
~Impl()
|
||||
{
|
||||
m_fonts.clear();
|
||||
if (m_library)
|
||||
FREETYPE_CHECK(FT_Done_FreeType(m_library));
|
||||
|
||||
hb_buffer_destroy(m_harfbuzzBuffer);
|
||||
}
|
||||
|
||||
FT_Library m_library;
|
||||
TUniBlocks m_blocks;
|
||||
TUniBlockIter m_lastUsedBlock;
|
||||
std::vector<std::unique_ptr<Font>> m_fonts;
|
||||
|
||||
// Required to use std::string_view as a search key for std::unordered_map::find().
|
||||
struct StringHash : public std::hash<std::string_view>
|
||||
{
|
||||
using is_transparent = void;
|
||||
};
|
||||
|
||||
// TODO(AB): Compare performance with std::map.
|
||||
std::unordered_map<std::string, text::TextMetrics, StringHash, std::equal_to<>> m_textMetricsCache;
|
||||
hb_buffer_t * m_harfbuzzBuffer;
|
||||
};
|
||||
|
||||
// Destructor is defined where pimpl's destructor is already known.
|
||||
GlyphManager::~GlyphManager() = default;
|
||||
|
||||
GlyphManager::GlyphManager(Params const & params) : m_impl(std::make_unique<Impl>())
|
||||
{
|
||||
using TFontAndBlockName = std::pair<std::string, std::string>;
|
||||
using TFontLst = buffer_vector<TFontAndBlockName, 64>;
|
||||
|
||||
TFontLst whitelst;
|
||||
TFontLst blacklst;
|
||||
|
||||
m_impl->m_blocks.reserve(160);
|
||||
ParseUniBlocks(params.m_uniBlocks, [this](std::string const & name, strings::UniChar start, strings::UniChar end)
|
||||
{ m_impl->m_blocks.emplace_back(name, start, end); });
|
||||
|
||||
ParseFontList(params.m_whitelist, [&whitelst](std::string const & ubName, std::string const & fontName)
|
||||
{ whitelst.emplace_back(fontName, ubName); });
|
||||
|
||||
ParseFontList(params.m_blacklist, [&blacklst](std::string const & ubName, std::string const & fontName)
|
||||
{ blacklst.emplace_back(fontName, ubName); });
|
||||
|
||||
m_impl->m_fonts.reserve(params.m_fonts.size());
|
||||
|
||||
FREETYPE_CHECK(FT_Init_FreeType(&m_impl->m_library));
|
||||
|
||||
// Default Freetype spread/sdf border is 8.
|
||||
static constexpr FT_Int kSdfBorder = dp::kSdfBorder;
|
||||
for (auto const module : {"sdf", "bsdf"})
|
||||
FREETYPE_CHECK(FT_Property_Set(m_impl->m_library, module, "spread", &kSdfBorder));
|
||||
|
||||
for (auto const & fontName : params.m_fonts)
|
||||
{
|
||||
bool ignoreFont = false;
|
||||
std::for_each(blacklst.begin(), blacklst.end(), [&ignoreFont, &fontName](TFontAndBlockName const & p)
|
||||
{
|
||||
if (p.first == fontName && p.second == "*")
|
||||
ignoreFont = true;
|
||||
});
|
||||
|
||||
if (ignoreFont)
|
||||
continue;
|
||||
|
||||
std::vector<FT_ULong> charCodes;
|
||||
try
|
||||
{
|
||||
m_impl->m_fonts.emplace_back(std::make_unique<Font>(GetPlatform().GetReader(fontName), m_impl->m_library));
|
||||
m_impl->m_fonts.back()->GetCharcodes(charCodes);
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LWARNING, ("Error reading font file =", fontName, "; Reason =", e.what()));
|
||||
continue;
|
||||
}
|
||||
|
||||
using BlockIndex = size_t;
|
||||
using CharCounter = int;
|
||||
using CoverNode = std::pair<BlockIndex, CharCounter>;
|
||||
using CoverInfo = std::vector<CoverNode>;
|
||||
|
||||
size_t currentUniBlock = 0;
|
||||
CoverInfo coverInfo;
|
||||
for (auto const charCode : charCodes)
|
||||
{
|
||||
size_t block = currentUniBlock;
|
||||
while (block < m_impl->m_blocks.size())
|
||||
{
|
||||
if (m_impl->m_blocks[block].HasSymbol(static_cast<strings::UniChar>(charCode)))
|
||||
break;
|
||||
++block;
|
||||
}
|
||||
|
||||
if (block < m_impl->m_blocks.size())
|
||||
{
|
||||
if (coverInfo.empty() || coverInfo.back().first != block)
|
||||
coverInfo.emplace_back(block, 1);
|
||||
else
|
||||
++coverInfo.back().second;
|
||||
|
||||
currentUniBlock = block;
|
||||
}
|
||||
}
|
||||
|
||||
using TUpdateCoverInfoFn = std::function<void(UnicodeBlock const & uniBlock, CoverNode & node)>;
|
||||
auto const enumerateFn = [this, &coverInfo, &fontName](TFontLst const & lst, TUpdateCoverInfoFn const & fn)
|
||||
{
|
||||
for (auto const & b : lst)
|
||||
{
|
||||
if (b.first != fontName)
|
||||
continue;
|
||||
|
||||
for (CoverNode & node : coverInfo)
|
||||
{
|
||||
auto const & uniBlock = m_impl->m_blocks[node.first];
|
||||
if (uniBlock.m_name == b.second)
|
||||
{
|
||||
fn(uniBlock, node);
|
||||
break;
|
||||
}
|
||||
else if (b.second == "*")
|
||||
{
|
||||
fn(uniBlock, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enumerateFn(blacklst, [](UnicodeBlock const &, CoverNode & node) { node.second = 0; });
|
||||
|
||||
enumerateFn(whitelst, [this](UnicodeBlock const & uniBlock, CoverNode & node)
|
||||
{ node.second = static_cast<int>(uniBlock.m_end + 1 - uniBlock.m_start + m_impl->m_fonts.size()); });
|
||||
|
||||
for (CoverNode const & node : coverInfo)
|
||||
{
|
||||
UnicodeBlock & uniBlock = m_impl->m_blocks[node.first];
|
||||
uniBlock.m_fontsWeight.resize(m_impl->m_fonts.size(), 0);
|
||||
uniBlock.m_fontsWeight.back() = node.second;
|
||||
}
|
||||
}
|
||||
|
||||
m_impl->m_lastUsedBlock = m_impl->m_blocks.end();
|
||||
|
||||
LOG(LDEBUG, ("How unicode blocks are mapped on font files:"));
|
||||
|
||||
// We don't have black list for now.
|
||||
ASSERT_EQUAL(m_impl->m_fonts.size(), params.m_fonts.size(), ());
|
||||
|
||||
for (auto const & b : m_impl->m_blocks)
|
||||
{
|
||||
auto const & weights = b.m_fontsWeight;
|
||||
ASSERT_LESS_OR_EQUAL(weights.size(), m_impl->m_fonts.size(), ());
|
||||
if (weights.empty())
|
||||
{
|
||||
LOG_SHORT(LDEBUG, (b.m_name, "is unsupported"));
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t const ind = std::distance(weights.begin(), std::max_element(weights.begin(), weights.end()));
|
||||
LOG_SHORT(LDEBUG, (b.m_name, "is in", params.m_fonts[ind]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GlyphManager::GetFontIndex(strings::UniChar unicodePoint)
|
||||
{
|
||||
auto iter = m_impl->m_blocks.cend();
|
||||
if (m_impl->m_lastUsedBlock != m_impl->m_blocks.end() && m_impl->m_lastUsedBlock->HasSymbol(unicodePoint))
|
||||
{
|
||||
iter = m_impl->m_lastUsedBlock;
|
||||
}
|
||||
else if (iter == m_impl->m_blocks.end() || !iter->HasSymbol(unicodePoint))
|
||||
{
|
||||
iter = std::lower_bound(m_impl->m_blocks.begin(), m_impl->m_blocks.end(), unicodePoint,
|
||||
[](UnicodeBlock const & block, strings::UniChar const & v) { return block.m_end < v; });
|
||||
}
|
||||
|
||||
if (iter == m_impl->m_blocks.end() || !iter->HasSymbol(unicodePoint))
|
||||
return kInvalidFont;
|
||||
|
||||
m_impl->m_lastUsedBlock = iter;
|
||||
|
||||
return FindFontIndexInBlock(*m_impl->m_lastUsedBlock, unicodePoint);
|
||||
}
|
||||
|
||||
int GlyphManager::GetFontIndex(std::u16string_view sv)
|
||||
{
|
||||
// Only get font for the first character.
|
||||
// TODO(AB): Make sure that text runs are split by fonts.
|
||||
auto it = sv.begin();
|
||||
return GetFontIndex(utf8::unchecked::next16(it));
|
||||
}
|
||||
|
||||
int GlyphManager::GetFontIndexImmutable(strings::UniChar unicodePoint) const
|
||||
{
|
||||
TUniBlockIter iter =
|
||||
std::lower_bound(m_impl->m_blocks.begin(), m_impl->m_blocks.end(), unicodePoint,
|
||||
[](UnicodeBlock const & block, strings::UniChar const & v) { return block.m_end < v; });
|
||||
|
||||
if (iter == m_impl->m_blocks.end() || !iter->HasSymbol(unicodePoint))
|
||||
return kInvalidFont;
|
||||
|
||||
return FindFontIndexInBlock(*iter, unicodePoint);
|
||||
}
|
||||
|
||||
int GlyphManager::FindFontIndexInBlock(UnicodeBlock const & block, strings::UniChar unicodePoint) const
|
||||
{
|
||||
ASSERT(block.HasSymbol(unicodePoint), ());
|
||||
for (int fontIndex = block.GetFontOffset(kInvalidFont); fontIndex != kInvalidFont;
|
||||
fontIndex = block.GetFontOffset(fontIndex))
|
||||
{
|
||||
ASSERT_LESS(fontIndex, static_cast<int>(m_impl->m_fonts.size()), ());
|
||||
auto const & f = m_impl->m_fonts[fontIndex];
|
||||
if (f->HasGlyph(unicodePoint))
|
||||
return fontIndex;
|
||||
}
|
||||
|
||||
return kInvalidFont;
|
||||
}
|
||||
|
||||
void GlyphManager::MarkGlyphReady(GlyphFontAndId key)
|
||||
{
|
||||
ASSERT_GREATER_OR_EQUAL(key.m_fontIndex, 0, ());
|
||||
ASSERT_LESS(key.m_fontIndex, static_cast<int>(m_impl->m_fonts.size()), ());
|
||||
m_impl->m_fonts[key.m_fontIndex]->MarkGlyphReady(key.m_glyphId);
|
||||
}
|
||||
|
||||
bool GlyphManager::AreGlyphsReady(TGlyphs const & glyphs) const
|
||||
{
|
||||
for (auto [fontIndex, glyphId] : glyphs)
|
||||
{
|
||||
ASSERT_NOT_EQUAL(fontIndex, kInvalidFont, ());
|
||||
|
||||
if (!m_impl->m_fonts[fontIndex]->IsGlyphReady(glyphId))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(AB): Check and support invalid glyphs.
|
||||
GlyphImage GlyphManager::GetGlyphImage(GlyphFontAndId key, int pixelHeight, bool sdf) const
|
||||
{
|
||||
return m_impl->m_fonts[key.m_fontIndex]->GetGlyphImage(key.m_glyphId, pixelHeight, sdf);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
hb_language_t OrganicMapsLanguageToHarfbuzzLanguage(int8_t lang)
|
||||
{
|
||||
// TODO(AB): can langs be converted faster?
|
||||
auto const svLang = StringUtf8Multilang::GetLangByCode(lang);
|
||||
auto const hbLanguage = hb_language_from_string(svLang.data(), static_cast<int>(svLang.size()));
|
||||
if (hbLanguage == HB_LANGUAGE_INVALID)
|
||||
return hb_language_get_default();
|
||||
return hbLanguage;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// This method is NOT multithreading-safe.
|
||||
text::TextMetrics GlyphManager::ShapeText(std::string_view utf8, int fontPixelHeight, int8_t lang)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
static int const fontSize = fontPixelHeight;
|
||||
ASSERT_EQUAL(fontSize, fontPixelHeight,
|
||||
("Cache relies on the same font height/metrics for each glyph", fontSize, fontPixelHeight));
|
||||
#endif
|
||||
|
||||
// A simple cache greatly speeds up text metrics calculation. It has 80+% hit ratio in most scenarios.
|
||||
if (auto const found = m_impl->m_textMetricsCache.find(utf8); found != m_impl->m_textMetricsCache.end())
|
||||
return found->second;
|
||||
|
||||
auto const [text, segments] = harfbuzz_shaping::GetTextSegments(utf8);
|
||||
|
||||
// TODO(AB): Optimize language conversion.
|
||||
hb_language_t const hbLanguage = OrganicMapsLanguageToHarfbuzzLanguage(lang);
|
||||
|
||||
text::TextMetrics allGlyphs;
|
||||
// For SplitText it's enough to know if the last visual (first logical) segment is RTL.
|
||||
allGlyphs.m_isRTL = segments.back().m_direction == HB_DIRECTION_RTL;
|
||||
|
||||
allGlyphs.m_glyphs.reserve(strings::CountChar(utf8));
|
||||
|
||||
for (auto const & substring : segments)
|
||||
{
|
||||
hb_buffer_clear_contents(m_impl->m_harfbuzzBuffer);
|
||||
|
||||
// TODO(AB): Some substrings use different fonts.
|
||||
hb_buffer_add_utf16(m_impl->m_harfbuzzBuffer, reinterpret_cast<uint16_t const *>(text.data()),
|
||||
static_cast<int>(text.size()), substring.m_start, substring.m_length);
|
||||
hb_buffer_set_direction(m_impl->m_harfbuzzBuffer, substring.m_direction);
|
||||
hb_buffer_set_script(m_impl->m_harfbuzzBuffer, substring.m_script);
|
||||
hb_buffer_set_language(m_impl->m_harfbuzzBuffer, hbLanguage);
|
||||
|
||||
auto u32CharacterIter{text.begin() + substring.m_start};
|
||||
auto const end{u32CharacterIter + substring.m_length};
|
||||
do
|
||||
{
|
||||
auto const u32Character = utf8::unchecked::next16(u32CharacterIter);
|
||||
// TODO(AB): Mapping characters to fonts can be optimized.
|
||||
int const fontIndex = GetFontIndex(u32Character);
|
||||
if (fontIndex < 0)
|
||||
LOG(LWARNING, ("No font was found for character", NumToHex(u32Character)));
|
||||
else
|
||||
{
|
||||
// TODO(AB): Mapping font only by the first character in a string may fail in theory in some cases.
|
||||
m_impl->m_fonts[fontIndex]->Shape(m_impl->m_harfbuzzBuffer, fontPixelHeight, fontIndex, allGlyphs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (u32CharacterIter != end);
|
||||
}
|
||||
|
||||
// Uncomment utf8 printing for debugging if necessary. It crashes JNI with non-modified UTF-8 strings on Android 5
|
||||
// and 6. See https://github.com/organicmaps/organicmaps/issues/10685
|
||||
if (allGlyphs.m_glyphs.empty())
|
||||
LOG(LWARNING, ("No glyphs were found in all fonts for string with characters in warnings above" /*, utf8*/));
|
||||
|
||||
// Empirically measured, may need more tuning.
|
||||
size_t constexpr kMaxCacheSize = 50000;
|
||||
if (m_impl->m_textMetricsCache.size() > kMaxCacheSize)
|
||||
{
|
||||
LOG(LINFO, ("Clearing text metrics cache"));
|
||||
// TODO(AB): Is there a better way? E.g. clear a half of the cache?
|
||||
m_impl->m_textMetricsCache.clear();
|
||||
}
|
||||
|
||||
m_impl->m_textMetricsCache.emplace(utf8, allGlyphs);
|
||||
|
||||
return allGlyphs;
|
||||
}
|
||||
|
||||
text::TextMetrics GlyphManager::ShapeText(std::string_view utf8, int fontPixelHeight, char const * lang)
|
||||
{
|
||||
return ShapeText(utf8, fontPixelHeight, StringUtf8Multilang::GetLangIndex(lang));
|
||||
}
|
||||
|
||||
} // namespace dp
|
||||
89
libs/drape/glyph_manager.hpp
Normal file
89
libs/drape/glyph_manager.hpp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/glyph.hpp"
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
struct UnicodeBlock;
|
||||
|
||||
// TODO(AB): Move to a separate file?
|
||||
namespace text
|
||||
{
|
||||
struct GlyphMetrics
|
||||
{
|
||||
GlyphFontAndId m_key;
|
||||
|
||||
// TODO(AB): Store original font units or floats?
|
||||
int32_t m_xOffset;
|
||||
int32_t m_yOffset;
|
||||
int32_t m_xAdvance;
|
||||
// yAdvance is used only in vertical text layouts and is 0 for horizontal texts.
|
||||
int32_t m_yAdvance{0};
|
||||
|
||||
bool operator==(GlyphMetrics const & other) const { return m_key == other.m_key; }
|
||||
};
|
||||
|
||||
// TODO(AB): Move to a separate file?
|
||||
struct TextMetrics
|
||||
{
|
||||
int32_t m_lineWidthInPixels{0};
|
||||
int32_t m_maxLineHeightInPixels{0};
|
||||
std::vector<GlyphMetrics> m_glyphs;
|
||||
// Used for SplitText.
|
||||
bool m_isRTL{false};
|
||||
|
||||
void AddGlyphMetrics(int16_t font, uint16_t glyphId, int32_t xOffset, int32_t yOffset, int32_t xAdvance,
|
||||
int32_t height)
|
||||
{
|
||||
m_glyphs.push_back({{font, glyphId}, xOffset, yOffset, xAdvance});
|
||||
|
||||
if (m_glyphs.size() == 1)
|
||||
xAdvance -= xOffset;
|
||||
// if (yOffset > 0)
|
||||
// height += yOffset; // TODO(AB): Is it needed? Is it correct?
|
||||
m_lineWidthInPixels += xAdvance;
|
||||
m_maxLineHeightInPixels = std::max(m_maxLineHeightInPixels, height);
|
||||
}
|
||||
};
|
||||
} // namespace text
|
||||
|
||||
class GlyphManager
|
||||
{
|
||||
public:
|
||||
struct Params
|
||||
{
|
||||
std::string m_uniBlocks;
|
||||
std::string m_whitelist;
|
||||
std::string m_blacklist;
|
||||
|
||||
std::vector<std::string> m_fonts;
|
||||
};
|
||||
|
||||
explicit GlyphManager(Params const & params);
|
||||
~GlyphManager();
|
||||
|
||||
void MarkGlyphReady(GlyphFontAndId key);
|
||||
bool AreGlyphsReady(TGlyphs const & str) const;
|
||||
|
||||
int GetFontIndex(strings::UniChar unicodePoint);
|
||||
int GetFontIndex(std::u16string_view sv);
|
||||
|
||||
text::TextMetrics ShapeText(std::string_view utf8, int fontPixelHeight, int8_t lang);
|
||||
text::TextMetrics ShapeText(std::string_view utf8, int fontPixelHeight, char const * lang);
|
||||
|
||||
GlyphImage GetGlyphImage(GlyphFontAndId key, int pixelHeight, bool sdf) const;
|
||||
|
||||
private:
|
||||
// Immutable version can be called from any thread and doesn't require internal synchronization.
|
||||
int GetFontIndexImmutable(strings::UniChar unicodePoint) const;
|
||||
int FindFontIndexInBlock(UnicodeBlock const & block, strings::UniChar unicodePoint) const;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
} // namespace dp
|
||||
162
libs/drape/gpu_buffer.cpp
Normal file
162
libs/drape/gpu_buffer.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#include "drape/gpu_buffer.hpp"
|
||||
#include "drape/drape_diagnostics.hpp"
|
||||
#include "drape/gl_functions.hpp"
|
||||
#include "drape/utils/gpu_mem_tracker.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool IsMapBufferSupported()
|
||||
{
|
||||
static bool const isSupported = GLFunctions::ExtensionsList.IsSupported(GLExtensionsList::MapBuffer);
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
glConst glTarget(GPUBuffer::Target t)
|
||||
{
|
||||
if (t == GPUBuffer::ElementBuffer)
|
||||
return gl_const::GLArrayBuffer;
|
||||
|
||||
return gl_const::GLElementArrayBuffer;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
GPUBuffer::GPUBuffer(Target t, void const * data, uint8_t elementSize, uint32_t capacity, uint64_t batcherHash)
|
||||
: TBase(elementSize, capacity)
|
||||
, m_t(t)
|
||||
, m_mappingOffset(0)
|
||||
#ifdef TRACK_GPU_MEM
|
||||
, m_batcherHash(batcherHash)
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
, m_isMapped(false)
|
||||
#endif
|
||||
{
|
||||
UNUSED_VALUE(batcherHash);
|
||||
m_bufferID = GLFunctions::glGenBuffer();
|
||||
Resize(data, capacity);
|
||||
}
|
||||
GPUBuffer::~GPUBuffer()
|
||||
{
|
||||
GLFunctions::glBindBuffer(0, glTarget(m_t));
|
||||
GLFunctions::glDeleteBuffer(m_bufferID);
|
||||
|
||||
#if defined(TRACK_GPU_MEM)
|
||||
dp::GPUMemTracker::Inst().RemoveDeallocated("VBO", m_bufferID);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPUBuffer::UploadData(void const * data, uint32_t elementCount)
|
||||
{
|
||||
ASSERT(!m_isMapped, ());
|
||||
|
||||
uint32_t currentSize = GetCurrentSize();
|
||||
uint8_t elementSize = GetElementSize();
|
||||
ASSERT(GetCapacity() >= elementCount + currentSize, ("Not enough memory to upload ", elementCount, " elements"));
|
||||
Bind();
|
||||
|
||||
#if defined(CHECK_VBO_BOUNDS)
|
||||
int32_t size = GLFunctions::glGetBufferParameter(glTarget(m_t), gl_const::GLBufferSize);
|
||||
ASSERT_EQUAL(GetCapacity() * elementSize, size, ());
|
||||
ASSERT_LESS_OR_EQUAL((elementCount + currentSize) * elementSize, size, ());
|
||||
#endif
|
||||
|
||||
GLFunctions::glBufferSubData(glTarget(m_t), elementCount * elementSize, data, currentSize * elementSize);
|
||||
TBase::UploadData(elementCount);
|
||||
|
||||
#if defined(TRACK_GPU_MEM)
|
||||
dp::GPUMemTracker::Inst().SetUsed("VBO", m_bufferID, (currentSize + elementCount) * elementSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPUBuffer::Bind()
|
||||
{
|
||||
GLFunctions::glBindBuffer(m_bufferID, glTarget(m_t));
|
||||
}
|
||||
|
||||
void * GPUBuffer::Map(uint32_t elementOffset, uint32_t elementCount)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
ASSERT(!m_isMapped, ());
|
||||
m_isMapped = true;
|
||||
#endif
|
||||
|
||||
if (!IsMapBufferSupported())
|
||||
{
|
||||
m_mappingOffset = elementOffset;
|
||||
return nullptr;
|
||||
}
|
||||
m_mappingOffset = 0;
|
||||
uint32_t const elementSize = GetElementSize();
|
||||
uint32_t const byteOffset = elementOffset * elementSize;
|
||||
uint32_t const byteCount = elementCount * elementSize;
|
||||
return GLFunctions::glMapBufferRange(glTarget(m_t), byteOffset, byteCount, gl_const::GLWriteBufferBit);
|
||||
}
|
||||
|
||||
void GPUBuffer::UpdateData(void * gpuPtr, void const * data, uint32_t elementOffset, uint32_t elementCount)
|
||||
{
|
||||
uint32_t const elementSize = GetElementSize();
|
||||
uint32_t const byteOffset = (elementOffset + m_mappingOffset) * elementSize;
|
||||
uint32_t const byteCount = elementCount * elementSize;
|
||||
uint32_t const byteCapacity = GetCapacity() * elementSize;
|
||||
ASSERT(m_isMapped, ());
|
||||
|
||||
#if defined(CHECK_VBO_BOUNDS)
|
||||
int32_t size = GLFunctions::glGetBufferParameter(glTarget(m_t), gl_const::GLBufferSize);
|
||||
ASSERT_EQUAL(size, byteCapacity, ());
|
||||
ASSERT_LESS_OR_EQUAL(byteOffset + byteCount, size, ());
|
||||
#endif
|
||||
|
||||
if (IsMapBufferSupported())
|
||||
{
|
||||
ASSERT(gpuPtr != nullptr, ());
|
||||
memcpy((uint8_t *)gpuPtr + byteOffset, data, byteCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(gpuPtr == nullptr, ());
|
||||
if (byteOffset == 0 && byteCount == byteCapacity)
|
||||
GLFunctions::glBufferData(glTarget(m_t), byteCount, data, gl_const::GLDynamicDraw);
|
||||
else
|
||||
GLFunctions::glBufferSubData(glTarget(m_t), byteCount, data, byteOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUBuffer::Unmap()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
ASSERT(m_isMapped, ());
|
||||
m_isMapped = false;
|
||||
#endif
|
||||
|
||||
m_mappingOffset = 0;
|
||||
if (IsMapBufferSupported())
|
||||
GLFunctions::glUnmapBuffer(glTarget(m_t));
|
||||
}
|
||||
|
||||
void GPUBuffer::Resize(void const * data, uint32_t elementCount)
|
||||
{
|
||||
TBase::Resize(elementCount);
|
||||
Bind();
|
||||
GLFunctions::glBufferData(glTarget(m_t), GetCapacity() * GetElementSize(), data, gl_const::GLDynamicDraw);
|
||||
|
||||
// If we have set up data already (in glBufferData), we have to call SetDataSize.
|
||||
if (data != nullptr)
|
||||
SetDataSize(elementCount);
|
||||
|
||||
#if defined(TRACK_GPU_MEM)
|
||||
dp::GPUMemTracker & memTracker = dp::GPUMemTracker::Inst();
|
||||
memTracker.RemoveDeallocated("VBO", m_bufferID);
|
||||
auto const sizeInBytes = GetCapacity() * GetElementSize();
|
||||
memTracker.AddAllocated("VBO", m_bufferID, sizeInBytes);
|
||||
memTracker.TrackAverageAllocation(m_batcherHash, sizeInBytes);
|
||||
if (data != nullptr)
|
||||
dp::GPUMemTracker::Inst().SetUsed("VBO", m_bufferID, GetCurrentSize() * GetElementSize());
|
||||
#endif
|
||||
}
|
||||
} // namespace dp
|
||||
46
libs/drape/gpu_buffer.hpp
Normal file
46
libs/drape/gpu_buffer.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/buffer_base.hpp"
|
||||
#include "drape/drape_diagnostics.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GPUBuffer : public BufferBase
|
||||
{
|
||||
using TBase = BufferBase;
|
||||
|
||||
public:
|
||||
enum Target
|
||||
{
|
||||
ElementBuffer,
|
||||
IndexBuffer
|
||||
};
|
||||
|
||||
GPUBuffer(Target t, void const * data, uint8_t elementSize, uint32_t capacity, uint64_t batcherHash);
|
||||
~GPUBuffer() override;
|
||||
|
||||
void UploadData(void const * data, uint32_t elementCount);
|
||||
void Bind();
|
||||
|
||||
void * Map(uint32_t elementOffset, uint32_t elementCount);
|
||||
void UpdateData(void * gpuPtr, void const * data, uint32_t elementOffset, uint32_t elementCount);
|
||||
void Unmap();
|
||||
|
||||
protected:
|
||||
// Discard old data.
|
||||
void Resize(void const * data, uint32_t elementCount);
|
||||
|
||||
private:
|
||||
Target m_t;
|
||||
uint32_t m_bufferID;
|
||||
uint32_t m_mappingOffset;
|
||||
#ifdef TRACK_GPU_MEM
|
||||
uint64_t m_batcherHash;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
bool m_isMapped;
|
||||
#endif
|
||||
};
|
||||
} // namespace dp
|
||||
22
libs/drape/gpu_program.hpp
Normal file
22
libs/drape/gpu_program.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GpuProgram
|
||||
{
|
||||
public:
|
||||
explicit GpuProgram(std::string const & programName) : m_programName(programName) {}
|
||||
|
||||
virtual ~GpuProgram() = default;
|
||||
|
||||
std::string const & GetName() const { return m_programName; }
|
||||
|
||||
virtual void Bind() = 0;
|
||||
virtual void Unbind() = 0;
|
||||
|
||||
protected:
|
||||
std::string const m_programName;
|
||||
};
|
||||
} // namespace dp
|
||||
94
libs/drape/graphics_context.hpp
Normal file
94
libs/drape/graphics_context.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/drape_global.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
enum ClearBits : uint32_t
|
||||
{
|
||||
ColorBit = 1,
|
||||
DepthBit = 1 << 1,
|
||||
StencilBit = 1 << 2
|
||||
};
|
||||
|
||||
uint32_t constexpr kClearBitsStoreAll = ClearBits::ColorBit | ClearBits::DepthBit | ClearBits::StencilBit;
|
||||
|
||||
enum class TestFunction : uint8_t
|
||||
{
|
||||
Never = 0,
|
||||
Less,
|
||||
Equal,
|
||||
LessOrEqual,
|
||||
Greater,
|
||||
NotEqual,
|
||||
GreaterOrEqual,
|
||||
Always
|
||||
};
|
||||
|
||||
enum class StencilFace : uint8_t
|
||||
{
|
||||
Front = 0,
|
||||
Back,
|
||||
FrontAndBack
|
||||
};
|
||||
|
||||
enum class StencilAction : uint8_t
|
||||
{
|
||||
Keep = 0,
|
||||
Zero,
|
||||
Replace,
|
||||
Increment,
|
||||
IncrementWrap,
|
||||
Decrement,
|
||||
DecrementWrap,
|
||||
Invert
|
||||
};
|
||||
|
||||
class GraphicsContext
|
||||
{
|
||||
public:
|
||||
virtual ~GraphicsContext() = default;
|
||||
virtual bool BeginRendering() { return true; }
|
||||
virtual void EndRendering() {}
|
||||
virtual void Present() = 0;
|
||||
virtual void MakeCurrent() = 0;
|
||||
virtual void DoneCurrent() {}
|
||||
// The value 'nullptr' means default(system) framebuffer.
|
||||
virtual void SetFramebuffer(ref_ptr<BaseFramebuffer> framebuffer) = 0;
|
||||
virtual void ForgetFramebuffer(ref_ptr<BaseFramebuffer> framebuffer) = 0;
|
||||
virtual void ApplyFramebuffer(std::string const & framebufferLabel) = 0;
|
||||
// w, h - pixel size of render target (logical size * visual scale).
|
||||
virtual void Resize(uint32_t /* w */, uint32_t /* h */) {}
|
||||
virtual void SetRenderingEnabled(bool /* enabled */) {}
|
||||
virtual void SetPresentAvailable(bool /* available */) {}
|
||||
virtual bool Validate() { return true; }
|
||||
virtual void CollectMemory() {}
|
||||
|
||||
virtual void Init(ApiVersion apiVersion) = 0;
|
||||
virtual ApiVersion GetApiVersion() const = 0;
|
||||
virtual std::string GetRendererName() const = 0;
|
||||
virtual std::string GetRendererVersion() const = 0;
|
||||
virtual bool HasPartialTextureUpdates() const { return true; }
|
||||
|
||||
virtual void DebugSynchronizeWithCPU() {}
|
||||
virtual void PushDebugLabel(std::string const & label) = 0;
|
||||
virtual void PopDebugLabel() = 0;
|
||||
|
||||
virtual void SetClearColor(Color const & color) = 0;
|
||||
virtual void Clear(uint32_t clearBits, uint32_t storeBits) = 0;
|
||||
virtual void Flush() = 0;
|
||||
virtual void SetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h) = 0;
|
||||
virtual void SetScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) = 0;
|
||||
virtual void SetDepthTestEnabled(bool enabled) = 0;
|
||||
virtual void SetDepthTestFunction(TestFunction depthFunction) = 0;
|
||||
virtual void SetStencilTestEnabled(bool enabled) = 0;
|
||||
virtual void SetStencilFunction(StencilFace face, TestFunction stencilFunction) = 0;
|
||||
virtual void SetStencilActions(StencilFace face, StencilAction stencilFailAction, StencilAction depthFailAction,
|
||||
StencilAction passAction) = 0;
|
||||
virtual void SetStencilReferenceValue(uint32_t stencilReferenceValue) = 0;
|
||||
virtual void SetCullingEnabled(bool enabled) = 0;
|
||||
};
|
||||
} // namespace dp
|
||||
51
libs/drape/graphics_context_factory.cpp
Normal file
51
libs/drape/graphics_context_factory.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include "drape/graphics_context_factory.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
ThreadSafeFactory::ThreadSafeFactory(GraphicsContextFactory * factory, bool enableSharing)
|
||||
: m_factory(factory)
|
||||
, m_enableSharing(enableSharing)
|
||||
{}
|
||||
|
||||
ThreadSafeFactory::~ThreadSafeFactory()
|
||||
{
|
||||
delete m_factory;
|
||||
}
|
||||
|
||||
GraphicsContext * ThreadSafeFactory::GetDrawContext()
|
||||
{
|
||||
return CreateContext([this]() { return m_factory->GetDrawContext(); },
|
||||
[this]() { return m_factory->IsUploadContextCreated(); });
|
||||
}
|
||||
|
||||
GraphicsContext * ThreadSafeFactory::GetResourcesUploadContext()
|
||||
{
|
||||
return CreateContext([this]() { return m_factory->GetResourcesUploadContext(); },
|
||||
[this]() { return m_factory->IsDrawContextCreated(); });
|
||||
}
|
||||
|
||||
GraphicsContext * ThreadSafeFactory::CreateContext(TCreateCtxFn const & createFn, TIsSeparateCreatedFn const & checkFn)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_condLock);
|
||||
GraphicsContext * ctx = createFn();
|
||||
if (m_enableSharing)
|
||||
{
|
||||
// Wait until context is created.
|
||||
m_Cond.wait(lock, checkFn);
|
||||
|
||||
m_Cond.notify_one();
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void ThreadSafeFactory::WaitForInitialization(GraphicsContext * context)
|
||||
{
|
||||
m_factory->WaitForInitialization(context);
|
||||
}
|
||||
|
||||
void ThreadSafeFactory::SetPresentAvailable(bool available)
|
||||
{
|
||||
m_factory->SetPresentAvailable(available);
|
||||
}
|
||||
} // namespace dp
|
||||
53
libs/drape/graphics_context_factory.hpp
Normal file
53
libs/drape/graphics_context_factory.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/graphics_context.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GraphicsContextFactory
|
||||
{
|
||||
public:
|
||||
virtual ~GraphicsContextFactory() = default;
|
||||
virtual GraphicsContext * GetDrawContext() = 0;
|
||||
virtual GraphicsContext * GetResourcesUploadContext() = 0;
|
||||
virtual bool IsDrawContextCreated() const { return false; }
|
||||
virtual bool IsUploadContextCreated() const { return false; }
|
||||
virtual void WaitForInitialization(dp::GraphicsContext * context) {}
|
||||
virtual void SetPresentAvailable(bool available) {}
|
||||
};
|
||||
|
||||
class ThreadSafeFactory : public GraphicsContextFactory
|
||||
{
|
||||
public:
|
||||
ThreadSafeFactory(GraphicsContextFactory * factory, bool enableSharing = true);
|
||||
~ThreadSafeFactory();
|
||||
GraphicsContext * GetDrawContext() override;
|
||||
GraphicsContext * GetResourcesUploadContext() override;
|
||||
|
||||
template <typename T>
|
||||
T * CastFactory()
|
||||
{
|
||||
return dynamic_cast<T *>(m_factory);
|
||||
}
|
||||
|
||||
void WaitForInitialization(dp::GraphicsContext * context) override;
|
||||
void SetPresentAvailable(bool available) override;
|
||||
|
||||
protected:
|
||||
using TCreateCtxFn = std::function<GraphicsContext *()>;
|
||||
using TIsSeparateCreatedFn = std::function<bool()>;
|
||||
GraphicsContext * CreateContext(TCreateCtxFn const & createFn, TIsSeparateCreatedFn const & checkFn);
|
||||
|
||||
private:
|
||||
GraphicsContextFactory * m_factory;
|
||||
std::mutex m_condLock;
|
||||
std::condition_variable m_Cond;
|
||||
bool m_enableSharing;
|
||||
};
|
||||
} // namespace dp
|
||||
234
libs/drape/harfbuzz_shaping.cpp
Normal file
234
libs/drape/harfbuzz_shaping.cpp
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
#include "drape/harfbuzz_shaping.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <unicode/ubidi.h> // ubidi_open, ubidi_setPara
|
||||
#include <unicode/uscript.h> // UScriptCode
|
||||
#include <utf8/unchecked.h>
|
||||
|
||||
namespace harfbuzz_shaping
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Some Unicode characters may be a part of up to 32 different scripts.
|
||||
using TScriptsArray = std::array<UScriptCode, 32>;
|
||||
|
||||
// Writes the script and the script extensions of the Unicode codepoint.
|
||||
// Returns the number of written scripts.
|
||||
size_t GetScriptExtensions(char32_t codepoint, TScriptsArray & scripts)
|
||||
{
|
||||
// Fill scripts with the script extensions.
|
||||
UErrorCode icu_error = U_ZERO_ERROR;
|
||||
size_t const count = uscript_getScriptExtensions(static_cast<UChar32>(codepoint), scripts.data(),
|
||||
static_cast<int32_t>(scripts.max_size()), &icu_error);
|
||||
if (U_FAILURE(icu_error))
|
||||
{
|
||||
LOG(LWARNING, ("uscript_getScriptExtensions failed with error", icu_error));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Intersects the script extensions set of codepoint with scripts and returns the updated size of the scripts.
|
||||
// The output result will be a subset of the input result (thus resultSize can only be smaller).
|
||||
size_t ScriptSetIntersect(char32_t codepoint, TScriptsArray & inOutScripts, size_t inOutScriptsCount)
|
||||
{
|
||||
// Each codepoint has a Script property and a Script Extensions (Scx) property.
|
||||
//
|
||||
// The implicit Script property values 'Common' and 'Inherited' indicate that a codepoint is widely used in many
|
||||
// scripts, rather than being associated to a specific script.
|
||||
//
|
||||
// However, some codepoints that are assigned a value of 'Common' or 'Inherited' are not commonly used with all
|
||||
// scripts, but rather only with a limited set of scripts. The Script Extension property is used to specify the set
|
||||
// of script which borrow the codepoint.
|
||||
//
|
||||
// Calls to GetScriptExtensions(...) return the set of scripts where the codepoints can be used.
|
||||
// (see table 7 from http://www.unicode.org/reports/tr24/tr24-29.html)
|
||||
//
|
||||
// Script Script Extensions -> Results
|
||||
// 1) Common {Common} -> {Common}
|
||||
// Inherited {Inherited} -> {Inherited}
|
||||
// 2) Latin {Latn} -> {Latn}
|
||||
// Inherited {Latn} -> {Latn}
|
||||
// 3) Common {Hira Kana} -> {Hira Kana}
|
||||
// Inherited {Hira Kana} -> {Hira Kana}
|
||||
// 4) Devanagari {Deva Dogr Kthi Mahj} -> {Deva Dogr Kthi Mahj}
|
||||
// Myanmar {Cakm Mymr Tale} -> {Cakm Mymr Tale}
|
||||
//
|
||||
// For most of the codepoints, the script extensions set contains only one element. For CJK codepoints, it's common
|
||||
// to see 3-4 scripts. For really rare cases, the set can go above 20 scripts.
|
||||
TScriptsArray codepointScripts;
|
||||
size_t const codepointScriptsCount = GetScriptExtensions(codepoint, codepointScripts);
|
||||
|
||||
// Implicit script 'inherited' is inheriting scripts from preceding codepoint.
|
||||
if (codepointScriptsCount == 1 && codepointScripts[0] == USCRIPT_INHERITED)
|
||||
return inOutScriptsCount;
|
||||
|
||||
auto const contains = [&codepointScripts, codepointScriptsCount](UScriptCode code)
|
||||
{
|
||||
for (size_t i = 0; i < codepointScriptsCount; ++i)
|
||||
if (codepointScripts[i] == code)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Intersect both script sets.
|
||||
ASSERT(!contains(USCRIPT_INHERITED), ());
|
||||
size_t outSize = 0;
|
||||
for (size_t i = 0; i < inOutScriptsCount; ++i)
|
||||
{
|
||||
auto const currentScript = inOutScripts[i];
|
||||
if (contains(currentScript))
|
||||
inOutScripts[outSize++] = currentScript;
|
||||
}
|
||||
|
||||
return outSize;
|
||||
}
|
||||
|
||||
// Find the longest sequence of characters from 0 and up to length that have at least one common UScriptCode value.
|
||||
// Writes the common script value to script and returns the length of the sequence. Takes the characters' script
|
||||
// extensions into account. http://www.unicode.org/reports/tr24/#ScriptX
|
||||
//
|
||||
// Consider 3 characters with the script values {Kana}, {Hira, Kana}, {Kana}. Without script extensions only the first
|
||||
// script in each set would be taken into account, resulting in 3 segments where 1 would be enough.
|
||||
size_t ScriptInterval(std::u16string const & text, int32_t start, size_t length, UScriptCode & outScript)
|
||||
{
|
||||
ASSERT_GREATER(length, 0U, ());
|
||||
|
||||
auto const begin = text.begin() + start;
|
||||
auto const end = text.begin() + start + static_cast<int32_t>(length);
|
||||
auto iterator = begin;
|
||||
|
||||
auto c32 = utf8::unchecked::next16(iterator);
|
||||
|
||||
TScriptsArray scripts;
|
||||
size_t scriptsSize = GetScriptExtensions(c32, scripts);
|
||||
|
||||
while (iterator != end)
|
||||
{
|
||||
c32 = utf8::unchecked::next16(iterator);
|
||||
scriptsSize = ScriptSetIntersect(c32, scripts, scriptsSize);
|
||||
if (scriptsSize == 0U)
|
||||
{
|
||||
length = iterator - begin - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outScript = scripts[0];
|
||||
return length;
|
||||
}
|
||||
|
||||
// A copy of hb_icu_script_to_script to avoid direct ICU dependency.
|
||||
hb_script_t ICUScriptToHarfbuzzScript(UScriptCode script)
|
||||
{
|
||||
if (script == USCRIPT_INVALID_CODE)
|
||||
return HB_SCRIPT_INVALID;
|
||||
return hb_script_from_string(uscript_getShortName(script), -1);
|
||||
}
|
||||
|
||||
void GetSingleTextLineRuns(TextSegments & segments)
|
||||
{
|
||||
auto const & text = segments.m_text;
|
||||
auto const textLength = static_cast<int32_t>(text.length());
|
||||
|
||||
// Deliberately not checking for nullptr.
|
||||
thread_local UBiDi * const bidi = ubidi_open();
|
||||
UErrorCode error = U_ZERO_ERROR;
|
||||
::ubidi_setPara(bidi, text.data(), textLength, UBIDI_DEFAULT_LTR, nullptr, &error);
|
||||
if (U_FAILURE(error))
|
||||
{
|
||||
LOG(LERROR, ("ubidi_setPara failed with code", error));
|
||||
segments.m_segments.emplace_back(0, 0, HB_SCRIPT_UNKNOWN, HB_DIRECTION_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Split the original text by logical runs, then each logical run by common script and each sequence at special
|
||||
// characters and style boundaries. This invariant holds: bidiRunStart <= scriptRunStart <= breakingRunStart
|
||||
// <= breakingRunEnd <= scriptRunStart <= bidiRunEnd. AB: Breaking runs are dropped now, they may not be needed.
|
||||
for (int32_t bidiRunStart = 0; bidiRunStart < textLength;)
|
||||
{
|
||||
// Determine the longest logical run (e.g. same bidi direction) from this point.
|
||||
int32_t bidiRunBreak = 0;
|
||||
UBiDiLevel bidiLevel = 0;
|
||||
::ubidi_getLogicalRun(bidi, bidiRunStart, &bidiRunBreak, &bidiLevel);
|
||||
int32_t const bidiRunEnd = bidiRunBreak;
|
||||
ASSERT_LESS(bidiRunStart, bidiRunEnd, ());
|
||||
|
||||
for (int32_t scriptRunStart = bidiRunStart; scriptRunStart < bidiRunEnd;)
|
||||
{
|
||||
// Find the longest sequence of characters that have at least one common UScriptCode value.
|
||||
UScriptCode script = USCRIPT_INVALID_CODE;
|
||||
size_t const scriptRunEnd =
|
||||
ScriptInterval(segments.m_text, scriptRunStart, bidiRunEnd - scriptRunStart, script) + scriptRunStart;
|
||||
ASSERT_LESS(scriptRunStart, base::asserted_cast<int32_t>(scriptRunEnd), ());
|
||||
|
||||
// TODO(AB): May need to break on different unicode blocks, parentheses, and control chars (spaces).
|
||||
|
||||
// TODO(AB): Support vertical layouts if necessary.
|
||||
segments.m_segments.emplace_back(scriptRunStart, scriptRunEnd - scriptRunStart, ICUScriptToHarfbuzzScript(script),
|
||||
bidiLevel & 0x01 ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
|
||||
|
||||
// Move to the next script sequence.
|
||||
scriptRunStart = static_cast<int32_t>(scriptRunEnd);
|
||||
}
|
||||
// Move to the next direction sequence.
|
||||
bidiRunStart = bidiRunEnd;
|
||||
}
|
||||
}
|
||||
|
||||
void ReorderRTL(TextSegments & segments)
|
||||
{
|
||||
// TODO(AB): Optimize implementation to use indexes to segments instead of copying them.
|
||||
auto it = segments.m_segments.begin();
|
||||
auto const end = segments.m_segments.end();
|
||||
// TODO(AB): Line (default rendering) direction is determined by the first segment. It should be defined as
|
||||
// a parameter depending on the language.
|
||||
auto const lineDirection = it->m_direction;
|
||||
while (it != end)
|
||||
{
|
||||
if (it->m_direction == lineDirection)
|
||||
++it;
|
||||
else
|
||||
{
|
||||
auto const start = it++;
|
||||
while (it != end && it->m_direction != lineDirection)
|
||||
++it;
|
||||
std::reverse(start, it);
|
||||
}
|
||||
}
|
||||
if (lineDirection != HB_DIRECTION_LTR)
|
||||
std::reverse(segments.m_segments.begin(), end);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TextSegments GetTextSegments(std::string_view utf8)
|
||||
{
|
||||
ASSERT(!utf8.empty(), ("Shaping of empty strings is not supported"));
|
||||
ASSERT(std::string::npos == utf8.find_first_of("\n"), ("Shaping with line breaks is not supported", utf8));
|
||||
|
||||
// TODO(AB): Can unnecessary conversion/allocation be avoided?
|
||||
TextSegments segments{strings::ToUtf16(utf8), {}};
|
||||
// TODO(AB): Runs are not split by breaking chars and by different fonts.
|
||||
GetSingleTextLineRuns(segments);
|
||||
ReorderRTL(segments);
|
||||
return segments;
|
||||
}
|
||||
|
||||
std::string DebugPrint(TextSegment const & segment)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "TextSegment[start=" << segment.m_start << ", length=" << segment.m_length << ", script=" << segment.m_script
|
||||
<< ", direction=" << segment.m_direction << ']';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace harfbuzz_shaping
|
||||
39
libs/drape/harfbuzz_shaping.hpp
Normal file
39
libs/drape/harfbuzz_shaping.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <hb.h>
|
||||
|
||||
namespace harfbuzz_shaping
|
||||
{
|
||||
struct TextSegment
|
||||
{
|
||||
// TODO(AB): Use 1 byte or 2 bytes for memory-efficient caching.
|
||||
int32_t m_start; // Offset to the segment start in the string.
|
||||
int32_t m_length;
|
||||
hb_script_t m_script;
|
||||
hb_direction_t m_direction;
|
||||
|
||||
TextSegment(int32_t start, int32_t length, hb_script_t script, hb_direction_t direction)
|
||||
: m_start(start)
|
||||
, m_length(length)
|
||||
, m_script(script)
|
||||
, m_direction(direction)
|
||||
{}
|
||||
};
|
||||
|
||||
struct TextSegments
|
||||
{
|
||||
std::u16string m_text;
|
||||
std::vector<TextSegment> m_segments;
|
||||
// TODO(AB): Reverse indexes to order segments instead of moving them.
|
||||
// std::vector<size_t> m_segmentsOrder;
|
||||
};
|
||||
|
||||
// Finds segments with common properties suitable for harfbuzz in a single line of text without newlines.
|
||||
// Any line breaking/trimming should be done by the caller.
|
||||
TextSegments GetTextSegments(std::string_view utf8);
|
||||
|
||||
std::string DebugPrint(TextSegment const & segment);
|
||||
} // namespace harfbuzz_shaping
|
||||
303
libs/drape/hw_texture.cpp
Normal file
303
libs/drape/hw_texture.cpp
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
#include "drape/hw_texture.hpp"
|
||||
|
||||
#include "drape/drape_global.hpp"
|
||||
#include "drape/gl_functions.hpp"
|
||||
#include "drape/utils/gpu_mem_tracker.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#if defined(OMIM_OS_IPHONE)
|
||||
#include "drape/hw_texture_ios.hpp"
|
||||
#endif
|
||||
|
||||
#if defined(OMIM_METAL_AVAILABLE)
|
||||
extern drape_ptr<dp::HWTextureAllocator> CreateMetalAllocator();
|
||||
extern ref_ptr<dp::HWTextureAllocator> GetDefaultMetalAllocator();
|
||||
#endif
|
||||
|
||||
extern drape_ptr<dp::HWTextureAllocator> CreateVulkanAllocator();
|
||||
extern ref_ptr<dp::HWTextureAllocator> GetDefaultVulkanAllocator();
|
||||
|
||||
namespace dp
|
||||
{
|
||||
std::string DebugPrint(HWTexture::Params const & p)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Width = " << p.m_width << "; Height = " << p.m_height << "; Format = " << DebugPrint(p.m_format)
|
||||
<< "; IsRenderTarget = " << p.m_isRenderTarget;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void UnpackFormat(ref_ptr<dp::GraphicsContext> context, TextureFormat format, glConst & layout, glConst & pixelType)
|
||||
{
|
||||
auto const apiVersion = context->GetApiVersion();
|
||||
CHECK(apiVersion == dp::ApiVersion::OpenGLES3, ());
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case TextureFormat::RGBA8:
|
||||
layout = gl_const::GLRGBA;
|
||||
pixelType = gl_const::GL8BitOnChannel;
|
||||
return;
|
||||
|
||||
case TextureFormat::Red:
|
||||
layout = gl_const::GLRed;
|
||||
pixelType = gl_const::GL8BitOnChannel;
|
||||
return;
|
||||
|
||||
case TextureFormat::RedGreen:
|
||||
layout = gl_const::GLRedGreen;
|
||||
pixelType = gl_const::GL8BitOnChannel;
|
||||
return;
|
||||
|
||||
case TextureFormat::DepthStencil:
|
||||
layout = gl_const::GLDepthStencil;
|
||||
pixelType = gl_const::GLUnsignedInt24_8Type;
|
||||
return;
|
||||
|
||||
case TextureFormat::Depth:
|
||||
layout = gl_const::GLDepthComponent;
|
||||
pixelType = gl_const::GLUnsignedIntType;
|
||||
return;
|
||||
|
||||
case TextureFormat::Unspecified: CHECK(false, ()); return;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
glConst DecodeTextureFilter(TextureFilter filter)
|
||||
{
|
||||
switch (filter)
|
||||
{
|
||||
case TextureFilter::Linear: return gl_const::GLLinear;
|
||||
case TextureFilter::Nearest: return gl_const::GLNearest;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
glConst DecodeTextureWrapping(TextureWrapping wrapping)
|
||||
{
|
||||
switch (wrapping)
|
||||
{
|
||||
case TextureWrapping::ClampToEdge: return gl_const::GLClampToEdge;
|
||||
case TextureWrapping::Repeat: return gl_const::GLRepeat;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
HWTexture::~HWTexture()
|
||||
{
|
||||
#if defined(TRACK_GPU_MEM)
|
||||
dp::GPUMemTracker::Inst().RemoveDeallocated("Texture", m_textureID);
|
||||
dp::GPUMemTracker::Inst().RemoveDeallocated("PBO", m_pixelBufferID);
|
||||
#endif
|
||||
}
|
||||
|
||||
void HWTexture::Create(ref_ptr<dp::GraphicsContext> context, Params const & params)
|
||||
{
|
||||
Create(context, params, nullptr);
|
||||
}
|
||||
|
||||
void HWTexture::Create(ref_ptr<dp::GraphicsContext>, Params const & params, ref_ptr<void>)
|
||||
{
|
||||
m_params = params;
|
||||
|
||||
#if defined(TRACK_GPU_MEM)
|
||||
uint32_t const memSize = (CHAR_BIT * bytesPerPixel * m_params.m_width * m_params.m_height) >> 3;
|
||||
dp::GPUMemTracker::Inst().AddAllocated("Texture", m_textureID, memSize);
|
||||
dp::GPUMemTracker::Inst().SetUsed("Texture", m_textureID, memSize);
|
||||
if (params.m_usePixelBuffer)
|
||||
{
|
||||
dp::GPUMemTracker::Inst().AddAllocated("PBO", m_pixelBufferID, m_pixelBufferSize);
|
||||
dp::GPUMemTracker::Inst().SetUsed("PBO", m_pixelBufferID, m_pixelBufferSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TextureFormat HWTexture::GetFormat() const
|
||||
{
|
||||
return m_params.m_format;
|
||||
}
|
||||
|
||||
uint32_t HWTexture::GetWidth() const
|
||||
{
|
||||
ASSERT(Validate(), ());
|
||||
return m_params.m_width;
|
||||
}
|
||||
|
||||
uint32_t HWTexture::GetHeight() const
|
||||
{
|
||||
ASSERT(Validate(), ());
|
||||
return m_params.m_height;
|
||||
}
|
||||
|
||||
float HWTexture::GetS(uint32_t x) const
|
||||
{
|
||||
ASSERT(Validate(), ());
|
||||
return x / static_cast<float>(m_params.m_width);
|
||||
}
|
||||
|
||||
float HWTexture::GetT(uint32_t y) const
|
||||
{
|
||||
ASSERT(Validate(), ());
|
||||
return y / static_cast<float>(m_params.m_height);
|
||||
}
|
||||
|
||||
uint32_t HWTexture::GetID() const
|
||||
{
|
||||
return m_textureID;
|
||||
}
|
||||
|
||||
OpenGLHWTexture::~OpenGLHWTexture()
|
||||
{
|
||||
if (m_textureID != 0)
|
||||
GLFunctions::glDeleteTexture(m_textureID);
|
||||
|
||||
if (m_pixelBufferID != 0)
|
||||
GLFunctions::glDeleteBuffer(m_pixelBufferID);
|
||||
}
|
||||
|
||||
void OpenGLHWTexture::Create(ref_ptr<dp::GraphicsContext> context, Params const & params, ref_ptr<void> data)
|
||||
{
|
||||
Base::Create(context, params, data);
|
||||
|
||||
CHECK(context && m_textureID == 0 && m_pixelBufferID == 0, ());
|
||||
|
||||
// OpenGL ES 2.0 does not support PBO.
|
||||
if (params.m_usePixelBuffer && context->GetApiVersion() == dp::ApiVersion::OpenGLES3)
|
||||
{
|
||||
m_pixelBufferElementSize = GetBytesPerPixel(m_params.m_format);
|
||||
if (m_pixelBufferElementSize > 0)
|
||||
{
|
||||
// Buffer size = 10%
|
||||
m_pixelBufferSize = static_cast<uint32_t>(0.1 * m_params.m_width * m_params.m_height * m_pixelBufferElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
m_textureID = GLFunctions::glGenTexture();
|
||||
Bind(context);
|
||||
|
||||
UnpackFormat(context, m_params.m_format, m_unpackedLayout, m_unpackedPixelType);
|
||||
|
||||
auto const f = DecodeTextureFilter(m_params.m_filter);
|
||||
GLFunctions::glTexImage2D(m_params.m_width, m_params.m_height, m_unpackedLayout, m_unpackedPixelType, data.get());
|
||||
GLFunctions::glTexParameter(gl_const::GLMinFilter, f);
|
||||
GLFunctions::glTexParameter(gl_const::GLMagFilter, f);
|
||||
GLFunctions::glTexParameter(gl_const::GLWrapS, DecodeTextureWrapping(m_params.m_wrapSMode));
|
||||
GLFunctions::glTexParameter(gl_const::GLWrapT, DecodeTextureWrapping(m_params.m_wrapTMode));
|
||||
|
||||
if (m_pixelBufferSize > 0)
|
||||
{
|
||||
m_pixelBufferID = GLFunctions::glGenBuffer();
|
||||
GLFunctions::glBindBuffer(m_pixelBufferID, gl_const::GLPixelBufferWrite);
|
||||
GLFunctions::glBufferData(gl_const::GLPixelBufferWrite, m_pixelBufferSize, nullptr, gl_const::GLDynamicDraw);
|
||||
GLFunctions::glBindBuffer(0, gl_const::GLPixelBufferWrite);
|
||||
}
|
||||
|
||||
GLFunctions::glBindTexture(0);
|
||||
GLFunctions::glFlush();
|
||||
}
|
||||
|
||||
void OpenGLHWTexture::UploadData(ref_ptr<dp::GraphicsContext> context, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, ref_ptr<void> data)
|
||||
{
|
||||
ASSERT(Validate(), ());
|
||||
uint32_t const mappingSize = height * width * m_pixelBufferElementSize;
|
||||
if (m_pixelBufferID != 0 && m_pixelBufferSize >= mappingSize)
|
||||
{
|
||||
ASSERT_GREATER(mappingSize, 0, ());
|
||||
GLFunctions::glBindBuffer(m_pixelBufferID, gl_const::GLPixelBufferWrite);
|
||||
GLFunctions::glBufferSubData(gl_const::GLPixelBufferWrite, mappingSize, data.get(), 0);
|
||||
GLFunctions::glTexSubImage2D(x, y, width, height, m_unpackedLayout, m_unpackedPixelType, nullptr);
|
||||
GLFunctions::glBindBuffer(0, gl_const::GLPixelBufferWrite);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLFunctions::glTexSubImage2D(x, y, width, height, m_unpackedLayout, m_unpackedPixelType, data.get());
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLHWTexture::Bind(ref_ptr<dp::GraphicsContext> context) const
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
ASSERT(Validate(), ());
|
||||
if (m_textureID != 0)
|
||||
GLFunctions::glBindTexture(GetID());
|
||||
}
|
||||
|
||||
void OpenGLHWTexture::SetFilter(TextureFilter filter)
|
||||
{
|
||||
ASSERT(Validate(), ());
|
||||
if (m_params.m_filter != filter)
|
||||
{
|
||||
m_params.m_filter = filter;
|
||||
auto const f = DecodeTextureFilter(m_params.m_filter);
|
||||
GLFunctions::glTexParameter(gl_const::GLMinFilter, f);
|
||||
GLFunctions::glTexParameter(gl_const::GLMagFilter, f);
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenGLHWTexture::Validate() const
|
||||
{
|
||||
return GetID() != 0;
|
||||
}
|
||||
|
||||
drape_ptr<HWTexture> OpenGLHWTextureAllocator::CreateTexture(ref_ptr<dp::GraphicsContext> context)
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
return make_unique_dp<OpenGLHWTexture>();
|
||||
}
|
||||
|
||||
void OpenGLHWTextureAllocator::Flush()
|
||||
{
|
||||
GLFunctions::glFlush();
|
||||
}
|
||||
|
||||
drape_ptr<HWTextureAllocator> CreateAllocator(ref_ptr<dp::GraphicsContext> context)
|
||||
{
|
||||
CHECK(context != nullptr, ());
|
||||
auto const apiVersion = context->GetApiVersion();
|
||||
if (apiVersion == dp::ApiVersion::Metal)
|
||||
{
|
||||
#if defined(OMIM_METAL_AVAILABLE)
|
||||
return CreateMetalAllocator();
|
||||
#endif
|
||||
CHECK(false, ("Metal rendering is supported now only on iOS."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (apiVersion == dp::ApiVersion::Vulkan)
|
||||
return CreateVulkanAllocator();
|
||||
|
||||
if (apiVersion == dp::ApiVersion::OpenGLES3)
|
||||
return make_unique_dp<OpenGLHWTextureAllocator>();
|
||||
|
||||
#if defined(OMIM_OS_IPHONE)
|
||||
return make_unique_dp<HWTextureAllocatorApple>();
|
||||
#else
|
||||
return make_unique_dp<OpenGLHWTextureAllocator>();
|
||||
#endif
|
||||
}
|
||||
|
||||
ref_ptr<HWTextureAllocator> GetDefaultAllocator(ref_ptr<dp::GraphicsContext> context)
|
||||
{
|
||||
CHECK(context != nullptr, ());
|
||||
auto const apiVersion = context->GetApiVersion();
|
||||
if (apiVersion == dp::ApiVersion::Metal)
|
||||
{
|
||||
#if defined(OMIM_METAL_AVAILABLE)
|
||||
return GetDefaultMetalAllocator();
|
||||
#endif
|
||||
CHECK(false, ("Metal rendering is supported now only on iOS."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (apiVersion == dp::ApiVersion::Vulkan)
|
||||
return GetDefaultVulkanAllocator();
|
||||
|
||||
static OpenGLHWTextureAllocator s_allocator;
|
||||
return make_ref<HWTextureAllocator>(&s_allocator);
|
||||
}
|
||||
} // namespace dp
|
||||
108
libs/drape/hw_texture.hpp
Normal file
108
libs/drape/hw_texture.hpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/gl_constants.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/texture_types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class HWTextureAllocator;
|
||||
|
||||
class HWTexture
|
||||
{
|
||||
public:
|
||||
virtual ~HWTexture();
|
||||
|
||||
struct Params
|
||||
{
|
||||
uint32_t m_width = 0;
|
||||
uint32_t m_height = 0;
|
||||
TextureFilter m_filter = TextureFilter::Linear;
|
||||
TextureWrapping m_wrapSMode = TextureWrapping::ClampToEdge;
|
||||
TextureWrapping m_wrapTMode = TextureWrapping::ClampToEdge;
|
||||
TextureFormat m_format = TextureFormat::Unspecified;
|
||||
bool m_usePixelBuffer = false;
|
||||
bool m_isRenderTarget = false;
|
||||
bool m_isMutable = false;
|
||||
|
||||
ref_ptr<HWTextureAllocator> m_allocator;
|
||||
|
||||
friend std::string DebugPrint(Params const & p);
|
||||
};
|
||||
|
||||
void Create(ref_ptr<dp::GraphicsContext> context, Params const & params);
|
||||
|
||||
virtual void Create(ref_ptr<dp::GraphicsContext> context, Params const & params, ref_ptr<void> data) = 0;
|
||||
virtual void UploadData(ref_ptr<dp::GraphicsContext> context, uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
ref_ptr<void> data) = 0;
|
||||
|
||||
virtual void Bind(ref_ptr<dp::GraphicsContext> context) const = 0;
|
||||
|
||||
// For OpenGL the texture must be bound before calling this method.
|
||||
virtual void SetFilter(TextureFilter filter) = 0;
|
||||
|
||||
virtual bool Validate() const = 0;
|
||||
|
||||
TextureFormat GetFormat() const;
|
||||
uint32_t GetWidth() const;
|
||||
uint32_t GetHeight() const;
|
||||
float GetS(uint32_t x) const;
|
||||
float GetT(uint32_t y) const;
|
||||
|
||||
Params const & GetParams() const { return m_params; }
|
||||
|
||||
uint32_t GetID() const;
|
||||
|
||||
protected:
|
||||
Params m_params;
|
||||
uint32_t m_textureID = 0;
|
||||
};
|
||||
|
||||
class HWTextureAllocator
|
||||
{
|
||||
public:
|
||||
virtual ~HWTextureAllocator() = default;
|
||||
|
||||
virtual drape_ptr<HWTexture> CreateTexture(ref_ptr<dp::GraphicsContext> context) = 0;
|
||||
virtual void Flush() = 0;
|
||||
};
|
||||
|
||||
class OpenGLHWTexture : public HWTexture
|
||||
{
|
||||
using Base = HWTexture;
|
||||
|
||||
public:
|
||||
~OpenGLHWTexture() override;
|
||||
void Create(ref_ptr<dp::GraphicsContext> context, Params const & params, ref_ptr<void> data) override;
|
||||
void UploadData(ref_ptr<dp::GraphicsContext> context, uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
ref_ptr<void> data) override;
|
||||
void Bind(ref_ptr<dp::GraphicsContext> context) const override;
|
||||
void SetFilter(TextureFilter filter) override;
|
||||
bool Validate() const override;
|
||||
|
||||
private:
|
||||
uint32_t m_pixelBufferID = 0;
|
||||
uint32_t m_pixelBufferSize = 0;
|
||||
uint8_t m_pixelBufferElementSize = 0;
|
||||
|
||||
glConst m_unpackedLayout = 0;
|
||||
glConst m_unpackedPixelType = 0;
|
||||
};
|
||||
|
||||
class OpenGLHWTextureAllocator : public HWTextureAllocator
|
||||
{
|
||||
public:
|
||||
drape_ptr<HWTexture> CreateTexture(ref_ptr<dp::GraphicsContext> context) override;
|
||||
void Flush() override;
|
||||
};
|
||||
|
||||
ref_ptr<HWTextureAllocator> GetDefaultAllocator(ref_ptr<dp::GraphicsContext> context);
|
||||
drape_ptr<HWTextureAllocator> CreateAllocator(ref_ptr<dp::GraphicsContext> context);
|
||||
|
||||
void UnpackFormat(ref_ptr<dp::GraphicsContext> context, TextureFormat format, glConst & layout, glConst & pixelType);
|
||||
glConst DecodeTextureFilter(TextureFilter filter);
|
||||
glConst DecodeTextureWrapping(TextureWrapping wrapping);
|
||||
} // namespace dp
|
||||
66
libs/drape/hw_texture_ios.hpp
Normal file
66
libs/drape/hw_texture_ios.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/gl_constants.hpp"
|
||||
#include "drape/hw_texture.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#ifndef OMIM_OS_IPHONE
|
||||
#error Only for iOS
|
||||
#endif
|
||||
|
||||
#import <CoreVideo/CVOpenGLESTexture.h>
|
||||
#import <CoreVideo/CVOpenGLESTextureCache.h>
|
||||
#import <CoreVideo/CVPixelBuffer.h>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class HWTextureAllocatorApple : public HWTextureAllocator
|
||||
{
|
||||
public:
|
||||
HWTextureAllocatorApple();
|
||||
~HWTextureAllocatorApple();
|
||||
|
||||
CVPixelBufferRef CVCreatePixelBuffer(uint32_t width, uint32_t height, dp::TextureFormat format);
|
||||
void CVDestroyPixelBuffer(CVPixelBufferRef buffer);
|
||||
|
||||
CVOpenGLESTextureRef CVCreateTexture(CVPixelBufferRef buffer, uint32_t width, uint32_t height, glConst layout,
|
||||
glConst pixelType);
|
||||
void CVDestroyTexture(CVOpenGLESTextureRef texture);
|
||||
|
||||
void RiseFlushFlag();
|
||||
|
||||
drape_ptr<HWTexture> CreateTexture(ref_ptr<dp::GraphicsContext> context) override;
|
||||
void Flush() override;
|
||||
|
||||
private:
|
||||
bool m_needFlush;
|
||||
CVOpenGLESTextureCacheRef m_textureCache;
|
||||
};
|
||||
|
||||
class HWTextureApple : public HWTexture
|
||||
{
|
||||
using TBase = HWTexture;
|
||||
|
||||
public:
|
||||
explicit HWTextureApple(ref_ptr<HWTextureAllocatorApple> allocator);
|
||||
~HWTextureApple();
|
||||
|
||||
void Create(ref_ptr<dp::GraphicsContext> context, Params const & params, ref_ptr<void> data) override;
|
||||
void UploadData(ref_ptr<dp::GraphicsContext> context, uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
ref_ptr<void> data) override;
|
||||
void Bind(ref_ptr<dp::GraphicsContext> context) const override;
|
||||
void SetFilter(TextureFilter filter) override;
|
||||
bool Validate() const override;
|
||||
|
||||
private:
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
CVPixelBufferRef m_directBuffer;
|
||||
CVOpenGLESTextureRef m_texture;
|
||||
ref_ptr<HWTextureAllocatorApple> m_allocator;
|
||||
|
||||
void * m_directPointer;
|
||||
};
|
||||
} // namespace dp
|
||||
236
libs/drape/hw_texture_ios.mm
Normal file
236
libs/drape/hw_texture_ios.mm
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
#include "drape/hw_texture_ios.hpp"
|
||||
#include "drape/gl_functions.hpp"
|
||||
#include "drape/gl_includes.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#import <QuartzCore/CAEAGLLayer.h>
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#include <boost/integer_traits.hpp>
|
||||
|
||||
#include <boost/gil/algorithm.hpp>
|
||||
#include <boost/gil/typedefs.hpp>
|
||||
|
||||
using boost::gil::gray8c_pixel_t;
|
||||
using boost::gil::gray8_pixel_t;
|
||||
using boost::gil::gray8c_view_t;
|
||||
using boost::gil::gray8_view_t;
|
||||
using boost::gil::rgba8c_pixel_t;
|
||||
using boost::gil::rgba8_pixel_t;
|
||||
using boost::gil::rgba8c_view_t;
|
||||
using boost::gil::rgba8_view_t;
|
||||
using boost::gil::interleaved_view;
|
||||
using boost::gil::subimage_view;
|
||||
using boost::gil::copy_pixels;
|
||||
|
||||
namespace dp
|
||||
{
|
||||
HWTextureAllocatorApple::HWTextureAllocatorApple()
|
||||
{
|
||||
CVReturn cvRetval = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, nullptr,
|
||||
[EAGLContext currentContext],
|
||||
nullptr, &m_textureCache);
|
||||
|
||||
CHECK_EQUAL(cvRetval, kCVReturnSuccess, ());
|
||||
}
|
||||
|
||||
HWTextureAllocatorApple::~HWTextureAllocatorApple()
|
||||
{
|
||||
CFRelease(m_textureCache);
|
||||
}
|
||||
|
||||
CVPixelBufferRef HWTextureAllocatorApple::CVCreatePixelBuffer(uint32_t width, uint32_t height,
|
||||
dp::TextureFormat format)
|
||||
{
|
||||
NSDictionary * attrs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSDictionary dictionary], kCVPixelBufferIOSurfacePropertiesKey,
|
||||
[NSNumber numberWithInt:16], kCVPixelBufferBytesPerRowAlignmentKey,
|
||||
[NSNumber numberWithBool:YES], kCVPixelBufferOpenGLESCompatibilityKey,
|
||||
nil];
|
||||
|
||||
CFDictionaryRef attrsRef = (__bridge CFDictionaryRef)attrs;
|
||||
|
||||
CVPixelBufferRef result = nullptr;
|
||||
CVReturn cvRetval = 0;
|
||||
switch (format)
|
||||
{
|
||||
case dp::TextureFormat::RGBA8:
|
||||
cvRetval = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA,
|
||||
attrsRef, &result);
|
||||
break;
|
||||
case dp::TextureFormat::Red:
|
||||
cvRetval = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_OneComponent8,
|
||||
attrsRef, &result);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false, ());
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK_EQUAL(cvRetval, kCVReturnSuccess, ());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void HWTextureAllocatorApple::CVDestroyPixelBuffer(CVPixelBufferRef buffer)
|
||||
{
|
||||
CFRelease(buffer);
|
||||
}
|
||||
|
||||
CVOpenGLESTextureRef HWTextureAllocatorApple::CVCreateTexture(CVPixelBufferRef buffer, uint32_t width, uint32_t height,
|
||||
glConst layout, glConst pixelType)
|
||||
{
|
||||
CVOpenGLESTextureRef texture;
|
||||
CVReturn cvRetval = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, m_textureCache, buffer,
|
||||
nullptr, gl_const::GLTexture2D, layout,
|
||||
width, height, layout, pixelType, 0, &texture);
|
||||
|
||||
CHECK_EQUAL(cvRetval, kCVReturnSuccess, ());
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
void HWTextureAllocatorApple::CVDestroyTexture(CVOpenGLESTextureRef texture)
|
||||
{
|
||||
CFRelease(texture);
|
||||
}
|
||||
|
||||
void HWTextureAllocatorApple::RiseFlushFlag()
|
||||
{
|
||||
m_needFlush = true;
|
||||
}
|
||||
|
||||
drape_ptr<HWTexture> HWTextureAllocatorApple::CreateTexture(ref_ptr<dp::GraphicsContext> context)
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
return make_unique_dp<HWTextureApple>(make_ref(this));
|
||||
}
|
||||
|
||||
void HWTextureAllocatorApple::Flush()
|
||||
{
|
||||
if (m_needFlush)
|
||||
{
|
||||
CVOpenGLESTextureCacheFlush(m_textureCache, 0);
|
||||
m_needFlush = false;
|
||||
}
|
||||
}
|
||||
|
||||
HWTextureApple::HWTextureApple(ref_ptr<HWTextureAllocatorApple> allocator)
|
||||
: m_directBuffer(nullptr)
|
||||
, m_texture(nullptr)
|
||||
, m_allocator(nullptr)
|
||||
, m_directPointer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
HWTextureApple::~HWTextureApple()
|
||||
{
|
||||
if (m_allocator != nullptr)
|
||||
{
|
||||
m_allocator->CVDestroyTexture(m_texture);
|
||||
m_allocator->CVDestroyPixelBuffer(m_directBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void HWTextureApple::Create(ref_ptr<dp::GraphicsContext> context, Params const & params, ref_ptr<void> data)
|
||||
{
|
||||
TBase::Create(context, params, data);
|
||||
|
||||
m_allocator = m_params.m_allocator;
|
||||
m_directBuffer = m_allocator->CVCreatePixelBuffer(m_params.m_width, m_params.m_height, params.m_format);
|
||||
|
||||
glConst layout, pixelType;
|
||||
UnpackFormat(context, params.m_format, layout, pixelType);
|
||||
m_texture = m_allocator->CVCreateTexture(m_directBuffer, params.m_width, params.m_height,
|
||||
layout, pixelType);
|
||||
|
||||
m_textureID = CVOpenGLESTextureGetName(m_texture);
|
||||
GLFunctions::glBindTexture(m_textureID);
|
||||
auto const f = DecodeTextureFilter(m_params.m_filter);
|
||||
GLFunctions::glTexParameter(gl_const::GLMinFilter, f);
|
||||
GLFunctions::glTexParameter(gl_const::GLMagFilter, f);
|
||||
GLFunctions::glTexParameter(gl_const::GLWrapS, DecodeTextureWrapping(m_params.m_wrapSMode));
|
||||
GLFunctions::glTexParameter(gl_const::GLWrapT, DecodeTextureWrapping(m_params.m_wrapTMode));
|
||||
|
||||
if (data == nullptr)
|
||||
return;
|
||||
|
||||
Lock();
|
||||
memcpy(m_directPointer, data.get(), m_params.m_width * m_params.m_height * GetBytesPerPixel(m_params.m_format));
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void HWTextureApple::UploadData(ref_ptr<dp::GraphicsContext> context, uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height, ref_ptr<void> data)
|
||||
{
|
||||
uint8_t bytesPerPixel = GetBytesPerPixel(GetFormat());
|
||||
Lock();
|
||||
if (bytesPerPixel == 1)
|
||||
{
|
||||
gray8c_view_t srcView = interleaved_view(width, height, (gray8c_pixel_t *)data.get(), width);
|
||||
gray8_view_t dstView = interleaved_view(m_params.m_width, m_params.m_height,
|
||||
(gray8_pixel_t *)m_directPointer, m_params.m_width);
|
||||
gray8_view_t subDstView = subimage_view(dstView, x, y, width, height);
|
||||
copy_pixels(srcView, subDstView);
|
||||
}
|
||||
else if (bytesPerPixel == 4)
|
||||
{
|
||||
rgba8c_view_t srcView = interleaved_view(width, height,
|
||||
(rgba8c_pixel_t *)data.get(),
|
||||
width * bytesPerPixel);
|
||||
rgba8_view_t dstView = interleaved_view(m_params.m_width, m_params.m_height,
|
||||
(rgba8_pixel_t *)m_directPointer,
|
||||
m_params.m_width * bytesPerPixel);
|
||||
rgba8_view_t subDstView = subimage_view(dstView, x, y, width, height);
|
||||
copy_pixels(srcView, subDstView);
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void HWTextureApple::Bind(ref_ptr<dp::GraphicsContext> context) const
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
ASSERT(Validate(), ());
|
||||
if (m_textureID != 0)
|
||||
GLFunctions::glBindTexture(GetID());
|
||||
}
|
||||
|
||||
void HWTextureApple::SetFilter(TextureFilter filter)
|
||||
{
|
||||
ASSERT(Validate(), ());
|
||||
if (m_params.m_filter != filter)
|
||||
{
|
||||
m_params.m_filter = filter;
|
||||
auto const f = DecodeTextureFilter(m_params.m_filter);
|
||||
GLFunctions::glTexParameter(gl_const::GLMinFilter, f);
|
||||
GLFunctions::glTexParameter(gl_const::GLMagFilter, f);
|
||||
}
|
||||
}
|
||||
|
||||
bool HWTextureApple::Validate() const
|
||||
{
|
||||
return GetID() != 0;
|
||||
}
|
||||
|
||||
void HWTextureApple::Lock()
|
||||
{
|
||||
ASSERT(m_directPointer == nullptr, ());
|
||||
|
||||
CHECK_EQUAL(CVPixelBufferLockBaseAddress(m_directBuffer, 0), kCVReturnSuccess, ());
|
||||
|
||||
ASSERT_EQUAL(CVPixelBufferGetBytesPerRow(m_directBuffer),
|
||||
m_params.m_width * GetBytesPerPixel(m_params.m_format), ());
|
||||
m_directPointer = CVPixelBufferGetBaseAddress(m_directBuffer);
|
||||
}
|
||||
|
||||
void HWTextureApple::Unlock()
|
||||
{
|
||||
ASSERT(m_directPointer != nullptr, ());
|
||||
m_directPointer = nullptr;
|
||||
CHECK_EQUAL(CVPixelBufferUnlockBaseAddress(m_directBuffer, 0), kCVReturnSuccess, ());
|
||||
m_allocator->RiseFlushFlag();
|
||||
}
|
||||
} // namespace dp
|
||||
23
libs/drape/index_buffer.cpp
Normal file
23
libs/drape/index_buffer.cpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#include "drape/index_buffer.hpp"
|
||||
#include "drape/index_storage.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
IndexBuffer::IndexBuffer(uint32_t capacity) : DataBuffer((uint8_t)IndexStorage::SizeOfIndex(), capacity) {}
|
||||
|
||||
void IndexBuffer::UploadData(ref_ptr<GraphicsContext> context, void const * data, uint32_t size)
|
||||
{
|
||||
GetBuffer()->UploadData(context, data, size);
|
||||
}
|
||||
|
||||
void IndexBuffer::UpdateData(ref_ptr<GraphicsContext> context, void const * data, uint32_t size)
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(size, GetBuffer()->GetCapacity(), ());
|
||||
|
||||
GetBuffer()->Seek(0);
|
||||
if (size > 0)
|
||||
UploadData(context, data, size);
|
||||
}
|
||||
} // namespace dp
|
||||
17
libs/drape/index_buffer.hpp
Normal file
17
libs/drape/index_buffer.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/data_buffer.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class IndexBuffer : public DataBuffer
|
||||
{
|
||||
public:
|
||||
explicit IndexBuffer(uint32_t capacity);
|
||||
|
||||
// Check size of buffer and size of uploaded data.
|
||||
void UploadData(ref_ptr<GraphicsContext> context, void const * data, uint32_t size);
|
||||
// Resize buffer to new size, and discard old data.
|
||||
void UpdateData(ref_ptr<GraphicsContext> context, void const * data, uint32_t size);
|
||||
};
|
||||
} // namespace dp
|
||||
38
libs/drape/index_buffer_mutator.cpp
Normal file
38
libs/drape/index_buffer_mutator.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include "drape/index_buffer_mutator.hpp"
|
||||
#include "drape/vertex_array_buffer.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
IndexBufferMutator::IndexBufferMutator(uint32_t baseSize)
|
||||
{
|
||||
m_buffer.Resize(baseSize);
|
||||
}
|
||||
|
||||
void IndexBufferMutator::AppendIndexes(void const * indexes, uint32_t count)
|
||||
{
|
||||
uint32_t dstActiveSize = m_activeSize + count;
|
||||
if (dstActiveSize > m_buffer.Size())
|
||||
m_buffer.Resize(std::max(m_buffer.Size() * 2, dstActiveSize));
|
||||
|
||||
memcpy(m_buffer.GetRaw(m_activeSize), indexes, count * IndexStorage::SizeOfIndex());
|
||||
m_activeSize = dstActiveSize;
|
||||
}
|
||||
|
||||
void const * IndexBufferMutator::GetIndexes() const
|
||||
{
|
||||
return m_buffer.GetRawConst();
|
||||
}
|
||||
|
||||
uint32_t IndexBufferMutator::GetIndexCount() const
|
||||
{
|
||||
return m_activeSize;
|
||||
}
|
||||
|
||||
uint32_t IndexBufferMutator::GetCapacity() const
|
||||
{
|
||||
return m_buffer.Size();
|
||||
}
|
||||
} // namespace dp
|
||||
30
libs/drape/index_buffer_mutator.hpp
Normal file
30
libs/drape/index_buffer_mutator.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/index_storage.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class VertexArrayBuffer;
|
||||
|
||||
class IndexBufferMutator
|
||||
{
|
||||
public:
|
||||
explicit IndexBufferMutator(uint32_t baseSize);
|
||||
|
||||
void AppendIndexes(void const * indexes, uint32_t count);
|
||||
|
||||
uint32_t GetCapacity() const;
|
||||
|
||||
private:
|
||||
friend class VertexArrayBuffer;
|
||||
void const * GetIndexes() const;
|
||||
uint32_t GetIndexCount() const;
|
||||
|
||||
private:
|
||||
IndexStorage m_buffer;
|
||||
uint32_t m_activeSize = 0;
|
||||
};
|
||||
} // namespace dp
|
||||
70
libs/drape/index_storage.cpp
Normal file
70
libs/drape/index_storage.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include "drape/index_storage.hpp"
|
||||
#include "drape/gl_functions.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
IndexStorage::IndexStorage() : m_size(0) {}
|
||||
|
||||
IndexStorage::IndexStorage(std::vector<uint32_t> && initial)
|
||||
{
|
||||
m_size = (uint32_t)initial.size();
|
||||
if (IsSupported32bit())
|
||||
{
|
||||
m_storage = std::move(initial);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We pack 2 uint16_t indices into single m_storage element.
|
||||
// Every element of "initial" vector is a single index.
|
||||
m_storage.resize(GetStorageSize(m_size));
|
||||
for (size_t i = 0; i < initial.size(); i++)
|
||||
{
|
||||
uint16_t * ptr = reinterpret_cast<uint16_t *>(m_storage.data()) + i;
|
||||
*ptr = (uint16_t)initial[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IndexStorage::Resize(uint32_t size)
|
||||
{
|
||||
m_size = size;
|
||||
m_storage.resize(GetStorageSize(m_size));
|
||||
}
|
||||
|
||||
uint32_t IndexStorage::Size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void * IndexStorage::GetRaw(uint32_t offsetInElements)
|
||||
{
|
||||
if (IsSupported32bit())
|
||||
return &m_storage[offsetInElements];
|
||||
|
||||
return reinterpret_cast<uint16_t *>(m_storage.data()) + offsetInElements;
|
||||
}
|
||||
|
||||
void const * IndexStorage::GetRawConst() const
|
||||
{
|
||||
return static_cast<void const *>(m_storage.data());
|
||||
}
|
||||
|
||||
bool IndexStorage::IsSupported32bit()
|
||||
{
|
||||
// We do not use 32-bit indices now to reduce size of index buffers.
|
||||
static bool const supports32Bit = false;
|
||||
return supports32Bit;
|
||||
}
|
||||
|
||||
uint32_t IndexStorage::SizeOfIndex()
|
||||
{
|
||||
return IsSupported32bit() ? sizeof(uint32_t) : sizeof(uint16_t);
|
||||
}
|
||||
|
||||
uint32_t IndexStorage::GetStorageSize(uint32_t elementsCount) const
|
||||
{
|
||||
return IsSupported32bit() ? elementsCount : (elementsCount / 2 + 1);
|
||||
}
|
||||
} // namespace dp
|
||||
29
libs/drape/index_storage.hpp
Normal file
29
libs/drape/index_storage.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class IndexStorage
|
||||
{
|
||||
public:
|
||||
IndexStorage();
|
||||
explicit IndexStorage(std::vector<uint32_t> && initial);
|
||||
|
||||
uint32_t Size() const;
|
||||
void Resize(uint32_t size);
|
||||
|
||||
void * GetRaw(uint32_t offsetInElements = 0);
|
||||
void const * GetRawConst() const;
|
||||
|
||||
static bool IsSupported32bit();
|
||||
static uint32_t SizeOfIndex();
|
||||
|
||||
private:
|
||||
std::vector<uint32_t> m_storage;
|
||||
uint32_t m_size;
|
||||
|
||||
uint32_t GetStorageSize(uint32_t elementsCount) const;
|
||||
};
|
||||
} // namespace dp
|
||||
327
libs/drape/mesh_object.cpp
Normal file
327
libs/drape/mesh_object.cpp
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
#include "drape/mesh_object.hpp"
|
||||
|
||||
#include "drape/gl_constants.hpp"
|
||||
#include "drape/gl_functions.hpp"
|
||||
#include "drape/gl_gpu_program.hpp"
|
||||
#include "drape/glsl_func.hpp"
|
||||
#include "drape/glsl_types.hpp"
|
||||
#include "drape/texture_manager.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
glConst GetGLDrawPrimitive(dp::MeshObject::DrawPrimitive drawPrimitive)
|
||||
{
|
||||
switch (drawPrimitive)
|
||||
{
|
||||
case dp::MeshObject::DrawPrimitive::Triangles: return gl_const::GLTriangles;
|
||||
case dp::MeshObject::DrawPrimitive::TriangleStrip: return gl_const::GLTriangleStrip;
|
||||
case dp::MeshObject::DrawPrimitive::LineStrip: return gl_const::GLLineStrip;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GLMeshObjectImpl : public MeshObjectImpl
|
||||
{
|
||||
public:
|
||||
explicit GLMeshObjectImpl(ref_ptr<dp::MeshObject> mesh) : m_mesh(mesh) {}
|
||||
|
||||
void Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
|
||||
m_VAO = GLFunctions::glGenVertexArray();
|
||||
GLFunctions::glBindVertexArray(m_VAO);
|
||||
|
||||
for (auto & buffer : m_mesh->m_buffers)
|
||||
{
|
||||
buffer->m_bufferId = GLFunctions::glGenBuffer();
|
||||
GLFunctions::glBindBuffer(buffer->m_bufferId, gl_const::GLArrayBuffer);
|
||||
|
||||
if (buffer->GetSizeInBytes() != 0)
|
||||
{
|
||||
GLFunctions::glBufferData(gl_const::GLArrayBuffer, buffer->GetSizeInBytes(), buffer->GetData(),
|
||||
gl_const::GLStaticDraw);
|
||||
}
|
||||
|
||||
if (!m_mesh->m_indices.empty())
|
||||
{
|
||||
m_indexBuffer = GLFunctions::glGenBuffer();
|
||||
GLFunctions::glBindBuffer(m_indexBuffer, gl_const::GLElementArrayBuffer);
|
||||
GLFunctions::glBufferData(gl_const::GLElementArrayBuffer,
|
||||
static_cast<uint32_t>(m_mesh->m_indices.size() * sizeof(uint16_t)),
|
||||
m_mesh->m_indices.data(), gl_const::GLStaticDraw);
|
||||
}
|
||||
|
||||
ref_ptr<dp::GLGpuProgram> p = program;
|
||||
for (auto const & attribute : buffer->m_attributes)
|
||||
{
|
||||
int8_t const attributePosition = p->GetAttributeLocation(attribute.m_attributeName);
|
||||
ASSERT_NOT_EQUAL(attributePosition, -1, ());
|
||||
GLFunctions::glEnableVertexAttribute(attributePosition);
|
||||
GLFunctions::glVertexAttributePointer(attributePosition, attribute.m_componentsCount, attribute.m_type, false,
|
||||
buffer->GetStrideInBytes(), attribute.m_offset);
|
||||
}
|
||||
}
|
||||
|
||||
GLFunctions::glBindVertexArray(0);
|
||||
GLFunctions::glBindBuffer(0, gl_const::GLArrayBuffer);
|
||||
if (!m_mesh->m_indices.empty())
|
||||
GLFunctions::glBindBuffer(0, gl_const::GLElementArrayBuffer);
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
for (auto & buffer : m_mesh->m_buffers)
|
||||
{
|
||||
if (buffer->m_bufferId != 0)
|
||||
{
|
||||
GLFunctions::glDeleteBuffer(buffer->m_bufferId);
|
||||
buffer->m_bufferId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_indexBuffer != 0)
|
||||
{
|
||||
GLFunctions::glDeleteBuffer(m_indexBuffer);
|
||||
m_indexBuffer = 0;
|
||||
}
|
||||
|
||||
if (m_VAO != 0)
|
||||
{
|
||||
GLFunctions::glDeleteVertexArray(m_VAO);
|
||||
m_VAO = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateBuffer(ref_ptr<dp::GraphicsContext> context, uint32_t bufferInd) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
auto & buffer = m_mesh->m_buffers[bufferInd];
|
||||
GLFunctions::glBindBuffer(buffer->m_bufferId, gl_const::GLArrayBuffer);
|
||||
GLFunctions::glBufferData(gl_const::GLArrayBuffer, buffer->GetSizeInBytes(), buffer->GetData(),
|
||||
gl_const::GLStaticDraw);
|
||||
GLFunctions::glBindBuffer(0, gl_const::GLArrayBuffer);
|
||||
}
|
||||
|
||||
void UpdateIndexBuffer(ref_ptr<dp::GraphicsContext> context) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
CHECK(!m_mesh->m_indices.empty(), ());
|
||||
CHECK(m_indexBuffer, ("Index buffer was not created"));
|
||||
GLFunctions::glBindBuffer(m_indexBuffer, gl_const::GLElementArrayBuffer);
|
||||
GLFunctions::glBufferData(gl_const::GLElementArrayBuffer,
|
||||
static_cast<uint32_t>(m_mesh->m_indices.size() * sizeof(uint16_t)),
|
||||
m_mesh->m_indices.data(), gl_const::GLStaticDraw);
|
||||
GLFunctions::glBindBuffer(0, gl_const::GLElementArrayBuffer);
|
||||
}
|
||||
|
||||
void Bind(ref_ptr<dp::GpuProgram> program) override { GLFunctions::glBindVertexArray(m_VAO); }
|
||||
|
||||
void Unbind() override
|
||||
{
|
||||
GLFunctions::glBindVertexArray(0);
|
||||
GLFunctions::glBindBuffer(0, gl_const::GLArrayBuffer);
|
||||
if (m_indexBuffer != 0)
|
||||
GLFunctions::glBindBuffer(0, gl_const::GLElementArrayBuffer);
|
||||
}
|
||||
|
||||
void DrawPrimitives(ref_ptr<dp::GraphicsContext> context, uint32_t verticesCount, uint32_t startVertex) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
GLFunctions::glDrawArrays(GetGLDrawPrimitive(m_mesh->m_drawPrimitive), static_cast<int32_t>(startVertex),
|
||||
verticesCount);
|
||||
}
|
||||
|
||||
void DrawPrimitivesIndexed(ref_ptr<dp::GraphicsContext> context, uint32_t indexCount, uint32_t startIndex) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
CHECK(m_indexBuffer != 0, ());
|
||||
GLFunctions::glDrawElements(GetGLDrawPrimitive(m_mesh->m_drawPrimitive), sizeof(uint16_t), indexCount, startIndex);
|
||||
}
|
||||
|
||||
private:
|
||||
ref_ptr<dp::MeshObject> m_mesh;
|
||||
uint32_t m_VAO = 0;
|
||||
uint32_t m_indexBuffer = 0;
|
||||
};
|
||||
|
||||
MeshObject::MeshObject(ref_ptr<dp::GraphicsContext> context, DrawPrimitive drawPrimitive, std::string const & debugName)
|
||||
: m_drawPrimitive(drawPrimitive)
|
||||
, m_debugName(debugName)
|
||||
{
|
||||
auto const apiVersion = context->GetApiVersion();
|
||||
if (apiVersion == dp::ApiVersion::OpenGLES3)
|
||||
{
|
||||
InitForOpenGL();
|
||||
}
|
||||
else if (apiVersion == dp::ApiVersion::Metal)
|
||||
{
|
||||
#if defined(OMIM_METAL_AVAILABLE)
|
||||
InitForMetal();
|
||||
#endif
|
||||
}
|
||||
else if (apiVersion == dp::ApiVersion::Vulkan)
|
||||
{
|
||||
InitForVulkan(context);
|
||||
}
|
||||
CHECK(m_impl != nullptr, ());
|
||||
}
|
||||
|
||||
MeshObject::~MeshObject()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void MeshObject::InitForOpenGL()
|
||||
{
|
||||
m_impl = make_unique_dp<GLMeshObjectImpl>(make_ref(this));
|
||||
}
|
||||
|
||||
void MeshObject::SetAttribute(std::string const & attributeName, uint32_t bufferInd, uint32_t offset,
|
||||
uint32_t componentsCount, glConst type)
|
||||
{
|
||||
CHECK_LESS(bufferInd, m_buffers.size(), ());
|
||||
CHECK(m_buffers[bufferInd], ());
|
||||
m_buffers[bufferInd]->m_attributes.emplace_back(attributeName, offset, componentsCount, type);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void MeshObject::Reset()
|
||||
{
|
||||
CHECK(m_impl != nullptr, ());
|
||||
m_impl->Reset();
|
||||
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
void MeshObject::Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program)
|
||||
{
|
||||
Reset();
|
||||
|
||||
CHECK(m_impl != nullptr, ());
|
||||
m_impl->Build(context, program);
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void MeshObject::Bind(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program)
|
||||
{
|
||||
program->Bind();
|
||||
|
||||
if (!m_initialized)
|
||||
Build(context, program);
|
||||
|
||||
CHECK(m_impl != nullptr, ());
|
||||
m_impl->Bind(program);
|
||||
}
|
||||
|
||||
void MeshObject::SetIndexBuffer(std::vector<uint16_t> && indices)
|
||||
{
|
||||
m_indices = std::move(indices);
|
||||
Reset();
|
||||
}
|
||||
|
||||
void MeshObject::UpdateIndexBuffer(ref_ptr<dp::GraphicsContext> context, std::vector<uint16_t> const & indices)
|
||||
{
|
||||
CHECK(!indices.empty(), ("Use SetIndexBuffer() to reset index buffer"));
|
||||
CHECK_LESS_OR_EQUAL(indices.size(), m_indices.size(), ());
|
||||
memcpy(m_indices.data(), indices.data(), indices.size() * sizeof(uint16_t));
|
||||
|
||||
CHECK(m_impl != nullptr, ());
|
||||
m_impl->UpdateIndexBuffer(context);
|
||||
}
|
||||
|
||||
void MeshObject::DrawPrimitivesSubset(ref_ptr<dp::GraphicsContext> context, uint32_t vertexCount, uint32_t startVertex)
|
||||
{
|
||||
CHECK(m_impl != nullptr, ());
|
||||
CHECK(!m_buffers.empty(), ());
|
||||
auto const & buffer = m_buffers[0];
|
||||
auto const vertexNum = buffer->GetSizeInBytes() / buffer->GetStrideInBytes();
|
||||
CHECK_LESS(startVertex, vertexNum, ());
|
||||
CHECK_LESS_OR_EQUAL(startVertex + vertexCount, vertexNum, ());
|
||||
|
||||
m_impl->DrawPrimitives(context, vertexCount, startVertex);
|
||||
}
|
||||
|
||||
void MeshObject::DrawPrimitivesSubsetIndexed(ref_ptr<dp::GraphicsContext> context, uint32_t indexCount,
|
||||
uint32_t startIndex)
|
||||
{
|
||||
CHECK(m_impl != nullptr, ());
|
||||
CHECK(!m_indices.empty(), ());
|
||||
CHECK_LESS(startIndex, m_indices.size(), ());
|
||||
CHECK_LESS_OR_EQUAL(startIndex + indexCount, m_indices.size(), ());
|
||||
|
||||
m_impl->DrawPrimitivesIndexed(context, indexCount, startIndex);
|
||||
}
|
||||
|
||||
void MeshObject::DrawPrimitives(ref_ptr<dp::GraphicsContext> context)
|
||||
{
|
||||
if (m_buffers.empty())
|
||||
return;
|
||||
|
||||
auto const & buffer = m_buffers[0];
|
||||
auto const vertexNum = buffer->GetSizeInBytes() / buffer->GetStrideInBytes();
|
||||
#ifdef DEBUG
|
||||
for (size_t i = 1; i < m_buffers.size(); i++)
|
||||
{
|
||||
ASSERT_EQUAL(m_buffers[i]->GetSizeInBytes() / m_buffers[i]->GetStrideInBytes(), vertexNum,
|
||||
("All buffers in a mesh must contain the same vertex number"));
|
||||
}
|
||||
#endif
|
||||
|
||||
CHECK(m_impl != nullptr, ());
|
||||
if (m_indices.empty())
|
||||
m_impl->DrawPrimitives(context, vertexNum, 0);
|
||||
else
|
||||
m_impl->DrawPrimitivesIndexed(context, static_cast<uint32_t>(m_indices.size()), 0);
|
||||
}
|
||||
|
||||
void MeshObject::Unbind(ref_ptr<dp::GpuProgram> program)
|
||||
{
|
||||
program->Unbind();
|
||||
|
||||
CHECK(m_impl != nullptr, ());
|
||||
m_impl->Unbind();
|
||||
}
|
||||
|
||||
void MeshObject::UpdateImpl(ref_ptr<dp::GraphicsContext> context, uint32_t bufferInd)
|
||||
{
|
||||
CHECK(m_impl != nullptr, ());
|
||||
m_impl->UpdateBuffer(context, bufferInd);
|
||||
}
|
||||
|
||||
// static
|
||||
std::vector<float> MeshObject::GenerateNormalsForTriangles(std::vector<float> const & vertices, size_t componentsCount)
|
||||
{
|
||||
auto const trianglesCount = vertices.size() / (3 * componentsCount);
|
||||
std::vector<float> normals;
|
||||
normals.reserve(trianglesCount * 9);
|
||||
for (size_t triangle = 0; triangle < trianglesCount; ++triangle)
|
||||
{
|
||||
glsl::vec3 v[3];
|
||||
for (size_t vertex = 0; vertex < 3; ++vertex)
|
||||
{
|
||||
size_t const offset = triangle * componentsCount * 3 + vertex * componentsCount;
|
||||
v[vertex] = glsl::vec3(vertices[offset], vertices[offset + 1], vertices[offset + 2]);
|
||||
}
|
||||
|
||||
glsl::vec3 normal = glsl::cross(glsl::vec3(v[1].x - v[0].x, v[1].y - v[0].y, v[1].z - v[0].z),
|
||||
glsl::vec3(v[2].x - v[0].x, v[2].y - v[0].y, v[2].z - v[0].z));
|
||||
normal = glsl::normalize(normal);
|
||||
|
||||
for (size_t vertex = 0; vertex < 3; ++vertex)
|
||||
{
|
||||
normals.push_back(normal.x);
|
||||
normals.push_back(normal.y);
|
||||
normals.push_back(normal.z);
|
||||
}
|
||||
}
|
||||
return normals;
|
||||
}
|
||||
} // namespace dp
|
||||
214
libs/drape/mesh_object.hpp
Normal file
214
libs/drape/mesh_object.hpp
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/drape_global.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/render_state.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GpuProgram;
|
||||
class GraphicsContext;
|
||||
class MeshObjectImpl;
|
||||
|
||||
namespace metal
|
||||
{
|
||||
class MetalMeshObjectImpl;
|
||||
} // namespace metal
|
||||
|
||||
namespace vulkan
|
||||
{
|
||||
class VulkanMeshObjectImpl;
|
||||
} // namespace vulkan
|
||||
|
||||
// This class implements a simple mesh object which does not use an index buffer.
|
||||
// Use this class only for simple geometry.
|
||||
class MeshObject
|
||||
{
|
||||
friend class MeshObjectImpl;
|
||||
friend class GLMeshObjectImpl;
|
||||
friend class metal::MetalMeshObjectImpl;
|
||||
friend class vulkan::VulkanMeshObjectImpl;
|
||||
|
||||
public:
|
||||
enum class DrawPrimitive : uint8_t
|
||||
{
|
||||
Triangles,
|
||||
TriangleStrip,
|
||||
LineStrip
|
||||
};
|
||||
|
||||
MeshObject(ref_ptr<dp::GraphicsContext> context, DrawPrimitive drawPrimitive, std::string const & debugName = "");
|
||||
virtual ~MeshObject();
|
||||
|
||||
template <typename T>
|
||||
void SetBuffer(uint32_t bufferInd, std::vector<T> && vertices, uint32_t stride = 0)
|
||||
{
|
||||
CHECK_LESS_OR_EQUAL(bufferInd, GetNextBufferIndex(), ());
|
||||
|
||||
if (bufferInd == GetNextBufferIndex())
|
||||
m_buffers.emplace_back(make_unique_dp<VertexBuffer<T>>(std::move(vertices), stride));
|
||||
else
|
||||
m_buffers[bufferInd] = make_unique_dp<VertexBuffer<T>>(std::move(vertices), stride);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void SetAttribute(std::string const & attributeName, uint32_t bufferInd, uint32_t offset, uint32_t componentsCount,
|
||||
glConst type = gl_const::GLFloatType);
|
||||
|
||||
template <typename T>
|
||||
void UpdateBuffer(ref_ptr<dp::GraphicsContext> context, uint32_t bufferInd, std::vector<T> const & vertices)
|
||||
{
|
||||
CHECK(m_initialized, ());
|
||||
CHECK_LESS(bufferInd, static_cast<uint32_t>(m_buffers.size()), ());
|
||||
CHECK(!vertices.empty(), ());
|
||||
|
||||
auto & buffer = m_buffers[bufferInd];
|
||||
CHECK_LESS_OR_EQUAL(static_cast<uint32_t>(vertices.size() * sizeof(T)), buffer->GetSizeInBytes(), ());
|
||||
memcpy(buffer->GetData(), vertices.data(), vertices.size() * sizeof(T));
|
||||
|
||||
UpdateImpl(context, bufferInd);
|
||||
}
|
||||
|
||||
void SetIndexBuffer(std::vector<uint16_t> && indices);
|
||||
void UpdateIndexBuffer(ref_ptr<dp::GraphicsContext> context, std::vector<uint16_t> const & indices);
|
||||
|
||||
template <typename TParamsSetter, typename TParams>
|
||||
void Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program, dp::RenderState const & state,
|
||||
ref_ptr<TParamsSetter> paramsSetter, TParams const & params)
|
||||
{
|
||||
Bind(context, program);
|
||||
|
||||
ApplyState(context, program, state);
|
||||
paramsSetter->Apply(context, program, params);
|
||||
|
||||
DrawPrimitives(context);
|
||||
|
||||
Unbind(program);
|
||||
}
|
||||
|
||||
template <typename TParamsSetter, typename TParams>
|
||||
void Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program, dp::RenderState const & state,
|
||||
ref_ptr<TParamsSetter> paramsSetter, TParams const & params, std::function<void()> && drawCallback)
|
||||
{
|
||||
Bind(context, program);
|
||||
|
||||
ApplyState(context, program, state);
|
||||
paramsSetter->Apply(context, program, params);
|
||||
|
||||
CHECK(drawCallback, ());
|
||||
drawCallback();
|
||||
|
||||
Unbind(program);
|
||||
}
|
||||
|
||||
uint32_t GetNextBufferIndex() const { return static_cast<uint32_t>(m_buffers.size()); }
|
||||
|
||||
bool IsInitialized() const { return m_initialized; }
|
||||
void Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program);
|
||||
void Reset();
|
||||
|
||||
// Should be called inside draw callback in Render() method
|
||||
void DrawPrimitivesSubset(ref_ptr<dp::GraphicsContext> context, uint32_t vertexCount, uint32_t startVertex);
|
||||
void DrawPrimitivesSubsetIndexed(ref_ptr<dp::GraphicsContext> context, uint32_t indexCount, uint32_t startIndex);
|
||||
|
||||
static std::vector<float> GenerateNormalsForTriangles(std::vector<float> const & vertices, size_t componentsCount);
|
||||
|
||||
private:
|
||||
struct AttributeMapping
|
||||
{
|
||||
AttributeMapping() = default;
|
||||
|
||||
AttributeMapping(std::string const & attributeName, uint32_t offset, uint32_t componentsCount,
|
||||
glConst type = gl_const::GLFloatType)
|
||||
: m_attributeName(attributeName)
|
||||
, m_offset(offset)
|
||||
, m_componentsCount(componentsCount)
|
||||
, m_type(type)
|
||||
{}
|
||||
|
||||
std::string m_attributeName;
|
||||
uint32_t m_offset = 0;
|
||||
uint32_t m_componentsCount = 0;
|
||||
glConst m_type = gl_const::GLFloatType;
|
||||
};
|
||||
|
||||
class VertexBufferBase
|
||||
{
|
||||
public:
|
||||
virtual ~VertexBufferBase() = default;
|
||||
virtual void * GetData() = 0;
|
||||
virtual uint32_t GetSizeInBytes() const = 0;
|
||||
virtual uint32_t GetStrideInBytes() const = 0;
|
||||
|
||||
uint32_t m_bufferId = 0;
|
||||
std::vector<AttributeMapping> m_attributes;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class VertexBuffer : public VertexBufferBase
|
||||
{
|
||||
public:
|
||||
VertexBuffer() = default;
|
||||
|
||||
VertexBuffer(std::vector<T> && data, uint32_t stride = 0)
|
||||
: m_data(std::move(data))
|
||||
, m_stride(stride == 0 ? sizeof(T) : stride)
|
||||
{
|
||||
CHECK_GREATER_OR_EQUAL(m_stride, sizeof(T), ());
|
||||
}
|
||||
|
||||
void * GetData() override { return m_data.data(); }
|
||||
uint32_t GetSizeInBytes() const override { return static_cast<uint32_t>(m_data.size() * sizeof(T)); }
|
||||
uint32_t GetStrideInBytes() const override { return m_stride; }
|
||||
|
||||
private:
|
||||
std::vector<T> m_data;
|
||||
uint32_t m_stride = 0;
|
||||
};
|
||||
|
||||
void InitForOpenGL();
|
||||
void InitForVulkan(ref_ptr<dp::GraphicsContext> context);
|
||||
|
||||
#if defined(OMIM_METAL_AVAILABLE)
|
||||
// Definition of this method is in a .mm-file.
|
||||
void InitForMetal();
|
||||
#endif
|
||||
|
||||
void UpdateImpl(ref_ptr<dp::GraphicsContext> context, uint32_t bufferInd);
|
||||
|
||||
void Bind(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program);
|
||||
void Unbind(ref_ptr<dp::GpuProgram> program);
|
||||
void DrawPrimitives(ref_ptr<dp::GraphicsContext> context);
|
||||
|
||||
std::vector<drape_ptr<VertexBufferBase>> m_buffers;
|
||||
std::vector<uint16_t> m_indices;
|
||||
DrawPrimitive m_drawPrimitive = DrawPrimitive::Triangles;
|
||||
|
||||
drape_ptr<MeshObjectImpl> m_impl;
|
||||
bool m_initialized = false;
|
||||
|
||||
std::string m_debugName;
|
||||
};
|
||||
|
||||
class MeshObjectImpl
|
||||
{
|
||||
public:
|
||||
virtual ~MeshObjectImpl() = default;
|
||||
virtual void Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program) = 0;
|
||||
virtual void Reset() = 0;
|
||||
virtual void UpdateBuffer(ref_ptr<dp::GraphicsContext> context, uint32_t bufferInd) = 0;
|
||||
virtual void UpdateIndexBuffer(ref_ptr<dp::GraphicsContext> context) = 0;
|
||||
virtual void Bind(ref_ptr<dp::GpuProgram> program) = 0;
|
||||
virtual void Unbind() = 0;
|
||||
virtual void DrawPrimitives(ref_ptr<dp::GraphicsContext> context, uint32_t verticesCount, uint32_t startVertex) = 0;
|
||||
virtual void DrawPrimitivesIndexed(ref_ptr<dp::GraphicsContext> context, uint32_t indexCount,
|
||||
uint32_t startIndex) = 0;
|
||||
};
|
||||
} // namespace dp
|
||||
106
libs/drape/metal/metal_base_context.hpp
Normal file
106
libs/drape/metal/metal_base_context.hpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/gpu_program.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/metal/metal_cleaner.hpp"
|
||||
#include "drape/metal/metal_states.hpp"
|
||||
#include "drape/metal/metal_texture.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/texture_types.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
class MetalBaseContext : public dp::GraphicsContext
|
||||
{
|
||||
public:
|
||||
using DrawableRequest = std::function<id<CAMetalDrawable>()>;
|
||||
|
||||
MetalBaseContext(id<MTLDevice> device, m2::PointU const & screenSize, DrawableRequest && drawableRequest);
|
||||
|
||||
bool BeginRendering() override;
|
||||
void EndRendering() override;
|
||||
void Present() override;
|
||||
void MakeCurrent() override {}
|
||||
void DoneCurrent() override {}
|
||||
bool Validate() override { return true; }
|
||||
void Resize(uint32_t w, uint32_t h) 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(ApiVersion apiVersion) override;
|
||||
ApiVersion GetApiVersion() const override;
|
||||
std::string GetRendererName() const override;
|
||||
std::string GetRendererVersion() const override;
|
||||
|
||||
void DebugSynchronizeWithCPU() override;
|
||||
void PushDebugLabel(std::string const & label) override;
|
||||
void PopDebugLabel() override;
|
||||
|
||||
void SetClearColor(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(TestFunction depthFunction) override;
|
||||
void SetStencilTestEnabled(bool enabled) override;
|
||||
void SetStencilFunction(StencilFace face, TestFunction stencilFunction) override;
|
||||
void SetStencilActions(StencilFace face, StencilAction stencilFailAction, StencilAction depthFailAction,
|
||||
StencilAction passAction) override;
|
||||
void SetStencilReferenceValue(uint32_t stencilReferenceValue) override
|
||||
{
|
||||
m_stencilReferenceValue = stencilReferenceValue;
|
||||
}
|
||||
void SetCullingEnabled(bool enabled) override;
|
||||
|
||||
id<MTLDevice> GetMetalDevice() const;
|
||||
id<MTLCommandBuffer> GetCommandBuffer() const;
|
||||
id<MTLRenderCommandEncoder> GetCommandEncoder() const;
|
||||
id<MTLDepthStencilState> GetDepthStencilState();
|
||||
id<MTLRenderPipelineState> GetPipelineState(ref_ptr<GpuProgram> program, bool blendingEnabled);
|
||||
id<MTLSamplerState> GetSamplerState(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode);
|
||||
|
||||
void SetSystemPrograms(drape_ptr<GpuProgram> && programClearColor, drape_ptr<GpuProgram> && programClearDepth,
|
||||
drape_ptr<GpuProgram> && programClearColorAndDepth);
|
||||
|
||||
void ApplyPipelineState(id<MTLRenderPipelineState> state);
|
||||
bool HasAppliedPipelineState() const;
|
||||
void ResetPipelineStatesCache();
|
||||
|
||||
MTLRenderPassDescriptor * GetRenderPassDescriptor() const;
|
||||
|
||||
protected:
|
||||
void RecreateDepthTexture(m2::PointU const & screenSize);
|
||||
void RequestFrameDrawable();
|
||||
void ResetFrameDrawable();
|
||||
void FinishCurrentEncoding();
|
||||
|
||||
id<MTLDevice> m_device;
|
||||
DrawableRequest m_drawableRequest;
|
||||
drape_ptr<MetalTexture> m_depthTexture;
|
||||
MTLRenderPassDescriptor * m_renderPassDescriptor;
|
||||
id<MTLCommandQueue> m_commandQueue;
|
||||
ref_ptr<dp::BaseFramebuffer> m_currentFramebuffer;
|
||||
MetalStates::DepthStencilKey m_currentDepthStencilKey;
|
||||
MetalStates m_metalStates;
|
||||
|
||||
// These objects are recreated each frame. They MUST NOT be stored anywhere.
|
||||
id<CAMetalDrawable> m_frameDrawable;
|
||||
id<MTLCommandBuffer> m_frameCommandBuffer;
|
||||
id<MTLRenderCommandEncoder> m_currentCommandEncoder;
|
||||
id<MTLRenderPipelineState> m_lastPipelineState;
|
||||
|
||||
MetalCleaner m_cleaner;
|
||||
|
||||
uint32_t m_stencilReferenceValue = 1;
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
462
libs/drape/metal/metal_base_context.mm
Normal file
462
libs/drape/metal/metal_base_context.mm
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
#include "drape/metal/metal_base_context.hpp"
|
||||
#include "drape/metal/metal_texture.hpp"
|
||||
|
||||
#include "drape/framebuffer.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
MetalBaseContext::MetalBaseContext(id<MTLDevice> device, m2::PointU const & screenSize,
|
||||
DrawableRequest && drawableRequest)
|
||||
: m_device(device)
|
||||
, m_drawableRequest(std::move(drawableRequest))
|
||||
{
|
||||
m_renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
m_renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||
m_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
m_renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear;
|
||||
m_renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
m_renderPassDescriptor.depthAttachment.clearDepth = 1.0;
|
||||
m_renderPassDescriptor.stencilAttachment.loadAction = MTLLoadActionClear;
|
||||
m_renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionStore;
|
||||
m_renderPassDescriptor.stencilAttachment.clearStencil = 0;
|
||||
|
||||
RecreateDepthTexture(screenSize);
|
||||
}
|
||||
|
||||
void MetalBaseContext::RecreateDepthTexture(m2::PointU const & screenSize)
|
||||
{
|
||||
if (screenSize.x == 0 || screenSize.y == 0)
|
||||
{
|
||||
m_depthTexture.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_depthTexture = make_unique_dp<MetalTexture>(nullptr /* allocator */);
|
||||
HWTexture::Params params;
|
||||
params.m_width = screenSize.x;
|
||||
params.m_height = screenSize.y;
|
||||
params.m_format = TextureFormat::Depth;
|
||||
params.m_isRenderTarget = true;
|
||||
m_depthTexture->Create(make_ref(this), params, nullptr /* data */);
|
||||
}
|
||||
|
||||
void MetalBaseContext::Init(dp::ApiVersion apiVersion)
|
||||
{
|
||||
CHECK(apiVersion == dp::ApiVersion::Metal, ());
|
||||
m_commandQueue = [m_device newCommandQueue];
|
||||
}
|
||||
|
||||
ApiVersion MetalBaseContext::GetApiVersion() const
|
||||
{
|
||||
return dp::ApiVersion::Metal;
|
||||
}
|
||||
|
||||
std::string MetalBaseContext::GetRendererName() const
|
||||
{
|
||||
return std::string([m_device.name UTF8String]);
|
||||
}
|
||||
|
||||
std::string MetalBaseContext::GetRendererVersion() const
|
||||
{
|
||||
static std::vector<std::pair<MTLFeatureSet, std::string>> features;
|
||||
if (features.empty())
|
||||
{
|
||||
#ifdef OMIM_OS_MAC
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily1_v1, "macOS_GPUFamily1_v1");
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily1_v2, "macOS_GPUFamily1_v2");
|
||||
if (@available(macOS 10.13, *))
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily1_v3, "macOS_GPUFamily1_v3");
|
||||
if (@available(macOS 10.14, *))
|
||||
{
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily1_v4, "macOS_GPUFamily1_v4");
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily2_v1, "macOS_GPUFamily2_v1");
|
||||
}
|
||||
#else
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v1, "iOS_GPUFamily1_v1");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v1, "iOS_GPUFamily2_v1");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v2, "iOS_GPUFamily1_v2");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v2, "iOS_GPUFamily2_v2");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily3_v1, "iOS_GPUFamily3_v1");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v3, "iOS_GPUFamily1_v3");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v3, "iOS_GPUFamily2_v3");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily3_v2, "iOS_GPUFamily3_v2");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v4, "iOS_GPUFamily1_v4");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v4, "iOS_GPUFamily2_v4");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily3_v3, "iOS_GPUFamily3_v3");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily4_v1, "iOS_GPUFamily4_v1");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v5, "iOS_GPUFamily1_v5");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v5, "iOS_GPUFamily2_v5");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily3_v4, "iOS_GPUFamily3_v4");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily4_v2, "iOS_GPUFamily4_v2");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily5_v1, "iOS_GPUFamily5_v1");
|
||||
#endif
|
||||
std::sort(features.begin(), features.end(), [](auto const & s1, auto const & s2)
|
||||
{
|
||||
return s1.first > s2.first;
|
||||
});
|
||||
}
|
||||
|
||||
for (auto featureSet : features)
|
||||
{
|
||||
if ([m_device supportsFeatureSet:featureSet.first])
|
||||
return featureSet.second;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
void MetalBaseContext::PushDebugLabel(std::string const & label)
|
||||
{
|
||||
if (m_currentCommandEncoder == nil)
|
||||
return;
|
||||
[m_currentCommandEncoder pushDebugGroup:@(label.c_str())];
|
||||
}
|
||||
|
||||
void MetalBaseContext::PopDebugLabel()
|
||||
{
|
||||
if (m_currentCommandEncoder == nil)
|
||||
return;
|
||||
[m_currentCommandEncoder popDebugGroup];
|
||||
}
|
||||
|
||||
void MetalBaseContext::Resize(uint32_t w, uint32_t h)
|
||||
{
|
||||
if (m_depthTexture && m_depthTexture->GetWidth() == w && m_depthTexture->GetHeight() == h)
|
||||
return;
|
||||
|
||||
RecreateDepthTexture({w, h});
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer)
|
||||
{
|
||||
FinishCurrentEncoding();
|
||||
m_currentFramebuffer = framebuffer;
|
||||
}
|
||||
|
||||
void MetalBaseContext::ApplyFramebuffer(std::string const & framebufferLabel)
|
||||
{
|
||||
// Initialize frame command buffer if there is no one.
|
||||
if (!m_frameCommandBuffer)
|
||||
{
|
||||
m_frameCommandBuffer = [m_commandQueue commandBuffer];
|
||||
m_frameCommandBuffer.label = @"Frame command buffer";
|
||||
}
|
||||
|
||||
if (!m_currentFramebuffer)
|
||||
{
|
||||
// Use default(system) framebuffer and depth-stencil.
|
||||
RequestFrameDrawable();
|
||||
m_renderPassDescriptor.colorAttachments[0].texture = m_frameDrawable != nil ? m_frameDrawable.texture : nil;
|
||||
m_renderPassDescriptor.depthAttachment.texture = m_depthTexture ? m_depthTexture->GetTexture() : nil;
|
||||
m_renderPassDescriptor.stencilAttachment.texture = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
ref_ptr<Framebuffer> framebuffer = m_currentFramebuffer;
|
||||
|
||||
ASSERT(dynamic_cast<MetalTexture *>(framebuffer->GetTexture()->GetHardwareTexture().get()) != nullptr, ());
|
||||
ref_ptr<MetalTexture> colorAttachment = framebuffer->GetTexture()->GetHardwareTexture();
|
||||
m_renderPassDescriptor.colorAttachments[0].texture = colorAttachment->GetTexture();
|
||||
|
||||
auto const depthStencilRef = framebuffer->GetDepthStencilRef();
|
||||
if (depthStencilRef != nullptr)
|
||||
{
|
||||
ASSERT(dynamic_cast<MetalTexture *>(depthStencilRef->GetTexture()->GetHardwareTexture().get()) != nullptr, ());
|
||||
ref_ptr<MetalTexture> depthStencilAttachment = depthStencilRef->GetTexture()->GetHardwareTexture();
|
||||
m_renderPassDescriptor.depthAttachment.texture = depthStencilAttachment->GetTexture();
|
||||
if (depthStencilAttachment->GetFormat() == dp::TextureFormat::DepthStencil)
|
||||
m_renderPassDescriptor.stencilAttachment.texture = depthStencilAttachment->GetTexture();
|
||||
else
|
||||
m_renderPassDescriptor.stencilAttachment.texture = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_renderPassDescriptor.depthAttachment.texture = nil;
|
||||
m_renderPassDescriptor.stencilAttachment.texture = nil;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(m_currentCommandEncoder == nil, ("Current command encoder was not finished."));
|
||||
m_currentCommandEncoder = [m_frameCommandBuffer renderCommandEncoderWithDescriptor:m_renderPassDescriptor];
|
||||
m_currentCommandEncoder.label = @(framebufferLabel.c_str());
|
||||
[m_currentCommandEncoder pushDebugGroup:@(framebufferLabel.c_str())];
|
||||
|
||||
// Default rendering options.
|
||||
[m_currentCommandEncoder setFrontFacingWinding:MTLWindingClockwise];
|
||||
[m_currentCommandEncoder setCullMode:MTLCullModeBack];
|
||||
[m_currentCommandEncoder setStencilReferenceValue:m_stencilReferenceValue];
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetClearColor(dp::Color const & color)
|
||||
{
|
||||
m_cleaner.SetClearColor(color);
|
||||
m_renderPassDescriptor.colorAttachments[0].clearColor =
|
||||
MTLClearColorMake(color.GetRedF(), color.GetGreenF(), color.GetBlueF(), color.GetAlphaF());
|
||||
}
|
||||
|
||||
void MetalBaseContext::Clear(uint32_t clearBits, uint32_t storeBits)
|
||||
{
|
||||
if (m_currentCommandEncoder != nil)
|
||||
{
|
||||
if ((clearBits & ClearBits::ColorBit) && (clearBits & ClearBits::DepthBit))
|
||||
m_cleaner.ClearColorAndDepth(make_ref(this), m_currentCommandEncoder);
|
||||
else if (clearBits & ClearBits::ColorBit)
|
||||
m_cleaner.ClearColor(make_ref(this), m_currentCommandEncoder);
|
||||
else if (clearBits & ClearBits::DepthBit)
|
||||
m_cleaner.ClearDepth(make_ref(this), m_currentCommandEncoder);
|
||||
|
||||
if (clearBits & ClearBits::StencilBit)
|
||||
CHECK(false, ("Stencil clearing is not implemented"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here, if we do not clear attachments, we load data ONLY if we store it afterwards, otherwise we use 'DontCare' option
|
||||
// to improve performance.
|
||||
if (clearBits & ClearBits::ColorBit)
|
||||
m_renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||
else
|
||||
m_renderPassDescriptor.colorAttachments[0].loadAction = (storeBits & ClearBits::ColorBit) ? MTLLoadActionLoad : MTLLoadActionDontCare;
|
||||
|
||||
if (clearBits & ClearBits::DepthBit)
|
||||
m_renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear;
|
||||
else
|
||||
m_renderPassDescriptor.depthAttachment.loadAction = (storeBits & ClearBits::DepthBit) ? MTLLoadActionLoad : MTLLoadActionDontCare;
|
||||
|
||||
if (clearBits & ClearBits::StencilBit)
|
||||
m_renderPassDescriptor.stencilAttachment.loadAction = MTLLoadActionClear;
|
||||
else
|
||||
m_renderPassDescriptor.stencilAttachment.loadAction = (storeBits & ClearBits::StencilBit) ? MTLLoadActionLoad : MTLLoadActionDontCare;
|
||||
|
||||
// Apply storing mode.
|
||||
if (storeBits & ClearBits::ColorBit)
|
||||
m_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
else
|
||||
m_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionDontCare;
|
||||
|
||||
if (storeBits & ClearBits::DepthBit)
|
||||
m_renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
else
|
||||
m_renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionDontCare;
|
||||
|
||||
if (storeBits & ClearBits::StencilBit)
|
||||
m_renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionStore;
|
||||
else
|
||||
m_renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionDontCare;
|
||||
}
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = GetCommandEncoder();
|
||||
[encoder setViewport:MTLViewport(x, y, w, h, 0.0, 1.0)];
|
||||
[encoder setScissorRect:{x, y, w, h}];
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = GetCommandEncoder();
|
||||
if (m_renderPassDescriptor.colorAttachments[0].texture != nil)
|
||||
{
|
||||
auto const rpWidth = static_cast<uint32_t>(m_renderPassDescriptor.colorAttachments[0].texture.width);
|
||||
auto const rpHeight = static_cast<uint32_t>(m_renderPassDescriptor.colorAttachments[0].texture.height);
|
||||
if (x < 0)
|
||||
x = 0;
|
||||
if (y < 0)
|
||||
y = 0;
|
||||
if (x + w > rpWidth)
|
||||
w = rpWidth - x;
|
||||
if (y + h > rpHeight)
|
||||
h = rpHeight - y;
|
||||
|
||||
[encoder setScissorRect:{x, y, w, h}];
|
||||
}
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetDepthTestEnabled(bool enabled)
|
||||
{
|
||||
m_currentDepthStencilKey.m_depthEnabled = enabled;
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetDepthTestFunction(dp::TestFunction depthFunction)
|
||||
{
|
||||
m_currentDepthStencilKey.m_depthFunction = depthFunction;
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetStencilTestEnabled(bool enabled)
|
||||
{
|
||||
m_currentDepthStencilKey.m_stencilEnabled = enabled;
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetStencilFunction(dp::StencilFace face, dp::TestFunction stencilFunction)
|
||||
{
|
||||
m_currentDepthStencilKey.SetStencilFunction(face, stencilFunction);
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetStencilActions(dp::StencilFace face,
|
||||
dp::StencilAction stencilFailAction,
|
||||
dp::StencilAction depthFailAction,
|
||||
dp::StencilAction passAction)
|
||||
{
|
||||
m_currentDepthStencilKey.SetStencilActions(face, stencilFailAction, depthFailAction, passAction);
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetCullingEnabled(bool enabled)
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = GetCommandEncoder();
|
||||
[encoder setCullMode: (enabled ? MTLCullModeBack : MTLCullModeNone)];
|
||||
}
|
||||
|
||||
id<MTLDevice> MetalBaseContext::GetMetalDevice() const
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
id<MTLRenderCommandEncoder> MetalBaseContext::GetCommandEncoder() const
|
||||
{
|
||||
CHECK(m_currentCommandEncoder != nil, ("Probably encoding commands were called before ApplyFramebuffer."));
|
||||
return m_currentCommandEncoder;
|
||||
}
|
||||
|
||||
id<MTLCommandBuffer> MetalBaseContext::GetCommandBuffer() const
|
||||
{
|
||||
CHECK(m_frameCommandBuffer != nil, ("Probably encoding commands were called before ApplyFramebuffer."));
|
||||
return m_frameCommandBuffer;
|
||||
}
|
||||
|
||||
id<MTLDepthStencilState> MetalBaseContext::GetDepthStencilState()
|
||||
{
|
||||
return m_metalStates.GetDepthStencilState(m_device, m_currentDepthStencilKey);
|
||||
}
|
||||
|
||||
id<MTLRenderPipelineState> MetalBaseContext::GetPipelineState(ref_ptr<GpuProgram> program, bool blendingEnabled)
|
||||
{
|
||||
CHECK(m_currentCommandEncoder != nil, ("Probably encoding commands were called before ApplyFramebuffer."));
|
||||
|
||||
id<MTLTexture> colorTexture = m_renderPassDescriptor.colorAttachments[0].texture;
|
||||
// It can be nil in the case when Metal drawable is absent (e.g. finish rendering in background).
|
||||
if (colorTexture == nil)
|
||||
return nil;
|
||||
|
||||
id<MTLTexture> depthTexture = m_renderPassDescriptor.depthAttachment.texture;
|
||||
MTLPixelFormat depthStencilFormat = (depthTexture != nil) ? depthTexture.pixelFormat : MTLPixelFormatInvalid;
|
||||
|
||||
MetalStates::PipelineKey const key(program, colorTexture.pixelFormat, depthStencilFormat, blendingEnabled);
|
||||
return m_metalStates.GetPipelineState(m_device, key);
|
||||
}
|
||||
|
||||
id<MTLSamplerState> MetalBaseContext::GetSamplerState(TextureFilter filter, TextureWrapping wrapSMode,
|
||||
TextureWrapping wrapTMode)
|
||||
{
|
||||
MetalStates::SamplerKey const key(filter, wrapSMode, wrapTMode);
|
||||
return m_metalStates.GetSamplerState(m_device, key);
|
||||
}
|
||||
|
||||
bool MetalBaseContext::BeginRendering()
|
||||
{
|
||||
CHECK(m_currentCommandEncoder == nil, ("Current command encoder was not finished."));
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalBaseContext::EndRendering()
|
||||
{
|
||||
FinishCurrentEncoding();
|
||||
}
|
||||
|
||||
void MetalBaseContext::Present()
|
||||
{
|
||||
RequestFrameDrawable();
|
||||
if (m_frameDrawable)
|
||||
[m_frameCommandBuffer presentDrawable:m_frameDrawable];
|
||||
|
||||
[m_frameCommandBuffer commit];
|
||||
m_frameDrawable = nil;
|
||||
[m_frameCommandBuffer waitUntilCompleted];
|
||||
m_frameCommandBuffer = nil;
|
||||
}
|
||||
|
||||
void MetalBaseContext::RequestFrameDrawable()
|
||||
{
|
||||
if (m_frameDrawable != nil)
|
||||
return;
|
||||
|
||||
CHECK(m_drawableRequest != nullptr, ());
|
||||
m_frameDrawable = m_drawableRequest();
|
||||
}
|
||||
|
||||
void MetalBaseContext::ResetFrameDrawable()
|
||||
{
|
||||
if (m_frameDrawable == nil)
|
||||
return;
|
||||
|
||||
m_frameDrawable = nil;
|
||||
RequestFrameDrawable();
|
||||
}
|
||||
|
||||
void MetalBaseContext::FinishCurrentEncoding()
|
||||
{
|
||||
[m_currentCommandEncoder popDebugGroup];
|
||||
[m_currentCommandEncoder endEncoding];
|
||||
m_currentCommandEncoder = nil;
|
||||
m_lastPipelineState = nil;
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetSystemPrograms(drape_ptr<GpuProgram> && programClearColor,
|
||||
drape_ptr<GpuProgram> && programClearDepth,
|
||||
drape_ptr<GpuProgram> && programClearColorAndDepth)
|
||||
{
|
||||
m_cleaner.Init(make_ref(this), std::move(programClearColor), std::move(programClearDepth),
|
||||
std::move(programClearColorAndDepth));
|
||||
}
|
||||
|
||||
void MetalBaseContext::ApplyPipelineState(id<MTLRenderPipelineState> state)
|
||||
{
|
||||
m_lastPipelineState = state;
|
||||
if (state != nil)
|
||||
[GetCommandEncoder() setRenderPipelineState:state];
|
||||
}
|
||||
|
||||
bool MetalBaseContext::HasAppliedPipelineState() const
|
||||
{
|
||||
return m_lastPipelineState != nil;
|
||||
}
|
||||
|
||||
void MetalBaseContext::ResetPipelineStatesCache()
|
||||
{
|
||||
m_metalStates.ResetPipelineStatesCache();
|
||||
}
|
||||
|
||||
void MetalBaseContext::DebugSynchronizeWithCPU()
|
||||
{
|
||||
FinishCurrentEncoding();
|
||||
RequestFrameDrawable();
|
||||
[m_frameCommandBuffer commit];
|
||||
m_frameDrawable = nil;
|
||||
[m_frameCommandBuffer waitUntilCompleted];
|
||||
m_frameCommandBuffer = nil;
|
||||
}
|
||||
|
||||
MTLRenderPassDescriptor * MetalBaseContext::GetRenderPassDescriptor() const
|
||||
{
|
||||
return m_renderPassDescriptor;
|
||||
}
|
||||
} // namespace metal
|
||||
|
||||
void RenderFrameMediator(std::function<void()> && renderFrameFunction)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
renderFrameFunction();
|
||||
}
|
||||
}
|
||||
} // namespace dp
|
||||
45
libs/drape/metal/metal_cleaner.hpp
Normal file
45
libs/drape/metal/metal_cleaner.hpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/color.hpp"
|
||||
#include "drape/glsl_types.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GpuProgram;
|
||||
|
||||
namespace metal
|
||||
{
|
||||
class MetalBaseContext;
|
||||
|
||||
class MetalCleaner
|
||||
{
|
||||
public:
|
||||
MetalCleaner() = default;
|
||||
|
||||
void Init(ref_ptr<MetalBaseContext> context, drape_ptr<GpuProgram> && programClearColor,
|
||||
drape_ptr<GpuProgram> && programClearDepth, drape_ptr<GpuProgram> && programClearColorAndDepth);
|
||||
|
||||
void SetClearColor(Color const & color);
|
||||
|
||||
void ClearDepth(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder);
|
||||
void ClearColor(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder);
|
||||
void ClearColorAndDepth(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder);
|
||||
|
||||
private:
|
||||
void ApplyColorParam(id<MTLRenderCommandEncoder> encoder, ref_ptr<GpuProgram> program);
|
||||
void RenderQuad(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder, ref_ptr<GpuProgram> program);
|
||||
|
||||
id<MTLBuffer> m_buffer;
|
||||
id<MTLDepthStencilState> m_depthEnabledState;
|
||||
id<MTLDepthStencilState> m_depthDisabledState;
|
||||
glsl::vec4 m_clearColor;
|
||||
|
||||
drape_ptr<GpuProgram> m_programClearColor;
|
||||
drape_ptr<GpuProgram> m_programClearDepth;
|
||||
drape_ptr<GpuProgram> m_programClearColorAndDepth;
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
95
libs/drape/metal/metal_cleaner.mm
Normal file
95
libs/drape/metal/metal_cleaner.mm
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#include "drape/metal/metal_cleaner.hpp"
|
||||
#include "drape/metal/metal_base_context.hpp"
|
||||
#include "drape/metal/metal_gpu_program.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
void MetalCleaner::Init(ref_ptr<MetalBaseContext> context,
|
||||
drape_ptr<GpuProgram> && programClearColor,
|
||||
drape_ptr<GpuProgram> && programClearDepth,
|
||||
drape_ptr<GpuProgram> && programClearColorAndDepth)
|
||||
{
|
||||
m_programClearColor = std::move(programClearColor);
|
||||
m_programClearDepth = std::move(programClearDepth);
|
||||
m_programClearColorAndDepth = std::move(programClearColorAndDepth);
|
||||
|
||||
ref_ptr<MetalBaseContext> metalContext = context;
|
||||
id<MTLDevice> device = metalContext->GetMetalDevice();
|
||||
std::vector<float> quad = {-1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f};
|
||||
m_buffer = [device newBufferWithBytes:quad.data()
|
||||
length:quad.size() * sizeof(quad[0])
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
m_buffer.label = @"MetalCleaner";
|
||||
|
||||
MTLDepthStencilDescriptor * desc = [[MTLDepthStencilDescriptor alloc] init];
|
||||
desc.depthWriteEnabled = YES;
|
||||
desc.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
m_depthEnabledState = [device newDepthStencilStateWithDescriptor:desc];
|
||||
CHECK(m_depthEnabledState != nil, ());
|
||||
|
||||
desc.depthWriteEnabled = NO;
|
||||
desc.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
m_depthDisabledState = [device newDepthStencilStateWithDescriptor:desc];
|
||||
CHECK(m_depthDisabledState != nil, ());
|
||||
}
|
||||
|
||||
void MetalCleaner::SetClearColor(Color const & color)
|
||||
{
|
||||
m_clearColor = glsl::ToVec4(color);
|
||||
}
|
||||
|
||||
void MetalCleaner::ApplyColorParam(id<MTLRenderCommandEncoder> encoder, ref_ptr<GpuProgram> program)
|
||||
{
|
||||
ref_ptr<MetalGpuProgram> metalProgram = program;
|
||||
auto const fsBindingIndex = metalProgram->GetFragmentShaderUniformsBindingIndex();
|
||||
if (fsBindingIndex >= 0)
|
||||
{
|
||||
[encoder setFragmentBytes:(void const *)&m_clearColor length:sizeof(m_clearColor)
|
||||
atIndex:fsBindingIndex];
|
||||
}
|
||||
}
|
||||
|
||||
void MetalCleaner::RenderQuad(ref_ptr<MetalBaseContext> metalContext, id<MTLRenderCommandEncoder> encoder,
|
||||
ref_ptr<GpuProgram> program)
|
||||
{
|
||||
id<MTLRenderPipelineState> pipelineState = metalContext->GetPipelineState(program, false /* blendingEnabled */);
|
||||
if (pipelineState == nil)
|
||||
return;
|
||||
|
||||
[encoder setRenderPipelineState:pipelineState];
|
||||
|
||||
[encoder setVertexBuffer:m_buffer offset:0 atIndex:0];
|
||||
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||
}
|
||||
|
||||
void MetalCleaner::ClearDepth(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder)
|
||||
{
|
||||
[encoder pushDebugGroup:@"ClearDepth"];
|
||||
[encoder setDepthStencilState:m_depthEnabledState];
|
||||
RenderQuad(context, encoder, make_ref(m_programClearDepth));
|
||||
[encoder popDebugGroup];
|
||||
}
|
||||
|
||||
void MetalCleaner::ClearColor(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder)
|
||||
{
|
||||
[encoder pushDebugGroup:@"ClearColor"];
|
||||
[encoder setDepthStencilState:m_depthDisabledState];
|
||||
ApplyColorParam(encoder, make_ref(m_programClearColor));
|
||||
RenderQuad(context, encoder, make_ref(m_programClearColor));
|
||||
[encoder popDebugGroup];
|
||||
}
|
||||
|
||||
void MetalCleaner::ClearColorAndDepth(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder)
|
||||
{
|
||||
[encoder pushDebugGroup:@"ClearColorAndDepth"];
|
||||
[encoder setDepthStencilState:m_depthEnabledState];
|
||||
ApplyColorParam(encoder, make_ref(m_programClearColorAndDepth));
|
||||
RenderQuad(context, encoder, make_ref(m_programClearColorAndDepth));
|
||||
[encoder popDebugGroup];
|
||||
}
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
73
libs/drape/metal/metal_gpu_buffer_impl.hpp
Normal file
73
libs/drape/metal/metal_gpu_buffer_impl.hpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/data_buffer.hpp"
|
||||
#include "drape/data_buffer_impl.hpp"
|
||||
#include "drape/metal/metal_base_context.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
class MetalGPUBuffer : public BufferBase
|
||||
{
|
||||
public:
|
||||
MetalGPUBuffer(ref_ptr<GraphicsContext> context, void const * data, uint8_t elementSize, uint32_t capacity);
|
||||
|
||||
void UploadData(void const * data, uint32_t elementCount);
|
||||
|
||||
void * Map(uint32_t elementOffset, uint32_t elementCount);
|
||||
|
||||
void UpdateData(void * gpuPtr, void const * data, uint32_t elementOffset, uint32_t elementCount);
|
||||
|
||||
id<MTLBuffer> GetMetalBuffer() const { return m_metalBuffer; }
|
||||
|
||||
protected:
|
||||
void Resize(ref_ptr<MetalBaseContext> context, void const * data, uint32_t elementCount);
|
||||
|
||||
id<MTLBuffer> m_metalBuffer;
|
||||
};
|
||||
|
||||
class MetalGpuBufferImpl : public DataBufferImpl<MetalGPUBuffer>
|
||||
{
|
||||
public:
|
||||
template <typename... Args>
|
||||
MetalGpuBufferImpl(Args &&... params) : DataBufferImpl(std::forward<Args>(params)...)
|
||||
{}
|
||||
|
||||
void const * Data() const override
|
||||
{
|
||||
ASSERT(false, ("Retrieving of raw data is unavailable for GPU buffer"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UploadData(ref_ptr<GraphicsContext> context, void const * data, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
m_buffer->UploadData(data, elementCount);
|
||||
}
|
||||
|
||||
void UpdateData(void * destPtr, void const * srcPtr, uint32_t elementOffset, uint32_t elementCount) override
|
||||
{
|
||||
m_buffer->UpdateData(destPtr, srcPtr, elementOffset, elementCount);
|
||||
}
|
||||
|
||||
void * Map(ref_ptr<GraphicsContext> context, uint32_t elementOffset, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
return m_buffer->Map(elementOffset, elementCount);
|
||||
}
|
||||
|
||||
id<MTLBuffer> GetMetalBuffer() const { return m_buffer->GetMetalBuffer(); }
|
||||
|
||||
void Bind() override {}
|
||||
void Unmap(ref_ptr<GraphicsContext>) override {}
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
81
libs/drape/metal/metal_gpu_buffer_impl.mm
Normal file
81
libs/drape/metal/metal_gpu_buffer_impl.mm
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#include "drape/metal/metal_gpu_buffer_impl.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
MetalGPUBuffer::MetalGPUBuffer(ref_ptr<GraphicsContext> context, void const * data,
|
||||
uint8_t elementSize, uint32_t capacity)
|
||||
: BufferBase(elementSize, capacity)
|
||||
{
|
||||
Resize(context, data, capacity);
|
||||
}
|
||||
|
||||
void MetalGPUBuffer::UploadData(void const * data, uint32_t elementCount)
|
||||
{
|
||||
uint32_t const currentSize = GetCurrentSize();
|
||||
uint8_t const elementSize = GetElementSize();
|
||||
ASSERT(GetCapacity() >= elementCount + currentSize,
|
||||
("Not enough memory to upload ", elementCount, " elements"));
|
||||
|
||||
uint32_t const byteOffset = currentSize * elementSize;
|
||||
uint32_t const sizeInBytes = elementCount * elementSize;
|
||||
uint8_t * gpuPtr = static_cast<uint8_t *>([m_metalBuffer contents]) + byteOffset;
|
||||
memcpy(gpuPtr, data, sizeInBytes);
|
||||
BufferBase::UploadData(elementCount);
|
||||
}
|
||||
|
||||
void * MetalGPUBuffer::Map(uint32_t elementOffset, uint32_t elementCount)
|
||||
{
|
||||
UNUSED_VALUE(elementCount);
|
||||
uint32_t const elementSize = GetElementSize();
|
||||
uint32_t const byteOffset = elementOffset * elementSize;
|
||||
uint8_t * bufferPointer = static_cast<uint8_t *>([m_metalBuffer contents]) + byteOffset;
|
||||
return bufferPointer;
|
||||
}
|
||||
|
||||
void MetalGPUBuffer::UpdateData(void * gpuPtr, void const * data,
|
||||
uint32_t elementOffset, uint32_t elementCount)
|
||||
{
|
||||
uint32_t const elementSize = GetElementSize();
|
||||
uint32_t const byteOffset = elementOffset * elementSize;
|
||||
uint32_t const byteCount = elementCount * elementSize;
|
||||
|
||||
ASSERT(gpuPtr != nullptr, ());
|
||||
memcpy((uint8_t *)gpuPtr + byteOffset, data, byteCount);
|
||||
}
|
||||
|
||||
void MetalGPUBuffer::Resize(ref_ptr<MetalBaseContext> context, void const * data, uint32_t elementCount)
|
||||
{
|
||||
BufferBase::Resize(elementCount);
|
||||
|
||||
id<MTLDevice> device = context->GetMetalDevice();
|
||||
uint32_t const sizeInBytes = GetCapacity() * GetElementSize();
|
||||
if (data != nil)
|
||||
{
|
||||
m_metalBuffer = [device newBufferWithBytes:data
|
||||
length:sizeInBytes
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_metalBuffer = [device newBufferWithLength:sizeInBytes
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
}
|
||||
|
||||
// If we have already set up data, we have to call SetDataSize.
|
||||
if (data != nullptr)
|
||||
SetDataSize(elementCount);
|
||||
}
|
||||
} // namespace metal
|
||||
|
||||
drape_ptr<DataBufferBase> DataBuffer::CreateImplForMetal(ref_ptr<GraphicsContext> context, void const * data,
|
||||
uint8_t elementSize, uint32_t capacity)
|
||||
{
|
||||
return make_unique_dp<metal::MetalGpuBufferImpl>(context, data, elementSize, capacity);
|
||||
}
|
||||
} // namespace dp
|
||||
81
libs/drape/metal/metal_gpu_program.hpp
Normal file
81
libs/drape/metal/metal_gpu_program.hpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/gpu_program.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
class MetalGpuProgram : public GpuProgram
|
||||
{
|
||||
public:
|
||||
static int8_t constexpr kInvalidBindingIndex = -1;
|
||||
struct TextureBindingInfo
|
||||
{
|
||||
int8_t m_textureBindingIndex = kInvalidBindingIndex;
|
||||
int8_t m_samplerBindingIndex = kInvalidBindingIndex;
|
||||
};
|
||||
using TexturesBindingInfo = std::map<std::string, TextureBindingInfo>;
|
||||
|
||||
MetalGpuProgram(std::string const & programName, id<MTLFunction> vertexShader, id<MTLFunction> fragmentShader,
|
||||
int8_t vsUniformsBindingIndex, int8_t fsUniformsBindingIndex,
|
||||
TexturesBindingInfo && vertexTextureBindingInfo, TexturesBindingInfo && fragmentTextureBindingInfo,
|
||||
MTLVertexDescriptor * vertexDescriptor)
|
||||
: GpuProgram(programName)
|
||||
, m_vertexShader(vertexShader)
|
||||
, m_fragmentShader(fragmentShader)
|
||||
, m_vsUniformsBindingIndex(vsUniformsBindingIndex)
|
||||
, m_fsUniformsBindingIndex(fsUniformsBindingIndex)
|
||||
, m_vertexTextureBindingInfo(std::move(vertexTextureBindingInfo))
|
||||
, m_fragmentTextureBindingInfo(std::move(fragmentTextureBindingInfo))
|
||||
, m_vertexDescriptor(vertexDescriptor)
|
||||
{}
|
||||
|
||||
void Bind() override {}
|
||||
void Unbind() override {}
|
||||
|
||||
id<MTLFunction> GetVertexShader() const { return m_vertexShader; }
|
||||
id<MTLFunction> GetFragmentShader() const { return m_fragmentShader; }
|
||||
|
||||
int8_t GetVertexShaderUniformsBindingIndex() const { return m_vsUniformsBindingIndex; }
|
||||
int8_t GetFragmentShaderUniformsBindingIndex() const { return m_fsUniformsBindingIndex; }
|
||||
|
||||
TextureBindingInfo const & GetVertexTextureBindingInfo(std::string const & textureName) const
|
||||
{
|
||||
return GetTextureBindingInfo(m_vertexTextureBindingInfo, textureName);
|
||||
}
|
||||
|
||||
TextureBindingInfo const & GetFragmentTextureBindingInfo(std::string const & textureName) const
|
||||
{
|
||||
return GetTextureBindingInfo(m_fragmentTextureBindingInfo, textureName);
|
||||
}
|
||||
|
||||
MTLVertexDescriptor * GetVertexDescriptor() const { return m_vertexDescriptor; }
|
||||
|
||||
private:
|
||||
TextureBindingInfo const & GetTextureBindingInfo(TexturesBindingInfo const & bindingInfo,
|
||||
std::string const & textureName) const
|
||||
{
|
||||
static TextureBindingInfo kEmptyBinding;
|
||||
auto const it = bindingInfo.find(textureName);
|
||||
if (it == bindingInfo.cend())
|
||||
return kEmptyBinding;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
id<MTLFunction> m_vertexShader;
|
||||
id<MTLFunction> m_fragmentShader;
|
||||
int8_t const m_vsUniformsBindingIndex;
|
||||
int8_t const m_fsUniformsBindingIndex;
|
||||
TexturesBindingInfo const m_vertexTextureBindingInfo;
|
||||
TexturesBindingInfo const m_fragmentTextureBindingInfo;
|
||||
MTLVertexDescriptor * m_vertexDescriptor;
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
156
libs/drape/metal/metal_mesh_object_impl.mm
Normal file
156
libs/drape/metal/metal_mesh_object_impl.mm
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/metal/metal_base_context.hpp"
|
||||
#include "drape/mesh_object.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
MTLPrimitiveType GetPrimitiveType(MeshObject::DrawPrimitive primitive)
|
||||
{
|
||||
switch (primitive)
|
||||
{
|
||||
case MeshObject::DrawPrimitive::Triangles: return MTLPrimitiveTypeTriangle;
|
||||
case MeshObject::DrawPrimitive::TriangleStrip: return MTLPrimitiveTypeTriangleStrip;
|
||||
case MeshObject::DrawPrimitive::LineStrip: return MTLPrimitiveTypeLineStrip;
|
||||
}
|
||||
CHECK(false, ("Unsupported type"));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class MetalMeshObjectImpl : public MeshObjectImpl
|
||||
{
|
||||
public:
|
||||
MetalMeshObjectImpl(ref_ptr<dp::MeshObject> mesh)
|
||||
: m_mesh(std::move(mesh))
|
||||
{}
|
||||
|
||||
void Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program) override
|
||||
{
|
||||
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
|
||||
id<MTLDevice> device = metalContext->GetMetalDevice();
|
||||
|
||||
m_geometryBuffers.resize(m_mesh->m_buffers.size());
|
||||
for (size_t i = 0; i < m_mesh->m_buffers.size(); i++)
|
||||
{
|
||||
auto const sizeInBytes = m_mesh->m_buffers[i]->GetSizeInBytes();
|
||||
if (sizeInBytes == 0)
|
||||
continue;
|
||||
|
||||
m_geometryBuffers[i] = [device newBufferWithBytes:m_mesh->m_buffers[i]->GetData()
|
||||
length:sizeInBytes
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
std::ostringstream ss;
|
||||
ss << "MeshVB:";
|
||||
for (size_t j = 0; j < m_mesh->m_buffers[i]->m_attributes.size(); j++)
|
||||
{
|
||||
ss << m_mesh->m_buffers[i]->m_attributes[j].m_attributeName;
|
||||
if (j + 1 < m_mesh->m_buffers[i]->m_attributes.size())
|
||||
ss << "+";
|
||||
}
|
||||
m_geometryBuffers[i].label = @(ss.str().c_str());
|
||||
}
|
||||
|
||||
if (!m_mesh->m_indices.empty())
|
||||
{
|
||||
m_indexBuffer = [device newBufferWithBytes:m_mesh->m_indices.data()
|
||||
length:m_mesh->m_indices.size() * sizeof(uint16_t)
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
m_indexBuffer.label = @"MeshIB";
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
m_geometryBuffers.clear();
|
||||
m_indexBuffer = nil;
|
||||
}
|
||||
|
||||
void UpdateBuffer(ref_ptr<dp::GraphicsContext> context, uint32_t bufferInd) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
CHECK_LESS(bufferInd, static_cast<uint32_t>(m_geometryBuffers.size()), ());
|
||||
|
||||
auto & buffer = m_mesh->m_buffers[bufferInd];
|
||||
auto const sizeInBytes = buffer->GetSizeInBytes();
|
||||
CHECK(sizeInBytes != 0, ());
|
||||
|
||||
uint8_t * bufferPointer = (uint8_t *)[m_geometryBuffers[bufferInd] contents];
|
||||
memcpy(bufferPointer, buffer->GetData(), sizeInBytes);
|
||||
}
|
||||
|
||||
void UpdateIndexBuffer(ref_ptr<dp::GraphicsContext> context) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
CHECK(m_indexBuffer != nil, ());
|
||||
|
||||
auto const sizeInBytes = m_mesh->m_indices.size() * sizeof(uint16_t);
|
||||
CHECK(sizeInBytes != 0, ());
|
||||
|
||||
uint8_t * bufferPointer = (uint8_t *)[m_indexBuffer contents];
|
||||
memcpy(bufferPointer, m_mesh->m_indices.data(), sizeInBytes);
|
||||
}
|
||||
|
||||
void Bind(ref_ptr<dp::GpuProgram> program) override {}
|
||||
|
||||
void Unbind() override {}
|
||||
|
||||
void DrawPrimitives(ref_ptr<dp::GraphicsContext> context, uint32_t vertexCount,
|
||||
uint32_t startVertex) override
|
||||
{
|
||||
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
|
||||
if (!metalContext->HasAppliedPipelineState())
|
||||
return;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = metalContext->GetCommandEncoder();
|
||||
for (size_t i = 0; i < m_geometryBuffers.size(); i++)
|
||||
[encoder setVertexBuffer:m_geometryBuffers[i] offset:0 atIndex:i];
|
||||
|
||||
[encoder drawPrimitives:GetPrimitiveType(m_mesh->m_drawPrimitive)
|
||||
vertexStart:startVertex
|
||||
vertexCount:vertexCount];
|
||||
}
|
||||
|
||||
void DrawPrimitivesIndexed(ref_ptr<dp::GraphicsContext> context, uint32_t indexCount,
|
||||
uint32_t startIndex) override
|
||||
{
|
||||
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
|
||||
if (!metalContext->HasAppliedPipelineState())
|
||||
return;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = metalContext->GetCommandEncoder();
|
||||
for (size_t i = 0; i < m_geometryBuffers.size(); i++)
|
||||
[encoder setVertexBuffer:m_geometryBuffers[i] offset:0 atIndex:i];
|
||||
|
||||
CHECK(m_indexBuffer != nil, ());
|
||||
[encoder drawIndexedPrimitives:GetPrimitiveType(m_mesh->m_drawPrimitive)
|
||||
indexCount:indexCount
|
||||
indexType:MTLIndexTypeUInt16
|
||||
indexBuffer:m_indexBuffer
|
||||
indexBufferOffset:startIndex * sizeof(uint16_t)];
|
||||
}
|
||||
|
||||
private:
|
||||
ref_ptr<dp::MeshObject> m_mesh;
|
||||
std::vector<id<MTLBuffer>> m_geometryBuffers;
|
||||
id<MTLBuffer> m_indexBuffer;
|
||||
};
|
||||
} // namespace metal
|
||||
|
||||
#ifdef OMIM_METAL_AVAILABLE
|
||||
void MeshObject::InitForMetal()
|
||||
{
|
||||
m_impl = make_unique_dp<metal::MetalMeshObjectImpl>(make_ref(this));
|
||||
}
|
||||
#endif // OMIM_METAL_AVAILABLE
|
||||
} // namespace dp
|
||||
79
libs/drape/metal/metal_states.hpp
Normal file
79
libs/drape/metal/metal_states.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/metal/metal_gpu_program.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/texture_types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
class MetalStates
|
||||
{
|
||||
public:
|
||||
struct DepthStencilKey
|
||||
{
|
||||
void SetDepthTestEnabled(bool enabled);
|
||||
void SetDepthTestFunction(TestFunction depthFunction);
|
||||
void SetStencilTestEnabled(bool enabled);
|
||||
void SetStencilFunction(StencilFace face, TestFunction stencilFunction);
|
||||
void SetStencilActions(StencilFace face, StencilAction stencilFailAction, StencilAction depthFailAction,
|
||||
StencilAction passAction);
|
||||
bool operator<(DepthStencilKey const & rhs) const;
|
||||
MTLDepthStencilDescriptor * BuildDescriptor() const;
|
||||
|
||||
bool m_depthEnabled = false;
|
||||
bool m_stencilEnabled = false;
|
||||
TestFunction m_depthFunction = TestFunction::Always;
|
||||
uint64_t m_stencil = 0;
|
||||
};
|
||||
|
||||
struct PipelineKey
|
||||
{
|
||||
PipelineKey() = default;
|
||||
PipelineKey(ref_ptr<GpuProgram> program, MTLPixelFormat colorFormat, MTLPixelFormat depthStencilFormat,
|
||||
bool blendingEnabled);
|
||||
|
||||
bool operator<(PipelineKey const & rhs) const;
|
||||
MTLRenderPipelineDescriptor * BuildDescriptor() const;
|
||||
|
||||
ref_ptr<GpuProgram> m_program;
|
||||
MTLPixelFormat m_colorFormat = MTLPixelFormatInvalid;
|
||||
MTLPixelFormat m_depthStencilFormat = MTLPixelFormatInvalid;
|
||||
bool m_blendingEnabled = false;
|
||||
};
|
||||
|
||||
struct SamplerKey
|
||||
{
|
||||
SamplerKey() = default;
|
||||
SamplerKey(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode);
|
||||
void Set(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode);
|
||||
bool operator<(SamplerKey const & rhs) const;
|
||||
MTLSamplerDescriptor * BuildDescriptor() const;
|
||||
|
||||
uint32_t m_sampler = 0;
|
||||
};
|
||||
|
||||
id<MTLDepthStencilState> GetDepthStencilState(id<MTLDevice> device, DepthStencilKey const & key);
|
||||
id<MTLRenderPipelineState> GetPipelineState(id<MTLDevice> device, PipelineKey const & key);
|
||||
id<MTLSamplerState> GetSamplerState(id<MTLDevice> device, SamplerKey const & key);
|
||||
|
||||
void ResetPipelineStatesCache();
|
||||
|
||||
private:
|
||||
using DepthStencilCache = std::map<DepthStencilKey, id<MTLDepthStencilState>>;
|
||||
DepthStencilCache m_depthStencilCache;
|
||||
|
||||
using PipelineCache = std::map<PipelineKey, id<MTLRenderPipelineState>>;
|
||||
PipelineCache m_pipelineCache;
|
||||
|
||||
using SamplerCache = std::map<SamplerKey, id<MTLSamplerState>>;
|
||||
SamplerCache m_samplerCache;
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
337
libs/drape/metal/metal_states.mm
Normal file
337
libs/drape/metal/metal_states.mm
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
#include "drape/metal/metal_states.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Stencil package.
|
||||
uint8_t constexpr kStencilBackFunctionByte = 7;
|
||||
uint8_t constexpr kStencilBackFailActionByte = 6;
|
||||
uint8_t constexpr kStencilBackDepthFailActionByte = 5;
|
||||
uint8_t constexpr kStencilBackPassActionByte = 4;
|
||||
uint8_t constexpr kStencilFrontFunctionByte = 3;
|
||||
uint8_t constexpr kStencilFrontFailActionByte = 2;
|
||||
uint8_t constexpr kStencilFrontDepthFailActionByte = 1;
|
||||
uint8_t constexpr kStencilFrontPassActionByte = 0;
|
||||
|
||||
// Sampler package.
|
||||
uint8_t constexpr kWrapSModeByte = 3;
|
||||
uint8_t constexpr kWrapTModeByte = 2;
|
||||
uint8_t constexpr kMagFilterByte = 1;
|
||||
uint8_t constexpr kMinFilterByte = 0;
|
||||
|
||||
template<typename T>
|
||||
void SetStateByte(T & state, uint8_t value, uint8_t byteNumber)
|
||||
{
|
||||
auto const shift = byteNumber * 8;
|
||||
auto const mask = ~(static_cast<T>(0xFF) << shift);
|
||||
state = (state & mask) | (static_cast<T>(value) << shift);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint8_t GetStateByte(T & state, uint8_t byteNumber)
|
||||
{
|
||||
return static_cast<uint8_t>((state >> byteNumber * 8) & 0xFF);
|
||||
}
|
||||
|
||||
MTLCompareFunction DecodeTestFunction(uint8_t testFunctionByte)
|
||||
{
|
||||
switch (static_cast<TestFunction>(testFunctionByte))
|
||||
{
|
||||
case TestFunction::Never: return MTLCompareFunctionNever;
|
||||
case TestFunction::Less: return MTLCompareFunctionLess;
|
||||
case TestFunction::Equal: return MTLCompareFunctionEqual;
|
||||
case TestFunction::LessOrEqual: return MTLCompareFunctionLessEqual;
|
||||
case TestFunction::Greater: return MTLCompareFunctionGreater;
|
||||
case TestFunction::NotEqual: return MTLCompareFunctionNotEqual;
|
||||
case TestFunction::GreaterOrEqual: return MTLCompareFunctionGreaterEqual;
|
||||
case TestFunction::Always: return MTLCompareFunctionAlways;
|
||||
}
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
MTLStencilOperation DecodeStencilAction(uint8_t stencilActionByte)
|
||||
{
|
||||
switch (static_cast<StencilAction>(stencilActionByte))
|
||||
{
|
||||
case StencilAction::Keep: return MTLStencilOperationKeep;
|
||||
case StencilAction::Zero: return MTLStencilOperationZero;
|
||||
case StencilAction::Replace: return MTLStencilOperationReplace;
|
||||
case StencilAction::Increment: return MTLStencilOperationIncrementClamp;
|
||||
case StencilAction::IncrementWrap: return MTLStencilOperationIncrementWrap;
|
||||
case StencilAction::Decrement: return MTLStencilOperationDecrementClamp;
|
||||
case StencilAction::DecrementWrap: return MTLStencilOperationDecrementWrap;
|
||||
case StencilAction::Invert: return MTLStencilOperationInvert;
|
||||
}
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
MTLSamplerMinMagFilter DecodeTextureFilter(uint8_t textureFilterByte)
|
||||
{
|
||||
switch (static_cast<TextureFilter>(textureFilterByte))
|
||||
{
|
||||
case TextureFilter::Nearest: return MTLSamplerMinMagFilterNearest;
|
||||
case TextureFilter::Linear: return MTLSamplerMinMagFilterLinear;
|
||||
}
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
MTLSamplerAddressMode DecodeTextureWrapping(uint8_t textureWrappingByte)
|
||||
{
|
||||
switch (static_cast<TextureWrapping>(textureWrappingByte))
|
||||
{
|
||||
case TextureWrapping::ClampToEdge: return MTLSamplerAddressModeClampToEdge;
|
||||
case TextureWrapping::Repeat: return MTLSamplerAddressModeRepeat;
|
||||
}
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
bool IsStencilFormat(MTLPixelFormat format)
|
||||
{
|
||||
return format == MTLPixelFormatDepth32Float_Stencil8 ||
|
||||
format == MTLPixelFormatStencil8 ||
|
||||
format == MTLPixelFormatX32_Stencil8;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
id<MTLDepthStencilState> MetalStates::GetDepthStencilState(id<MTLDevice> device, DepthStencilKey const & key)
|
||||
{
|
||||
auto const it = m_depthStencilCache.find(key);
|
||||
if (it != m_depthStencilCache.end())
|
||||
return it->second;
|
||||
|
||||
id<MTLDepthStencilState> depthState = [device newDepthStencilStateWithDescriptor:key.BuildDescriptor()];
|
||||
CHECK(depthState != nil, ());
|
||||
m_depthStencilCache.insert(std::make_pair(key, depthState));
|
||||
return depthState;
|
||||
}
|
||||
|
||||
id<MTLRenderPipelineState> MetalStates::GetPipelineState(id<MTLDevice> device, PipelineKey const & key)
|
||||
{
|
||||
auto const it = m_pipelineCache.find(key);
|
||||
if (it != m_pipelineCache.end())
|
||||
return it->second;
|
||||
|
||||
NSError * error = nil;
|
||||
id<MTLRenderPipelineState> pipelineState = [device newRenderPipelineStateWithDescriptor:key.BuildDescriptor()
|
||||
error:&error];
|
||||
if (pipelineState == nil || error != nil)
|
||||
{
|
||||
NSLog(@"%@", error);
|
||||
CHECK(false, ("Failed to create pipeline state."));
|
||||
}
|
||||
m_pipelineCache.insert(std::make_pair(key, pipelineState));
|
||||
return pipelineState;
|
||||
}
|
||||
|
||||
id<MTLSamplerState> MetalStates::GetSamplerState(id<MTLDevice> device, SamplerKey const & key)
|
||||
{
|
||||
auto const it = m_samplerCache.find(key);
|
||||
if (it != m_samplerCache.end())
|
||||
return it->second;
|
||||
|
||||
id<MTLSamplerState> samplerState = [device newSamplerStateWithDescriptor:key.BuildDescriptor()];
|
||||
CHECK(samplerState != nil, ());
|
||||
m_samplerCache.insert(std::make_pair(key, samplerState));
|
||||
return samplerState;
|
||||
}
|
||||
|
||||
void MetalStates::ResetPipelineStatesCache()
|
||||
{
|
||||
m_pipelineCache.clear();
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetDepthTestEnabled(bool enabled)
|
||||
{
|
||||
m_depthEnabled = enabled;
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetDepthTestFunction(TestFunction depthFunction)
|
||||
{
|
||||
m_depthFunction = depthFunction;
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetStencilTestEnabled(bool enabled)
|
||||
{
|
||||
m_stencilEnabled = enabled;
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetStencilFunction(StencilFace face, TestFunction stencilFunction)
|
||||
{
|
||||
switch (face)
|
||||
{
|
||||
case StencilFace::Front:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFunction), kStencilFrontFunctionByte);
|
||||
break;
|
||||
case StencilFace::Back:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFunction), kStencilBackFunctionByte);
|
||||
break;
|
||||
case StencilFace::FrontAndBack:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFunction), kStencilFrontFunctionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFunction), kStencilBackFunctionByte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetStencilActions(StencilFace face, StencilAction stencilFailAction,
|
||||
StencilAction depthFailAction, StencilAction passAction)
|
||||
{
|
||||
switch (face)
|
||||
{
|
||||
case StencilFace::Front:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFailAction), kStencilFrontFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(depthFailAction), kStencilFrontDepthFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(passAction), kStencilFrontPassActionByte);
|
||||
break;
|
||||
case StencilFace::Back:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFailAction), kStencilBackFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(depthFailAction), kStencilBackDepthFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(passAction), kStencilBackPassActionByte);
|
||||
break;
|
||||
case StencilFace::FrontAndBack:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFailAction), kStencilFrontFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(depthFailAction), kStencilFrontDepthFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(passAction), kStencilFrontPassActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFailAction), kStencilBackFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(depthFailAction), kStencilBackDepthFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(passAction), kStencilBackPassActionByte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalStates::DepthStencilKey::operator<(DepthStencilKey const & rhs) const
|
||||
{
|
||||
if (m_depthEnabled != rhs.m_depthEnabled)
|
||||
return m_depthEnabled < rhs.m_depthEnabled;
|
||||
|
||||
if (m_stencilEnabled != rhs.m_stencilEnabled)
|
||||
return m_stencilEnabled < rhs.m_stencilEnabled;
|
||||
|
||||
if (m_depthFunction != rhs.m_depthFunction)
|
||||
return m_depthFunction < rhs.m_depthFunction;
|
||||
|
||||
return m_stencil < rhs.m_stencil;
|
||||
}
|
||||
|
||||
MTLDepthStencilDescriptor * MetalStates::DepthStencilKey::BuildDescriptor() const
|
||||
{
|
||||
MTLDepthStencilDescriptor * desc = [[MTLDepthStencilDescriptor alloc] init];
|
||||
if (m_depthEnabled)
|
||||
{
|
||||
desc.depthWriteEnabled = YES;
|
||||
desc.depthCompareFunction = DecodeTestFunction(static_cast<uint8_t>(m_depthFunction));
|
||||
}
|
||||
else
|
||||
{
|
||||
desc.depthWriteEnabled = NO;
|
||||
desc.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
}
|
||||
if (m_stencilEnabled)
|
||||
{
|
||||
MTLStencilDescriptor * frontDesc = [[MTLStencilDescriptor alloc] init];
|
||||
frontDesc.stencilCompareFunction = DecodeTestFunction(GetStateByte(m_stencil, kStencilFrontFunctionByte));
|
||||
frontDesc.stencilFailureOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilFrontFailActionByte));
|
||||
frontDesc.depthFailureOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilFrontDepthFailActionByte));
|
||||
frontDesc.depthStencilPassOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilFrontPassActionByte));
|
||||
desc.frontFaceStencil = frontDesc;
|
||||
|
||||
MTLStencilDescriptor * backDesc = [[MTLStencilDescriptor alloc] init];
|
||||
backDesc.stencilCompareFunction = DecodeTestFunction(GetStateByte(m_stencil, kStencilBackFunctionByte));
|
||||
backDesc.stencilFailureOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilBackFailActionByte));
|
||||
backDesc.depthFailureOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilBackDepthFailActionByte));
|
||||
backDesc.depthStencilPassOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilBackPassActionByte));
|
||||
desc.backFaceStencil = backDesc;
|
||||
}
|
||||
else
|
||||
{
|
||||
desc.frontFaceStencil = nil;
|
||||
desc.backFaceStencil = nil;
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
MetalStates::PipelineKey::PipelineKey(ref_ptr<GpuProgram> program, MTLPixelFormat colorFormat,
|
||||
MTLPixelFormat depthStencilFormat, bool blendingEnabled)
|
||||
: m_program(std::move(program))
|
||||
, m_colorFormat(colorFormat)
|
||||
, m_depthStencilFormat(depthStencilFormat)
|
||||
, m_blendingEnabled(blendingEnabled)
|
||||
{}
|
||||
|
||||
bool MetalStates::PipelineKey::operator<(PipelineKey const & rhs) const
|
||||
{
|
||||
if (m_program != rhs.m_program)
|
||||
return m_program < rhs.m_program;
|
||||
|
||||
if (m_colorFormat != rhs.m_colorFormat)
|
||||
return m_colorFormat < rhs.m_colorFormat;
|
||||
|
||||
if (m_depthStencilFormat != rhs.m_depthStencilFormat)
|
||||
return m_depthStencilFormat < rhs.m_depthStencilFormat;
|
||||
|
||||
return m_blendingEnabled < rhs.m_blendingEnabled;
|
||||
}
|
||||
|
||||
MTLRenderPipelineDescriptor * MetalStates::PipelineKey::BuildDescriptor() const
|
||||
{
|
||||
MTLRenderPipelineDescriptor * desc = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
desc.rasterSampleCount = 1;
|
||||
desc.vertexBuffers[0].mutability = MTLMutabilityImmutable; // The first VB is always immutable.
|
||||
ref_ptr<MetalGpuProgram> metalProgram = m_program;
|
||||
desc.vertexFunction = metalProgram->GetVertexShader();
|
||||
desc.fragmentFunction = metalProgram->GetFragmentShader();
|
||||
desc.vertexDescriptor = metalProgram->GetVertexDescriptor();
|
||||
MTLRenderPipelineColorAttachmentDescriptor * colorAttachment = desc.colorAttachments[0];
|
||||
colorAttachment.pixelFormat = m_colorFormat;
|
||||
desc.depthAttachmentPixelFormat = m_depthStencilFormat;
|
||||
if (IsStencilFormat(m_depthStencilFormat))
|
||||
desc.stencilAttachmentPixelFormat = m_depthStencilFormat;
|
||||
else
|
||||
desc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;
|
||||
colorAttachment.blendingEnabled = m_blendingEnabled ? YES : NO;
|
||||
colorAttachment.rgbBlendOperation = MTLBlendOperationAdd;
|
||||
colorAttachment.alphaBlendOperation = MTLBlendOperationAdd;
|
||||
colorAttachment.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
colorAttachment.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
colorAttachment.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
colorAttachment.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
return desc;
|
||||
}
|
||||
|
||||
MetalStates::SamplerKey::SamplerKey(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode)
|
||||
{
|
||||
Set(filter, wrapSMode, wrapTMode);
|
||||
}
|
||||
|
||||
void MetalStates::SamplerKey::Set(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode)
|
||||
{
|
||||
SetStateByte(m_sampler, static_cast<uint8_t>(filter), kMinFilterByte);
|
||||
SetStateByte(m_sampler, static_cast<uint8_t>(filter), kMagFilterByte);
|
||||
SetStateByte(m_sampler, static_cast<uint8_t>(wrapSMode), kWrapSModeByte);
|
||||
SetStateByte(m_sampler, static_cast<uint8_t>(wrapTMode), kWrapTModeByte);
|
||||
}
|
||||
|
||||
bool MetalStates::SamplerKey::operator<(SamplerKey const & rhs) const
|
||||
{
|
||||
return m_sampler < rhs.m_sampler;
|
||||
}
|
||||
|
||||
MTLSamplerDescriptor * MetalStates::SamplerKey::BuildDescriptor() const
|
||||
{
|
||||
MTLSamplerDescriptor * desc = [[MTLSamplerDescriptor alloc] init];
|
||||
desc.minFilter = DecodeTextureFilter(GetStateByte(m_sampler, kMinFilterByte));
|
||||
desc.magFilter = DecodeTextureFilter(GetStateByte(m_sampler, kMagFilterByte));
|
||||
desc.sAddressMode = DecodeTextureWrapping(GetStateByte(m_sampler, kWrapSModeByte));
|
||||
desc.tAddressMode = DecodeTextureWrapping(GetStateByte(m_sampler, kWrapTModeByte));
|
||||
return desc;
|
||||
}
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue