686 lines
20 KiB
C++
686 lines
20 KiB
C++
#include "Metal/Metal.hpp"
|
|
#include "math3d.h"
|
|
#include "filesystem.h"
|
|
#include "rendering.h"
|
|
#include "tier0/lib.h"
|
|
#include "tier1/utlvector.h"
|
|
#include "tier0/platform.h"
|
|
#include "rendering.h"
|
|
#include "ml_video.h"
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
#include "TargetConditionals.h"
|
|
#if TARGET_OS_IPHONE
|
|
// iOS
|
|
#define STBI_NO_THREAD_LOCALS
|
|
#else
|
|
// macOS
|
|
#endif
|
|
#else
|
|
// Other platforms
|
|
#endif
|
|
#include "stb_image.h"
|
|
|
|
mat4 g_cameraView;
|
|
IImage *g_meshDepth;
|
|
IImage *g_meshDepthMSAA;
|
|
IImage *g_meshColor;
|
|
IImage *g_meshColorMSAA;
|
|
|
|
CUtlVector<ITexture*> g_textures;
|
|
|
|
IBuffer *g_cameraProperties;
|
|
struct CameraProjection {
|
|
mat4 viewprojection;
|
|
};
|
|
|
|
CameraProjection *g_cameraDataMap;
|
|
|
|
static MTL::RenderPassDescriptor *s_pRenderPass;
|
|
static MTL::RenderCommandEncoder *s_pEncoder;
|
|
static uint32_t s_nNumAttachments;
|
|
|
|
CUtlVector<MTL::Texture*> g_destroyImageBuffer;
|
|
CUtlVector<MTL::Buffer*> g_destroyBuffersBuffer;
|
|
|
|
extern MTL::PixelFormat g_swapchainFormat;
|
|
|
|
MTL::PixelFormat IRenderer_FormatToMl( EImageFormat format )
|
|
{
|
|
switch (format)
|
|
{
|
|
case IMAGE_FORMAT_R8G8B8A8: return MTL::PixelFormatRGBA8Unorm;
|
|
case IMAGE_FORMAT_R16G16B16A16: return MTL::PixelFormatRGBA16Unorm;
|
|
case IMAGE_FORMAT_DEPTH: return MTL::PixelFormatDepth32Float;
|
|
case IMAGE_FORMAT_WINDOW: return g_swapchainFormat;
|
|
default: return MTL::PixelFormatRGBA8Unorm;
|
|
}
|
|
};
|
|
|
|
MTL::VertexFormat IRenderer_VertexToMl( EVertexFormat format )
|
|
{
|
|
switch (format)
|
|
{
|
|
case VERTEX_FORMAT_X16: return MTL::VertexFormatHalf;
|
|
case VERTEX_FORMAT_X16Y16: return MTL::VertexFormatHalf2;
|
|
case VERTEX_FORMAT_X16Y16Z16: return MTL::VertexFormatHalf3;
|
|
case VERTEX_FORMAT_X16Y16Z16W16: return MTL::VertexFormatHalf4;
|
|
case VERTEX_FORMAT_X32: return MTL::VertexFormatFloat;
|
|
case VERTEX_FORMAT_X32Y32: return MTL::VertexFormatFloat2;
|
|
case VERTEX_FORMAT_X32Y32Z32: return MTL::VertexFormatFloat3;
|
|
case VERTEX_FORMAT_X32Y32Z32W32: return MTL::VertexFormatFloat4;
|
|
default: return MTL::VertexFormatFloat3;
|
|
}
|
|
};
|
|
|
|
MTL::LoadAction IRenderer_LoadOpMl( EAttachmentLoadMode mode )
|
|
{
|
|
switch (mode)
|
|
{
|
|
case ATTACHMENT_LOAD_MODE_DONT_CARE: return MTL::LoadActionDontCare;
|
|
case ATTACHMENT_LOAD_MODE_CLEAR: return MTL::LoadActionClear;
|
|
case ATTACHMENT_LOAD_MODE_LOAD: return MTL::LoadActionLoad;
|
|
default: return MTL::LoadActionDontCare;
|
|
}
|
|
}
|
|
|
|
MTL::StoreAction IRenderer_StoreOpMl( EAttachmentStoreMode mode )
|
|
{
|
|
switch (mode)
|
|
{
|
|
case ATTACHMENT_STORE_MODE_DONT_CARE: return MTL::StoreActionStore;
|
|
case ATTACHMENT_STORE_MODE_STORE: return MTL::StoreActionStore;
|
|
default: return MTL::StoreActionDontCare;
|
|
}
|
|
}
|
|
|
|
class CMlBuffer: public IBuffer
|
|
{
|
|
public:
|
|
void *Map() override;
|
|
void Unmap() override;
|
|
MTL::Buffer *m_buffer;
|
|
uint32_t m_nSize;
|
|
};
|
|
|
|
|
|
class CMlImage: public IImage
|
|
{
|
|
public:
|
|
MTL::Texture *m_image;
|
|
};
|
|
|
|
|
|
|
|
void *CMlBuffer::Map()
|
|
{
|
|
return m_buffer->contents();
|
|
}
|
|
|
|
void CMlBuffer::Unmap()
|
|
{
|
|
|
|
}
|
|
struct MLShader_t {
|
|
EShaderType shaderType;
|
|
MTL::Library *library;
|
|
MTL::Function *function;
|
|
NS::String *szMain;
|
|
};
|
|
class CMlGraphicsPipeline: public IGraphicsPipeline
|
|
{
|
|
public:
|
|
CUtlVector<MLShader_t> m_shaders;
|
|
MTL::RenderPipelineState *m_pipeline;
|
|
CUtlVector<ShaderInput_t> m_inputs;
|
|
uint32_t m_nVertexSize;
|
|
};
|
|
|
|
class CMlTexture: public ITexture
|
|
{
|
|
public:
|
|
MTL::Texture *m_texture;
|
|
};
|
|
|
|
IStorageBuffer *IRenderer::CreateStorageBuffer( uint32_t uSize )
|
|
{
|
|
CMlBuffer *pBuffer = new CMlBuffer;
|
|
pBuffer->m_buffer = g_mlDevice->newBuffer(uSize, MTL::ResourceStorageModeShared);
|
|
pBuffer->m_nSize = uSize;
|
|
return pBuffer;
|
|
}
|
|
|
|
IUniformBuffer *IRenderer::CreateUniformBuffer( uint32_t uSize )
|
|
{
|
|
CMlBuffer *pBuffer = new CMlBuffer;
|
|
pBuffer->m_buffer = g_mlDevice->newBuffer(uSize, MTL::ResourceStorageModeShared);
|
|
pBuffer->m_nSize = uSize;
|
|
return pBuffer;
|
|
}
|
|
|
|
|
|
IVertexBuffer *IRenderer::CreateVertexBuffer( uint32_t uSize )
|
|
{
|
|
CMlBuffer *pBuffer = new CMlBuffer;
|
|
pBuffer->m_buffer = g_mlDevice->newBuffer(uSize, MTL::ResourceStorageModeShared);
|
|
pBuffer->m_nSize = uSize;
|
|
return pBuffer;
|
|
}
|
|
|
|
IIndexBuffer *IRenderer::CreateIndexBuffer( uint32_t uSize )
|
|
{
|
|
CMlBuffer *pBuffer = new CMlBuffer;
|
|
pBuffer->m_buffer = g_mlDevice->newBuffer(uSize, MTL::ResourceStorageModeShared);
|
|
pBuffer->m_nSize = uSize;
|
|
return pBuffer;
|
|
}
|
|
|
|
|
|
IImage *IRenderer::CreateImage( EImageFormat format, uint32_t usage, uint32_t nWidth, uint32_t nHeight, uint32_t nSamples )
|
|
{
|
|
CMlImage *pImage = new CMlImage;
|
|
|
|
MTL::TextureDescriptor *pCreateInfo = MTL::TextureDescriptor::alloc()->init();
|
|
pCreateInfo->setPixelFormat(IRenderer_FormatToMl(format));
|
|
pCreateInfo->setWidth(nWidth);
|
|
pCreateInfo->setHeight(nHeight);
|
|
|
|
MTL::TextureUsage mlusage = 0;
|
|
if (usage&IMAGE_USAGE_COLOR_ATTACHMENT) mlusage |= MTL::TextureUsageRenderTarget;
|
|
if (usage&IMAGE_USAGE_DEPTH_ATTACHMENT) mlusage |= MTL::TextureUsageRenderTarget;
|
|
mlusage |= MTL::TextureUsageShaderRead;
|
|
mlusage |= MTL::TextureUsageShaderWrite;
|
|
pCreateInfo->setUsage(mlusage);
|
|
|
|
pImage->m_image = g_mlDevice->newTexture(pCreateInfo);
|
|
|
|
pCreateInfo->release();
|
|
|
|
return pImage;
|
|
};
|
|
|
|
void IRenderer::DestroyBuffer( IBuffer *pBuffer )
|
|
{
|
|
if (!pBuffer)
|
|
return;
|
|
CMlBuffer *pMlBuffer = (CMlBuffer*)pBuffer;
|
|
g_destroyBuffersBuffer.AppendTail(pMlBuffer->m_buffer);
|
|
delete (pMlBuffer);
|
|
}
|
|
|
|
void IRenderer::DestroyImage( IImage *pImage )
|
|
{
|
|
if (!pImage)
|
|
return;
|
|
CMlImage *pMlImage = (CMlImage*)pImage;
|
|
g_destroyImageBuffer.AppendTail(pMlImage->m_image);
|
|
delete (pMlImage);
|
|
}
|
|
|
|
IPipeline *g_pCurrentPipeline;
|
|
|
|
|
|
void IRenderer::SetConstants( uint32_t nSize, void *pData )
|
|
{
|
|
|
|
if (!g_pCurrentPipeline)
|
|
return;
|
|
uint32_t nRoundedSize = nSize/8*8;
|
|
void *pTemporaryBuffer = V_malloc(nRoundedSize);
|
|
V_memcpy(pTemporaryBuffer, pData, nSize);
|
|
if (g_pCurrentPipeline->type == PIPELINE_TYPE_RASTERIZATION)
|
|
{
|
|
CMlGraphicsPipeline *pMlPipeline = (CMlGraphicsPipeline*)g_pCurrentPipeline;
|
|
s_pEncoder->setVertexBytes(pTemporaryBuffer, nRoundedSize, 29);
|
|
s_pEncoder->setFragmentBytes(pTemporaryBuffer, nRoundedSize, 29);
|
|
}
|
|
V_free(pTemporaryBuffer);
|
|
}
|
|
|
|
void IRenderer::Barrier( uint32_t stageIn, uint32_t stageOut, CUtlVector<BufferBarrier_t> buffers, CUtlVector<ImageBarrier_t> images )
|
|
{
|
|
|
|
}
|
|
|
|
void IRenderer::BindData( uint32_t binding, IBuffer *pBuffer, IImage* pImage)
|
|
{
|
|
if (!g_pCurrentPipeline)
|
|
return;
|
|
if (g_pCurrentPipeline->type == PIPELINE_TYPE_RASTERIZATION)
|
|
{
|
|
CMlBuffer *pMlBuffer = (CMlBuffer*)pBuffer;
|
|
CMlGraphicsPipeline *pMlPipeline = (CMlGraphicsPipeline*)g_pCurrentPipeline;
|
|
for (auto &input: pMlPipeline->m_inputs)
|
|
{
|
|
if (input.binding != binding)
|
|
continue;
|
|
|
|
switch (input.type)
|
|
{
|
|
case SHADER_INPUT_TYPE_STORAGE_BUFFER:
|
|
case SHADER_INPUT_TYPE_UNIFORM_BUFFER:
|
|
if (!pBuffer)
|
|
Plat_FatalErrorFunc("pBuffer is NULL\n");
|
|
s_pEncoder->setVertexBuffer(pMlBuffer->m_buffer, 0, binding);
|
|
s_pEncoder->setFragmentBuffer(pMlBuffer->m_buffer, 0, binding);
|
|
break;
|
|
case SHADER_INPUT_TYPE_IMAGE:
|
|
break;
|
|
case SHADER_INPUT_TYPE_TLAS:
|
|
break;
|
|
case SHADER_INPUT_TYPE_TEXTURES:
|
|
break;
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
void IRenderer::BindPipeline( IPipeline *pPipeline )
|
|
{
|
|
if (!pPipeline)
|
|
return;
|
|
if (pPipeline->type == PIPELINE_TYPE_RASTERIZATION)
|
|
{
|
|
CMlGraphicsPipeline *pVkPipeline = (CMlGraphicsPipeline*)pPipeline;
|
|
|
|
s_pEncoder->setRenderPipelineState(pVkPipeline->m_pipeline);
|
|
}
|
|
g_pCurrentPipeline = pPipeline;
|
|
}
|
|
|
|
void IRenderer::PushBindings()
|
|
{
|
|
if (!g_pCurrentPipeline)
|
|
return;
|
|
if (g_pCurrentPipeline->type == PIPELINE_TYPE_RASTERIZATION)
|
|
{
|
|
CMlGraphicsPipeline *pPipeline = (CMlGraphicsPipeline*)g_pCurrentPipeline;
|
|
bool bHasTextures = false;
|
|
for ( int i = 0; i < pPipeline->m_inputs.GetSize(); i++ )
|
|
{
|
|
if (pPipeline->m_inputs[i].type == SHADER_INPUT_TYPE_TEXTURES)
|
|
{
|
|
bHasTextures = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!bHasTextures)
|
|
return;
|
|
|
|
MTL::ArgumentEncoder *argumentEncoder = 0;
|
|
for (auto &shader: pPipeline->m_shaders)
|
|
{
|
|
if (shader.shaderType == SHADER_TYPE_FRAGMENT)
|
|
{
|
|
argumentEncoder = shader.function->newArgumentEncoder(21);
|
|
break;
|
|
};
|
|
}
|
|
MTL::Buffer *argumentBuffer = g_mlDevice->newBuffer(argumentEncoder->encodedLength(), MTL::ResourceStorageModeShared);
|
|
argumentEncoder->setArgumentBuffer(argumentBuffer, 0);
|
|
for ( uint32_t i = 0; i < g_textures.GetSize(); i++ )
|
|
{
|
|
CMlTexture *texture = (CMlTexture*)g_textures[i];
|
|
argumentEncoder->setTexture(texture->m_texture, i);
|
|
s_pEncoder->useResource(texture->m_texture, MTL::ResourceUsageRead);
|
|
}
|
|
s_pEncoder->useResource(argumentBuffer, MTL::ResourceUsageRead);
|
|
s_pEncoder->setFragmentBuffer(argumentBuffer, 0, 21);
|
|
g_destroyBuffersBuffer.AppendTail(argumentBuffer);
|
|
argumentEncoder->release();
|
|
}
|
|
}
|
|
|
|
void IRenderer::Begin( uint32_t nWidth, uint32_t nHeight, CUtlVector<RenderingColorAttachment_t> attachments, RenderingDepthAttachment_t depth )
|
|
{
|
|
s_pRenderPass = MTL::RenderPassDescriptor::alloc()->init();
|
|
uint32_t i = 0;
|
|
for (auto &attachment: attachments)
|
|
{
|
|
CMlImage *pImage = (CMlImage*)attachment.pOutput;
|
|
CMlImage *pTemporary = (CMlImage*)attachment.pTemporary;
|
|
auto mlattachment = s_pRenderPass->colorAttachments()->object(i);
|
|
mlattachment->setTexture(pImage->m_image);
|
|
mlattachment->setLoadAction(IRenderer_LoadOpMl(attachment.loadMode));
|
|
mlattachment->setStoreAction(IRenderer_StoreOpMl(attachment.storeMode));
|
|
mlattachment->setClearColor(MTL::ClearColor(attachment.clearColor[0],attachment.clearColor[1],attachment.clearColor[2],attachment.clearColor[3]));
|
|
i++;
|
|
};
|
|
if (depth.pOutput)
|
|
{
|
|
CMlImage *pImage = (CMlImage*)depth.pOutput;
|
|
CMlImage *pTemporary = (CMlImage*)depth.pTemporary;
|
|
s_pRenderPass->depthAttachment()->setClearDepth(depth.clearValue);
|
|
s_pRenderPass->depthAttachment()->setTexture(pImage->m_image);
|
|
s_pRenderPass->depthAttachment()->setLoadAction(IRenderer_LoadOpMl(depth.loadMode));
|
|
s_pRenderPass->depthAttachment()->setStoreAction(IRenderer_StoreOpMl(depth.storeMode));
|
|
}
|
|
|
|
s_pEncoder = g_mlCommandBuffer->renderCommandEncoder(s_pRenderPass);
|
|
}
|
|
|
|
void IRenderer::ResetState()
|
|
{
|
|
|
|
}
|
|
|
|
void IRenderer::SetDepthMode( EDepthMode mode )
|
|
{
|
|
MTL::DepthStencilDescriptor* depthStencilDesc = MTL::DepthStencilDescriptor::alloc()->init();
|
|
if (mode == DEPTH_MODE_DISABLED)
|
|
{
|
|
depthStencilDesc->setDepthWriteEnabled(false);
|
|
} else {
|
|
depthStencilDesc->setDepthWriteEnabled(true);
|
|
switch (mode)
|
|
{
|
|
case DEPTH_MODE_LESS:
|
|
depthStencilDesc->setDepthCompareFunction(MTL::CompareFunctionLess); break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
auto depthStencilState = g_mlDevice->newDepthStencilState(depthStencilDesc);
|
|
s_pEncoder->setDepthStencilState(depthStencilState);
|
|
depthStencilDesc->release();
|
|
}
|
|
|
|
void IRenderer::Draw( IVertexBuffer *pVertex, IIndexBuffer *pIndex )
|
|
{
|
|
CMlBuffer *pMlVertex = (CMlBuffer*)pVertex;
|
|
CMlBuffer *pMlIndex = (CMlBuffer*)pIndex;
|
|
if (pMlIndex)
|
|
{
|
|
|
|
} else {
|
|
|
|
|
|
CMlGraphicsPipeline *pPipeline = (CMlGraphicsPipeline*)g_pCurrentPipeline;
|
|
s_pEncoder->setVertexBuffer(pMlVertex->m_buffer, 0, 30);
|
|
s_pEncoder->drawPrimitives(MTL::PrimitiveTypeTriangle, NS::UInteger(0), NS::UInteger(pMlVertex->m_nSize/pPipeline->m_nVertexSize));
|
|
}
|
|
}
|
|
|
|
void IRenderer::End()
|
|
{
|
|
s_pEncoder->endEncoding();
|
|
}
|
|
|
|
IBuffer *IRenderer::GetCameraMatrix()
|
|
{
|
|
return g_cameraProperties;
|
|
}
|
|
|
|
static CMlImage *pOutputImage = new CMlImage;
|
|
IImage *IRenderer::GetOutputImage()
|
|
{
|
|
pOutputImage->m_image = g_mlDrawableTexture;
|
|
return pOutputImage;
|
|
}
|
|
|
|
IGraphicsPipeline *IRenderer::CreateGraphicsPipeline(
|
|
CUtlVector<Shader_t> shaders,
|
|
CUtlVector<ShaderInput_t> inputs,
|
|
uint32_t nConstantsSize,
|
|
uint32_t nVertexSize,
|
|
CUtlVector<VertexAttribute_t> vertexFormat,
|
|
CUtlVector<EImageFormat> outputFormats,
|
|
bool bDepth
|
|
)
|
|
{
|
|
CMlGraphicsPipeline *pPipeline = new CMlGraphicsPipeline;
|
|
|
|
|
|
CUtlVector<MLShader_t> mlshaders;
|
|
MTL::RenderPipelineDescriptor *pCreateInfo = MTL::RenderPipelineDescriptor::alloc()->init();
|
|
MTL::VertexDescriptor *vertexDescriptor = MTL::VertexDescriptor::alloc()->init();
|
|
uint32_t i = 0;
|
|
for ( auto &vertex: vertexFormat )
|
|
{
|
|
vertexDescriptor->attributes()->object(i)->setFormat(IRenderer_VertexToMl(vertex.format));
|
|
vertexDescriptor->attributes()->object(i)->setOffset(vertex.offset);
|
|
vertexDescriptor->attributes()->object(i)->setBufferIndex(30);
|
|
i++;
|
|
}
|
|
vertexDescriptor->layouts()->object(30)->setStride(nVertexSize);
|
|
vertexDescriptor->layouts()->object(30)->setStepRate(1);
|
|
vertexDescriptor->layouts()->object(30)->setStepFunction(MTL::VertexStepFunctionPerVertex);
|
|
|
|
NS::Error *error = NULL;
|
|
for ( auto &shader: shaders )
|
|
{
|
|
MLShader_t mlshader = {};
|
|
FileHandle_t f = IFileSystem::Open(shader.szPath, IFILE_READ);
|
|
if (!f)
|
|
Plat_FatalErrorFunc("Failed to open shader %s\n", shader.szPath.GetString());
|
|
CUtlBuffer<uint8_t> buffer(IFileSystem::Size(f)+1);
|
|
IFileSystem::Read(f, buffer.GetMemory(), buffer.GetSize());
|
|
buffer[IFileSystem::Size(f)] = 0;
|
|
IFileSystem::Close(f);
|
|
NS::String *szSourceCode = NS::String::string((const char*)buffer.GetMemory(), NS::StringEncoding::UTF8StringEncoding);
|
|
MTL::CompileOptions *options = NULL;
|
|
mlshader.library = g_mlDevice->newLibrary(szSourceCode, options, &error);
|
|
if (error)
|
|
Plat_FatalErrorFunc("%s\n",error->localizedDescription()->utf8String());
|
|
mlshader.szMain = NS::String::string("_main", NS::StringEncoding::UTF8StringEncoding);
|
|
mlshader.function = mlshader.library->newFunction(mlshader.szMain);
|
|
|
|
if (shader.type == SHADER_TYPE_FRAGMENT)
|
|
pCreateInfo->setFragmentFunction(mlshader.function);
|
|
if (shader.type == SHADER_TYPE_VERTEX)
|
|
pCreateInfo->setVertexFunction(mlshader.function);
|
|
mlshader.shaderType = shader.type;
|
|
mlshaders.AppendTail(mlshader);
|
|
}
|
|
for ( i = 0; i < outputFormats.GetSize(); i++ )
|
|
{
|
|
pCreateInfo->colorAttachments()->object(i)->setPixelFormat(IRenderer_FormatToMl(outputFormats[i]));
|
|
};
|
|
if (bDepth)
|
|
pCreateInfo->setDepthAttachmentPixelFormat(MTL::PixelFormatDepth32Float);
|
|
pCreateInfo->setVertexDescriptor(vertexDescriptor);
|
|
pPipeline->type = PIPELINE_TYPE_RASTERIZATION;
|
|
pPipeline->m_pipeline = g_mlDevice->newRenderPipelineState(pCreateInfo, &error);
|
|
pPipeline->m_nVertexSize = nVertexSize;
|
|
pPipeline->m_inputs = inputs;
|
|
pPipeline->m_shaders = mlshaders;
|
|
if (error)
|
|
Plat_FatalErrorFunc("%s\n",error->localizedDescription()->utf8String());
|
|
return pPipeline;
|
|
};
|
|
|
|
uint32_t ITextureManager::GetTextureID(ITexture *pTexture)
|
|
{
|
|
uint32_t i = 0;
|
|
for (auto &t: g_textures)
|
|
{
|
|
if (pTexture == t)
|
|
return i;
|
|
i++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ITexture *ITextureManager::LoadTexture( void *pData, uint32_t X, uint32_t Y, uint32_t numChannels )
|
|
{
|
|
CMlTexture *pTexture = new CMlTexture;
|
|
*pTexture = {};
|
|
pTexture->x = X;
|
|
pTexture->y = Y;
|
|
|
|
MTL::TextureDescriptor *pCreateInfo = MTL::TextureDescriptor::alloc()->init();
|
|
pCreateInfo->setPixelFormat(MTL::PixelFormatRGBA8Unorm_sRGB);
|
|
pCreateInfo->setWidth(X);
|
|
pCreateInfo->setHeight(Y);
|
|
pCreateInfo->setUsage(MTL::TextureUsageShaderRead);
|
|
|
|
pTexture->m_texture = g_mlDevice->newTexture(pCreateInfo);
|
|
|
|
MTL::Region region = MTL::Region(0, 0, 0, X, Y, 1);
|
|
NS::UInteger bytesPerRow = 4 * X;
|
|
|
|
pTexture->m_texture->replaceRegion(region, 0, pData, bytesPerRow);
|
|
|
|
pCreateInfo->release();
|
|
|
|
g_textures.AppendTail(pTexture);
|
|
|
|
return pTexture;
|
|
};
|
|
|
|
ITexture *ITextureManager::LoadTexture( const char *szName )
|
|
{
|
|
for (ITexture *texture: g_textures)
|
|
{
|
|
if (!texture->szName)
|
|
continue;
|
|
if (!V_strcmp(texture->szName, szName))
|
|
return texture;
|
|
};
|
|
FileHandle_t file = IFileSystem::Open(szName, IFILE_READ);
|
|
if (!file)
|
|
Plat_FatalErrorFunc("Failed to load %s\n", szName);
|
|
|
|
CUtlBuffer<stbi_uc> buffer(IFileSystem::Size(file));
|
|
IFileSystem::Read(file, buffer.GetMemory(), buffer.GetSize());
|
|
int nImageX;
|
|
int nImageY;
|
|
int nImageChannels;
|
|
stbi_uc *pixels = stbi_load_from_memory((stbi_uc*)buffer.GetMemory(), buffer.GetSize(), &nImageX, &nImageY, &nImageChannels, STBI_rgb_alpha);
|
|
ITexture *pTexture = ITextureManager::LoadTexture(pixels, nImageX, nImageY, 4);
|
|
pTexture->szName = szName;
|
|
return pTexture;
|
|
};
|
|
|
|
void IMetal::Init()
|
|
{
|
|
char invalidTexture[1024] = {};
|
|
for (int i = 0; i < 16; i++) {
|
|
for (int j = 0; j < 16; j++) {
|
|
int index = i * 16 + j;
|
|
if ((i + j) % 2 == 0) {
|
|
invalidTexture[index*4] = 255;
|
|
}
|
|
}
|
|
}
|
|
g_cameraProperties = IRenderer::CreateUniformBuffer(sizeof(CameraProjection));
|
|
g_cameraDataMap = (CameraProjection*)g_cameraProperties->Map();
|
|
ITextureManager::LoadTexture(invalidTexture, 16, 16, 4);
|
|
|
|
g_meshDepth = IRenderer::CreateImage(IMAGE_FORMAT_DEPTH, IMAGE_USAGE_DEPTH_ATTACHMENT, 1280, 720, 1);
|
|
g_meshDepthMSAA = IRenderer::CreateImage(IMAGE_FORMAT_DEPTH, IMAGE_USAGE_DEPTH_ATTACHMENT, 1280, 720, 4);
|
|
g_meshColor = IRenderer::CreateImage(IMAGE_FORMAT_R8G8B8A8, IMAGE_USAGE_COLOR_ATTACHMENT, 1280, 720, 1);
|
|
g_meshColorMSAA = IRenderer::CreateImage(IMAGE_FORMAT_R8G8B8A8, IMAGE_USAGE_COLOR_ATTACHMENT, 1280, 720, 4);
|
|
glm_mat4_identity(g_cameraView);
|
|
}
|
|
|
|
static MTL::RenderPipelineState* s_fullScreenDraw;
|
|
void IMetal::CreatePipelines()
|
|
{
|
|
for (auto &step: g_StepPrepass)
|
|
step.pPipeline->Init();
|
|
for (auto &step: g_StepMeshRendering)
|
|
step.pPipeline->Init();
|
|
for (auto &step: g_StepShading)
|
|
step.pPipeline->Init();
|
|
for (auto &step: g_StepPostProcessing)
|
|
step.pPipeline->Init();
|
|
for (auto &step: g_StepUI)
|
|
step.pPipeline->Init();
|
|
|
|
NS::Error *error = 0;
|
|
FileHandle_t f = IFileSystem::Open("gfx/ml_quad.metal", IFILE_READ);
|
|
CUtlBuffer<uint8_t> buffer(IFileSystem::Size(f)+1);
|
|
IFileSystem::Read(f, buffer.GetMemory(), buffer.GetSize());
|
|
buffer[IFileSystem::Size(f)] = 0;
|
|
IFileSystem::Close(f);
|
|
NS::String *szSourceCode = NS::String::string((const char*)buffer.GetMemory(), NS::StringEncoding::UTF8StringEncoding);
|
|
MTL::CompileOptions *options = NULL;
|
|
MTL::Library *library = g_mlDevice->newLibrary(szSourceCode, options, &error);
|
|
MTL::Function *vertexFunc = library->newFunction(NS::String::string("vertex_main", NS::UTF8StringEncoding));
|
|
MTL::Function *fragmentFunc = library->newFunction(NS::String::string("fragment_main", NS::UTF8StringEncoding));
|
|
|
|
MTL::RenderPipelineDescriptor *pipelineDesc = MTL::RenderPipelineDescriptor::alloc()->init();
|
|
pipelineDesc->setVertexFunction(vertexFunc);
|
|
pipelineDesc->setFragmentFunction(fragmentFunc);
|
|
pipelineDesc->colorAttachments()->object(0)->setPixelFormat(MTL::PixelFormatBGRA8Unorm);
|
|
|
|
s_fullScreenDraw = g_mlDevice->newRenderPipelineState(pipelineDesc, &error);
|
|
}
|
|
|
|
void IMetal::Frame()
|
|
{
|
|
mat4 perspective;
|
|
glm_mat4_inv(g_cameraView, g_cameraDataMap->viewprojection);
|
|
glm_perspective(glm_rad(90),(float)g_nWindowWidth/g_nWindowHeight, 0.01, 100, perspective);
|
|
glm_rotate(perspective, glm_rad(90), (vec4){1,0,0,0});
|
|
glm_scale(perspective, (vec4){1,-1,1,1});
|
|
glm_rotate(perspective, glm_rad(90), (vec4){0,0,1,0});
|
|
glm_mat4_mul(perspective,g_cameraDataMap->viewprojection,g_cameraDataMap->viewprojection);
|
|
|
|
if (g_bConfigNotify)
|
|
{
|
|
IRenderer::DestroyImage(g_meshDepth);
|
|
IRenderer::DestroyImage(g_meshDepthMSAA);
|
|
IRenderer::DestroyImage(g_meshColor);
|
|
IRenderer::DestroyImage(g_meshColorMSAA);
|
|
g_meshDepth = IRenderer::CreateImage(IMAGE_FORMAT_DEPTH, IMAGE_USAGE_DEPTH_ATTACHMENT, g_nWindowWidth, g_nWindowHeight, 1);
|
|
g_meshDepthMSAA = IRenderer::CreateImage(IMAGE_FORMAT_DEPTH, IMAGE_USAGE_DEPTH_ATTACHMENT, g_nWindowWidth, g_nWindowHeight, 4);
|
|
g_meshColor = IRenderer::CreateImage(IMAGE_FORMAT_R8G8B8A8, IMAGE_USAGE_COLOR_ATTACHMENT, g_nWindowWidth, g_nWindowHeight, 1);
|
|
g_meshColorMSAA = IRenderer::CreateImage(IMAGE_FORMAT_R8G8B8A8, IMAGE_USAGE_COLOR_ATTACHMENT, g_nWindowWidth, g_nWindowHeight, 4);
|
|
}
|
|
|
|
for (auto &step: g_StepPrepass)
|
|
step.pPipeline->Frame(0);
|
|
IRenderer::Begin(g_nWindowWidth, g_nWindowHeight,
|
|
{
|
|
{
|
|
g_meshColor,
|
|
0,
|
|
ATTACHMENT_LOAD_MODE_CLEAR,
|
|
ATTACHMENT_STORE_MODE_STORE,
|
|
}
|
|
},
|
|
{
|
|
g_meshDepth,
|
|
0,
|
|
ATTACHMENT_LOAD_MODE_CLEAR,
|
|
ATTACHMENT_STORE_MODE_STORE,
|
|
1,
|
|
});
|
|
for (auto &step: g_StepMeshRendering)
|
|
step.pPipeline->Frame(0);
|
|
IRenderer::End();
|
|
|
|
CMlImage *pInImage = (CMlImage*)g_meshColor;
|
|
MTL::RenderPassDescriptor *renderPassDesc = MTL::RenderPassDescriptor::renderPassDescriptor();
|
|
renderPassDesc->colorAttachments()->object(0)->setTexture(g_mlDrawableTexture);
|
|
renderPassDesc->colorAttachments()->object(0)->setLoadAction(MTL::LoadActionClear);
|
|
renderPassDesc->colorAttachments()->object(0)->setStoreAction(MTL::StoreActionStore);
|
|
renderPassDesc->colorAttachments()->object(0)->setClearColor(MTL::ClearColor(0.1, 0.1, 0.1, 1.0));
|
|
|
|
MTL::RenderCommandEncoder *encoder = g_mlCommandBuffer->renderCommandEncoder(renderPassDesc);
|
|
|
|
encoder->setRenderPipelineState(s_fullScreenDraw);
|
|
encoder->setFragmentTexture(pInImage->m_image, 0);
|
|
|
|
encoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));
|
|
|
|
encoder->endEncoding();
|
|
|
|
for (auto &step: g_StepShading)
|
|
step.pPipeline->Frame(0);
|
|
for (auto &step: g_StepPostProcessing)
|
|
step.pPipeline->Frame(0);
|
|
for (auto &step: g_StepUI)
|
|
step.pPipeline->Frame(0);
|
|
}
|
|
|
|
void IMetal::Deinit()
|
|
{
|
|
|
|
}
|
|
|