Repo created

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

View file

@ -0,0 +1,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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -0,0 +1,39 @@
#pragma once
#import <MetalKit/MetalKit.h>
#include "drape/hw_texture.hpp"
#include "drape/pointers.hpp"
namespace dp
{
namespace metal
{
class MetalTextureAllocator : public HWTextureAllocator
{
public:
drape_ptr<HWTexture> CreateTexture(ref_ptr<dp::GraphicsContext> context) override;
void Flush() override {}
};
class MetalTexture : public HWTexture
{
using Base = HWTexture;
public:
explicit MetalTexture(ref_ptr<MetalTextureAllocator>) {}
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;
id<MTLTexture> GetTexture() const { return m_texture; }
private:
id<MTLTexture> m_texture;
bool m_isMutable = false;
};
} // namespace metal
} // namespace dp

View file

@ -0,0 +1,95 @@
#include "drape/metal/metal_texture.hpp"
#include "drape/metal/metal_base_context.hpp"
#include "base/logging.hpp"
drape_ptr<dp::HWTextureAllocator> CreateMetalAllocator()
{
return make_unique_dp<dp::metal::MetalTextureAllocator>();
}
ref_ptr<dp::HWTextureAllocator> GetDefaultMetalAllocator()
{
static dp::metal::MetalTextureAllocator allocator;
return make_ref(&allocator);
}
namespace dp
{
namespace metal
{
namespace
{
MTLPixelFormat UnpackFormat(TextureFormat format)
{
switch (format)
{
case TextureFormat::RGBA8: return MTLPixelFormatRGBA8Unorm;
case TextureFormat::Red: return MTLPixelFormatA8Unorm; // TODO: change to R8, fix shaders
case TextureFormat::RedGreen: return MTLPixelFormatRG8Unorm;
case TextureFormat::DepthStencil: return MTLPixelFormatDepth32Float_Stencil8;
case TextureFormat::Depth: return MTLPixelFormatDepth32Float;
case TextureFormat::Unspecified:
CHECK(false, ());
return MTLPixelFormatInvalid;
}
CHECK(false, ());
}
} // namespace
drape_ptr<HWTexture> MetalTextureAllocator::CreateTexture(ref_ptr<dp::GraphicsContext> context)
{
return make_unique_dp<MetalTexture>(make_ref(this));
}
void MetalTexture::Create(ref_ptr<dp::GraphicsContext> context, Params const & params, ref_ptr<void> data)
{
Base::Create(context, params, data);
ref_ptr<MetalBaseContext> metalContext = context;
id<MTLDevice> metalDevice = metalContext->GetMetalDevice();
MTLTextureDescriptor * texDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:UnpackFormat(params.m_format)
width:params.m_width
height:params.m_height
mipmapped:NO];
texDesc.usage = MTLTextureUsageShaderRead;
m_isMutable = params.m_isMutable;
if (params.m_isRenderTarget)
{
texDesc.usage |= MTLTextureUsageRenderTarget;
texDesc.storageMode = MTLStorageModePrivate;
m_texture = [metalDevice newTextureWithDescriptor:texDesc];
CHECK(m_texture != nil, ());
}
else
{
texDesc.storageMode = MTLStorageModeShared;
m_texture = [metalDevice newTextureWithDescriptor:texDesc];
CHECK(m_texture != nil, ());
MTLRegion region = MTLRegionMake2D(0, 0, m_params.m_width, m_params.m_height);
auto const rowBytes = m_params.m_width * GetBytesPerPixel(m_params.m_format);
[m_texture replaceRegion:region mipmapLevel:0 withBytes:data.get() bytesPerRow:rowBytes];
}
}
void MetalTexture::UploadData(ref_ptr<dp::GraphicsContext> context, uint32_t x, uint32_t y,
uint32_t width, uint32_t height, ref_ptr<void> data)
{
UNUSED_VALUE(context);
CHECK(m_isMutable, ("Upload data is avaivable only for mutable textures."));
MTLRegion region = MTLRegionMake2D(x, y, width, height);
auto const rowBytes = width * GetBytesPerPixel(m_params.m_format);
[m_texture replaceRegion:region mipmapLevel:0 withBytes:data.get() bytesPerRow:rowBytes];
}
void MetalTexture::SetFilter(TextureFilter filter)
{
m_params.m_filter = filter;
}
bool MetalTexture::Validate() const
{
return m_texture != nil;
}
} // namespace metal
} // namespace dp

View file

