1112 lines
44 KiB
C++
Executable file
1112 lines
44 KiB
C++
Executable file
#include "gfx_metal.hpp"
|
|
|
|
#include "gfx_metal_buffer.hpp"
|
|
#include "gfx_metal_pipeline.hpp"
|
|
#include "gfx_commandbuffer.hpp"
|
|
#include "gfx_metal_texture.hpp"
|
|
#include "gfx_metal_renderpass.hpp"
|
|
#include "gfx_metal_framebuffer.hpp"
|
|
#include "gfx_metal_sampler.hpp"
|
|
#include "file.hpp"
|
|
#include "log.hpp"
|
|
#include "utility.hpp"
|
|
#include "string_utils.hpp"
|
|
|
|
static inline bool debug_enabled = false;
|
|
|
|
static inline std::array<GFXCommandBuffer*, 15> command_buffers;
|
|
static inline std::array<bool, 15> free_command_buffers;
|
|
|
|
MTL::PixelFormat toPixelFormat(GFXPixelFormat format) {
|
|
switch(format) {
|
|
case GFXPixelFormat::R_32F:
|
|
return MTL::PixelFormatR32Float;
|
|
case GFXPixelFormat::R_16F:
|
|
return MTL::PixelFormatR16Float;
|
|
case GFXPixelFormat::RGBA_32F:
|
|
return MTL::PixelFormatRGBA32Float;
|
|
case GFXPixelFormat::RGBA8_UNORM:
|
|
return MTL::PixelFormatRGBA8Unorm;
|
|
case GFXPixelFormat::R8_UNORM:
|
|
return MTL::PixelFormatR8Unorm;
|
|
case GFXPixelFormat::R8G8_UNORM:
|
|
return MTL::PixelFormatRG8Unorm;
|
|
case GFXPixelFormat::R8G8_SFLOAT:
|
|
return MTL::PixelFormatRG16Float;
|
|
case GFXPixelFormat::R8G8B8A8_UNORM:
|
|
return MTL::PixelFormatRGBA8Unorm;
|
|
case GFXPixelFormat::R16G16B16A16_SFLOAT:
|
|
return MTL::PixelFormatRGBA16Float;
|
|
case GFXPixelFormat::DEPTH_32F:
|
|
return MTL::PixelFormatDepth32Float;
|
|
}
|
|
}
|
|
|
|
MTL::BlendFactor toBlendFactor(GFXBlendFactor factor) {
|
|
switch(factor) {
|
|
case GFXBlendFactor::One:
|
|
return MTL::BlendFactorOne;
|
|
case GFXBlendFactor::Zero:
|
|
return MTL::BlendFactorZero;
|
|
case GFXBlendFactor::SrcColor:
|
|
return MTL::BlendFactorSourceColor;
|
|
case GFXBlendFactor::DstColor:
|
|
return MTL::BlendFactorDestinationColor;
|
|
case GFXBlendFactor::SrcAlpha:
|
|
return MTL::BlendFactorSourceAlpha;
|
|
case GFXBlendFactor::DstAlpha:
|
|
return MTL::BlendFactorDestinationAlpha;
|
|
case GFXBlendFactor::OneMinusSrcAlpha:
|
|
return MTL::BlendFactorOneMinusSourceAlpha;
|
|
case GFXBlendFactor::OneMinusSrcColor:
|
|
return MTL::BlendFactorOneMinusSourceColor;
|
|
}
|
|
}
|
|
|
|
MTL::SamplerAddressMode toSamplingMode(SamplingMode mode) {
|
|
switch(mode) {
|
|
case SamplingMode::Repeat:
|
|
return MTL::SamplerAddressModeRepeat;
|
|
case SamplingMode::ClampToEdge:
|
|
return MTL::SamplerAddressModeClampToEdge;
|
|
case SamplingMode::ClampToBorder:
|
|
{
|
|
#if defined(PLATFORM_IOS) || defined(PLATFORM_TVOS)
|
|
return MTL::SamplerAddressModeRepeat;
|
|
#else
|
|
return MTL::SamplerAddressModeClampToBorderColor;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined(PLATFORM_IOS) && !defined(PLATFORM_TVOS)
|
|
MTL::SamplerBorderColor toBorderColor(GFXBorderColor color) {
|
|
switch(color) {
|
|
case GFXBorderColor::OpaqueWhite:
|
|
return MTL::SamplerBorderColorOpaqueWhite;
|
|
case GFXBorderColor::OpaqueBlack:
|
|
return MTL::SamplerBorderColorOpaqueBlack;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MTL::CompareFunction toCompare(GFXCompareFunction function) {
|
|
switch(function) {
|
|
case GFXCompareFunction::Never:
|
|
return MTL::CompareFunctionNever;
|
|
case GFXCompareFunction::Less:
|
|
return MTL::CompareFunctionLess;
|
|
case GFXCompareFunction::Equal:
|
|
return MTL::CompareFunctionEqual;
|
|
case GFXCompareFunction::LessOrEqual:
|
|
return MTL::CompareFunctionLessEqual;
|
|
case GFXCompareFunction::Greater:
|
|
return MTL::CompareFunctionGreater;
|
|
case GFXCompareFunction::NotEqual:
|
|
return MTL::CompareFunctionNotEqual;
|
|
case GFXCompareFunction::GreaterOrEqual:
|
|
return MTL::CompareFunctionGreaterEqual;
|
|
case GFXCompareFunction::Always:
|
|
return MTL::CompareFunctionAlways;
|
|
}
|
|
}
|
|
|
|
MTL::SamplerMinMagFilter toFilter(GFXFilter filter) {
|
|
switch(filter) {
|
|
case GFXFilter::Nearest:
|
|
return MTL::SamplerMinMagFilterNearest;
|
|
case GFXFilter::Linear:
|
|
return MTL::SamplerMinMagFilterLinear;
|
|
}
|
|
}
|
|
|
|
MTL::Winding toWinding(GFXWindingMode mode) {
|
|
switch(mode) {
|
|
case GFXWindingMode::Clockwise:
|
|
return MTL::WindingClockwise;
|
|
case GFXWindingMode::CounterClockwise:
|
|
return MTL::WindingCounterClockwise;
|
|
}
|
|
}
|
|
|
|
bool GFXMetal::is_supported() {
|
|
return true;
|
|
}
|
|
|
|
bool GFXMetal::initialize(const GFXCreateInfo& createInfo) {
|
|
debug_enabled = createInfo.api_validation_enabled;
|
|
|
|
device = MTL::CreateSystemDefaultDevice();
|
|
if(device) {
|
|
command_queue = device->newCommandQueue();
|
|
|
|
for(int i = 0; i < 15; i++) {
|
|
command_buffers[i] = new GFXCommandBuffer();
|
|
free_command_buffers[i] = true;
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const char* GFXMetal::get_name() {
|
|
return "Metal";
|
|
}
|
|
|
|
bool GFXMetal::supports_feature(const GFXFeature feature) {
|
|
if(feature == GFXFeature::CubemapArray) {
|
|
#if defined(PLATFORM_TVOS)
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void GFXMetal::initialize_view(void* native_handle, const platform::window_ptr identifier, const uint32_t, const uint32_t) {
|
|
auto native = new NativeMTLView();
|
|
native->identifier = identifier;
|
|
|
|
native->format = (MTL::PixelFormat)platform::initialize_metal_layer(identifier, device);
|
|
|
|
nativeViews.push_back(native);
|
|
}
|
|
|
|
void GFXMetal::remove_view(const platform::window_ptr identifier) {
|
|
utility::erase_if(nativeViews, [identifier](NativeMTLView* view) {
|
|
return view->identifier == identifier;
|
|
});
|
|
}
|
|
|
|
GFXBuffer* GFXMetal::create_buffer(void* data, const GFXSize size, const bool dynamicData, const GFXBufferUsage) {
|
|
auto buffer = new GFXMetalBuffer();
|
|
buffer->dynamicData = dynamicData;
|
|
|
|
if(buffer->dynamicData) {
|
|
for(int i = 0; i < 3; i++) {
|
|
if(data == nullptr) {
|
|
buffer->handles[i] = device->newBuffer(size, MTL::ResourceOptionCPUCacheModeDefault);
|
|
} else {
|
|
buffer->handles[i] = device->newBuffer(data, size, MTL::ResourceOptionCPUCacheModeDefault);
|
|
}
|
|
}
|
|
} else {
|
|
if(data == nullptr) {
|
|
buffer->handles[0] = device->newBuffer(size, MTL::ResourceOptionCPUCacheModeDefault);
|
|
} else {
|
|
buffer->handles[0] = device->newBuffer(data, size, MTL::ResourceOptionCPUCacheModeDefault);
|
|
}
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
int currentFrameIndex = 0;
|
|
|
|
void GFXMetal::copy_buffer(GFXBuffer* buffer, void* data, const GFXSize offset, const GFXSize size) {
|
|
auto metalBuffer = (GFXMetalBuffer*)buffer;
|
|
|
|
auto src = reinterpret_cast<const unsigned char*>(data);
|
|
auto dest = reinterpret_cast<unsigned char *>(metalBuffer->get(currentFrameIndex)->contents());
|
|
if(dest != nullptr)
|
|
memcpy(dest + offset, src, size);
|
|
}
|
|
|
|
void* GFXMetal::get_buffer_contents(GFXBuffer* buffer) {
|
|
auto metalBuffer = (GFXMetalBuffer*)buffer;
|
|
|
|
return reinterpret_cast<unsigned char *>(metalBuffer->get(currentFrameIndex)->contents());
|
|
}
|
|
|
|
GFXTexture* GFXMetal::create_texture(const GFXTextureCreateInfo& info) {
|
|
auto texture = new GFXMetalTexture();
|
|
|
|
MTL::TextureDescriptor* textureDescriptor = MTL::TextureDescriptor::alloc()->init();
|
|
|
|
MTL::PixelFormat mtlFormat = toPixelFormat(info.format);
|
|
|
|
switch(info.type) {
|
|
case GFXTextureType::Single2D:
|
|
textureDescriptor->setTextureType(MTL::TextureType2D);
|
|
break;
|
|
case GFXTextureType::Array2D:
|
|
textureDescriptor->setTextureType(MTL::TextureType2DArray);
|
|
break;
|
|
case GFXTextureType::Cubemap:
|
|
{
|
|
textureDescriptor->setTextureType(MTL::TextureTypeCube);
|
|
texture->is_cubemap = true;
|
|
}
|
|
break;
|
|
case GFXTextureType::CubemapArray:
|
|
{
|
|
textureDescriptor->setTextureType(MTL::TextureTypeCubeArray);
|
|
texture->is_cubemap = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if((info.usage & GFXTextureUsage::Attachment) == GFXTextureUsage::Attachment) {
|
|
textureDescriptor->setStorageMode(MTL::StorageModePrivate);
|
|
textureDescriptor->setUsage(textureDescriptor->usage() | MTL::TextureUsageRenderTarget);
|
|
} else {
|
|
#if defined(PLATFORM_IOS) || defined(PLATFORM_TVOS)
|
|
textureDescriptor->setStorageMode(MTL::StorageModeShared);
|
|
#else
|
|
if(info.format == GFXPixelFormat::DEPTH_32F) {
|
|
textureDescriptor->setStorageMode(MTL::StorageModePrivate);
|
|
} else {
|
|
textureDescriptor->setStorageMode(MTL::StorageModeManaged);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if((info.usage & GFXTextureUsage::Sampled) == GFXTextureUsage::Sampled) {
|
|
textureDescriptor->setUsage(textureDescriptor->usage() | MTL::TextureUsageShaderRead);
|
|
}
|
|
|
|
if((info.usage & GFXTextureUsage::ShaderWrite) == GFXTextureUsage::ShaderWrite) {
|
|
textureDescriptor->setUsage(textureDescriptor->usage() | MTL::TextureUsageShaderWrite);
|
|
}
|
|
|
|
textureDescriptor->setPixelFormat(mtlFormat);
|
|
textureDescriptor->setWidth(info.width);
|
|
textureDescriptor->setHeight(info.height);
|
|
textureDescriptor->setDepth(1);
|
|
textureDescriptor->setArrayLength(info.array_length);
|
|
|
|
texture->array_length = info.array_length;
|
|
|
|
textureDescriptor->setMipmapLevelCount(info.mip_count);
|
|
|
|
texture->format = mtlFormat;
|
|
|
|
texture->handle = device->newTexture(textureDescriptor);
|
|
|
|
texture->width = info.width;
|
|
texture->height = info.height;
|
|
|
|
MTL::SamplerDescriptor* samplerDescriptor = MTL::SamplerDescriptor::alloc()->init();
|
|
samplerDescriptor->setMinFilter(MTL::SamplerMinMagFilterLinear);
|
|
samplerDescriptor->setMagFilter(MTL::SamplerMinMagFilterLinear);
|
|
samplerDescriptor->setSAddressMode(toSamplingMode(info.samplingMode));
|
|
samplerDescriptor->setTAddressMode(toSamplingMode(info.samplingMode));
|
|
samplerDescriptor->setMipFilter(MTL::SamplerMipFilterLinear);
|
|
samplerDescriptor->setMaxAnisotropy(16);
|
|
|
|
#if !defined(PLATFORM_IOS) && !defined(PLATFORM_TVOS)
|
|
samplerDescriptor->setBorderColor(toBorderColor(info.border_color));
|
|
#endif
|
|
|
|
if(info.compare_enabled)
|
|
samplerDescriptor->setCompareFunction(toCompare(info.compare_function));
|
|
|
|
texture->sampler = device->newSamplerState(samplerDescriptor);
|
|
|
|
return texture;
|
|
}
|
|
|
|
void GFXMetal::copy_texture(GFXTexture* texture, void* data, const GFXSize) {
|
|
auto metalTexture = (GFXMetalTexture*)texture;
|
|
|
|
MTL::Region region = {};
|
|
region.size.width = texture->width;
|
|
region.size.height = texture->height;
|
|
region.size.depth = 1;
|
|
|
|
int byteSize = 1;
|
|
if(metalTexture->format == MTL::PixelFormatRGBA8Unorm)
|
|
byteSize = 4;
|
|
else if(metalTexture->format == MTL::PixelFormatRG8Unorm)
|
|
byteSize = 2;
|
|
|
|
metalTexture->handle->replaceRegion(region, 0, data, texture->width * byteSize);
|
|
}
|
|
|
|
void GFXMetal::copy_texture(GFXTexture* from, GFXTexture* to) {
|
|
auto metalFromTexture = (GFXMetalTexture*)from;
|
|
auto metalToTexture = (GFXMetalTexture*)to;
|
|
|
|
MTL::CommandBuffer* commandBuffer = command_queue->commandBuffer();
|
|
MTL::BlitCommandEncoder* commandEncoder = commandBuffer->blitCommandEncoder();
|
|
commandEncoder->copyFromTexture(metalFromTexture->handle, metalToTexture->handle);
|
|
commandEncoder->endEncoding();
|
|
commandBuffer->commit();
|
|
commandBuffer->waitUntilCompleted();
|
|
}
|
|
|
|
void GFXMetal::copy_texture(GFXTexture* from, GFXBuffer* to) {
|
|
auto metalFromTexture = (GFXMetalTexture*)from;
|
|
auto metalToBuffer = (GFXMetalBuffer*)to;
|
|
|
|
MTL::Origin origin = {};
|
|
origin.x = 0;
|
|
origin.y = 0;
|
|
origin.z = 0;
|
|
|
|
MTL::Size size = {};
|
|
size.width = from->width;
|
|
size.height = from->height;
|
|
size.depth = 1;
|
|
|
|
int byteSize = 1;
|
|
if(metalFromTexture->format == MTL::PixelFormatRGBA8Unorm)
|
|
byteSize = 4;
|
|
|
|
MTL::CommandBuffer* commandBuffer = command_queue->commandBuffer();
|
|
MTL::BlitCommandEncoder* commandEncoder = commandBuffer->blitCommandEncoder();
|
|
commandEncoder->copyFromTexture(metalFromTexture->handle, 0, 0, origin, size, metalToBuffer->get(currentFrameIndex), 0, metalFromTexture->width * byteSize, 0);
|
|
commandEncoder->endEncoding();
|
|
commandBuffer->commit();
|
|
commandBuffer->waitUntilCompleted();
|
|
}
|
|
|
|
GFXSampler* GFXMetal::create_sampler(const GFXSamplerCreateInfo& info) {
|
|
auto sampler = new GFXMetalSampler();
|
|
|
|
MTL::SamplerDescriptor* samplerDescriptor = MTL::SamplerDescriptor::alloc()->init();
|
|
samplerDescriptor->setMinFilter(toFilter(info.min_filter));
|
|
samplerDescriptor->setMagFilter(toFilter(info.mag_filter));
|
|
samplerDescriptor->setSAddressMode(toSamplingMode(info.samplingMode));
|
|
samplerDescriptor->setTAddressMode(toSamplingMode(info.samplingMode));
|
|
samplerDescriptor->setMipFilter(MTL::SamplerMipFilterLinear);
|
|
samplerDescriptor->setMaxAnisotropy(16);
|
|
|
|
#if !defined(PLATFORM_IOS) && !defined(PLATFORM_TVOS)
|
|
samplerDescriptor->setBorderColor(toBorderColor(info.border_color));
|
|
#endif
|
|
|
|
if(info.compare_enabled)
|
|
samplerDescriptor->setCompareFunction(toCompare(info.compare_function));
|
|
|
|
sampler->handle = device->newSamplerState(samplerDescriptor);
|
|
|
|
return sampler;
|
|
}
|
|
|
|
GFXFramebuffer* GFXMetal::create_framebuffer(const GFXFramebufferCreateInfo& info) {
|
|
auto framebuffer = new GFXMetalFramebuffer();
|
|
|
|
for(auto& attachment : info.attachments)
|
|
framebuffer->attachments.push_back((GFXMetalTexture*)attachment);
|
|
|
|
return framebuffer;
|
|
}
|
|
|
|
GFXRenderPass* GFXMetal::create_render_pass(const GFXRenderPassCreateInfo& info) {
|
|
auto renderPass = new GFXMetalRenderPass();
|
|
|
|
for(const auto& attachment : info.attachments)
|
|
renderPass->attachments.push_back(toPixelFormat(attachment));
|
|
|
|
return renderPass;
|
|
}
|
|
|
|
MTL::FunctionConstantValues* get_constant_values(GFXShaderConstants constants) {
|
|
MTL::FunctionConstantValues* constantValues = MTL::FunctionConstantValues::alloc()->init();
|
|
|
|
for(auto& constant : constants) {
|
|
switch(constant.type) {
|
|
case GFXShaderConstant::Type::Integer:
|
|
constantValues->setConstantValue(&constant.value, MTL::DataTypeInt, constant.index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return constantValues;
|
|
}
|
|
|
|
GFXPipeline* GFXMetal::create_graphics_pipeline(const GFXGraphicsPipelineCreateInfo& info) {
|
|
auto pipeline = new GFXMetalPipeline();
|
|
pipeline->label = info.label;
|
|
|
|
NS::Error* error = nullptr;
|
|
|
|
MTL::RenderPipelineDescriptor* pipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
|
|
|
|
const bool has_vertex_stage = !info.shaders.vertex_src.empty();
|
|
const bool has_fragment_stage = !info.shaders.fragment_src.empty();
|
|
|
|
MTL::Function* vertexFunc;
|
|
if(has_vertex_stage) {
|
|
MTL::Library* vertexLibrary;
|
|
{
|
|
std::string vertex_src;
|
|
if(info.shaders.vertex_src.is_string()) {
|
|
vertex_src = info.shaders.vertex_src.as_string();
|
|
} else {
|
|
const auto vertex_path = info.shaders.vertex_src.as_path().string() + ".msl";
|
|
|
|
auto file = prism::open_file(prism::internal_domain / vertex_path);
|
|
if(file != std::nullopt) {
|
|
vertex_src = file->read_as_string();
|
|
} else {
|
|
prism::log("Failed to load vertex shader from {}!", vertex_path.data());
|
|
}
|
|
}
|
|
|
|
vertexLibrary = device->newLibrary(NS::String::string(vertex_src.c_str(), NS::ASCIIStringEncoding), nullptr,&error);
|
|
if(vertexLibrary == nullptr)
|
|
prism::log("Metal shader compiler error: {}", error->debugDescription()->cString(NS::ASCIIStringEncoding));
|
|
|
|
auto vertex_constants = get_constant_values(info.shaders.vertex_constants);
|
|
|
|
vertexFunc = vertexLibrary->newFunction(NS::String::string("main0", NS::ASCIIStringEncoding), vertex_constants, (NS::Error**)nullptr);
|
|
|
|
if(debug_enabled && info.shaders.vertex_src.is_path())
|
|
vertexFunc->setLabel(NS::String::string(info.shaders.vertex_src.as_path().string().data(), NS::ASCIIStringEncoding));
|
|
|
|
pipelineDescriptor->setVertexFunction(vertexFunc);
|
|
}
|
|
}
|
|
|
|
MTL::Function* fragmentFunc;
|
|
if(has_fragment_stage) {
|
|
MTL::Library* fragmentLibrary;
|
|
{
|
|
std::string fragment_src;
|
|
if(info.shaders.fragment_src.is_string()) {
|
|
fragment_src = info.shaders.fragment_src.as_string();
|
|
} else {
|
|
const auto fragment_path = info.shaders.fragment_src.as_path().string() + ".msl";
|
|
|
|
auto file = prism::open_file(prism::internal_domain / fragment_path);
|
|
if(file != std::nullopt) {
|
|
fragment_src = file->read_as_string();
|
|
} else {
|
|
prism::log("Failed to load fragment shader from {}!", fragment_path.data());
|
|
}
|
|
}
|
|
|
|
fragmentLibrary = device->newLibrary(NS::String::string(fragment_src.c_str(), NS::ASCIIStringEncoding), nullptr,&error);
|
|
if(fragmentLibrary == nullptr)
|
|
prism::log("Metal shader compiler error: {}", error->debugDescription()->cString(NS::ASCIIStringEncoding));
|
|
}
|
|
|
|
auto fragment_constants = get_constant_values(info.shaders.fragment_constants);
|
|
|
|
fragmentFunc = fragmentLibrary->newFunction(NS::String::string("main0", NS::ASCIIStringEncoding), fragment_constants, (NS::Error**)nullptr);
|
|
|
|
if(debug_enabled && info.shaders.fragment_src.is_path())
|
|
fragmentFunc->setLabel(NS::String::string(info.shaders.fragment_src.as_path().string().data(), NS::ASCIIStringEncoding));
|
|
|
|
pipelineDescriptor->setFragmentFunction(fragmentFunc);
|
|
}
|
|
|
|
MTL::VertexDescriptor* descriptor = MTL::VertexDescriptor::alloc()->init();
|
|
|
|
for(auto input : info.vertex_input.inputs) {
|
|
MTL::VertexBufferLayoutDescriptor* inputDescriptor = descriptor->layouts()->object(input.location);
|
|
inputDescriptor->setStride(input.stride);
|
|
inputDescriptor->setStepFunction(MTL::VertexStepFunctionPerVertex);
|
|
inputDescriptor->setStepRate(1);
|
|
|
|
GFXMetalPipeline::VertexStride vs = {};
|
|
vs.location = input.location;
|
|
vs.stride = input.stride;
|
|
|
|
pipeline->vertexStrides.push_back(vs);
|
|
}
|
|
|
|
for(auto attribute : info.vertex_input.attributes) {
|
|
MTL::VertexFormat format = MTL::VertexFormatFloat3;
|
|
switch(attribute.format) {
|
|
case GFXVertexFormat::FLOAT2:
|
|
format = MTL::VertexFormatFloat2;
|
|
break;
|
|
case GFXVertexFormat::FLOAT3:
|
|
format = MTL::VertexFormatFloat3;
|
|
break;
|
|
case GFXVertexFormat::FLOAT4:
|
|
format = MTL::VertexFormatFloat4;
|
|
break;
|
|
case GFXVertexFormat::INT:
|
|
format = MTL::VertexFormatInt;
|
|
break;
|
|
case GFXVertexFormat::UNORM4:
|
|
format = MTL::VertexFormatUChar4Normalized;
|
|
break;
|
|
case GFXVertexFormat::INT4:
|
|
format = MTL::VertexFormatInt4;
|
|
break;
|
|
}
|
|
|
|
MTL::VertexAttributeDescriptor* attributeDescriptor = descriptor->attributes()->object(attribute.location);
|
|
attributeDescriptor->setFormat(format);
|
|
attributeDescriptor->setBufferIndex(attribute.binding);
|
|
attributeDescriptor->setOffset(attribute.offset);
|
|
}
|
|
|
|
pipelineDescriptor->setVertexDescriptor(descriptor);
|
|
|
|
if(info.render_pass != nullptr) {
|
|
auto metalRenderPass = (GFXMetalRenderPass*)info.render_pass;
|
|
|
|
unsigned int i = 0;
|
|
for(const auto& attachment : metalRenderPass->attachments) {
|
|
if(attachment != MTL::PixelFormatDepth32Float) {
|
|
MTL::RenderPipelineColorAttachmentDescriptor* colorAttachmentDescriptor = pipelineDescriptor->colorAttachments()->object(i++);
|
|
|
|
colorAttachmentDescriptor->setPixelFormat(attachment);
|
|
colorAttachmentDescriptor->setBlendingEnabled(info.blending.enable_blending);
|
|
|
|
colorAttachmentDescriptor->setSourceRGBBlendFactor(toBlendFactor(info.blending.src_rgb));
|
|
colorAttachmentDescriptor->setDestinationRGBBlendFactor(toBlendFactor(info.blending.dst_rgb));
|
|
|
|
colorAttachmentDescriptor->setSourceAlphaBlendFactor(toBlendFactor(info.blending.src_alpha));
|
|
colorAttachmentDescriptor->setDestinationAlphaBlendFactor(toBlendFactor(info.blending.dst_alpha));
|
|
} else {
|
|
pipelineDescriptor->setDepthAttachmentPixelFormat(MTL::PixelFormatDepth32Float);
|
|
}
|
|
}
|
|
} else {
|
|
MTL::RenderPipelineColorAttachmentDescriptor* colorAttachmentDescriptor = pipelineDescriptor->colorAttachments()->object(0);
|
|
|
|
colorAttachmentDescriptor->setPixelFormat(nativeViews[0]->format);
|
|
colorAttachmentDescriptor->setBlendingEnabled(info.blending.enable_blending);
|
|
|
|
colorAttachmentDescriptor->setSourceRGBBlendFactor(toBlendFactor(info.blending.src_rgb));
|
|
colorAttachmentDescriptor->setDestinationRGBBlendFactor(toBlendFactor(info.blending.dst_rgb));
|
|
|
|
colorAttachmentDescriptor->setSourceAlphaBlendFactor(toBlendFactor(info.blending.src_alpha));
|
|
colorAttachmentDescriptor->setDestinationAlphaBlendFactor(toBlendFactor(info.blending.dst_alpha));
|
|
}
|
|
|
|
if(debug_enabled) {
|
|
pipelineDescriptor->setLabel(NS::String::string(info.label.data(), NS::ASCIIStringEncoding));
|
|
pipeline->label = info.label;
|
|
}
|
|
|
|
pipeline->handle = device->newRenderPipelineState(pipelineDescriptor, &error);
|
|
if(!pipeline->handle)
|
|
prism::log("Metal render pipeline creation error: {}", error->debugDescription()->cString(NS::ASCIIStringEncoding));
|
|
|
|
switch(info.rasterization.primitive_type) {
|
|
case GFXPrimitiveType::Triangle:
|
|
pipeline->primitiveType = MTL::PrimitiveTypeTriangle;
|
|
break;
|
|
case GFXPrimitiveType::TriangleStrip:
|
|
pipeline->primitiveType = MTL::PrimitiveTypeTriangleStrip;
|
|
break;
|
|
}
|
|
|
|
pipeline->winding_mode = info.rasterization.winding_mode;
|
|
|
|
MTL::DepthStencilDescriptor* depthStencil = MTL::DepthStencilDescriptor::alloc()->init();
|
|
|
|
if(info.depth.depth_mode != GFXDepthMode::None) {
|
|
switch(info.depth.depth_mode) {
|
|
case GFXDepthMode::Less:
|
|
depthStencil->setDepthCompareFunction(MTL::CompareFunctionLess);
|
|
break;
|
|
case GFXDepthMode::LessOrEqual:
|
|
depthStencil->setDepthCompareFunction(MTL::CompareFunctionLessEqual);
|
|
break;
|
|
case GFXDepthMode::Greater:
|
|
depthStencil->setDepthCompareFunction(MTL::CompareFunctionGreater);
|
|
break;
|
|
}
|
|
|
|
depthStencil->setDepthWriteEnabled(true);
|
|
}
|
|
|
|
pipeline->depthStencil = device->newDepthStencilState(depthStencil);
|
|
|
|
switch(info.rasterization.culling_mode) {
|
|
case GFXCullingMode::Frontface:
|
|
pipeline->cullMode = MTL::CullModeFront;
|
|
break;
|
|
case GFXCullingMode::Backface:
|
|
pipeline->cullMode = MTL::CullModeBack;
|
|
break;
|
|
case GFXCullingMode::None:
|
|
pipeline->cullMode = MTL::CullModeNone;
|
|
break;
|
|
}
|
|
|
|
if(info.rasterization.polygon_type == GFXPolygonType::Line)
|
|
pipeline->renderWire = true;
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
GFXPipeline* GFXMetal::create_compute_pipeline(const GFXComputePipelineCreateInfo& info) {
|
|
auto pipeline = new GFXMetalPipeline();
|
|
|
|
NS::Error* error = nullptr;
|
|
|
|
MTL::Library* computeLibrary;
|
|
{
|
|
std::string compute_src;
|
|
if(info.compute_src.is_string()) {
|
|
compute_src = info.compute_src.as_string();
|
|
} else {
|
|
const auto compute_path = info.compute_src.as_path().string() + ".msl";
|
|
|
|
auto file = prism::open_file(prism::internal_domain / compute_path);
|
|
if(file != std::nullopt) {
|
|
compute_src = file->read_as_string();
|
|
} else {
|
|
prism::log("Failed to load compute shader from {}!", compute_path.data());
|
|
}
|
|
}
|
|
|
|
computeLibrary = device->newLibrary(NS::String::string(compute_src.c_str(), NS::ASCIIStringEncoding), nullptr, &error);
|
|
if(!computeLibrary)
|
|
prism::log("Compute library compilation error: {}", error->debugDescription()->cString(NS::ASCIIStringEncoding));
|
|
}
|
|
|
|
MTL::Function* computeFunc = computeLibrary->newFunction(NS::String::string("main0", NS::ASCIIStringEncoding));
|
|
|
|
MTL::ComputePipelineDescriptor* pipelineDescriptor = MTL::ComputePipelineDescriptor::alloc()->init();
|
|
pipelineDescriptor->setComputeFunction(computeFunc);
|
|
|
|
pipeline->threadGroupSize = MTL::Size(info.workgroup_size_x, info.workgroup_size_y, info.workgroup_size_z);
|
|
|
|
if(debug_enabled) {
|
|
pipelineDescriptor->setLabel(NS::String::string(info.label.c_str(), NS::ASCIIStringEncoding));
|
|
pipeline->label = info.label;
|
|
}
|
|
|
|
pipeline->compute_handle = device->newComputePipelineState(pipelineDescriptor, MTL::PipelineOptionNone, nullptr, &error);
|
|
if(!pipeline->compute_handle)
|
|
prism::log("Compute pipeline error: {}", error->debugDescription()->cString(NS::ASCIIStringEncoding));
|
|
|
|
computeLibrary->release();
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
GFXSize GFXMetal::get_alignment(GFXSize size) {
|
|
#ifdef PLATFORM_MACOS
|
|
return (size + 32 / 2) & ~int(32 - 1);
|
|
#else
|
|
return size;
|
|
#endif
|
|
}
|
|
|
|
GFXCommandBuffer* GFXMetal::acquire_command_buffer(bool for_presentation_use) {
|
|
GFXCommandBuffer* cmdbuf = nullptr;
|
|
while(cmdbuf == nullptr) {
|
|
for(const auto [i, buffer_status] : utility::enumerate(free_command_buffers)) {
|
|
if(buffer_status) {
|
|
GFXCommandBuffer* buffer = command_buffers[i];
|
|
|
|
free_command_buffers[i] = false;
|
|
|
|
buffer->commands.clear();
|
|
|
|
return buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
return cmdbuf;
|
|
}
|
|
|
|
void GFXMetal::submit(GFXCommandBuffer* command_buffer, const platform::window_ptr window) {
|
|
NS::AutoreleasePool* pPool = NS::AutoreleasePool::alloc()->init();
|
|
|
|
NativeMTLView* native = getNativeView(window);
|
|
|
|
CA::MetalDrawable* drawable = nullptr;
|
|
if(native != nullptr)
|
|
drawable = ((CA::MetalDrawable*)platform::get_next_drawable(window));
|
|
|
|
MTL::CommandBuffer* commandBuffer = command_queue->commandBuffer();
|
|
|
|
MTL::RenderCommandEncoder* renderEncoder = nullptr;
|
|
MTL::ComputeCommandEncoder* computeEncoder = nullptr;
|
|
MTL::BlitCommandEncoder* blitEncoder = nullptr;
|
|
|
|
GFXMetalRenderPass* currentRenderPass = nullptr;
|
|
GFXMetalFramebuffer* currentFramebuffer = nullptr;
|
|
GFXMetalPipeline* currentPipeline = nullptr;
|
|
GFXMetalBuffer* currentIndexBuffer = nullptr;
|
|
IndexType currentIndextype = IndexType::UINT32;
|
|
MTL::Viewport currentViewport = MTL::Viewport();
|
|
MTL::ClearColor currentClearColor = {};
|
|
|
|
enum class CurrentEncoder {
|
|
None,
|
|
Render,
|
|
Compute,
|
|
Blit
|
|
} current_encoder = CurrentEncoder::None;
|
|
|
|
const auto needEncoder = [&](CurrentEncoder encoder, bool needs_reset = false) {
|
|
if(encoder != current_encoder || needs_reset) {
|
|
if(renderEncoder != nullptr)
|
|
renderEncoder->endEncoding();
|
|
|
|
if(computeEncoder != nullptr)
|
|
computeEncoder->endEncoding();
|
|
|
|
if(blitEncoder != nullptr)
|
|
blitEncoder->endEncoding();
|
|
|
|
renderEncoder = nullptr;
|
|
computeEncoder = nullptr;
|
|
blitEncoder = nullptr;
|
|
}
|
|
|
|
if(current_encoder == encoder && !needs_reset)
|
|
return;
|
|
|
|
switch(encoder) {
|
|
case CurrentEncoder::None:
|
|
break;
|
|
case CurrentEncoder::Render:
|
|
{
|
|
MTL::RenderPassDescriptor* descriptor = MTL::RenderPassDescriptor::alloc()->init();
|
|
|
|
if(currentRenderPass != nullptr && currentFramebuffer != nullptr) {
|
|
unsigned int i = 0;
|
|
for(const auto& attachment : currentFramebuffer->attachments) {
|
|
if(attachment->format == MTL::PixelFormatDepth32Float) {
|
|
MTL::RenderPassDepthAttachmentDescriptor* depthAttachment = descriptor->depthAttachment();
|
|
depthAttachment->setTexture(attachment->handle);
|
|
depthAttachment->setLoadAction(MTL::LoadActionClear);
|
|
depthAttachment->setStoreAction(MTL::StoreActionStore);
|
|
} else {
|
|
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = descriptor->colorAttachments()->object(i);
|
|
|
|
colorAttachment->setTexture(attachment->handle);
|
|
colorAttachment->setLoadAction(MTL::LoadActionClear);
|
|
colorAttachment->setStoreAction(MTL::StoreActionStore);
|
|
colorAttachment->setClearColor(currentClearColor);
|
|
}
|
|
}
|
|
} else {
|
|
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = descriptor->colorAttachments()->object(0);
|
|
|
|
colorAttachment->setTexture(drawable->texture());
|
|
colorAttachment->setLoadAction(MTL::LoadActionClear);
|
|
colorAttachment->setStoreAction(MTL::StoreActionStore);
|
|
colorAttachment->setClearColor(currentClearColor);
|
|
}
|
|
|
|
renderEncoder = commandBuffer->renderCommandEncoder(descriptor);
|
|
|
|
if(currentViewport.width != 0.0f && currentViewport.height != 0.0f)
|
|
renderEncoder->setViewport(currentViewport);
|
|
|
|
descriptor->release();
|
|
}
|
|
break;
|
|
case CurrentEncoder::Compute:
|
|
{
|
|
computeEncoder = commandBuffer->computeCommandEncoder();
|
|
}
|
|
break;
|
|
case CurrentEncoder::Blit:
|
|
{
|
|
blitEncoder = commandBuffer->blitCommandEncoder();
|
|
}
|
|
break;
|
|
}
|
|
|
|
current_encoder = encoder;
|
|
};
|
|
|
|
for(auto command : command_buffer->commands) {
|
|
switch(command.type) {
|
|
case GFXCommandType::Invalid:
|
|
break;
|
|
case GFXCommandType::SetRenderPass:
|
|
{
|
|
currentClearColor = MTL::ClearColor(command.data.set_render_pass.clear_color.r,
|
|
command.data.set_render_pass.clear_color.g,
|
|
command.data.set_render_pass.clear_color.b,
|
|
command.data.set_render_pass.clear_color.a );
|
|
|
|
currentFramebuffer = (GFXMetalFramebuffer*)command.data.set_render_pass.framebuffer;
|
|
currentRenderPass = (GFXMetalRenderPass*)command.data.set_render_pass.render_pass;
|
|
currentViewport = MTL::Viewport();
|
|
|
|
needEncoder(CurrentEncoder::Render, true);
|
|
}
|
|
break;
|
|
case GFXCommandType::SetGraphicsPipeline:
|
|
{
|
|
needEncoder(CurrentEncoder::Render);
|
|
|
|
currentPipeline = (GFXMetalPipeline*)command.data.set_graphics_pipeline.pipeline;
|
|
|
|
renderEncoder->setRenderPipelineState(((GFXMetalPipeline*)command.data.set_graphics_pipeline.pipeline)->handle);
|
|
renderEncoder->setDepthStencilState(currentPipeline->depthStencil);
|
|
renderEncoder->setCullMode(((GFXMetalPipeline*)command.data.set_graphics_pipeline.pipeline)->cullMode);
|
|
renderEncoder->setFrontFacingWinding(toWinding(((GFXMetalPipeline*)command.data.set_graphics_pipeline.pipeline)->winding_mode));
|
|
|
|
if(currentPipeline->renderWire)
|
|
renderEncoder->setTriangleFillMode(MTL::TriangleFillModeLines);
|
|
else
|
|
renderEncoder->setTriangleFillMode(MTL::TriangleFillModeFill);
|
|
}
|
|
break;
|
|
case GFXCommandType::SetComputePipeline:
|
|
{
|
|
needEncoder(CurrentEncoder::Compute);
|
|
|
|
currentPipeline = (GFXMetalPipeline*)command.data.set_compute_pipeline.pipeline;
|
|
|
|
computeEncoder->setComputePipelineState(((GFXMetalPipeline*)command.data.set_compute_pipeline.pipeline)->compute_handle);
|
|
}
|
|
break;
|
|
case GFXCommandType::SetVertexBuffer:
|
|
{
|
|
needEncoder(CurrentEncoder::Render);
|
|
|
|
renderEncoder->setVertexBuffer(((GFXMetalBuffer*)command.data.set_vertex_buffer.buffer)->get(currentFrameIndex), command.data.set_vertex_buffer.offset, command.data.set_vertex_buffer.index);
|
|
}
|
|
break;
|
|
case GFXCommandType::SetIndexBuffer:
|
|
{
|
|
currentIndexBuffer = (GFXMetalBuffer*)command.data.set_index_buffer.buffer;
|
|
currentIndextype = command.data.set_index_buffer.index_type;
|
|
}
|
|
break;
|
|
case GFXCommandType::SetPushConstant:
|
|
{
|
|
if(currentPipeline == nullptr)
|
|
continue;
|
|
|
|
if(current_encoder == CurrentEncoder::Render) {
|
|
renderEncoder->setVertexBytes(command.data.set_push_constant.bytes.data(), command.data.set_push_constant.size, currentPipeline->pushConstantIndex);
|
|
renderEncoder->setFragmentBytes(command.data.set_push_constant.bytes.data(), command.data.set_push_constant.size, currentPipeline->pushConstantIndex);
|
|
} else if(current_encoder == CurrentEncoder::Compute) {
|
|
computeEncoder->setBytes(command.data.set_push_constant.bytes.data(), command.data.set_push_constant.size, currentPipeline->pushConstantIndex);
|
|
}
|
|
}
|
|
break;
|
|
case GFXCommandType::BindShaderBuffer:
|
|
{
|
|
if(current_encoder == CurrentEncoder::Render) {
|
|
renderEncoder->setVertexBuffer(((GFXMetalBuffer*)command.data.bind_shader_buffer.buffer)->get(currentFrameIndex), command.data.bind_shader_buffer.offset, command.data.bind_shader_buffer.index);
|
|
renderEncoder->setFragmentBuffer(((GFXMetalBuffer*)command.data.bind_shader_buffer.buffer)->get(currentFrameIndex), command.data.bind_shader_buffer.offset, command.data.bind_shader_buffer.index);
|
|
} else if(current_encoder == CurrentEncoder::Compute) {
|
|
computeEncoder->setBuffer(((GFXMetalBuffer*)command.data.bind_shader_buffer.buffer)->get(currentFrameIndex), command.data.bind_shader_buffer.offset, command.data.bind_shader_buffer.index);
|
|
}
|
|
}
|
|
break;
|
|
case GFXCommandType::BindTexture:
|
|
{
|
|
if(current_encoder == CurrentEncoder::Render) {
|
|
if(command.data.bind_texture.texture != nullptr) {
|
|
renderEncoder->setVertexSamplerState(((GFXMetalTexture*)command.data.bind_texture.texture)->sampler, command.data.bind_texture.index);
|
|
|
|
renderEncoder->setVertexTexture(((GFXMetalTexture*)command.data.bind_texture.texture)->handle, command.data.bind_texture.index);
|
|
|
|
renderEncoder->setFragmentSamplerState(((GFXMetalTexture*)command.data.bind_texture.texture)->sampler, command.data.bind_texture.index);
|
|
|
|
renderEncoder->setFragmentTexture(((GFXMetalTexture*)command.data.bind_texture.texture)->handle, command.data.bind_texture.index);
|
|
} else {
|
|
renderEncoder->setVertexTexture(nullptr, command.data.bind_texture.index);
|
|
|
|
renderEncoder->setFragmentTexture(nullptr, command.data.bind_texture.index);
|
|
}
|
|
} else if(current_encoder == CurrentEncoder::Compute) {
|
|
computeEncoder->setTexture(((GFXMetalTexture*)command.data.bind_texture.texture)->handle, command.data.bind_texture.index);
|
|
}
|
|
}
|
|
break;
|
|
case GFXCommandType::BindSampler:
|
|
{
|
|
needEncoder(CurrentEncoder::Render);
|
|
|
|
if(command.data.bind_sampler.sampler != nullptr) {
|
|
renderEncoder->setFragmentSamplerState(((GFXMetalSampler*)command.data.bind_sampler.sampler)->handle, command.data.bind_sampler.index);
|
|
} else {
|
|
renderEncoder->setFragmentSamplerState(nullptr, command.data.bind_sampler.index);
|
|
}
|
|
}
|
|
break;
|
|
case GFXCommandType::Draw:
|
|
{
|
|
needEncoder(CurrentEncoder::Render);
|
|
|
|
if(currentPipeline == nullptr)
|
|
continue;
|
|
|
|
renderEncoder->drawPrimitives(currentPipeline->primitiveType,
|
|
command.data.draw.vertex_offset,
|
|
command.data.draw.vertex_count,
|
|
command.data.draw.instance_count,
|
|
command.data.draw.base_instance);
|
|
}
|
|
break;
|
|
case GFXCommandType::DrawIndexed:
|
|
{
|
|
needEncoder(CurrentEncoder::Render);
|
|
|
|
if(currentIndexBuffer == nullptr)
|
|
continue;
|
|
|
|
if(currentPipeline == nullptr)
|
|
continue;
|
|
|
|
MTL::IndexType indexType;
|
|
int indexSize;
|
|
switch(currentIndextype) {
|
|
case IndexType::UINT16:
|
|
{
|
|
indexType = MTL::IndexTypeUInt16;
|
|
indexSize = sizeof(uint16_t);
|
|
}
|
|
break;
|
|
case IndexType::UINT32:
|
|
{
|
|
indexType = MTL::IndexTypeUInt32;
|
|
indexSize = sizeof(uint32_t);
|
|
}
|
|
break;
|
|
}
|
|
|
|
for(auto& stride : currentPipeline->vertexStrides)
|
|
renderEncoder->setVertexBufferOffset(command.data.draw_indexed.vertex_offset * stride.stride, stride.location);
|
|
|
|
renderEncoder->drawIndexedPrimitives(currentPipeline->primitiveType,
|
|
command.data.draw_indexed.index_count,
|
|
indexType,
|
|
currentIndexBuffer->get(currentFrameIndex),
|
|
command.data.draw_indexed.first_index * indexSize);
|
|
}
|
|
break;
|
|
case GFXCommandType::MemoryBarrier:
|
|
{
|
|
needEncoder(CurrentEncoder::Render);
|
|
|
|
#ifdef PLATFORM_MACOS
|
|
renderEncoder->memoryBarrier(MTL::BarrierScopeTextures, MTL::RenderStageFragment, MTL::RenderStageFragment);
|
|
#endif
|
|
}
|
|
break;
|
|
case GFXCommandType::CopyTexture:
|
|
{
|
|
needEncoder(CurrentEncoder::Blit);
|
|
|
|
auto metalFromTexture = (GFXMetalTexture*)command.data.copy_texture.src;
|
|
auto metalToTexture = (GFXMetalTexture*)command.data.copy_texture.dst;
|
|
if(metalFromTexture != nullptr && metalToTexture != nullptr) {
|
|
const int slice_offset = command.data.copy_texture.to_slice + command.data.copy_texture.to_layer * 6;
|
|
|
|
blitEncoder->copyFromTexture(metalFromTexture->handle,
|
|
0,
|
|
0,
|
|
MTL::Origin(0, 0, 0),
|
|
MTL::Size(command.data.copy_texture.width, command.data.copy_texture.height, 1),
|
|
metalToTexture->handle,
|
|
slice_offset,
|
|
command.data.copy_texture.to_level,
|
|
MTL::Origin(0, 0, 0));
|
|
}
|
|
}
|
|
break;
|
|
case GFXCommandType::SetViewport:
|
|
{
|
|
MTL::Viewport viewport = {};
|
|
viewport.originX = command.data.set_viewport.viewport.x;
|
|
viewport.originY = command.data.set_viewport.viewport.y;
|
|
viewport.width = command.data.set_viewport.viewport.width;
|
|
viewport.height = command.data.set_viewport.viewport.height;
|
|
viewport.znear = command.data.set_viewport.viewport.min_depth;
|
|
viewport.zfar = command.data.set_viewport.viewport.max_depth;
|
|
|
|
if(renderEncoder != nullptr)
|
|
renderEncoder->setViewport(viewport);
|
|
|
|
currentViewport = viewport;
|
|
}
|
|
break;
|
|
case GFXCommandType::SetScissor:
|
|
{
|
|
needEncoder(CurrentEncoder::Render);
|
|
|
|
MTL::ScissorRect rect = {};
|
|
rect.x = command.data.set_scissor.rect.offset.x;
|
|
rect.y = command.data.set_scissor.rect.offset.y;
|
|
rect.width = command.data.set_scissor.rect.extent.width;
|
|
rect.height = command.data.set_scissor.rect.extent.height;
|
|
|
|
renderEncoder->setScissorRect(rect);
|
|
}
|
|
break;
|
|
case GFXCommandType::GenerateMipmaps: {
|
|
needEncoder(CurrentEncoder::Blit);
|
|
|
|
auto metalTexture = (GFXMetalTexture*)command.data.generate_mipmaps.texture;
|
|
|
|
blitEncoder->generateMipmaps(metalTexture->handle);
|
|
}
|
|
break;
|
|
case GFXCommandType::SetDepthBias: {
|
|
needEncoder(CurrentEncoder::Render);
|
|
|
|
renderEncoder->setDepthBias(command.data.set_depth_bias.constant, command.data.set_depth_bias.slope_factor, command.data.set_depth_bias.clamp);
|
|
}
|
|
break;
|
|
case GFXCommandType::PushGroup: {
|
|
commandBuffer->pushDebugGroup(NS::String::string(command.data.push_group.name.data(), NS::ASCIIStringEncoding));
|
|
}
|
|
break;
|
|
case GFXCommandType::PopGroup: {
|
|
commandBuffer->popDebugGroup();
|
|
}
|
|
break;
|
|
case GFXCommandType::InsertLabel: {
|
|
switch(current_encoder) {
|
|
case CurrentEncoder::Render:
|
|
renderEncoder->insertDebugSignpost(NS::String::string(command.data.insert_label.name.data(), NS::ASCIIStringEncoding));
|
|
break;
|
|
case CurrentEncoder::Blit:
|
|
blitEncoder->insertDebugSignpost(NS::String::string(command.data.insert_label.name.data(), NS::ASCIIStringEncoding));
|
|
break;
|
|
case CurrentEncoder::Compute:
|
|
computeEncoder->insertDebugSignpost(NS::String::string(command.data.insert_label.name.data(), NS::ASCIIStringEncoding));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case GFXCommandType::Dispatch: {
|
|
needEncoder(CurrentEncoder::Compute);
|
|
|
|
computeEncoder->dispatchThreadgroups(MTL::Size(command.data.dispatch.group_count_x, command.data.dispatch.group_count_y, command.data.dispatch.group_count_z), currentPipeline->threadGroupSize);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(renderEncoder != nullptr)
|
|
renderEncoder->endEncoding();
|
|
|
|
if(blitEncoder != nullptr)
|
|
blitEncoder->endEncoding();
|
|
|
|
if(computeEncoder != nullptr)
|
|
computeEncoder->endEncoding();
|
|
|
|
commandBuffer->addCompletedHandler([command_buffer](MTL::CommandBuffer* _) {
|
|
for(auto [i, buffer] : utility::enumerate(command_buffers)) {
|
|
if(buffer == command_buffer)
|
|
free_command_buffers[i] = true;
|
|
}
|
|
});
|
|
|
|
if(window != nullptr) {
|
|
commandBuffer->presentDrawable(drawable);
|
|
commandBuffer->commit();
|
|
|
|
currentFrameIndex = (currentFrameIndex + 1) % 3;
|
|
} else {
|
|
commandBuffer->commit();
|
|
}
|
|
|
|
pPool->release();
|
|
}
|