246 lines
12 KiB
Plaintext
246 lines
12 KiB
Plaintext
//
|
|
// Copyright Contributors to the MaterialX Project
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
#include "MetalState.h"
|
|
#import <Metal/Metal.h>
|
|
|
|
#include <MaterialXRenderMsl/MetalFramebuffer.h>
|
|
|
|
MATERIALX_NAMESPACE_BEGIN
|
|
|
|
std::unique_ptr<MetalState> MetalState::singleton = nullptr;
|
|
|
|
MetalState::MetalState()
|
|
{
|
|
}
|
|
|
|
void MetalState::initialize(id<MTLDevice> mtlDevice, id<MTLCommandQueue> mtlCmdQueue)
|
|
{
|
|
device = mtlDevice;
|
|
cmdQueue = mtlCmdQueue;
|
|
|
|
#ifdef MAC_OS_VERSION_11_0
|
|
if (@available(macOS 11.0, ios 14.0, *))
|
|
{
|
|
supportsTiledPipeline = [device supportsFamily:MTLGPUFamilyApple4];
|
|
}
|
|
#else
|
|
supportsTiledPipeline = false;
|
|
#endif
|
|
|
|
MTLDepthStencilDescriptor* depthStencilDesc = [MTLDepthStencilDescriptor new];
|
|
depthStencilDesc.depthWriteEnabled = true;
|
|
depthStencilDesc.depthCompareFunction = MTLCompareFunctionLess;
|
|
opaqueDepthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDesc];
|
|
|
|
depthStencilDesc.depthWriteEnabled = false;
|
|
depthStencilDesc.depthCompareFunction = MTLCompareFunctionLess;
|
|
transparentDepthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDesc];
|
|
|
|
depthStencilDesc.depthWriteEnabled = true;
|
|
depthStencilDesc.depthCompareFunction = MTLCompareFunctionAlways;
|
|
envMapDepthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDesc];
|
|
|
|
initLinearToSRGBKernel();
|
|
}
|
|
|
|
void MetalState::initLinearToSRGBKernel()
|
|
{
|
|
NSError* error = nil;
|
|
MTLCompileOptions* options = [MTLCompileOptions new];
|
|
#ifdef MAC_OS_VERSION_11_0
|
|
if (@available(macOS 11.0, ios 14.0, *))
|
|
options.languageVersion = MTLLanguageVersion2_3;
|
|
else
|
|
#endif
|
|
options.languageVersion = MTLLanguageVersion2_0;
|
|
options.fastMathEnabled = true;
|
|
|
|
#ifdef MAC_OS_VERSION_11_0
|
|
bool useTiledPipeline = supportsTiledPipeline;
|
|
if (useTiledPipeline)
|
|
{
|
|
if (@available(macOS 11.0, ios 14.0, *))
|
|
{
|
|
NSString* linearToSRGB_kernel =
|
|
@"#include <metal_stdlib> \n"
|
|
"#include <simd/simd.h> \n"
|
|
" \n"
|
|
"using namespace metal; \n"
|
|
" \n"
|
|
"struct RenderTarget { \n"
|
|
" half4 colorTarget [[color(0)]]; \n"
|
|
"}; \n"
|
|
" \n"
|
|
" \n"
|
|
" \n"
|
|
"half4 linearToSRGB(half4 color_linear) \n"
|
|
"{ \n"
|
|
" half4 color_srgb; \n"
|
|
" for(int i = 0; i < 3; ++i) \n"
|
|
" color_srgb[i] = (color_linear[i] < 0.0031308) ? \n"
|
|
" (12.92 * color_linear[i]) : \n"
|
|
" (1.055 * pow(color_linear[i], 1.0h / 2.2h) - 0.055); \n"
|
|
" color_srgb[3] = color_linear[3]; \n"
|
|
" return color_srgb; \n"
|
|
"} \n"
|
|
" \n"
|
|
"kernel void LinearToSRGB_kernel( \n"
|
|
" imageblock<RenderTarget,imageblock_layout_implicit> imageBlock, \n"
|
|
" ushort2 tid [[ thread_position_in_threadgroup ]]) \n"
|
|
"{ \n"
|
|
" RenderTarget linearValue = imageBlock.read(tid); \n"
|
|
" RenderTarget srgbValue; \n"
|
|
" srgbValue.colorTarget = linearToSRGB(linearValue.colorTarget); \n"
|
|
" imageBlock.write(srgbValue, tid); \n"
|
|
"} \n";
|
|
|
|
id<MTLLibrary> library = [device newLibraryWithSource:linearToSRGB_kernel options:options error:&error];
|
|
id<MTLFunction> function = [library newFunctionWithName:@"LinearToSRGB_kernel"];
|
|
|
|
MTLTileRenderPipelineDescriptor* renderPipelineDescriptor = [MTLTileRenderPipelineDescriptor new];
|
|
[renderPipelineDescriptor setRasterSampleCount:1];
|
|
[[renderPipelineDescriptor colorAttachments][0] setPixelFormat:MTLPixelFormatRGBA16Float];
|
|
[renderPipelineDescriptor setTileFunction:function];
|
|
linearToSRGB_pso = [device newRenderPipelineStateWithTileDescriptor:renderPipelineDescriptor options:0 reflection:nil error:&error];
|
|
}
|
|
else
|
|
{
|
|
useTiledPipeline = false;
|
|
}
|
|
}
|
|
|
|
if (!useTiledPipeline)
|
|
#endif
|
|
{
|
|
NSString* linearToSRGB_kernel =
|
|
@"#include <metal_stdlib> \n"
|
|
"#include <simd/simd.h> \n"
|
|
" \n"
|
|
"using namespace metal; \n"
|
|
" \n"
|
|
"struct VSOutput \n"
|
|
"{ \n"
|
|
" float4 position [[position]]; \n"
|
|
"}; \n"
|
|
" \n"
|
|
"vertex VSOutput VertexMain(uint vertexId [[ vertex_id ]]) \n"
|
|
"{ \n"
|
|
" VSOutput vsOut; \n"
|
|
" \n"
|
|
" switch(vertexId) \n"
|
|
" { \n"
|
|
" case 0: vsOut.position = float4(-1, -1, 0.5, 1); break; \n"
|
|
" case 1: vsOut.position = float4(-1, 3, 0.5, 1); break; \n"
|
|
" case 2: vsOut.position = float4( 3, -1, 0.5, 1); break; \n"
|
|
" }; \n"
|
|
" \n"
|
|
" return vsOut; \n"
|
|
"} \n"
|
|
" \n"
|
|
"half4 linearToSRGB(half4 color_linear) \n"
|
|
"{ \n"
|
|
" half4 color_srgb; \n"
|
|
" for(int i = 0; i < 3; ++i) \n"
|
|
" color_srgb[i] = (color_linear[i] < 0.0031308) ? \n"
|
|
" (12.92 * color_linear[i]) : \n"
|
|
" (1.055 * pow(color_linear[i], 1.0h / 2.2h) - 0.055);\n"
|
|
" color_srgb[3] = color_linear[3]; \n"
|
|
" return color_srgb; \n"
|
|
"} \n"
|
|
" \n"
|
|
"fragment half4 FragmentMain( \n"
|
|
" texture2d<half> inputTex [[ texture(0) ]], \n"
|
|
" float4 fragCoord [[ position ]] \n"
|
|
") \n"
|
|
"{ \n"
|
|
" constexpr sampler ss( \n"
|
|
" coord::pixel, \n"
|
|
" address::clamp_to_border, \n"
|
|
" filter::linear); \n"
|
|
" return linearToSRGB(inputTex.sample(ss, fragCoord.xy)); \n"
|
|
"} \n";
|
|
|
|
id<MTLLibrary> library = [device newLibraryWithSource:linearToSRGB_kernel options:options error:&error];
|
|
|
|
id<MTLFunction> vertexfunction = [library newFunctionWithName:@"VertexMain"];
|
|
id<MTLFunction> Fragmentfunction = [library newFunctionWithName:@"FragmentMain"];
|
|
|
|
MTLRenderPipelineDescriptor* renderPipelineDesc = [MTLRenderPipelineDescriptor new];
|
|
[renderPipelineDesc setVertexFunction:vertexfunction];
|
|
[renderPipelineDesc setFragmentFunction:Fragmentfunction];
|
|
[[renderPipelineDesc colorAttachments][0] setPixelFormat:MTLPixelFormatRGBA16Float];
|
|
[renderPipelineDesc setDepthAttachmentPixelFormat:MTLPixelFormatDepth32Float];
|
|
linearToSRGB_pso = [device newRenderPipelineStateWithDescriptor:renderPipelineDesc error:&error];
|
|
}
|
|
}
|
|
|
|
void MetalState::triggerProgrammaticCapture()
|
|
{
|
|
MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager];
|
|
MTLCaptureDescriptor* captureDescriptor = [MTLCaptureDescriptor new];
|
|
|
|
[captureDescriptor setCaptureObject:device];
|
|
|
|
NSError* error = nil;
|
|
if (![captureManager startCaptureWithDescriptor:captureDescriptor error:&error])
|
|
{
|
|
NSLog(@"Failed to start capture, error %@", error);
|
|
}
|
|
}
|
|
|
|
void MetalState::stopProgrammaticCapture()
|
|
{
|
|
MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager];
|
|
[captureManager stopCapture];
|
|
}
|
|
|
|
void MetalState::beginCommandBuffer()
|
|
{
|
|
cmdBuffer = [cmdQueue commandBuffer];
|
|
inFlightCommandBuffers++;
|
|
}
|
|
|
|
void MetalState::beginEncoder(MTLRenderPassDescriptor* renderpassDesc)
|
|
{
|
|
renderCmdEncoder = [cmdBuffer
|
|
renderCommandEncoderWithDescriptor:renderpassDesc];
|
|
}
|
|
|
|
void MetalState::endEncoder()
|
|
{
|
|
[renderCmdEncoder endEncoding];
|
|
}
|
|
|
|
void MetalState::endCommandBuffer()
|
|
{
|
|
endEncoder();
|
|
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
|
|
inFlightCommandBuffers--;
|
|
inFlightCV.notify_one();
|
|
}];
|
|
[cmdBuffer commit];
|
|
[cmdBuffer waitUntilCompleted];
|
|
}
|
|
|
|
void MetalState::waitForCompletion()
|
|
{
|
|
std::unique_lock<std::mutex> lock(inFlightMutex);
|
|
while (inFlightCommandBuffers != 0)
|
|
{
|
|
inFlightCV.wait(lock, [this]
|
|
{
|
|
return inFlightCommandBuffers.load() == 0;
|
|
});
|
|
}
|
|
}
|
|
|
|
MaterialX::MetalFramebufferPtr MetalState::currentFramebuffer()
|
|
{
|
|
return framebufferStack.top();
|
|
}
|
|
|
|
MATERIALX_NAMESPACE_END
|