@ -0,0 +1,82 @@
#import <MetalKit/MetalKit.h>
#include "drape/metal/metal_base_context.hpp"
#include "drape/metal/metal_gpu_buffer_impl.hpp"
#include "drape/pointers.hpp"
#include "drape/vertex_array_buffer.hpp"
#include "base/assert.hpp"
#include "base/macros.hpp"
#include <cstdint>
#include <cstring>
#include <utility>
namespace dp
{
namespace metal
{
class MetalVertexArrayBufferImpl : public VertexArrayBufferImpl
{
public:
explicit MetalVertexArrayBufferImpl(ref_ptr<VertexArrayBuffer> buffer)
: m_vertexArrayBuffer(buffer)
{}
bool Build(ref_ptr<GpuProgram> program) override
{
UNUSED_VALUE(program);
return true;
}
bool Bind() override { return true; }
void Unbind() override {}
void BindBuffers(dp::BuffersMap const & buffers) const override {}
void RenderRange(ref_ptr<GraphicsContext> context, bool drawAsLine,
IndicesRange const & range) override
{
CHECK(m_vertexArrayBuffer->HasBuffers(), ());
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
if (!metalContext->HasAppliedPipelineState())
return;
id<MTLRenderCommandEncoder> encoder = metalContext->GetCommandEncoder();
uint32_t bufferIndex = 0;
for (auto & buffer : m_vertexArrayBuffer->m_staticBuffers)
{
ref_ptr<MetalGpuBufferImpl> b = buffer.second->GetBuffer();
[encoder setVertexBuffer:b->GetMetalBuffer() offset:0 atIndex:bufferIndex];
bufferIndex++;
}
for (auto & buffer : m_vertexArrayBuffer->m_dynamicBuffers)
{
ref_ptr<MetalGpuBufferImpl> b = buffer.second->GetBuffer();
[encoder setVertexBuffer:b->GetMetalBuffer() offset:0 atIndex:bufferIndex];
bufferIndex++;
}
ref_ptr<MetalGpuBufferImpl> ib = m_vertexArrayBuffer->m_indexBuffer->GetBuffer();
auto const isSupported32bit = dp::IndexStorage::IsSupported32bit();
auto const indexType = isSupported32bit ? MTLIndexTypeUInt32 : MTLIndexTypeUInt16;
auto const indexSize = isSupported32bit ? sizeof(unsigned int) : sizeof(unsigned short);
[encoder drawIndexedPrimitives:(drawAsLine ? MTLPrimitiveTypeLine : MTLPrimitiveTypeTriangle)
indexCount:range.m_idxCount
indexType:indexType
indexBuffer:ib->GetMetalBuffer()
indexBufferOffset:range.m_idxStart * indexSize];
}
private:
ref_ptr<VertexArrayBuffer> m_vertexArrayBuffer;
};
} // namespace metal
drape_ptr<VertexArrayBufferImpl> VertexArrayBuffer::CreateImplForMetal(ref_ptr<VertexArrayBuffer> buffer)
{
return make_unique_dp<metal::MetalVertexArrayBufferImpl>(buffer);
}
} // namespace dp

View file

@ -0,0 +1,79 @@
#import <MetalKit/MetalKit.h>
#include "drape/metal/metal_base_context.hpp"
#include "drape/metal/metal_gpu_program.hpp"
#include "drape/metal/metal_texture.hpp"
#include "drape/pointers.hpp"
#include "drape/render_state.hpp"
#include "base/assert.hpp"
#include <utility>
namespace dp
{
void ApplyDepthStencilStateForMetal(ref_ptr<GraphicsContext> context)
{
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
id<MTLDepthStencilState> state = metalContext->GetDepthStencilState();
[metalContext->GetCommandEncoder() setDepthStencilState:state];
}
void ApplyPipelineStateForMetal(ref_ptr<GraphicsContext> context, ref_ptr<GpuProgram> program,
bool blendingEnabled)
{
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
id<MTLRenderPipelineState> state = metalContext->GetPipelineState(std::move(program), blendingEnabled);
metalContext->ApplyPipelineState(state);
}
void ApplyTexturesForMetal(ref_ptr<GraphicsContext> context, ref_ptr<GpuProgram> program,
RenderState const & state)
{
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
ref_ptr<dp::metal::MetalGpuProgram> p = program;
id<MTLRenderCommandEncoder> encoder = metalContext->GetCommandEncoder();
for (auto const & texture : state.GetTextures())
{
if (texture.second == nullptr)
continue;
ref_ptr<dp::metal::MetalTexture> t = texture.second->GetHardwareTexture();
if (t == nullptr)
{
texture.second->UpdateState(context);
t = texture.second->GetHardwareTexture();
CHECK(t != nullptr, ());
}
t->SetFilter(state.GetTextureFilter());
dp::HWTexture::Params const & params = t->GetParams();
// Set texture to the vertex shader.
auto const & vsBindingInfo = p->GetVertexTextureBindingInfo(texture.first);
if (vsBindingInfo.m_textureBindingIndex >= 0)
{
[encoder setVertexTexture:t->GetTexture() atIndex:vsBindingInfo.m_textureBindingIndex];
if (vsBindingInfo.m_samplerBindingIndex >= 0)
{
id<MTLSamplerState> samplerState = metalContext->GetSamplerState(params.m_filter, params.m_wrapSMode,
params.m_wrapTMode);
[encoder setVertexSamplerState:samplerState atIndex:vsBindingInfo.m_samplerBindingIndex];
}
}
// Set texture to the fragment shader.
auto const & fsBindingInfo = p->GetFragmentTextureBindingInfo(texture.first);
if (fsBindingInfo.m_textureBindingIndex >= 0)
{
[encoder setFragmentTexture:t->GetTexture() atIndex:fsBindingInfo.m_textureBindingIndex];
if (fsBindingInfo.m_samplerBindingIndex >= 0)
{
id<MTLSamplerState> samplerState = metalContext->GetSamplerState(params.m_filter, params.m_wrapSMode,
params.m_wrapTMode);
[encoder setFragmentSamplerState:samplerState atIndex:fsBindingInfo.m_samplerBindingIndex];
}
}
}
}
} // namespace dp