diff --git a/CMakeLists.txt b/CMakeLists.txt index 1db54ade4c9..48592b16b6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1216,6 +1216,7 @@ if(MBGL_WITH_METAL) ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/drawable.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/drawable_impl.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/drawable_builder.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/index_buffer_resource.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/layer_group.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/mtl.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/offscreen_texture.cpp @@ -1227,6 +1228,7 @@ if(MBGL_WITH_METAL) ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/uniform_buffer.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/upload_pass.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/vertex_attribute.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/mtl/vertex_buffer_resource.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/shader_program.cpp ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/background.cpp ${PROJECT_SOURCE_DIR}src/mbgl/shaders/mtl/background_pattern.cpp diff --git a/bazel/core.bzl b/bazel/core.bzl index 613bf535c41..ebc73f9b64e 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -895,7 +895,7 @@ MLN_OPENGL_HEADERS = [ "include/mbgl/gl/renderable_resource.hpp", "include/mbgl/gl/renderer_backend.hpp", "include/mbgl/layermanager/location_indicator_layer_factory.hpp", - "include/mbgl/platform/gl_functions.hpp" + "include/mbgl/platform/gl_functions.hpp", ] MLN_DRAWABLES_SOURCE = [ @@ -1009,7 +1009,7 @@ MLN_DRAWABLES_GL_HEADERS = [ "include/mbgl/gl/vertex_attribute_gl.hpp", "include/mbgl/gl/texture2d.hpp", "include/mbgl/shaders/gl/shader_program_gl.hpp", - "include/mbgl/shaders/gl/shader_group_gl.hpp" + "include/mbgl/shaders/gl/shader_group_gl.hpp", ] MLN_DRAWABLES_MTL_SOURCE = [ @@ -1019,6 +1019,7 @@ MLN_DRAWABLES_MTL_SOURCE = [ "src/mbgl/mtl/drawable.cpp", "src/mbgl/mtl/drawable_impl.hpp", "src/mbgl/mtl/drawable_builder.cpp", + "src/mbgl/mtl/index_buffer_resource.cpp", "src/mbgl/mtl/layer_group.cpp", "src/mbgl/mtl/mtl.cpp", "src/mbgl/mtl/offscreen_texture.cpp", @@ -1030,6 +1031,7 @@ MLN_DRAWABLES_MTL_SOURCE = [ "src/mbgl/mtl/uniform_buffer.cpp", "src/mbgl/mtl/upload_pass.cpp", "src/mbgl/mtl/vertex_attribute.cpp", + "src/mbgl/mtl/vertex_buffer_resource.cpp", "src/mbgl/shaders/mtl/shader_program.cpp", "src/mbgl/shaders/mtl/background.cpp", "src/mbgl/shaders/mtl/background_pattern.cpp", diff --git a/include/mbgl/gfx/rendering_stats.hpp b/include/mbgl/gfx/rendering_stats.hpp index 4edbc1600f8..b64804c05e8 100644 --- a/include/mbgl/gfx/rendering_stats.hpp +++ b/include/mbgl/gfx/rendering_stats.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + namespace mbgl { namespace gfx { @@ -7,38 +10,44 @@ struct RenderingStats { RenderingStats() = default; bool isZero() const; - int numDrawCalls; - int numActiveTextures; - int numCreatedTextures; - int numBuffers; - int numFrameBuffers; + int numFrames = 0; + int numDrawCalls = 0; + int totalDrawCalls = 0; - int memTextures; - int memIndexBuffers; - int memVertexBuffers; + int numCreatedTextures = 0; + int numActiveTextures = 0; + int numTextureBindings = 0; + int numTextureUpdates = 0; + std::size_t textureUpdateBytes = 0; - int stencilClears = 0; - int stencilUpdates = 0; + int numBuffers = 0; + int numFrameBuffers = 0; - RenderingStats& operator+=(const RenderingStats& right); -}; + int numIndexBuffers = 0; + std::size_t indexUpdateBytes = 0; -inline RenderingStats& RenderingStats::operator+=(const RenderingStats& r) { - numDrawCalls += r.numDrawCalls; - numActiveTextures += r.numActiveTextures; - numCreatedTextures += r.numCreatedTextures; - numBuffers += r.numBuffers; - numFrameBuffers += r.numFrameBuffers; + int numVertexBuffers = 0; + std::size_t vertexUpdateBytes = 0; - memTextures += r.memTextures; - memIndexBuffers += r.memIndexBuffers; - memVertexBuffers += r.memVertexBuffers; + int numUniformBuffers = 0; + int numUniformUpdates = 0; + std::size_t uniformUpdateBytes = 0; - stencilClears += r.stencilClears; - stencilUpdates += r.stencilUpdates; + int memTextures = 0; + int memBuffers = 0; + int memIndexBuffers = 0; + int memVertexBuffers = 0; + int memUniformBuffers = 0; - return *this; -} + int stencilClears = 0; + int stencilUpdates = 0; + + RenderingStats& operator+=(const RenderingStats&); + +#if !defined(NDEBUG) + std::string toString(std::string_view separator) const; +#endif +}; } // namespace gfx } // namespace mbgl diff --git a/include/mbgl/gfx/uniform_buffer.hpp b/include/mbgl/gfx/uniform_buffer.hpp index 5f29dd5ca85..1e86b09b18e 100644 --- a/include/mbgl/gfx/uniform_buffer.hpp +++ b/include/mbgl/gfx/uniform_buffer.hpp @@ -22,7 +22,8 @@ class UniformBuffer { protected: UniformBuffer(std::size_t size_) : size(size_) {} - UniformBuffer(const UniformBuffer&) = default; + UniformBuffer(const UniformBuffer& other) + : size(other.size) {} UniformBuffer(UniformBuffer&& other) : size(other.size) {} @@ -32,8 +33,9 @@ class UniformBuffer { std::size_t getSize() const { return size; } + UniformBuffer& operator=(const UniformBuffer&) = delete; + protected: - UniformBuffer& operator=(const UniformBuffer&) = default; UniformBuffer& operator=(UniformBuffer&& other) { size = other.size; return *this; diff --git a/include/mbgl/gl/uniform_buffer_gl.hpp b/include/mbgl/gl/uniform_buffer_gl.hpp index 5edfaa2f399..579f5f523bf 100644 --- a/include/mbgl/gl/uniform_buffer_gl.hpp +++ b/include/mbgl/gl/uniform_buffer_gl.hpp @@ -7,10 +7,10 @@ namespace mbgl { namespace gl { class UniformBufferGL final : public gfx::UniformBuffer { + UniformBufferGL(const UniformBufferGL&); + public: UniformBufferGL(const void* data, std::size_t size_); - UniformBufferGL(const UniformBufferGL& other) - : UniformBuffer(other) {} UniformBufferGL(UniformBufferGL&& other) : UniformBuffer(std::move(other)) {} ~UniformBufferGL() override; @@ -18,6 +18,8 @@ class UniformBufferGL final : public gfx::UniformBuffer { BufferID getID() const { return id; } void update(const void* data, std::size_t size_) override; + UniformBufferGL clone() const { return {*this}; } + protected: BufferID id = 0; uint32_t hash; @@ -42,7 +44,7 @@ class UniformBufferArrayGL final : public gfx::UniformBufferArray { private: std::unique_ptr copy(const gfx::UniformBuffer& uniformBuffers) override { - return std::make_unique(static_cast(uniformBuffers)); + return std::make_unique(static_cast(uniformBuffers).clone()); } }; diff --git a/include/mbgl/mtl/buffer_resource.hpp b/include/mbgl/mtl/buffer_resource.hpp index 31be915b756..69d4b4114cf 100644 --- a/include/mbgl/mtl/buffer_resource.hpp +++ b/include/mbgl/mtl/buffer_resource.hpp @@ -10,32 +10,40 @@ namespace mbgl { namespace mtl { +class Context; + class BufferResource { public: - BufferResource() = default; + BufferResource() = delete; /** @brief Create a new Metal buffer @param device The Metal device on which to create the buffer. @param raw Data to use for the contents of the new buffer. May be null. @param size The minimum size of the new buffer. Must be non-zero. @param usage A `MTL::ResourceOptions` value. Currently, only `ResourceStorageModeShared` is supported. */ - BufferResource(MTLDevicePtr device, const void* raw, std::size_t size, MTL::ResourceOptions usage); - BufferResource(const BufferResource&); + BufferResource(Context& context_, const void* raw, std::size_t size, MTL::ResourceOptions usage); BufferResource(BufferResource&&); + virtual ~BufferResource(); + BufferResource& operator=(BufferResource&&); + BufferResource clone() const; + void update(const void* data, std::size_t size, std::size_t offset); std::size_t getSizeInBytes() const { return buffer ? buffer->length() : 0; } void* contents() const { return buffer ? buffer->contents() : nullptr; } + Context& getContext() const { return context; } const MTLBufferPtr& getMetalBuffer() const { return buffer; } operator bool() const { return buffer.operator bool(); } + bool operator!() const { return !buffer.operator bool(); } protected: - MTLDevicePtr device; + Context& context; MTLBufferPtr buffer; + NS::UInteger size; NS::UInteger usage; }; diff --git a/include/mbgl/mtl/context.hpp b/include/mbgl/mtl/context.hpp index 943cc801784..3efa9bbd949 100644 --- a/include/mbgl/mtl/context.hpp +++ b/include/mbgl/mtl/context.hpp @@ -44,9 +44,6 @@ class Context final : public gfx::Context { std::unique_ptr createCommandEncoder() override; - gfx::RenderingStats& renderingStats() { return stats; } - const gfx::RenderingStats& renderingStats() const override { return stats; } - BufferResource createBuffer(const void* data, std::size_t size, gfx::BufferUsageType) const; UniqueShaderProgram createProgram(std::string name, @@ -59,6 +56,8 @@ class Context final : public gfx::Context { MTLTexturePtr createMetalTexture(MTLTextureDescriptorPtr textureDescriptor) const; MTLSamplerStatePtr createMetalSamplerState(MTLSamplerDescriptorPtr samplerDescriptor) const; + void clear(); + // Actually remove the objects we marked as abandoned with the above methods. void performCleanup() override; @@ -119,6 +118,9 @@ class Context final : public gfx::Context { /// Get a reusable buffer containing the standard fixed tile indexes const BufferResource& getTileIndexBuffer(); + /// Get a buffer to be bound to unused vertex buffers + const gfx::UniqueVertexBufferResource& getEmptyVertexBuffer(); + bool renderTileClippingMasks(gfx::RenderPass& renderPass, RenderStaticData& staticData, const std::vector& tileUBOs); @@ -130,14 +132,14 @@ class Context final : public gfx::Context { std::optional tileVertexBuffer; std::optional tileIndexBuffer; + gfx::UniqueVertexBufferResource emptyVertexBuffer; + gfx::ShaderProgramBasePtr clipMaskShader; MTLDepthStencilStatePtr clipMaskDepthStencilState; MTLRenderPipelineStatePtr clipMaskPipelineState; - BufferResource clipMaskUniformsBuffer; + std::optional clipMaskUniformsBuffer; bool clipMaskUniformsBufferUsed = false; const gfx::Renderable* stencilStateRenderable = nullptr; - - gfx::RenderingStats stats; }; } // namespace mtl diff --git a/include/mbgl/mtl/index_buffer_resource.hpp b/include/mbgl/mtl/index_buffer_resource.hpp index 178bd40ff99..4abb7c5e6e1 100644 --- a/include/mbgl/mtl/index_buffer_resource.hpp +++ b/include/mbgl/mtl/index_buffer_resource.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include namespace mbgl { @@ -8,10 +9,10 @@ namespace mtl { class IndexBufferResource : public gfx::IndexBufferResource { public: IndexBufferResource() = default; - IndexBufferResource(BufferResource&& ptr) - : buffer(std::move(ptr)) {} + IndexBufferResource(BufferResource&&); IndexBufferResource(IndexBufferResource&& other) : buffer(std::move(other.buffer)) {} + ~IndexBufferResource() override; std::size_t getSizeInBytes() const { return buffer.getSizeInBytes(); } void* contents() const { return buffer.contents(); } diff --git a/include/mbgl/mtl/uniform_buffer.hpp b/include/mbgl/mtl/uniform_buffer.hpp index 1e9fea93d2b..8e5c9735684 100644 --- a/include/mbgl/mtl/uniform_buffer.hpp +++ b/include/mbgl/mtl/uniform_buffer.hpp @@ -9,12 +9,14 @@ namespace mtl { class UniformBuffer final : public gfx::UniformBuffer { public: UniformBuffer(BufferResource&&); - UniformBuffer(const UniformBuffer&) = default; + UniformBuffer(const UniformBuffer&) = delete; UniformBuffer(UniformBuffer&&); - ~UniformBuffer() override = default; + ~UniformBuffer() override; const BufferResource& getBufferResource() const { return buffer; } + UniformBuffer clone() const { return {buffer.clone()}; } + void update(const void* data, std::size_t size_) override; protected: @@ -40,7 +42,7 @@ class UniformBufferArray final : public gfx::UniformBufferArray { private: gfx::UniqueUniformBuffer copy(const gfx::UniformBuffer& buffer) override { - return std::make_unique(static_cast(buffer)); + return std::make_unique(static_cast(buffer).clone()); } }; diff --git a/include/mbgl/mtl/vertex_buffer_resource.hpp b/include/mbgl/mtl/vertex_buffer_resource.hpp index 70a0415bbaa..230aa791c88 100644 --- a/include/mbgl/mtl/vertex_buffer_resource.hpp +++ b/include/mbgl/mtl/vertex_buffer_resource.hpp @@ -8,10 +8,10 @@ namespace mtl { class VertexBufferResource : public gfx::VertexBufferResource { public: VertexBufferResource() = default; - VertexBufferResource(BufferResource&& ptr) - : buffer(std::move(ptr)) {} + VertexBufferResource(BufferResource&&); VertexBufferResource(VertexBufferResource&& other) : buffer(std::move(other.buffer)) {} + ~VertexBufferResource() override; std::size_t getSizeInBytes() const { return buffer.getSizeInBytes(); } void* contents() const { return buffer.contents(); } diff --git a/src/mbgl/gfx/context.hpp b/src/mbgl/gfx/context.hpp index 16d2ff7f867..c57da13f432 100644 --- a/src/mbgl/gfx/context.hpp +++ b/src/mbgl/gfx/context.hpp @@ -103,7 +103,8 @@ class Context { public: virtual std::unique_ptr createCommandEncoder() = 0; - virtual const RenderingStats& renderingStats() const = 0; + gfx::RenderingStats& renderingStats() { return stats; } + const gfx::RenderingStats& renderingStats() const { return stats; } #if !defined(NDEBUG) public: @@ -156,6 +157,9 @@ class Context { } #endif + +protected: + gfx::RenderingStats stats; }; } // namespace gfx diff --git a/src/mbgl/gfx/rendering_stats.cpp b/src/mbgl/gfx/rendering_stats.cpp index 0a239ebd113..39794fb1589 100644 --- a/src/mbgl/gfx/rendering_stats.cpp +++ b/src/mbgl/gfx/rendering_stats.cpp @@ -1,12 +1,74 @@ #include +#include +#include +#include + namespace mbgl { namespace gfx { bool RenderingStats::isZero() const { - return numActiveTextures == 0 && numCreatedTextures == 0 && numBuffers == 0 && numFrameBuffers == 0 && - memTextures == 0 && memIndexBuffers == 0 && memVertexBuffers == 0; + const auto expectedZeros = {numActiveTextures, + numTextureBindings, + numBuffers, + numVertexBuffers, + numIndexBuffers, + numUniformBuffers, + numFrameBuffers, + memTextures, + memBuffers, + memIndexBuffers, + memVertexBuffers, + memUniformBuffers}; + return std::all_of(expectedZeros.begin(), expectedZeros.end(), [](auto x) { return x == 0; }); +} + +RenderingStats& RenderingStats::operator+=(const RenderingStats& r) { + numFrames += r.numFrames; + numDrawCalls += r.numDrawCalls; + totalDrawCalls += r.totalDrawCalls; + numCreatedTextures += r.numCreatedTextures; + numActiveTextures += r.numActiveTextures; + numTextureBindings += r.numTextureBindings; + numTextureUpdates += r.numTextureUpdates; + textureUpdateBytes += r.textureUpdateBytes; + numBuffers += r.numBuffers; + numFrameBuffers += r.numFrameBuffers; + numIndexBuffers += r.numIndexBuffers; + indexUpdateBytes += r.indexUpdateBytes; + numVertexBuffers += r.numVertexBuffers; + vertexUpdateBytes += r.vertexUpdateBytes; + numUniformBuffers += r.numUniformBuffers; + numUniformUpdates += r.numUniformUpdates; + uniformUpdateBytes += r.uniformUpdateBytes; + memTextures += r.memTextures; + memBuffers += r.memBuffers; + memIndexBuffers += r.memIndexBuffers; + memVertexBuffers += r.memVertexBuffers; + memUniformBuffers += r.memUniformBuffers; + stencilClears += r.stencilClears; + stencilUpdates += r.stencilUpdates; + return *this; +} + +#if !defined(NDEBUG) +std::string RenderingStats::toString(std::string_view sep) const { + std::stringstream ss; + ss << "numFrames = " << numFrames << sep << "numDrawCalls = " << numDrawCalls << sep + << "totalDrawCalls = " << totalDrawCalls << sep << "numCreatedTextures = " << numCreatedTextures << sep + << "numActiveTextures = " << numActiveTextures << sep << "numTextureBindings = " << numTextureBindings << sep + << "numTextureUpdates = " << numTextureUpdates << sep << "textureUpdateBytes = " << textureUpdateBytes << sep + << "numBuffers = " << numBuffers << sep << "numFrameBuffers = " << numFrameBuffers << sep + << "numIndexBuffers = " << numIndexBuffers << sep << "indexUpdateBytes = " << indexUpdateBytes << sep + << "numVertexBuffers = " << numVertexBuffers << sep << "vertexUpdateBytes = " << vertexUpdateBytes << sep + << "numUniformBuffers = " << numUniformBuffers << sep << "numUniformUpdates = " << numUniformUpdates << sep + << "uniformUpdateBytes = " << uniformUpdateBytes << sep << "memTextures = " << memTextures << sep + << "memBuffers = " << memBuffers << sep << "memIndexBuffers = " << memIndexBuffers << sep + << "memVertexBuffers = " << memVertexBuffers << sep << "memUniformBuffers = " << memUniformBuffers << sep + << "stencilClears = " << stencilClears << sep << "stencilUpdates = " << stencilUpdates << sep; + return ss.str(); } +#endif } // namespace gfx -} // namespace mbgl \ No newline at end of file +} // namespace mbgl diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 4907dbb3061..0c655d0dc65 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -63,14 +63,17 @@ static_assert(underlying_type(UniformDataType::FloatMat4) == GL_FLOAT_MAT4, "Ope static_assert(underlying_type(UniformDataType::Sampler2D) == GL_SAMPLER_2D, "OpenGL type mismatch"); static_assert(underlying_type(UniformDataType::SamplerCube) == GL_SAMPLER_CUBE, "OpenGL type mismatch"); +namespace { +GLint getMaxVertexAttribs() { + GLint value = 0; + MBGL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value)); + return value; +} +} // namespace + Context::Context(RendererBackend& backend_) - : gfx::Context([] { - GLint value; - MBGL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value)); - return value; - }()), - backend(backend_), - stats() {} + : gfx::Context(/*maximumVertexBindingCount=*/getMaxVertexAttribs()), + backend(backend_) {} Context::~Context() noexcept { if (cleanupOnDestruction) { @@ -616,14 +619,6 @@ std::unique_ptr Context::createCommandEncoder() { return std::make_unique(*this); } -gfx::RenderingStats& Context::renderingStats() { - return stats; -} - -const gfx::RenderingStats& Context::renderingStats() const { - return stats; -} - void Context::finish() { MBGL_CHECK_ERROR(glFinish()); } diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index a8f030d4cda..2a407b107b0 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -44,9 +44,6 @@ class Context final : public gfx::Context { std::unique_ptr createCommandEncoder() override; - gfx::RenderingStats& renderingStats(); - const gfx::RenderingStats& renderingStats() const override; - void initializeExtensions(const std::function&); void enableDebugging(); @@ -131,7 +128,6 @@ class Context final : public gfx::Context { RendererBackend& backend; bool cleanupOnDestruction = true; - gfx::RenderingStats stats; std::unique_ptr debugging; public: diff --git a/src/mbgl/gl/uniform_buffer_gl.cpp b/src/mbgl/gl/uniform_buffer_gl.cpp index b2df0c20f4f..209ddc1ec18 100644 --- a/src/mbgl/gl/uniform_buffer_gl.cpp +++ b/src/mbgl/gl/uniform_buffer_gl.cpp @@ -20,6 +20,16 @@ UniformBufferGL::UniformBufferGL(const void* data_, std::size_t size_) MBGL_CHECK_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, 0)); } +UniformBufferGL::UniformBufferGL(const UniformBufferGL& other) + : UniformBuffer(other), + hash(other.hash) { + MBGL_CHECK_ERROR(glGenBuffers(1, &id)); + MBGL_CHECK_ERROR(glCopyBufferSubData(other.id, id, 0, 0, size)); + MBGL_CHECK_ERROR(glBindBuffer(GL_COPY_READ_BUFFER, other.id)); + MBGL_CHECK_ERROR(glBindBuffer(GL_COPY_WRITE_BUFFER, id)); + MBGL_CHECK_ERROR(glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, size)); +} + UniformBufferGL::~UniformBufferGL() { if (id) { MBGL_CHECK_ERROR(glDeleteBuffers(1, &id)); diff --git a/src/mbgl/mtl/buffer_resource.cpp b/src/mbgl/mtl/buffer_resource.cpp index 02521cd1917..72032bcdae1 100644 --- a/src/mbgl/mtl/buffer_resource.cpp +++ b/src/mbgl/mtl/buffer_resource.cpp @@ -1,37 +1,52 @@ #include +#include +#include + #include + #include namespace mbgl { namespace mtl { -BufferResource::BufferResource(MTLDevicePtr device_, const void* data, std::size_t size, MTL::ResourceOptions usage_) - : device(std::move(device_)), +BufferResource::BufferResource(Context& context_, const void* data, std::size_t size_, MTL::ResourceOptions usage_) + : context(context_), + size(static_cast(size_)), usage(usage_) { - if (data && size) { - buffer = NS::TransferPtr(device->newBuffer(data, static_cast(size), usage)); - } else { - buffer = NS::TransferPtr(device->newBuffer(static_cast(size), usage)); - } -} - -BufferResource::BufferResource(const BufferResource& other) - : device(other.device), - usage(other.usage) { - if (other.buffer) { - buffer = NS::TransferPtr(device->newBuffer(other.buffer->contents(), other.buffer->length(), other.usage)); + auto& device = context.getBackend().getDevice(); + buffer = NS::TransferPtr((data && size) ? device->newBuffer(data, size, usage) : device->newBuffer(size, usage)); + if (buffer) { + context.renderingStats().numBuffers++; + context.renderingStats().memBuffers += size; } } BufferResource::BufferResource(BufferResource&& other) - : device(std::move(other.device)), + : context(other.context), buffer(std::move(other.buffer)), + size(other.size), usage(other.usage) {} +BufferResource::~BufferResource() { + if (buffer) { + context.renderingStats().numBuffers--; + context.renderingStats().memBuffers -= size; + } +} + +BufferResource BufferResource::clone() const { + return {context, buffer->contents(), size, usage}; +} + BufferResource& BufferResource::operator=(BufferResource&& other) { - device = std::move(other.device); + assert(&context == &other.context); + if (buffer) { + context.renderingStats().numBuffers--; + context.renderingStats().memBuffers -= size; + } buffer = std::move(other.buffer); + size = other.size; usage = other.usage; return *this; } diff --git a/src/mbgl/mtl/context.cpp b/src/mbgl/mtl/context.cpp index 9e3585751c9..afb406d98d0 100644 --- a/src/mbgl/mtl/context.cpp +++ b/src/mbgl/mtl/context.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -37,12 +38,24 @@ constexpr uint32_t maximumVertexBindingCount = 31; Context::Context(RendererBackend& backend_) : gfx::Context(mtl::maximumVertexBindingCount), - backend(backend_), - stats() {} + backend(backend_) {} Context::~Context() noexcept { if (cleanupOnDestruction) { performCleanup(); + + emptyVertexBuffer.reset(); + tileVertexBuffer.reset(); + tileIndexBuffer.reset(); + clipMaskShader.reset(); + clipMaskDepthStencilState.reset(); + clipMaskPipelineState.reset(); + clipMaskUniformsBuffer.reset(); + stencilStateRenderable = nullptr; + +#if !defined(NDEBUG) + Log::Debug(Event::General, "Rendering Stats:\n" + stats.toString("\n")); +#endif assert(stats.isZero()); } } @@ -52,7 +65,7 @@ std::unique_ptr Context::createCommandEncoder() { } BufferResource Context::createBuffer(const void* data, std::size_t size, gfx::BufferUsageType) const { - return {backend.getDevice(), data, size, MTL::ResourceStorageModeShared}; + return {const_cast(*this), data, size, MTL::ResourceStorageModeShared}; } UniqueShaderProgram Context::createProgram(std::string name, @@ -144,6 +157,11 @@ MTLSamplerStatePtr Context::createMetalSamplerState(MTLSamplerDescriptorPtr samp return NS::TransferPtr(backend.getDevice()->newSamplerState(samplerDescriptor.get())); } +void Context::clear() { + stats.numDrawCalls = 0; + stats.numFrames++; +} + void Context::performCleanup() { clipMaskUniformsBufferUsed = false; } @@ -207,6 +225,20 @@ const BufferResource& Context::getTileIndexBuffer() { return *tileIndexBuffer; } +const gfx::UniqueVertexBufferResource& Context::getEmptyVertexBuffer() { + if (!emptyVertexBuffer) { + // This buffer is bound to vertex attribtue indexes when the uniforms are used instead and + // shaders are expected not to access the attribute values, but Metal requires a binding. + // This buffer could also hold a single default value applied for all vertices, in which case + // it could not be shared (See `MTL::VertexStepFunctionConstant` in the vertex attribute + // descriptors, but that isn't currently being used. + constexpr std::size_t size = 32; + constexpr auto usage = gfx::BufferUsageType::StaticDraw; + emptyVertexBuffer = std::make_unique(createBuffer(nullptr, size, usage)); + } + return emptyVertexBuffer; +} + namespace { const auto clipMaskStencilMode = gfx::StencilMode{ /*.test=*/gfx::StencilMode::Always(), @@ -316,16 +348,16 @@ bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass, constexpr auto uboSize = sizeof(shaders::ClipUBO); const auto bufferSize = tileUBOs.size() * uboSize; - BufferResource tempBuffer; + std::optional tempBuffer; auto& uboBuffer = clipMaskUniformsBufferUsed ? tempBuffer : clipMaskUniformsBuffer; clipMaskUniformsBufferUsed = true; - if (!uboBuffer || uboBuffer.getSizeInBytes() < bufferSize) { + if (!uboBuffer || !*uboBuffer || uboBuffer->getSizeInBytes() < bufferSize) { uboBuffer = createBuffer(tileUBOs.data(), bufferSize, gfx::BufferUsageType::StaticDraw); if (!uboBuffer) { return false; } } else { - uboBuffer.update(tileUBOs.data(), bufferSize, /*offset=*/0); + uboBuffer->update(tileUBOs.data(), bufferSize, /*offset=*/0); } encoder->setCullMode(MTL::CullModeNone); @@ -350,7 +382,7 @@ bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass, for (std::size_t ii = 0; ii < tileUBOs.size(); ++ii) { encoder->setStencilReferenceValue(tileUBOs[ii].stencil_ref); encoder->setVertexBuffer( - uboBuffer.getMetalBuffer().get(), /*offset=*/ii * uboSize, ShaderClass::uniforms[0].index); + uboBuffer->getMetalBuffer().get(), /*offset=*/ii * uboSize, ShaderClass::uniforms[0].index); encoder->drawIndexedPrimitives(MTL::PrimitiveType::PrimitiveTypeTriangle, indexCount, MTL::IndexType::IndexTypeUInt16, @@ -362,6 +394,8 @@ bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass, } #endif + stats.numDrawCalls++; + stats.totalDrawCalls++; return true; } diff --git a/src/mbgl/mtl/drawable.cpp b/src/mbgl/mtl/drawable.cpp index 87b4e9c9b9e..ba7ebe6ee51 100644 --- a/src/mbgl/mtl/drawable.cpp +++ b/src/mbgl/mtl/drawable.cpp @@ -157,7 +157,7 @@ void Drawable::draw(PaintParameters& parameters) const { return; } - const auto& context = static_cast(parameters.context); + auto& context = static_cast(parameters.context); const auto& renderPass = static_cast(*parameters.renderPass); const auto& encoder = renderPass.getMetalEncoder(); if (!encoder) { @@ -289,8 +289,8 @@ void Drawable::draw(PaintParameters& parameters) const { if (binding) { if (const auto buffer = getMetalBuffer(binding ? binding->vertexBufferResource : nullptr)) { assert((maxIndex + mlSegment.vertexOffset) * binding->vertexStride <= buffer->length()); - } else if (const auto buffer = getMetalBuffer(impl->noBindingBuffer.get())) { - assert(binding->vertexStride <= buffer->length()); + } else if (impl->noBindingBuffer) { + assert(binding->vertexStride <= impl->noBindingBuffer->length()); } } } @@ -304,6 +304,7 @@ void Drawable::draw(PaintParameters& parameters) const { instanceCount, baseVertex, baseInstance); + context.renderingStats().numDrawCalls++; } } @@ -371,8 +372,8 @@ void Drawable::bindAttributes(const RenderPass& renderPass) const { if (const auto buffer = getMetalBuffer(binding ? binding->vertexBufferResource : nullptr)) { assert(binding->vertexStride * impl->vertexCount <= getBufferSize(binding->vertexBufferResource)); encoder->setVertexBuffer(buffer, /*offset=*/0, attributeIndex); - } else if (const auto buffer = getMetalBuffer(impl->noBindingBuffer.get())) { - encoder->setVertexBuffer(buffer, /*offset=*/0, attributeIndex); + } else if (impl->noBindingBuffer) { + encoder->setVertexBuffer(impl->noBindingBuffer, /*offset=*/0, attributeIndex); } attributeIndex += 1; } @@ -593,8 +594,9 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) { } if (!binding->vertexBufferResource && !impl->noBindingBuffer) { - impl->noBindingBuffer = uploadPass.createVertexBufferResource( - nullptr, 64, gfx::BufferUsageType::StaticDraw); + if (const auto& buf = context.getEmptyVertexBuffer()) { + impl->noBindingBuffer = getMetalBuffer(buf.get()); + } } auto attribDesc = NS::TransferPtr(MTL::VertexAttributeDescriptor::alloc()->init()); diff --git a/src/mbgl/mtl/drawable_impl.hpp b/src/mbgl/mtl/drawable_impl.hpp index e1bebaf96d2..0d81a27830a 100644 --- a/src/mbgl/mtl/drawable_impl.hpp +++ b/src/mbgl/mtl/drawable_impl.hpp @@ -55,7 +55,7 @@ class Drawable::Impl final { // GLfloat pointSize = 0.0f; StringIdentity idVertexAttrName = stringIndexer().get("a_pos"); - gfx::UniqueVertexBufferResource noBindingBuffer; + MTL::Buffer* noBindingBuffer = nullptr; gfx::AttributeBindingArray attributeBindings; diff --git a/src/mbgl/mtl/index_buffer_resource.cpp b/src/mbgl/mtl/index_buffer_resource.cpp new file mode 100644 index 00000000000..0af135067b9 --- /dev/null +++ b/src/mbgl/mtl/index_buffer_resource.cpp @@ -0,0 +1,24 @@ +#include + +#include + +namespace mbgl { +namespace mtl { + +IndexBufferResource::IndexBufferResource(BufferResource&& ptr) + : buffer(std::move(ptr)) { + if (buffer) { + buffer.getContext().renderingStats().numIndexBuffers++; + buffer.getContext().renderingStats().memIndexBuffers += buffer.getSizeInBytes(); + } +} + +IndexBufferResource::~IndexBufferResource() { + if (buffer) { + buffer.getContext().renderingStats().numIndexBuffers--; + buffer.getContext().renderingStats().memIndexBuffers -= buffer.getSizeInBytes(); + } +} + +} // namespace mtl +} // namespace mbgl diff --git a/src/mbgl/mtl/offscreen_texture.cpp b/src/mbgl/mtl/offscreen_texture.cpp index 03e52915c4c..1152085fca3 100644 --- a/src/mbgl/mtl/offscreen_texture.cpp +++ b/src/mbgl/mtl/offscreen_texture.cpp @@ -44,9 +44,10 @@ class OffscreenTextureResource final : public RenderableResource { static_cast(stencilTexture.get()) ->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite | MTL::TextureUsageRenderTarget); } + context.renderingStats().numFrameBuffers++; } - ~OffscreenTextureResource() noexcept override = default; + ~OffscreenTextureResource() noexcept override { context.renderingStats().numFrameBuffers--; } void bind() override { assert(context.getBackend().getCommandQueue()); diff --git a/src/mbgl/mtl/render_pass.cpp b/src/mbgl/mtl/render_pass.cpp index 453e340338a..b5ed4e8e992 100644 --- a/src/mbgl/mtl/render_pass.cpp +++ b/src/mbgl/mtl/render_pass.cpp @@ -47,6 +47,8 @@ RenderPass::RenderPass(CommandEncoder& commandEncoder_, const char* name, const // Let the encoder pass along any groups pushed to it after this commandEncoder.trackRenderPass(this); + + commandEncoder.context.clear(); } RenderPass::~RenderPass() { diff --git a/src/mbgl/mtl/texture2d.cpp b/src/mbgl/mtl/texture2d.cpp index 6abdfc1bb04..2e5ebf0a36b 100644 --- a/src/mbgl/mtl/texture2d.cpp +++ b/src/mbgl/mtl/texture2d.cpp @@ -13,7 +13,12 @@ namespace mtl { Texture2D::Texture2D(Context& context_) : context(context_) {} -Texture2D::~Texture2D() {} +Texture2D::~Texture2D() { + if (metalTexture) { + context.renderingStats().numActiveTextures--; + context.renderingStats().memTextures -= getDataSize(); + } +} gfx::Texture2D& Texture2D::setSamplerConfiguration(const SamplerState& samplerState_) noexcept { samplerState = samplerState_; @@ -138,6 +143,8 @@ void Texture2D::createMetalTexture() noexcept { if (metalTexture) { textureDirty = false; + context.renderingStats().numCreatedTextures++; + context.renderingStats().numActiveTextures++; context.renderingStats().memTextures += getDataSize(); } } @@ -190,12 +197,16 @@ void Texture2D::bind(const RenderPass& renderPass, int32_t location) noexcept { encoder->setFragmentTexture(metalTexture.get(), location); encoder->setFragmentSamplerState(metalSamplerState.get(), location); + + context.renderingStats().numTextureBindings++; } void Texture2D::unbind(const RenderPass& renderPass, int32_t location) noexcept { const auto& encoder = renderPass.getMetalEncoder(); encoder->setFragmentTexture(nullptr, location); encoder->setFragmentSamplerState(nullptr, location); + + context.renderingStats().numTextureBindings--; } void Texture2D::upload(const void* pixelData, const Size& size_) noexcept { @@ -215,9 +226,11 @@ void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16 assert(metalTexture.get()); assert(!textureDirty); - MTL::Region region = MTL::Region::Make2D(xOffset, yOffset, size_.width, size_.height); - NS::UInteger bytesPerRow = size_.width * getPixelStride(); + const MTL::Region region = MTL::Region::Make2D(xOffset, yOffset, size_.width, size_.height); + const NS::UInteger bytesPerRow = size_.width * getPixelStride(); metalTexture->replaceRegion(region, 0, pixelData, bytesPerRow); + context.renderingStats().numTextureUpdates++; + context.renderingStats().textureUpdateBytes += bytesPerRow * size_.height; } void Texture2D::upload() noexcept { diff --git a/src/mbgl/mtl/uniform_buffer.cpp b/src/mbgl/mtl/uniform_buffer.cpp index 38a9449e753..57107870f84 100644 --- a/src/mbgl/mtl/uniform_buffer.cpp +++ b/src/mbgl/mtl/uniform_buffer.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -9,11 +10,20 @@ namespace mtl { UniformBuffer::UniformBuffer(BufferResource&& buffer_) : gfx::UniformBuffer(buffer_.getSizeInBytes()), - buffer(std::move(buffer_)) {} + buffer(std::move(buffer_)) { + buffer.getContext().renderingStats().numUniformBuffers++; + buffer.getContext().renderingStats().memUniformBuffers += size; +} + UniformBuffer::UniformBuffer(UniformBuffer&& other) : gfx::UniformBuffer(std::move(other)), buffer(std::move(other.buffer)) {} +UniformBuffer::~UniformBuffer() { + buffer.getContext().renderingStats().numUniformBuffers--; + buffer.getContext().renderingStats().memUniformBuffers -= size; +} + void UniformBuffer::update(const void* data, std::size_t size_) { assert(size == size_); if (size != size_ || size != buffer.getSizeInBytes()) { @@ -23,6 +33,8 @@ void UniformBuffer::update(const void* data, std::size_t size_) { return; } + buffer.getContext().renderingStats().numUniformUpdates++; + buffer.getContext().renderingStats().uniformUpdateBytes += size_; buffer.update(data, size, /*offset=*/0); } diff --git a/src/mbgl/mtl/vertex_buffer_resource.cpp b/src/mbgl/mtl/vertex_buffer_resource.cpp new file mode 100644 index 00000000000..c288f2a286c --- /dev/null +++ b/src/mbgl/mtl/vertex_buffer_resource.cpp @@ -0,0 +1,24 @@ +#include + +#include + +namespace mbgl { +namespace mtl { + +VertexBufferResource::VertexBufferResource(BufferResource&& ptr) + : buffer(std::move(ptr)) { + if (buffer) { + buffer.getContext().renderingStats().numVertexBuffers++; + buffer.getContext().renderingStats().memVertexBuffers += buffer.getSizeInBytes(); + } +} + +VertexBufferResource::~VertexBufferResource() { + if (buffer) { + buffer.getContext().renderingStats().numVertexBuffers--; + buffer.getContext().renderingStats().memVertexBuffers -= buffer.getSizeInBytes(); + } +} + +} // namespace mtl +} // namespace mbgl diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index c415b843223..158975619b1 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -158,6 +158,7 @@ void PaintParameters::clearStencil() { 0, 0}}; mtlContext.renderTileClippingMasks(*renderPass, staticData, tileUBO); + context.renderingStats().stencilClears++; #else // !MLN_RENDER_BACKEND_METAL context.clearStencilBuffer(0b00000000); #endif @@ -231,6 +232,8 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun auto& mtlContext = static_cast(context); mtlContext.renderTileClippingMasks(*renderPass, staticData, tileUBOs); + + mtlContext.renderingStats().stencilUpdates++; } #else // !MLN_RENDER_BACKEND_METAL diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp index 728b0ec8d5d..87591eb4022 100644 --- a/src/mbgl/renderer/render_orchestrator.cpp +++ b/src/mbgl/renderer/render_orchestrator.cpp @@ -136,7 +136,7 @@ RenderOrchestrator::~RenderOrchestrator() { layer.markContextDestroyed(); } } -}; +} void RenderOrchestrator::setObserver(RendererObserver* observer_) { observer = observer_ ? observer_ : &nullObserver(); diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index f668ca0ee4c..c9116651381 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -1607,6 +1608,7 @@ TEST(Map, StencilOverflow) { const auto& backend = test.frontend.getBackend(); gfx::BackendScope scope{*backend}; + const auto& context = backend->getContext(); auto& style = test.map.getStyle(); style.loadJSON("{}"); @@ -1628,13 +1630,15 @@ TEST(Map, StencilOverflow) { test.frontend.render(test.map); // In drawable builds, no drawables are built because no bucket/tiledata is available. -#if !MLN_DRAWABLE_RENDERER - // TODO: Collect stats on Metal context -#if MLN_RENDER_BACKEND_OPENGL - const auto& context = static_cast(backend->getContext()); +#if MLN_DRAWABLE_RENDERER + ASSERT_LE(0, context.renderingStats().stencilUpdates); +#else ASSERT_LT(0, context.renderingStats().stencilClears); -#endif // MLN_RENDER_BACKEND_OPENGL -#endif // !MLN_DRAWABLE_RENDERER +#endif // MLN_DRAWABLE_RENDERER + +#if !defined(NDEBUG) + Log::Info(Event::General, context.renderingStats().toString("\n")); +#endif // !defined(NDEBUG) // TODO: confirm that the stencil masking actually worked }