Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
106
libs/drape/metal/metal_base_context.hpp
Normal file
106
libs/drape/metal/metal_base_context.hpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/gpu_program.hpp"
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/metal/metal_cleaner.hpp"
|
||||
#include "drape/metal/metal_states.hpp"
|
||||
#include "drape/metal/metal_texture.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/texture_types.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
class MetalBaseContext : public dp::GraphicsContext
|
||||
{
|
||||
public:
|
||||
using DrawableRequest = std::function<id<CAMetalDrawable>()>;
|
||||
|
||||
MetalBaseContext(id<MTLDevice> device, m2::PointU const & screenSize, DrawableRequest && drawableRequest);
|
||||
|
||||
bool BeginRendering() override;
|
||||
void EndRendering() override;
|
||||
void Present() override;
|
||||
void MakeCurrent() override {}
|
||||
void DoneCurrent() override {}
|
||||
bool Validate() override { return true; }
|
||||
void Resize(uint32_t w, uint32_t h) override;
|
||||
void SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer) override;
|
||||
void ForgetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer) override {}
|
||||
void ApplyFramebuffer(std::string const & framebufferLabel) override;
|
||||
void Init(ApiVersion apiVersion) override;
|
||||
ApiVersion GetApiVersion() const override;
|
||||
std::string GetRendererName() const override;
|
||||
std::string GetRendererVersion() const override;
|
||||
|
||||
void DebugSynchronizeWithCPU() override;
|
||||
void PushDebugLabel(std::string const & label) override;
|
||||
void PopDebugLabel() override;
|
||||
|
||||
void SetClearColor(Color const & color) override;
|
||||
void Clear(uint32_t clearBits, uint32_t storeBits) override;
|
||||
void Flush() override {}
|
||||
void SetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
|
||||
void SetScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
|
||||
void SetDepthTestEnabled(bool enabled) override;
|
||||
void SetDepthTestFunction(TestFunction depthFunction) override;
|
||||
void SetStencilTestEnabled(bool enabled) override;
|
||||
void SetStencilFunction(StencilFace face, TestFunction stencilFunction) override;
|
||||
void SetStencilActions(StencilFace face, StencilAction stencilFailAction, StencilAction depthFailAction,
|
||||
StencilAction passAction) override;
|
||||
void SetStencilReferenceValue(uint32_t stencilReferenceValue) override
|
||||
{
|
||||
m_stencilReferenceValue = stencilReferenceValue;
|
||||
}
|
||||
void SetCullingEnabled(bool enabled) override;
|
||||
|
||||
id<MTLDevice> GetMetalDevice() const;
|
||||
id<MTLCommandBuffer> GetCommandBuffer() const;
|
||||
id<MTLRenderCommandEncoder> GetCommandEncoder() const;
|
||||
id<MTLDepthStencilState> GetDepthStencilState();
|
||||
id<MTLRenderPipelineState> GetPipelineState(ref_ptr<GpuProgram> program, bool blendingEnabled);
|
||||
id<MTLSamplerState> GetSamplerState(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode);
|
||||
|
||||
void SetSystemPrograms(drape_ptr<GpuProgram> && programClearColor, drape_ptr<GpuProgram> && programClearDepth,
|
||||
drape_ptr<GpuProgram> && programClearColorAndDepth);
|
||||
|
||||
void ApplyPipelineState(id<MTLRenderPipelineState> state);
|
||||
bool HasAppliedPipelineState() const;
|
||||
void ResetPipelineStatesCache();
|
||||
|
||||
MTLRenderPassDescriptor * GetRenderPassDescriptor() const;
|
||||
|
||||
protected:
|
||||
void RecreateDepthTexture(m2::PointU const & screenSize);
|
||||
void RequestFrameDrawable();
|
||||
void ResetFrameDrawable();
|
||||
void FinishCurrentEncoding();
|
||||
|
||||
id<MTLDevice> m_device;
|
||||
DrawableRequest m_drawableRequest;
|
||||
drape_ptr<MetalTexture> m_depthTexture;
|
||||
MTLRenderPassDescriptor * m_renderPassDescriptor;
|
||||
id<MTLCommandQueue> m_commandQueue;
|
||||
ref_ptr<dp::BaseFramebuffer> m_currentFramebuffer;
|
||||
MetalStates::DepthStencilKey m_currentDepthStencilKey;
|
||||
MetalStates m_metalStates;
|
||||
|
||||
// These objects are recreated each frame. They MUST NOT be stored anywhere.
|
||||
id<CAMetalDrawable> m_frameDrawable;
|
||||
id<MTLCommandBuffer> m_frameCommandBuffer;
|
||||
id<MTLRenderCommandEncoder> m_currentCommandEncoder;
|
||||
id<MTLRenderPipelineState> m_lastPipelineState;
|
||||
|
||||
MetalCleaner m_cleaner;
|
||||
|
||||
uint32_t m_stencilReferenceValue = 1;
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
462
libs/drape/metal/metal_base_context.mm
Normal file
462
libs/drape/metal/metal_base_context.mm
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
#include "drape/metal/metal_base_context.hpp"
|
||||
#include "drape/metal/metal_texture.hpp"
|
||||
|
||||
#include "drape/framebuffer.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
MetalBaseContext::MetalBaseContext(id<MTLDevice> device, m2::PointU const & screenSize,
|
||||
DrawableRequest && drawableRequest)
|
||||
: m_device(device)
|
||||
, m_drawableRequest(std::move(drawableRequest))
|
||||
{
|
||||
m_renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
m_renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||
m_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
m_renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear;
|
||||
m_renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
m_renderPassDescriptor.depthAttachment.clearDepth = 1.0;
|
||||
m_renderPassDescriptor.stencilAttachment.loadAction = MTLLoadActionClear;
|
||||
m_renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionStore;
|
||||
m_renderPassDescriptor.stencilAttachment.clearStencil = 0;
|
||||
|
||||
RecreateDepthTexture(screenSize);
|
||||
}
|
||||
|
||||
void MetalBaseContext::RecreateDepthTexture(m2::PointU const & screenSize)
|
||||
{
|
||||
if (screenSize.x == 0 || screenSize.y == 0)
|
||||
{
|
||||
m_depthTexture.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_depthTexture = make_unique_dp<MetalTexture>(nullptr /* allocator */);
|
||||
HWTexture::Params params;
|
||||
params.m_width = screenSize.x;
|
||||
params.m_height = screenSize.y;
|
||||
params.m_format = TextureFormat::Depth;
|
||||
params.m_isRenderTarget = true;
|
||||
m_depthTexture->Create(make_ref(this), params, nullptr /* data */);
|
||||
}
|
||||
|
||||
void MetalBaseContext::Init(dp::ApiVersion apiVersion)
|
||||
{
|
||||
CHECK(apiVersion == dp::ApiVersion::Metal, ());
|
||||
m_commandQueue = [m_device newCommandQueue];
|
||||
}
|
||||
|
||||
ApiVersion MetalBaseContext::GetApiVersion() const
|
||||
{
|
||||
return dp::ApiVersion::Metal;
|
||||
}
|
||||
|
||||
std::string MetalBaseContext::GetRendererName() const
|
||||
{
|
||||
return std::string([m_device.name UTF8String]);
|
||||
}
|
||||
|
||||
std::string MetalBaseContext::GetRendererVersion() const
|
||||
{
|
||||
static std::vector<std::pair<MTLFeatureSet, std::string>> features;
|
||||
if (features.empty())
|
||||
{
|
||||
#ifdef OMIM_OS_MAC
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily1_v1, "macOS_GPUFamily1_v1");
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily1_v2, "macOS_GPUFamily1_v2");
|
||||
if (@available(macOS 10.13, *))
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily1_v3, "macOS_GPUFamily1_v3");
|
||||
if (@available(macOS 10.14, *))
|
||||
{
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily1_v4, "macOS_GPUFamily1_v4");
|
||||
features.emplace_back(MTLFeatureSet_macOS_GPUFamily2_v1, "macOS_GPUFamily2_v1");
|
||||
}
|
||||
#else
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v1, "iOS_GPUFamily1_v1");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v1, "iOS_GPUFamily2_v1");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v2, "iOS_GPUFamily1_v2");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v2, "iOS_GPUFamily2_v2");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily3_v1, "iOS_GPUFamily3_v1");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v3, "iOS_GPUFamily1_v3");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v3, "iOS_GPUFamily2_v3");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily3_v2, "iOS_GPUFamily3_v2");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v4, "iOS_GPUFamily1_v4");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v4, "iOS_GPUFamily2_v4");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily3_v3, "iOS_GPUFamily3_v3");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily4_v1, "iOS_GPUFamily4_v1");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily1_v5, "iOS_GPUFamily1_v5");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily2_v5, "iOS_GPUFamily2_v5");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily3_v4, "iOS_GPUFamily3_v4");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily4_v2, "iOS_GPUFamily4_v2");
|
||||
features.emplace_back(MTLFeatureSet_iOS_GPUFamily5_v1, "iOS_GPUFamily5_v1");
|
||||
#endif
|
||||
std::sort(features.begin(), features.end(), [](auto const & s1, auto const & s2)
|
||||
{
|
||||
return s1.first > s2.first;
|
||||
});
|
||||
}
|
||||
|
||||
for (auto featureSet : features)
|
||||
{
|
||||
if ([m_device supportsFeatureSet:featureSet.first])
|
||||
return featureSet.second;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
void MetalBaseContext::PushDebugLabel(std::string const & label)
|
||||
{
|
||||
if (m_currentCommandEncoder == nil)
|
||||
return;
|
||||
[m_currentCommandEncoder pushDebugGroup:@(label.c_str())];
|
||||
}
|
||||
|
||||
void MetalBaseContext::PopDebugLabel()
|
||||
{
|
||||
if (m_currentCommandEncoder == nil)
|
||||
return;
|
||||
[m_currentCommandEncoder popDebugGroup];
|
||||
}
|
||||
|
||||
void MetalBaseContext::Resize(uint32_t w, uint32_t h)
|
||||
{
|
||||
if (m_depthTexture && m_depthTexture->GetWidth() == w && m_depthTexture->GetHeight() == h)
|
||||
return;
|
||||
|
||||
RecreateDepthTexture({w, h});
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer)
|
||||
{
|
||||
FinishCurrentEncoding();
|
||||
m_currentFramebuffer = framebuffer;
|
||||
}
|
||||
|
||||
void MetalBaseContext::ApplyFramebuffer(std::string const & framebufferLabel)
|
||||
{
|
||||
// Initialize frame command buffer if there is no one.
|
||||
if (!m_frameCommandBuffer)
|
||||
{
|
||||
m_frameCommandBuffer = [m_commandQueue commandBuffer];
|
||||
m_frameCommandBuffer.label = @"Frame command buffer";
|
||||
}
|
||||
|
||||
if (!m_currentFramebuffer)
|
||||
{
|
||||
// Use default(system) framebuffer and depth-stencil.
|
||||
RequestFrameDrawable();
|
||||
m_renderPassDescriptor.colorAttachments[0].texture = m_frameDrawable != nil ? m_frameDrawable.texture : nil;
|
||||
m_renderPassDescriptor.depthAttachment.texture = m_depthTexture ? m_depthTexture->GetTexture() : nil;
|
||||
m_renderPassDescriptor.stencilAttachment.texture = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
ref_ptr<Framebuffer> framebuffer = m_currentFramebuffer;
|
||||
|
||||
ASSERT(dynamic_cast<MetalTexture *>(framebuffer->GetTexture()->GetHardwareTexture().get()) != nullptr, ());
|
||||
ref_ptr<MetalTexture> colorAttachment = framebuffer->GetTexture()->GetHardwareTexture();
|
||||
m_renderPassDescriptor.colorAttachments[0].texture = colorAttachment->GetTexture();
|
||||
|
||||
auto const depthStencilRef = framebuffer->GetDepthStencilRef();
|
||||
if (depthStencilRef != nullptr)
|
||||
{
|
||||
ASSERT(dynamic_cast<MetalTexture *>(depthStencilRef->GetTexture()->GetHardwareTexture().get()) != nullptr, ());
|
||||
ref_ptr<MetalTexture> depthStencilAttachment = depthStencilRef->GetTexture()->GetHardwareTexture();
|
||||
m_renderPassDescriptor.depthAttachment.texture = depthStencilAttachment->GetTexture();
|
||||
if (depthStencilAttachment->GetFormat() == dp::TextureFormat::DepthStencil)
|
||||
m_renderPassDescriptor.stencilAttachment.texture = depthStencilAttachment->GetTexture();
|
||||
else
|
||||
m_renderPassDescriptor.stencilAttachment.texture = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_renderPassDescriptor.depthAttachment.texture = nil;
|
||||
m_renderPassDescriptor.stencilAttachment.texture = nil;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(m_currentCommandEncoder == nil, ("Current command encoder was not finished."));
|
||||
m_currentCommandEncoder = [m_frameCommandBuffer renderCommandEncoderWithDescriptor:m_renderPassDescriptor];
|
||||
m_currentCommandEncoder.label = @(framebufferLabel.c_str());
|
||||
[m_currentCommandEncoder pushDebugGroup:@(framebufferLabel.c_str())];
|
||||
|
||||
// Default rendering options.
|
||||
[m_currentCommandEncoder setFrontFacingWinding:MTLWindingClockwise];
|
||||
[m_currentCommandEncoder setCullMode:MTLCullModeBack];
|
||||
[m_currentCommandEncoder setStencilReferenceValue:m_stencilReferenceValue];
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetClearColor(dp::Color const & color)
|
||||
{
|
||||
m_cleaner.SetClearColor(color);
|
||||
m_renderPassDescriptor.colorAttachments[0].clearColor =
|
||||
MTLClearColorMake(color.GetRedF(), color.GetGreenF(), color.GetBlueF(), color.GetAlphaF());
|
||||
}
|
||||
|
||||
void MetalBaseContext::Clear(uint32_t clearBits, uint32_t storeBits)
|
||||
{
|
||||
if (m_currentCommandEncoder != nil)
|
||||
{
|
||||
if ((clearBits & ClearBits::ColorBit) && (clearBits & ClearBits::DepthBit))
|
||||
m_cleaner.ClearColorAndDepth(make_ref(this), m_currentCommandEncoder);
|
||||
else if (clearBits & ClearBits::ColorBit)
|
||||
m_cleaner.ClearColor(make_ref(this), m_currentCommandEncoder);
|
||||
else if (clearBits & ClearBits::DepthBit)
|
||||
m_cleaner.ClearDepth(make_ref(this), m_currentCommandEncoder);
|
||||
|
||||
if (clearBits & ClearBits::StencilBit)
|
||||
CHECK(false, ("Stencil clearing is not implemented"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here, if we do not clear attachments, we load data ONLY if we store it afterwards, otherwise we use 'DontCare' option
|
||||
// to improve performance.
|
||||
if (clearBits & ClearBits::ColorBit)
|
||||
m_renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||
else
|
||||
m_renderPassDescriptor.colorAttachments[0].loadAction = (storeBits & ClearBits::ColorBit) ? MTLLoadActionLoad : MTLLoadActionDontCare;
|
||||
|
||||
if (clearBits & ClearBits::DepthBit)
|
||||
m_renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear;
|
||||
else
|
||||
m_renderPassDescriptor.depthAttachment.loadAction = (storeBits & ClearBits::DepthBit) ? MTLLoadActionLoad : MTLLoadActionDontCare;
|
||||
|
||||
if (clearBits & ClearBits::StencilBit)
|
||||
m_renderPassDescriptor.stencilAttachment.loadAction = MTLLoadActionClear;
|
||||
else
|
||||
m_renderPassDescriptor.stencilAttachment.loadAction = (storeBits & ClearBits::StencilBit) ? MTLLoadActionLoad : MTLLoadActionDontCare;
|
||||
|
||||
// Apply storing mode.
|
||||
if (storeBits & ClearBits::ColorBit)
|
||||
m_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
else
|
||||
m_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionDontCare;
|
||||
|
||||
if (storeBits & ClearBits::DepthBit)
|
||||
m_renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
else
|
||||
m_renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionDontCare;
|
||||
|
||||
if (storeBits & ClearBits::StencilBit)
|
||||
m_renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionStore;
|
||||
else
|
||||
m_renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionDontCare;
|
||||
}
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = GetCommandEncoder();
|
||||
[encoder setViewport:MTLViewport(x, y, w, h, 0.0, 1.0)];
|
||||
[encoder setScissorRect:{x, y, w, h}];
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = GetCommandEncoder();
|
||||
if (m_renderPassDescriptor.colorAttachments[0].texture != nil)
|
||||
{
|
||||
auto const rpWidth = static_cast<uint32_t>(m_renderPassDescriptor.colorAttachments[0].texture.width);
|
||||
auto const rpHeight = static_cast<uint32_t>(m_renderPassDescriptor.colorAttachments[0].texture.height);
|
||||
if (x < 0)
|
||||
x = 0;
|
||||
if (y < 0)
|
||||
y = 0;
|
||||
if (x + w > rpWidth)
|
||||
w = rpWidth - x;
|
||||
if (y + h > rpHeight)
|
||||
h = rpHeight - y;
|
||||
|
||||
[encoder setScissorRect:{x, y, w, h}];
|
||||
}
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetDepthTestEnabled(bool enabled)
|
||||
{
|
||||
m_currentDepthStencilKey.m_depthEnabled = enabled;
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetDepthTestFunction(dp::TestFunction depthFunction)
|
||||
{
|
||||
m_currentDepthStencilKey.m_depthFunction = depthFunction;
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetStencilTestEnabled(bool enabled)
|
||||
{
|
||||
m_currentDepthStencilKey.m_stencilEnabled = enabled;
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetStencilFunction(dp::StencilFace face, dp::TestFunction stencilFunction)
|
||||
{
|
||||
m_currentDepthStencilKey.SetStencilFunction(face, stencilFunction);
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetStencilActions(dp::StencilFace face,
|
||||
dp::StencilAction stencilFailAction,
|
||||
dp::StencilAction depthFailAction,
|
||||
dp::StencilAction passAction)
|
||||
{
|
||||
m_currentDepthStencilKey.SetStencilActions(face, stencilFailAction, depthFailAction, passAction);
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetCullingEnabled(bool enabled)
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = GetCommandEncoder();
|
||||
[encoder setCullMode: (enabled ? MTLCullModeBack : MTLCullModeNone)];
|
||||
}
|
||||
|
||||
id<MTLDevice> MetalBaseContext::GetMetalDevice() const
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
id<MTLRenderCommandEncoder> MetalBaseContext::GetCommandEncoder() const
|
||||
{
|
||||
CHECK(m_currentCommandEncoder != nil, ("Probably encoding commands were called before ApplyFramebuffer."));
|
||||
return m_currentCommandEncoder;
|
||||
}
|
||||
|
||||
id<MTLCommandBuffer> MetalBaseContext::GetCommandBuffer() const
|
||||
{
|
||||
CHECK(m_frameCommandBuffer != nil, ("Probably encoding commands were called before ApplyFramebuffer."));
|
||||
return m_frameCommandBuffer;
|
||||
}
|
||||
|
||||
id<MTLDepthStencilState> MetalBaseContext::GetDepthStencilState()
|
||||
{
|
||||
return m_metalStates.GetDepthStencilState(m_device, m_currentDepthStencilKey);
|
||||
}
|
||||
|
||||
id<MTLRenderPipelineState> MetalBaseContext::GetPipelineState(ref_ptr<GpuProgram> program, bool blendingEnabled)
|
||||
{
|
||||
CHECK(m_currentCommandEncoder != nil, ("Probably encoding commands were called before ApplyFramebuffer."));
|
||||
|
||||
id<MTLTexture> colorTexture = m_renderPassDescriptor.colorAttachments[0].texture;
|
||||
// It can be nil in the case when Metal drawable is absent (e.g. finish rendering in background).
|
||||
if (colorTexture == nil)
|
||||
return nil;
|
||||
|
||||
id<MTLTexture> depthTexture = m_renderPassDescriptor.depthAttachment.texture;
|
||||
MTLPixelFormat depthStencilFormat = (depthTexture != nil) ? depthTexture.pixelFormat : MTLPixelFormatInvalid;
|
||||
|
||||
MetalStates::PipelineKey const key(program, colorTexture.pixelFormat, depthStencilFormat, blendingEnabled);
|
||||
return m_metalStates.GetPipelineState(m_device, key);
|
||||
}
|
||||
|
||||
id<MTLSamplerState> MetalBaseContext::GetSamplerState(TextureFilter filter, TextureWrapping wrapSMode,
|
||||
TextureWrapping wrapTMode)
|
||||
{
|
||||
MetalStates::SamplerKey const key(filter, wrapSMode, wrapTMode);
|
||||
return m_metalStates.GetSamplerState(m_device, key);
|
||||
}
|
||||
|
||||
bool MetalBaseContext::BeginRendering()
|
||||
{
|
||||
CHECK(m_currentCommandEncoder == nil, ("Current command encoder was not finished."));
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalBaseContext::EndRendering()
|
||||
{
|
||||
FinishCurrentEncoding();
|
||||
}
|
||||
|
||||
void MetalBaseContext::Present()
|
||||
{
|
||||
RequestFrameDrawable();
|
||||
if (m_frameDrawable)
|
||||
[m_frameCommandBuffer presentDrawable:m_frameDrawable];
|
||||
|
||||
[m_frameCommandBuffer commit];
|
||||
m_frameDrawable = nil;
|
||||
[m_frameCommandBuffer waitUntilCompleted];
|
||||
m_frameCommandBuffer = nil;
|
||||
}
|
||||
|
||||
void MetalBaseContext::RequestFrameDrawable()
|
||||
{
|
||||
if (m_frameDrawable != nil)
|
||||
return;
|
||||
|
||||
CHECK(m_drawableRequest != nullptr, ());
|
||||
m_frameDrawable = m_drawableRequest();
|
||||
}
|
||||
|
||||
void MetalBaseContext::ResetFrameDrawable()
|
||||
{
|
||||
if (m_frameDrawable == nil)
|
||||
return;
|
||||
|
||||
m_frameDrawable = nil;
|
||||
RequestFrameDrawable();
|
||||
}
|
||||
|
||||
void MetalBaseContext::FinishCurrentEncoding()
|
||||
{
|
||||
[m_currentCommandEncoder popDebugGroup];
|
||||
[m_currentCommandEncoder endEncoding];
|
||||
m_currentCommandEncoder = nil;
|
||||
m_lastPipelineState = nil;
|
||||
}
|
||||
|
||||
void MetalBaseContext::SetSystemPrograms(drape_ptr<GpuProgram> && programClearColor,
|
||||
drape_ptr<GpuProgram> && programClearDepth,
|
||||
drape_ptr<GpuProgram> && programClearColorAndDepth)
|
||||
{
|
||||
m_cleaner.Init(make_ref(this), std::move(programClearColor), std::move(programClearDepth),
|
||||
std::move(programClearColorAndDepth));
|
||||
}
|
||||
|
||||
void MetalBaseContext::ApplyPipelineState(id<MTLRenderPipelineState> state)
|
||||
{
|
||||
m_lastPipelineState = state;
|
||||
if (state != nil)
|
||||
[GetCommandEncoder() setRenderPipelineState:state];
|
||||
}
|
||||
|
||||
bool MetalBaseContext::HasAppliedPipelineState() const
|
||||
{
|
||||
return m_lastPipelineState != nil;
|
||||
}
|
||||
|
||||
void MetalBaseContext::ResetPipelineStatesCache()
|
||||
{
|
||||
m_metalStates.ResetPipelineStatesCache();
|
||||
}
|
||||
|
||||
void MetalBaseContext::DebugSynchronizeWithCPU()
|
||||
{
|
||||
FinishCurrentEncoding();
|
||||
RequestFrameDrawable();
|
||||
[m_frameCommandBuffer commit];
|
||||
m_frameDrawable = nil;
|
||||
[m_frameCommandBuffer waitUntilCompleted];
|
||||
m_frameCommandBuffer = nil;
|
||||
}
|
||||
|
||||
MTLRenderPassDescriptor * MetalBaseContext::GetRenderPassDescriptor() const
|
||||
{
|
||||
return m_renderPassDescriptor;
|
||||
}
|
||||
} // namespace metal
|
||||
|
||||
void RenderFrameMediator(std::function<void()> && renderFrameFunction)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
renderFrameFunction();
|
||||
}
|
||||
}
|
||||
} // namespace dp
|
||||
45
libs/drape/metal/metal_cleaner.hpp
Normal file
45
libs/drape/metal/metal_cleaner.hpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/color.hpp"
|
||||
#include "drape/glsl_types.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
namespace dp
|
||||
{
|
||||
class GpuProgram;
|
||||
|
||||
namespace metal
|
||||
{
|
||||
class MetalBaseContext;
|
||||
|
||||
class MetalCleaner
|
||||
{
|
||||
public:
|
||||
MetalCleaner() = default;
|
||||
|
||||
void Init(ref_ptr<MetalBaseContext> context, drape_ptr<GpuProgram> && programClearColor,
|
||||
drape_ptr<GpuProgram> && programClearDepth, drape_ptr<GpuProgram> && programClearColorAndDepth);
|
||||
|
||||
void SetClearColor(Color const & color);
|
||||
|
||||
void ClearDepth(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder);
|
||||
void ClearColor(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder);
|
||||
void ClearColorAndDepth(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder);
|
||||
|
||||
private:
|
||||
void ApplyColorParam(id<MTLRenderCommandEncoder> encoder, ref_ptr<GpuProgram> program);
|
||||
void RenderQuad(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder, ref_ptr<GpuProgram> program);
|
||||
|
||||
id<MTLBuffer> m_buffer;
|
||||
id<MTLDepthStencilState> m_depthEnabledState;
|
||||
id<MTLDepthStencilState> m_depthDisabledState;
|
||||
glsl::vec4 m_clearColor;
|
||||
|
||||
drape_ptr<GpuProgram> m_programClearColor;
|
||||
drape_ptr<GpuProgram> m_programClearDepth;
|
||||
drape_ptr<GpuProgram> m_programClearColorAndDepth;
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
95
libs/drape/metal/metal_cleaner.mm
Normal file
95
libs/drape/metal/metal_cleaner.mm
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#include "drape/metal/metal_cleaner.hpp"
|
||||
#include "drape/metal/metal_base_context.hpp"
|
||||
#include "drape/metal/metal_gpu_program.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
void MetalCleaner::Init(ref_ptr<MetalBaseContext> context,
|
||||
drape_ptr<GpuProgram> && programClearColor,
|
||||
drape_ptr<GpuProgram> && programClearDepth,
|
||||
drape_ptr<GpuProgram> && programClearColorAndDepth)
|
||||
{
|
||||
m_programClearColor = std::move(programClearColor);
|
||||
m_programClearDepth = std::move(programClearDepth);
|
||||
m_programClearColorAndDepth = std::move(programClearColorAndDepth);
|
||||
|
||||
ref_ptr<MetalBaseContext> metalContext = context;
|
||||
id<MTLDevice> device = metalContext->GetMetalDevice();
|
||||
std::vector<float> quad = {-1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f};
|
||||
m_buffer = [device newBufferWithBytes:quad.data()
|
||||
length:quad.size() * sizeof(quad[0])
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
m_buffer.label = @"MetalCleaner";
|
||||
|
||||
MTLDepthStencilDescriptor * desc = [[MTLDepthStencilDescriptor alloc] init];
|
||||
desc.depthWriteEnabled = YES;
|
||||
desc.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
m_depthEnabledState = [device newDepthStencilStateWithDescriptor:desc];
|
||||
CHECK(m_depthEnabledState != nil, ());
|
||||
|
||||
desc.depthWriteEnabled = NO;
|
||||
desc.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
m_depthDisabledState = [device newDepthStencilStateWithDescriptor:desc];
|
||||
CHECK(m_depthDisabledState != nil, ());
|
||||
}
|
||||
|
||||
void MetalCleaner::SetClearColor(Color const & color)
|
||||
{
|
||||
m_clearColor = glsl::ToVec4(color);
|
||||
}
|
||||
|
||||
void MetalCleaner::ApplyColorParam(id<MTLRenderCommandEncoder> encoder, ref_ptr<GpuProgram> program)
|
||||
{
|
||||
ref_ptr<MetalGpuProgram> metalProgram = program;
|
||||
auto const fsBindingIndex = metalProgram->GetFragmentShaderUniformsBindingIndex();
|
||||
if (fsBindingIndex >= 0)
|
||||
{
|
||||
[encoder setFragmentBytes:(void const *)&m_clearColor length:sizeof(m_clearColor)
|
||||
atIndex:fsBindingIndex];
|
||||
}
|
||||
}
|
||||
|
||||
void MetalCleaner::RenderQuad(ref_ptr<MetalBaseContext> metalContext, id<MTLRenderCommandEncoder> encoder,
|
||||
ref_ptr<GpuProgram> program)
|
||||
{
|
||||
id<MTLRenderPipelineState> pipelineState = metalContext->GetPipelineState(program, false /* blendingEnabled */);
|
||||
if (pipelineState == nil)
|
||||
return;
|
||||
|
||||
[encoder setRenderPipelineState:pipelineState];
|
||||
|
||||
[encoder setVertexBuffer:m_buffer offset:0 atIndex:0];
|
||||
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||
}
|
||||
|
||||
void MetalCleaner::ClearDepth(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder)
|
||||
{
|
||||
[encoder pushDebugGroup:@"ClearDepth"];
|
||||
[encoder setDepthStencilState:m_depthEnabledState];
|
||||
RenderQuad(context, encoder, make_ref(m_programClearDepth));
|
||||
[encoder popDebugGroup];
|
||||
}
|
||||
|
||||
void MetalCleaner::ClearColor(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder)
|
||||
{
|
||||
[encoder pushDebugGroup:@"ClearColor"];
|
||||
[encoder setDepthStencilState:m_depthDisabledState];
|
||||
ApplyColorParam(encoder, make_ref(m_programClearColor));
|
||||
RenderQuad(context, encoder, make_ref(m_programClearColor));
|
||||
[encoder popDebugGroup];
|
||||
}
|
||||
|
||||
void MetalCleaner::ClearColorAndDepth(ref_ptr<MetalBaseContext> context, id<MTLRenderCommandEncoder> encoder)
|
||||
{
|
||||
[encoder pushDebugGroup:@"ClearColorAndDepth"];
|
||||
[encoder setDepthStencilState:m_depthEnabledState];
|
||||
ApplyColorParam(encoder, make_ref(m_programClearColorAndDepth));
|
||||
RenderQuad(context, encoder, make_ref(m_programClearColorAndDepth));
|
||||
[encoder popDebugGroup];
|
||||
}
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
73
libs/drape/metal/metal_gpu_buffer_impl.hpp
Normal file
73
libs/drape/metal/metal_gpu_buffer_impl.hpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/data_buffer.hpp"
|
||||
#include "drape/data_buffer_impl.hpp"
|
||||
#include "drape/metal/metal_base_context.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
class MetalGPUBuffer : public BufferBase
|
||||
{
|
||||
public:
|
||||
MetalGPUBuffer(ref_ptr<GraphicsContext> context, void const * data, uint8_t elementSize, uint32_t capacity);
|
||||
|
||||
void UploadData(void const * data, uint32_t elementCount);
|
||||
|
||||
void * Map(uint32_t elementOffset, uint32_t elementCount);
|
||||
|
||||
void UpdateData(void * gpuPtr, void const * data, uint32_t elementOffset, uint32_t elementCount);
|
||||
|
||||
id<MTLBuffer> GetMetalBuffer() const { return m_metalBuffer; }
|
||||
|
||||
protected:
|
||||
void Resize(ref_ptr<MetalBaseContext> context, void const * data, uint32_t elementCount);
|
||||
|
||||
id<MTLBuffer> m_metalBuffer;
|
||||
};
|
||||
|
||||
class MetalGpuBufferImpl : public DataBufferImpl<MetalGPUBuffer>
|
||||
{
|
||||
public:
|
||||
template <typename... Args>
|
||||
MetalGpuBufferImpl(Args &&... params) : DataBufferImpl(std::forward<Args>(params)...)
|
||||
{}
|
||||
|
||||
void const * Data() const override
|
||||
{
|
||||
ASSERT(false, ("Retrieving of raw data is unavailable for GPU buffer"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UploadData(ref_ptr<GraphicsContext> context, void const * data, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
m_buffer->UploadData(data, elementCount);
|
||||
}
|
||||
|
||||
void UpdateData(void * destPtr, void const * srcPtr, uint32_t elementOffset, uint32_t elementCount) override
|
||||
{
|
||||
m_buffer->UpdateData(destPtr, srcPtr, elementOffset, elementCount);
|
||||
}
|
||||
|
||||
void * Map(ref_ptr<GraphicsContext> context, uint32_t elementOffset, uint32_t elementCount) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
return m_buffer->Map(elementOffset, elementCount);
|
||||
}
|
||||
|
||||
id<MTLBuffer> GetMetalBuffer() const { return m_buffer->GetMetalBuffer(); }
|
||||
|
||||
void Bind() override {}
|
||||
void Unmap(ref_ptr<GraphicsContext>) override {}
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
81
libs/drape/metal/metal_gpu_buffer_impl.mm
Normal file
81
libs/drape/metal/metal_gpu_buffer_impl.mm
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#include "drape/metal/metal_gpu_buffer_impl.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
MetalGPUBuffer::MetalGPUBuffer(ref_ptr<GraphicsContext> context, void const * data,
|
||||
uint8_t elementSize, uint32_t capacity)
|
||||
: BufferBase(elementSize, capacity)
|
||||
{
|
||||
Resize(context, data, capacity);
|
||||
}
|
||||
|
||||
void MetalGPUBuffer::UploadData(void const * data, uint32_t elementCount)
|
||||
{
|
||||
uint32_t const currentSize = GetCurrentSize();
|
||||
uint8_t const elementSize = GetElementSize();
|
||||
ASSERT(GetCapacity() >= elementCount + currentSize,
|
||||
("Not enough memory to upload ", elementCount, " elements"));
|
||||
|
||||
uint32_t const byteOffset = currentSize * elementSize;
|
||||
uint32_t const sizeInBytes = elementCount * elementSize;
|
||||
uint8_t * gpuPtr = static_cast<uint8_t *>([m_metalBuffer contents]) + byteOffset;
|
||||
memcpy(gpuPtr, data, sizeInBytes);
|
||||
BufferBase::UploadData(elementCount);
|
||||
}
|
||||
|
||||
void * MetalGPUBuffer::Map(uint32_t elementOffset, uint32_t elementCount)
|
||||
{
|
||||
UNUSED_VALUE(elementCount);
|
||||
uint32_t const elementSize = GetElementSize();
|
||||
uint32_t const byteOffset = elementOffset * elementSize;
|
||||
uint8_t * bufferPointer = static_cast<uint8_t *>([m_metalBuffer contents]) + byteOffset;
|
||||
return bufferPointer;
|
||||
}
|
||||
|
||||
void MetalGPUBuffer::UpdateData(void * gpuPtr, void const * data,
|
||||
uint32_t elementOffset, uint32_t elementCount)
|
||||
{
|
||||
uint32_t const elementSize = GetElementSize();
|
||||
uint32_t const byteOffset = elementOffset * elementSize;
|
||||
uint32_t const byteCount = elementCount * elementSize;
|
||||
|
||||
ASSERT(gpuPtr != nullptr, ());
|
||||
memcpy((uint8_t *)gpuPtr + byteOffset, data, byteCount);
|
||||
}
|
||||
|
||||
void MetalGPUBuffer::Resize(ref_ptr<MetalBaseContext> context, void const * data, uint32_t elementCount)
|
||||
{
|
||||
BufferBase::Resize(elementCount);
|
||||
|
||||
id<MTLDevice> device = context->GetMetalDevice();
|
||||
uint32_t const sizeInBytes = GetCapacity() * GetElementSize();
|
||||
if (data != nil)
|
||||
{
|
||||
m_metalBuffer = [device newBufferWithBytes:data
|
||||
length:sizeInBytes
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_metalBuffer = [device newBufferWithLength:sizeInBytes
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
}
|
||||
|
||||
// If we have already set up data, we have to call SetDataSize.
|
||||
if (data != nullptr)
|
||||
SetDataSize(elementCount);
|
||||
}
|
||||
} // namespace metal
|
||||
|
||||
drape_ptr<DataBufferBase> DataBuffer::CreateImplForMetal(ref_ptr<GraphicsContext> context, void const * data,
|
||||
uint8_t elementSize, uint32_t capacity)
|
||||
{
|
||||
return make_unique_dp<metal::MetalGpuBufferImpl>(context, data, elementSize, capacity);
|
||||
}
|
||||
} // namespace dp
|
||||
81
libs/drape/metal/metal_gpu_program.hpp
Normal file
81
libs/drape/metal/metal_gpu_program.hpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/gpu_program.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
class MetalGpuProgram : public GpuProgram
|
||||
{
|
||||
public:
|
||||
static int8_t constexpr kInvalidBindingIndex = -1;
|
||||
struct TextureBindingInfo
|
||||
{
|
||||
int8_t m_textureBindingIndex = kInvalidBindingIndex;
|
||||
int8_t m_samplerBindingIndex = kInvalidBindingIndex;
|
||||
};
|
||||
using TexturesBindingInfo = std::map<std::string, TextureBindingInfo>;
|
||||
|
||||
MetalGpuProgram(std::string const & programName, id<MTLFunction> vertexShader, id<MTLFunction> fragmentShader,
|
||||
int8_t vsUniformsBindingIndex, int8_t fsUniformsBindingIndex,
|
||||
TexturesBindingInfo && vertexTextureBindingInfo, TexturesBindingInfo && fragmentTextureBindingInfo,
|
||||
MTLVertexDescriptor * vertexDescriptor)
|
||||
: GpuProgram(programName)
|
||||
, m_vertexShader(vertexShader)
|
||||
, m_fragmentShader(fragmentShader)
|
||||
, m_vsUniformsBindingIndex(vsUniformsBindingIndex)
|
||||
, m_fsUniformsBindingIndex(fsUniformsBindingIndex)
|
||||
, m_vertexTextureBindingInfo(std::move(vertexTextureBindingInfo))
|
||||
, m_fragmentTextureBindingInfo(std::move(fragmentTextureBindingInfo))
|
||||
, m_vertexDescriptor(vertexDescriptor)
|
||||
{}
|
||||
|
||||
void Bind() override {}
|
||||
void Unbind() override {}
|
||||
|
||||
id<MTLFunction> GetVertexShader() const { return m_vertexShader; }
|
||||
id<MTLFunction> GetFragmentShader() const { return m_fragmentShader; }
|
||||
|
||||
int8_t GetVertexShaderUniformsBindingIndex() const { return m_vsUniformsBindingIndex; }
|
||||
int8_t GetFragmentShaderUniformsBindingIndex() const { return m_fsUniformsBindingIndex; }
|
||||
|
||||
TextureBindingInfo const & GetVertexTextureBindingInfo(std::string const & textureName) const
|
||||
{
|
||||
return GetTextureBindingInfo(m_vertexTextureBindingInfo, textureName);
|
||||
}
|
||||
|
||||
TextureBindingInfo const & GetFragmentTextureBindingInfo(std::string const & textureName) const
|
||||
{
|
||||
return GetTextureBindingInfo(m_fragmentTextureBindingInfo, textureName);
|
||||
}
|
||||
|
||||
MTLVertexDescriptor * GetVertexDescriptor() const { return m_vertexDescriptor; }
|
||||
|
||||
private:
|
||||
TextureBindingInfo const & GetTextureBindingInfo(TexturesBindingInfo const & bindingInfo,
|
||||
std::string const & textureName) const
|
||||
{
|
||||
static TextureBindingInfo kEmptyBinding;
|
||||
auto const it = bindingInfo.find(textureName);
|
||||
if (it == bindingInfo.cend())
|
||||
return kEmptyBinding;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
id<MTLFunction> m_vertexShader;
|
||||
id<MTLFunction> m_fragmentShader;
|
||||
int8_t const m_vsUniformsBindingIndex;
|
||||
int8_t const m_fsUniformsBindingIndex;
|
||||
TexturesBindingInfo const m_vertexTextureBindingInfo;
|
||||
TexturesBindingInfo const m_fragmentTextureBindingInfo;
|
||||
MTLVertexDescriptor * m_vertexDescriptor;
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
156
libs/drape/metal/metal_mesh_object_impl.mm
Normal file
156
libs/drape/metal/metal_mesh_object_impl.mm
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/metal/metal_base_context.hpp"
|
||||
#include "drape/mesh_object.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
MTLPrimitiveType GetPrimitiveType(MeshObject::DrawPrimitive primitive)
|
||||
{
|
||||
switch (primitive)
|
||||
{
|
||||
case MeshObject::DrawPrimitive::Triangles: return MTLPrimitiveTypeTriangle;
|
||||
case MeshObject::DrawPrimitive::TriangleStrip: return MTLPrimitiveTypeTriangleStrip;
|
||||
case MeshObject::DrawPrimitive::LineStrip: return MTLPrimitiveTypeLineStrip;
|
||||
}
|
||||
CHECK(false, ("Unsupported type"));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class MetalMeshObjectImpl : public MeshObjectImpl
|
||||
{
|
||||
public:
|
||||
MetalMeshObjectImpl(ref_ptr<dp::MeshObject> mesh)
|
||||
: m_mesh(std::move(mesh))
|
||||
{}
|
||||
|
||||
void Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program) override
|
||||
{
|
||||
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
|
||||
id<MTLDevice> device = metalContext->GetMetalDevice();
|
||||
|
||||
m_geometryBuffers.resize(m_mesh->m_buffers.size());
|
||||
for (size_t i = 0; i < m_mesh->m_buffers.size(); i++)
|
||||
{
|
||||
auto const sizeInBytes = m_mesh->m_buffers[i]->GetSizeInBytes();
|
||||
if (sizeInBytes == 0)
|
||||
continue;
|
||||
|
||||
m_geometryBuffers[i] = [device newBufferWithBytes:m_mesh->m_buffers[i]->GetData()
|
||||
length:sizeInBytes
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
std::ostringstream ss;
|
||||
ss << "MeshVB:";
|
||||
for (size_t j = 0; j < m_mesh->m_buffers[i]->m_attributes.size(); j++)
|
||||
{
|
||||
ss << m_mesh->m_buffers[i]->m_attributes[j].m_attributeName;
|
||||
if (j + 1 < m_mesh->m_buffers[i]->m_attributes.size())
|
||||
ss << "+";
|
||||
}
|
||||
m_geometryBuffers[i].label = @(ss.str().c_str());
|
||||
}
|
||||
|
||||
if (!m_mesh->m_indices.empty())
|
||||
{
|
||||
m_indexBuffer = [device newBufferWithBytes:m_mesh->m_indices.data()
|
||||
length:m_mesh->m_indices.size() * sizeof(uint16_t)
|
||||
options:MTLResourceCPUCacheModeWriteCombined];
|
||||
m_indexBuffer.label = @"MeshIB";
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
m_geometryBuffers.clear();
|
||||
m_indexBuffer = nil;
|
||||
}
|
||||
|
||||
void UpdateBuffer(ref_ptr<dp::GraphicsContext> context, uint32_t bufferInd) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
CHECK_LESS(bufferInd, static_cast<uint32_t>(m_geometryBuffers.size()), ());
|
||||
|
||||
auto & buffer = m_mesh->m_buffers[bufferInd];
|
||||
auto const sizeInBytes = buffer->GetSizeInBytes();
|
||||
CHECK(sizeInBytes != 0, ());
|
||||
|
||||
uint8_t * bufferPointer = (uint8_t *)[m_geometryBuffers[bufferInd] contents];
|
||||
memcpy(bufferPointer, buffer->GetData(), sizeInBytes);
|
||||
}
|
||||
|
||||
void UpdateIndexBuffer(ref_ptr<dp::GraphicsContext> context) override
|
||||
{
|
||||
UNUSED_VALUE(context);
|
||||
CHECK(m_indexBuffer != nil, ());
|
||||
|
||||
auto const sizeInBytes = m_mesh->m_indices.size() * sizeof(uint16_t);
|
||||
CHECK(sizeInBytes != 0, ());
|
||||
|
||||
uint8_t * bufferPointer = (uint8_t *)[m_indexBuffer contents];
|
||||
memcpy(bufferPointer, m_mesh->m_indices.data(), sizeInBytes);
|
||||
}
|
||||
|
||||
void Bind(ref_ptr<dp::GpuProgram> program) override {}
|
||||
|
||||
void Unbind() override {}
|
||||
|
||||
void DrawPrimitives(ref_ptr<dp::GraphicsContext> context, uint32_t vertexCount,
|
||||
uint32_t startVertex) override
|
||||
{
|
||||
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
|
||||
if (!metalContext->HasAppliedPipelineState())
|
||||
return;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = metalContext->GetCommandEncoder();
|
||||
for (size_t i = 0; i < m_geometryBuffers.size(); i++)
|
||||
[encoder setVertexBuffer:m_geometryBuffers[i] offset:0 atIndex:i];
|
||||
|
||||
[encoder drawPrimitives:GetPrimitiveType(m_mesh->m_drawPrimitive)
|
||||
vertexStart:startVertex
|
||||
vertexCount:vertexCount];
|
||||
}
|
||||
|
||||
void DrawPrimitivesIndexed(ref_ptr<dp::GraphicsContext> context, uint32_t indexCount,
|
||||
uint32_t startIndex) override
|
||||
{
|
||||
ref_ptr<dp::metal::MetalBaseContext> metalContext = context;
|
||||
if (!metalContext->HasAppliedPipelineState())
|
||||
return;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = metalContext->GetCommandEncoder();
|
||||
for (size_t i = 0; i < m_geometryBuffers.size(); i++)
|
||||
[encoder setVertexBuffer:m_geometryBuffers[i] offset:0 atIndex:i];
|
||||
|
||||
CHECK(m_indexBuffer != nil, ());
|
||||
[encoder drawIndexedPrimitives:GetPrimitiveType(m_mesh->m_drawPrimitive)
|
||||
indexCount:indexCount
|
||||
indexType:MTLIndexTypeUInt16
|
||||
indexBuffer:m_indexBuffer
|
||||
indexBufferOffset:startIndex * sizeof(uint16_t)];
|
||||
}
|
||||
|
||||
private:
|
||||
ref_ptr<dp::MeshObject> m_mesh;
|
||||
std::vector<id<MTLBuffer>> m_geometryBuffers;
|
||||
id<MTLBuffer> m_indexBuffer;
|
||||
};
|
||||
} // namespace metal
|
||||
|
||||
#ifdef OMIM_METAL_AVAILABLE
|
||||
void MeshObject::InitForMetal()
|
||||
{
|
||||
m_impl = make_unique_dp<metal::MetalMeshObjectImpl>(make_ref(this));
|
||||
}
|
||||
#endif // OMIM_METAL_AVAILABLE
|
||||
} // namespace dp
|
||||
79
libs/drape/metal/metal_states.hpp
Normal file
79
libs/drape/metal/metal_states.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include "drape/graphics_context.hpp"
|
||||
#include "drape/metal/metal_gpu_program.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/texture_types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
class MetalStates
|
||||
{
|
||||
public:
|
||||
struct DepthStencilKey
|
||||
{
|
||||
void SetDepthTestEnabled(bool enabled);
|
||||
void SetDepthTestFunction(TestFunction depthFunction);
|
||||
void SetStencilTestEnabled(bool enabled);
|
||||
void SetStencilFunction(StencilFace face, TestFunction stencilFunction);
|
||||
void SetStencilActions(StencilFace face, StencilAction stencilFailAction, StencilAction depthFailAction,
|
||||
StencilAction passAction);
|
||||
bool operator<(DepthStencilKey const & rhs) const;
|
||||
MTLDepthStencilDescriptor * BuildDescriptor() const;
|
||||
|
||||
bool m_depthEnabled = false;
|
||||
bool m_stencilEnabled = false;
|
||||
TestFunction m_depthFunction = TestFunction::Always;
|
||||
uint64_t m_stencil = 0;
|
||||
};
|
||||
|
||||
struct PipelineKey
|
||||
{
|
||||
PipelineKey() = default;
|
||||
PipelineKey(ref_ptr<GpuProgram> program, MTLPixelFormat colorFormat, MTLPixelFormat depthStencilFormat,
|
||||
bool blendingEnabled);
|
||||
|
||||
bool operator<(PipelineKey const & rhs) const;
|
||||
MTLRenderPipelineDescriptor * BuildDescriptor() const;
|
||||
|
||||
ref_ptr<GpuProgram> m_program;
|
||||
MTLPixelFormat m_colorFormat = MTLPixelFormatInvalid;
|
||||
MTLPixelFormat m_depthStencilFormat = MTLPixelFormatInvalid;
|
||||
bool m_blendingEnabled = false;
|
||||
};
|
||||
|
||||
struct SamplerKey
|
||||
{
|
||||
SamplerKey() = default;
|
||||
SamplerKey(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode);
|
||||
void Set(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode);
|
||||
bool operator<(SamplerKey const & rhs) const;
|
||||
MTLSamplerDescriptor * BuildDescriptor() const;
|
||||
|
||||
uint32_t m_sampler = 0;
|
||||
};
|
||||
|
||||
id<MTLDepthStencilState> GetDepthStencilState(id<MTLDevice> device, DepthStencilKey const & key);
|
||||
id<MTLRenderPipelineState> GetPipelineState(id<MTLDevice> device, PipelineKey const & key);
|
||||
id<MTLSamplerState> GetSamplerState(id<MTLDevice> device, SamplerKey const & key);
|
||||
|
||||
void ResetPipelineStatesCache();
|
||||
|
||||
private:
|
||||
using DepthStencilCache = std::map<DepthStencilKey, id<MTLDepthStencilState>>;
|
||||
DepthStencilCache m_depthStencilCache;
|
||||
|
||||
using PipelineCache = std::map<PipelineKey, id<MTLRenderPipelineState>>;
|
||||
PipelineCache m_pipelineCache;
|
||||
|
||||
using SamplerCache = std::map<SamplerKey, id<MTLSamplerState>>;
|
||||
SamplerCache m_samplerCache;
|
||||
};
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
337
libs/drape/metal/metal_states.mm
Normal file
337
libs/drape/metal/metal_states.mm
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
#include "drape/metal/metal_states.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace dp
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Stencil package.
|
||||
uint8_t constexpr kStencilBackFunctionByte = 7;
|
||||
uint8_t constexpr kStencilBackFailActionByte = 6;
|
||||
uint8_t constexpr kStencilBackDepthFailActionByte = 5;
|
||||
uint8_t constexpr kStencilBackPassActionByte = 4;
|
||||
uint8_t constexpr kStencilFrontFunctionByte = 3;
|
||||
uint8_t constexpr kStencilFrontFailActionByte = 2;
|
||||
uint8_t constexpr kStencilFrontDepthFailActionByte = 1;
|
||||
uint8_t constexpr kStencilFrontPassActionByte = 0;
|
||||
|
||||
// Sampler package.
|
||||
uint8_t constexpr kWrapSModeByte = 3;
|
||||
uint8_t constexpr kWrapTModeByte = 2;
|
||||
uint8_t constexpr kMagFilterByte = 1;
|
||||
uint8_t constexpr kMinFilterByte = 0;
|
||||
|
||||
template<typename T>
|
||||
void SetStateByte(T & state, uint8_t value, uint8_t byteNumber)
|
||||
{
|
||||
auto const shift = byteNumber * 8;
|
||||
auto const mask = ~(static_cast<T>(0xFF) << shift);
|
||||
state = (state & mask) | (static_cast<T>(value) << shift);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint8_t GetStateByte(T & state, uint8_t byteNumber)
|
||||
{
|
||||
return static_cast<uint8_t>((state >> byteNumber * 8) & 0xFF);
|
||||
}
|
||||
|
||||
MTLCompareFunction DecodeTestFunction(uint8_t testFunctionByte)
|
||||
{
|
||||
switch (static_cast<TestFunction>(testFunctionByte))
|
||||
{
|
||||
case TestFunction::Never: return MTLCompareFunctionNever;
|
||||
case TestFunction::Less: return MTLCompareFunctionLess;
|
||||
case TestFunction::Equal: return MTLCompareFunctionEqual;
|
||||
case TestFunction::LessOrEqual: return MTLCompareFunctionLessEqual;
|
||||
case TestFunction::Greater: return MTLCompareFunctionGreater;
|
||||
case TestFunction::NotEqual: return MTLCompareFunctionNotEqual;
|
||||
case TestFunction::GreaterOrEqual: return MTLCompareFunctionGreaterEqual;
|
||||
case TestFunction::Always: return MTLCompareFunctionAlways;
|
||||
}
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
MTLStencilOperation DecodeStencilAction(uint8_t stencilActionByte)
|
||||
{
|
||||
switch (static_cast<StencilAction>(stencilActionByte))
|
||||
{
|
||||
case StencilAction::Keep: return MTLStencilOperationKeep;
|
||||
case StencilAction::Zero: return MTLStencilOperationZero;
|
||||
case StencilAction::Replace: return MTLStencilOperationReplace;
|
||||
case StencilAction::Increment: return MTLStencilOperationIncrementClamp;
|
||||
case StencilAction::IncrementWrap: return MTLStencilOperationIncrementWrap;
|
||||
case StencilAction::Decrement: return MTLStencilOperationDecrementClamp;
|
||||
case StencilAction::DecrementWrap: return MTLStencilOperationDecrementWrap;
|
||||
case StencilAction::Invert: return MTLStencilOperationInvert;
|
||||
}
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
MTLSamplerMinMagFilter DecodeTextureFilter(uint8_t textureFilterByte)
|
||||
{
|
||||
switch (static_cast<TextureFilter>(textureFilterByte))
|
||||
{
|
||||
case TextureFilter::Nearest: return MTLSamplerMinMagFilterNearest;
|
||||
case TextureFilter::Linear: return MTLSamplerMinMagFilterLinear;
|
||||
}
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
MTLSamplerAddressMode DecodeTextureWrapping(uint8_t textureWrappingByte)
|
||||
{
|
||||
switch (static_cast<TextureWrapping>(textureWrappingByte))
|
||||
{
|
||||
case TextureWrapping::ClampToEdge: return MTLSamplerAddressModeClampToEdge;
|
||||
case TextureWrapping::Repeat: return MTLSamplerAddressModeRepeat;
|
||||
}
|
||||
ASSERT(false, ());
|
||||
}
|
||||
|
||||
bool IsStencilFormat(MTLPixelFormat format)
|
||||
{
|
||||
return format == MTLPixelFormatDepth32Float_Stencil8 ||
|
||||
format == MTLPixelFormatStencil8 ||
|
||||
format == MTLPixelFormatX32_Stencil8;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
id<MTLDepthStencilState> MetalStates::GetDepthStencilState(id<MTLDevice> device, DepthStencilKey const & key)
|
||||
{
|
||||
auto const it = m_depthStencilCache.find(key);
|
||||
if (it != m_depthStencilCache.end())
|
||||
return it->second;
|
||||
|
||||
id<MTLDepthStencilState> depthState = [device newDepthStencilStateWithDescriptor:key.BuildDescriptor()];
|
||||
CHECK(depthState != nil, ());
|
||||
m_depthStencilCache.insert(std::make_pair(key, depthState));
|
||||
return depthState;
|
||||
}
|
||||
|
||||
id<MTLRenderPipelineState> MetalStates::GetPipelineState(id<MTLDevice> device, PipelineKey const & key)
|
||||
{
|
||||
auto const it = m_pipelineCache.find(key);
|
||||
if (it != m_pipelineCache.end())
|
||||
return it->second;
|
||||
|
||||
NSError * error = nil;
|
||||
id<MTLRenderPipelineState> pipelineState = [device newRenderPipelineStateWithDescriptor:key.BuildDescriptor()
|
||||
error:&error];
|
||||
if (pipelineState == nil || error != nil)
|
||||
{
|
||||
NSLog(@"%@", error);
|
||||
CHECK(false, ("Failed to create pipeline state."));
|
||||
}
|
||||
m_pipelineCache.insert(std::make_pair(key, pipelineState));
|
||||
return pipelineState;
|
||||
}
|
||||
|
||||
id<MTLSamplerState> MetalStates::GetSamplerState(id<MTLDevice> device, SamplerKey const & key)
|
||||
{
|
||||
auto const it = m_samplerCache.find(key);
|
||||
if (it != m_samplerCache.end())
|
||||
return it->second;
|
||||
|
||||
id<MTLSamplerState> samplerState = [device newSamplerStateWithDescriptor:key.BuildDescriptor()];
|
||||
CHECK(samplerState != nil, ());
|
||||
m_samplerCache.insert(std::make_pair(key, samplerState));
|
||||
return samplerState;
|
||||
}
|
||||
|
||||
void MetalStates::ResetPipelineStatesCache()
|
||||
{
|
||||
m_pipelineCache.clear();
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetDepthTestEnabled(bool enabled)
|
||||
{
|
||||
m_depthEnabled = enabled;
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetDepthTestFunction(TestFunction depthFunction)
|
||||
{
|
||||
m_depthFunction = depthFunction;
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetStencilTestEnabled(bool enabled)
|
||||
{
|
||||
m_stencilEnabled = enabled;
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetStencilFunction(StencilFace face, TestFunction stencilFunction)
|
||||
{
|
||||
switch (face)
|
||||
{
|
||||
case StencilFace::Front:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFunction), kStencilFrontFunctionByte);
|
||||
break;
|
||||
case StencilFace::Back:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFunction), kStencilBackFunctionByte);
|
||||
break;
|
||||
case StencilFace::FrontAndBack:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFunction), kStencilFrontFunctionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFunction), kStencilBackFunctionByte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MetalStates::DepthStencilKey::SetStencilActions(StencilFace face, StencilAction stencilFailAction,
|
||||
StencilAction depthFailAction, StencilAction passAction)
|
||||
{
|
||||
switch (face)
|
||||
{
|
||||
case StencilFace::Front:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFailAction), kStencilFrontFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(depthFailAction), kStencilFrontDepthFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(passAction), kStencilFrontPassActionByte);
|
||||
break;
|
||||
case StencilFace::Back:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFailAction), kStencilBackFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(depthFailAction), kStencilBackDepthFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(passAction), kStencilBackPassActionByte);
|
||||
break;
|
||||
case StencilFace::FrontAndBack:
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFailAction), kStencilFrontFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(depthFailAction), kStencilFrontDepthFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(passAction), kStencilFrontPassActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(stencilFailAction), kStencilBackFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(depthFailAction), kStencilBackDepthFailActionByte);
|
||||
SetStateByte(m_stencil, static_cast<uint8_t>(passAction), kStencilBackPassActionByte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalStates::DepthStencilKey::operator<(DepthStencilKey const & rhs) const
|
||||
{
|
||||
if (m_depthEnabled != rhs.m_depthEnabled)
|
||||
return m_depthEnabled < rhs.m_depthEnabled;
|
||||
|
||||
if (m_stencilEnabled != rhs.m_stencilEnabled)
|
||||
return m_stencilEnabled < rhs.m_stencilEnabled;
|
||||
|
||||
if (m_depthFunction != rhs.m_depthFunction)
|
||||
return m_depthFunction < rhs.m_depthFunction;
|
||||
|
||||
return m_stencil < rhs.m_stencil;
|
||||
}
|
||||
|
||||
MTLDepthStencilDescriptor * MetalStates::DepthStencilKey::BuildDescriptor() const
|
||||
{
|
||||
MTLDepthStencilDescriptor * desc = [[MTLDepthStencilDescriptor alloc] init];
|
||||
if (m_depthEnabled)
|
||||
{
|
||||
desc.depthWriteEnabled = YES;
|
||||
desc.depthCompareFunction = DecodeTestFunction(static_cast<uint8_t>(m_depthFunction));
|
||||
}
|
||||
else
|
||||
{
|
||||
desc.depthWriteEnabled = NO;
|
||||
desc.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
}
|
||||
if (m_stencilEnabled)
|
||||
{
|
||||
MTLStencilDescriptor * frontDesc = [[MTLStencilDescriptor alloc] init];
|
||||
frontDesc.stencilCompareFunction = DecodeTestFunction(GetStateByte(m_stencil, kStencilFrontFunctionByte));
|
||||
frontDesc.stencilFailureOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilFrontFailActionByte));
|
||||
frontDesc.depthFailureOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilFrontDepthFailActionByte));
|
||||
frontDesc.depthStencilPassOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilFrontPassActionByte));
|
||||
desc.frontFaceStencil = frontDesc;
|
||||
|
||||
MTLStencilDescriptor * backDesc = [[MTLStencilDescriptor alloc] init];
|
||||
backDesc.stencilCompareFunction = DecodeTestFunction(GetStateByte(m_stencil, kStencilBackFunctionByte));
|
||||
backDesc.stencilFailureOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilBackFailActionByte));
|
||||
backDesc.depthFailureOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilBackDepthFailActionByte));
|
||||
backDesc.depthStencilPassOperation = DecodeStencilAction(GetStateByte(m_stencil, kStencilBackPassActionByte));
|
||||
desc.backFaceStencil = backDesc;
|
||||
}
|
||||
else
|
||||
{
|
||||
desc.frontFaceStencil = nil;
|
||||
desc.backFaceStencil = nil;
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
MetalStates::PipelineKey::PipelineKey(ref_ptr<GpuProgram> program, MTLPixelFormat colorFormat,
|
||||
MTLPixelFormat depthStencilFormat, bool blendingEnabled)
|
||||
: m_program(std::move(program))
|
||||
, m_colorFormat(colorFormat)
|
||||
, m_depthStencilFormat(depthStencilFormat)
|
||||
, m_blendingEnabled(blendingEnabled)
|
||||
{}
|
||||
|
||||
bool MetalStates::PipelineKey::operator<(PipelineKey const & rhs) const
|
||||
{
|
||||
if (m_program != rhs.m_program)
|
||||
return m_program < rhs.m_program;
|
||||
|
||||
if (m_colorFormat != rhs.m_colorFormat)
|
||||
return m_colorFormat < rhs.m_colorFormat;
|
||||
|
||||
if (m_depthStencilFormat != rhs.m_depthStencilFormat)
|
||||
return m_depthStencilFormat < rhs.m_depthStencilFormat;
|
||||
|
||||
return m_blendingEnabled < rhs.m_blendingEnabled;
|
||||
}
|
||||
|
||||
MTLRenderPipelineDescriptor * MetalStates::PipelineKey::BuildDescriptor() const
|
||||
{
|
||||
MTLRenderPipelineDescriptor * desc = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
desc.rasterSampleCount = 1;
|
||||
desc.vertexBuffers[0].mutability = MTLMutabilityImmutable; // The first VB is always immutable.
|
||||
ref_ptr<MetalGpuProgram> metalProgram = m_program;
|
||||
desc.vertexFunction = metalProgram->GetVertexShader();
|
||||
desc.fragmentFunction = metalProgram->GetFragmentShader();
|
||||
desc.vertexDescriptor = metalProgram->GetVertexDescriptor();
|
||||
MTLRenderPipelineColorAttachmentDescriptor * colorAttachment = desc.colorAttachments[0];
|
||||
colorAttachment.pixelFormat = m_colorFormat;
|
||||
desc.depthAttachmentPixelFormat = m_depthStencilFormat;
|
||||
if (IsStencilFormat(m_depthStencilFormat))
|
||||
desc.stencilAttachmentPixelFormat = m_depthStencilFormat;
|
||||
else
|
||||
desc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;
|
||||
colorAttachment.blendingEnabled = m_blendingEnabled ? YES : NO;
|
||||
colorAttachment.rgbBlendOperation = MTLBlendOperationAdd;
|
||||
colorAttachment.alphaBlendOperation = MTLBlendOperationAdd;
|
||||
colorAttachment.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
colorAttachment.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
colorAttachment.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
colorAttachment.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
return desc;
|
||||
}
|
||||
|
||||
MetalStates::SamplerKey::SamplerKey(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode)
|
||||
{
|
||||
Set(filter, wrapSMode, wrapTMode);
|
||||
}
|
||||
|
||||
void MetalStates::SamplerKey::Set(TextureFilter filter, TextureWrapping wrapSMode, TextureWrapping wrapTMode)
|
||||
{
|
||||
SetStateByte(m_sampler, static_cast<uint8_t>(filter), kMinFilterByte);
|
||||
SetStateByte(m_sampler, static_cast<uint8_t>(filter), kMagFilterByte);
|
||||
SetStateByte(m_sampler, static_cast<uint8_t>(wrapSMode), kWrapSModeByte);
|
||||
SetStateByte(m_sampler, static_cast<uint8_t>(wrapTMode), kWrapTModeByte);
|
||||
}
|
||||
|
||||
bool MetalStates::SamplerKey::operator<(SamplerKey const & rhs) const
|
||||
{
|
||||
return m_sampler < rhs.m_sampler;
|
||||
}
|
||||
|
||||
MTLSamplerDescriptor * MetalStates::SamplerKey::BuildDescriptor() const
|
||||
{
|
||||
MTLSamplerDescriptor * desc = [[MTLSamplerDescriptor alloc] init];
|
||||
desc.minFilter = DecodeTextureFilter(GetStateByte(m_sampler, kMinFilterByte));
|
||||
desc.magFilter = DecodeTextureFilter(GetStateByte(m_sampler, kMagFilterByte));
|
||||
desc.sAddressMode = DecodeTextureWrapping(GetStateByte(m_sampler, kWrapSModeByte));
|
||||
desc.tAddressMode = DecodeTextureWrapping(GetStateByte(m_sampler, kWrapTModeByte));
|
||||
return desc;
|
||||
}
|
||||
} // namespace metal
|
||||
} // namespace dp
|
||||
39
libs/drape/metal/metal_texture.hpp
Normal file
39
libs/drape/metal/metal_texture.hpp
Normal 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
|
||||
95
libs/drape/metal/metal_texture.mm
Normal file
95
libs/drape/metal/metal_texture.mm
Normal 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
|
||||
82
libs/drape/metal/metal_vertex_array_buffer_impl.mm
Normal file
82
libs/drape/metal/metal_vertex_array_buffer_impl.mm
Normal 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
|
||||
79
libs/drape/metal/render_state_metal.mm
Normal file
79
libs/drape/metal/render_state_metal.mm
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue