diff --git a/MetalLearning/MetalLearning.xcodeproj/project.pbxproj b/MetalLearning/MetalLearning.xcodeproj/project.pbxproj index ec537ba..9aaa3a1 100644 --- a/MetalLearning/MetalLearning.xcodeproj/project.pbxproj +++ b/MetalLearning/MetalLearning.xcodeproj/project.pbxproj @@ -99,6 +99,7 @@ 47A97AA92E8EB34B00293FA8 /* Renderer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = Renderer.mm; path = src/MEL/Renderer/Renderer.mm; sourceTree = ""; }; 47A97AAC2E8EDBAD00293FA8 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 47A97AAD2E8EDBAD00293FA8 /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = ""; }; + 47BB8EEB2E9559770005EA9F /* BufferLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BufferLayout.h; sourceTree = ""; }; 47F2B4282E918103005D0A7A /* Shader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Shader.h; sourceTree = ""; }; 47F2B4292E918103005D0A7A /* Shader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Shader.mm; sourceTree = ""; }; 500811C6F3539AF80E246806 /* Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Core.h; sourceTree = ""; }; @@ -234,6 +235,7 @@ 479A9D0C2E942C4E0019F59F /* IndexBuffer.mm */, 479A9D0E2E942CBD0019F59F /* VertexBuffer.h */, 479A9D0F2E942CBD0019F59F /* VertexBuffer.mm */, + 47BB8EEB2E9559770005EA9F /* BufferLayout.h */, ); name = Buffer; path = src/MEL/Buffer; diff --git a/MetalLearning/ShaderSrc/Shader.metal b/MetalLearning/ShaderSrc/Shader.metal index fff6fb0..8dc4b0e 100644 --- a/MetalLearning/ShaderSrc/Shader.metal +++ b/MetalLearning/ShaderSrc/Shader.metal @@ -3,7 +3,7 @@ using namespace metal; struct VertexIn { float3 position [[attribute(0)]]; - float3 color [[attribute(1)]]; + float4 color [[attribute(1)]]; }; struct VertexOut{ @@ -14,7 +14,7 @@ struct VertexOut{ vertex VertexOut vertexShader(const VertexIn in [[stage_in]]){ VertexOut out; out.position=float4(in.position,1.0); - out.color=float4(in.color,1.0); + out.color=in.color; return out; } diff --git a/MetalLearning/src/MEL/Application.mm b/MetalLearning/src/MEL/Application.mm index 7badd1c..eae1189 100644 --- a/MetalLearning/src/MEL/Application.mm +++ b/MetalLearning/src/MEL/Application.mm @@ -5,6 +5,7 @@ #import "Buffer/VertexBuffer.h" #import "Buffer/IndexBuffer.h" +#import "Buffer/BufferLayout.h" #import "VertexArray/VertexArray.h" namespace MEL{ @@ -23,32 +24,42 @@ namespace MEL{ //create vertex array m_VertexArray=MEL::VertexArray::Create(); //set vertex and index buffer - - float vertices[]={ - -0.5f,-0.5f,0.0f, - 0.4f,0.2f,0.4f, + struct Vertex{ + float position[3]; + float color[4]; + }; + Vertex vertices[]={ + {{-0.5f,-0.5f,0.0f}, + {0.4f,0.2f,0.4f,1.0f}}, - 0.5f,-0.5f,0.0f, - 0.1f,0.7f,0.1f, + {{0.5f,-0.5f,0.0f}, + {0.1f,0.7f,0.1f,1.0f}}, - 0.0f,0.5f,0.0f, - 0.1f,0.3f,0.4f + {{0.0f,0.5f,0.0f}, + {0.1f,0.3f,0.4f,1.0f}} }; uint32_t indices[]={0,1,2}; - + //create bufferlayout + BufferLayout layout={ + {ShaderDataType::Float3,"a_Position"}, + {ShaderDataType::Float4,"a_Color"} + }; + //Set buffers auto basicVB=VertexBuffer::Create(vertices, sizeof(vertices)); auto basicIB=IndexBuffer::Create(indices, 3); basicVB->SetSlot(0); + basicVB->SetLayout(layout); m_VertexArray->AddVertexBuffer(basicVB); m_VertexArray->SetIndexBuffer(basicIB); + //create shader(this can be done in sandbox) auto defaultShader=MEL::Shader::CreateFromDefaultLibrary("DefaultShader", @"vertexShader",@"fragmentShader"); if(defaultShader) - defaultShader->CreatePipelineState(); + defaultShader->CreatePipelineState(layout); m_CurrentShader=defaultShader; //initialize layers diff --git a/MetalLearning/src/MEL/Buffer/Buffer.h b/MetalLearning/src/MEL/Buffer/Buffer.h index 993d011..37f39be 100644 --- a/MetalLearning/src/MEL/Buffer/Buffer.h +++ b/MetalLearning/src/MEL/Buffer/Buffer.h @@ -1,6 +1,7 @@ #pragma once #include #include "Application.h" +#include "BufferLayout.h" namespace MEL{ enum class BufferUsage{ diff --git a/MetalLearning/src/MEL/Buffer/BufferLayout.h b/MetalLearning/src/MEL/Buffer/BufferLayout.h new file mode 100644 index 0000000..e840995 --- /dev/null +++ b/MetalLearning/src/MEL/Buffer/BufferLayout.h @@ -0,0 +1,146 @@ +#pragma once +#import +#include +#include + +namespace MEL { + enum class ShaderDataType{ + None=0, + Float,Float2,Float3,Float4, + Mat3,Mat4, + Int,Int2,Int3,Int4, + Bool + }; + + static uint32_t ShaderDataTypeSize(ShaderDataType type){ + switch(type){ + case ShaderDataType::Float: return 4; break; + case ShaderDataType::Float2: return 4*2; break; + case ShaderDataType::Float3: return 4*3; break; + case ShaderDataType::Float4: return 4*4; break; + case ShaderDataType::Mat3: return 4*3*3; break; + case ShaderDataType::Mat4: return 4*4*4; break; + case ShaderDataType::Int: return 4; break; + case ShaderDataType::Int2: return 4*2; break; + case ShaderDataType::Int3: return 4*3; break; + case ShaderDataType::Int4: return 4*4; break; + case ShaderDataType::Bool: return true; break; + default: return 0; break; + } + } + + static MTLVertexFormat ShaderDataTypeToMetal(ShaderDataType type){ + switch(type){ + case ShaderDataType::Float: return MTLVertexFormatFloat; break; + case ShaderDataType::Float2: return MTLVertexFormatFloat2; break; + case ShaderDataType::Float3: return MTLVertexFormatFloat3; break; + case ShaderDataType::Float4: return MTLVertexFormatFloat4; break; + case ShaderDataType::Mat3: return MTLVertexFormatFloat3; break; + case ShaderDataType::Mat4: return MTLVertexFormatFloat3; break; + case ShaderDataType::Int: return MTLVertexFormatInt; break; + case ShaderDataType::Int2: return MTLVertexFormatInt2; break; + case ShaderDataType::Int3: return MTLVertexFormatInt3; break; + case ShaderDataType::Int4: return MTLVertexFormatInt4; break; + case ShaderDataType::Bool: return MTLVertexFormatChar; break; + default: return MTLVertexFormatFloat; break; + } + } + + struct BufferElement{ + std::string Name; + ShaderDataType Type; + uint32_t Size; + uint32_t Offset; + bool Normalized; + + BufferElement()=default; + + BufferElement(ShaderDataType type,const std::string& name,bool normalized=false) + :Name(name),Type(type),Size(ShaderDataTypeSize(type)),Offset(0),Normalized(normalized){} + + uint32_t GetComponentCount()const{ + switch (Type){ + case ShaderDataType::Float: return 1; break; + case ShaderDataType::Float2: return 2; break; + case ShaderDataType::Float3: return 3; break; + case ShaderDataType::Float4: return 4; break; + case ShaderDataType::Mat3: return 3; break; + case ShaderDataType::Mat4: return 4; break; + case ShaderDataType::Int: return 1; break; + case ShaderDataType::Int2: return 2; break; + case ShaderDataType::Int3: return 3; break; + case ShaderDataType::Int4: return 4; break; + case ShaderDataType::Bool: return 1; break; + default: return 0; break; + } + } + }; + + class BufferLayout{ + public: + BufferLayout()=default; + + BufferLayout(const std::initializer_list& elements) + :m_Elements(elements){ + CalculateOffsetAndStride(); + } + + const std::vector& GetElements() const{return m_Elements;} + uint32_t GetStride()const {return m_Stride;} + + std::vector::iterator begin(){return m_Elements.begin();} + std::vector::iterator end(){return m_Elements.end();} + + std::vector::const_iterator begin()const{return m_Elements.begin();} + std::vector::const_iterator end()const{return m_Elements.end();} + + static BufferLayout Default(){ + return { + {ShaderDataType::Float3,"a_Position"}, + {ShaderDataType::Float4,"a_Color"} + }; + } + + static BufferLayout SimplePosition(){ + return{ + {ShaderDataType::Float3,"a_Position"} + }; + } + + static BufferLayout PositionColor(){ + return { + {ShaderDataType::Float3,"a_Position"}, + {ShaderDataType::Float4,"a_Color"} + }; + } + + static BufferLayout PositionTexture(){ + return { + {ShaderDataType::Float3,"a_Position"}, + {ShaderDataType::Float2,"a_TexCoord"} + }; + } + + static BufferLayout PositionNormalTexture(){ + return { + {ShaderDataType::Float3,"a_Position"}, + {ShaderDataType::Float3,"a_Normal"}, + {ShaderDataType::Float2,"a_TexCoord"} + }; + } + + private: + void CalculateOffsetAndStride(){ + uint32_t offset=0; + m_Stride=0; + for(auto& element:m_Elements){ + element.Offset=offset; + offset+=element.Size; + m_Stride+=element.Size; + } + } + private: + std::vector m_Elements; + uint32_t m_Stride=0; + }; +} diff --git a/MetalLearning/src/MEL/Buffer/VertexBuffer.h b/MetalLearning/src/MEL/Buffer/VertexBuffer.h index fef75ba..e6ad874 100644 --- a/MetalLearning/src/MEL/Buffer/VertexBuffer.h +++ b/MetalLearning/src/MEL/Buffer/VertexBuffer.h @@ -15,7 +15,11 @@ namespace MEL { void SetSlot(uint32_t slot){m_Slot=slot;} uint32_t GetSlot()const{return m_Slot;} + + void SetLayout(const BufferLayout& layout){m_Layout=layout;} + const BufferLayout& GetLayout()const{return m_Layout;} private: uint32_t m_Slot=0; + BufferLayout m_Layout; }; } diff --git a/MetalLearning/src/MEL/Shader/Shader.h b/MetalLearning/src/MEL/Shader/Shader.h index ea8c608..140ee4d 100644 --- a/MetalLearning/src/MEL/Shader/Shader.h +++ b/MetalLearning/src/MEL/Shader/Shader.h @@ -3,6 +3,7 @@ #include "MEL.h" #include #include +#include"Buffer/BufferLayout.h" namespace MEL{ class Shader{ @@ -22,7 +23,8 @@ namespace MEL{ const std::string& GetName(){return m_Name;} - bool CreatePipelineState(); + bool CreatePipelineState(const BufferLayout& layout); + MTLVertexDescriptor* CreateVertexDescriptor(const BufferLayout& layout); id GetPipelineState()const{return m_PipelineState;} void Bind(); private: diff --git a/MetalLearning/src/MEL/Shader/Shader.mm b/MetalLearning/src/MEL/Shader/Shader.mm index 50b887a..1450cb5 100644 --- a/MetalLearning/src/MEL/Shader/Shader.mm +++ b/MetalLearning/src/MEL/Shader/Shader.mm @@ -102,7 +102,7 @@ namespace MEL{ return true; } - bool Shader::CreatePipelineState(){ + bool Shader::CreatePipelineState(const BufferLayout& layout){ auto renderer=Application::Get().GetRenderer(); id device=renderer->GetMetalDevice(); @@ -116,21 +116,9 @@ namespace MEL{ pipelineDescriptor.fragmentFunction=m_FragmentFunction; pipelineDescriptor.colorAttachments[0].pixelFormat=MTLPixelFormatBGRA8Unorm; - MTLVertexDescriptor* vertexDescriptor=[[MTLVertexDescriptor alloc]init]; - vertexDescriptor.attributes[0].format=MTLVertexFormatFloat3; - vertexDescriptor.attributes[0].offset=0; - vertexDescriptor.attributes[0].bufferIndex=0; - - vertexDescriptor.attributes[1].format=MTLVertexFormatFloat3; - vertexDescriptor.attributes[1].offset=12; - vertexDescriptor.attributes[1].bufferIndex=0; - - vertexDescriptor.layouts[0].stride=24; - vertexDescriptor.layouts[0].stepFunction=MTLVertexStepFunctionPerVertex; - + MTLVertexDescriptor* vertexDescriptor=CreateVertexDescriptor(layout); pipelineDescriptor.vertexDescriptor=vertexDescriptor; - NSError* error=nil; m_PipelineState=[device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error]; @@ -144,6 +132,32 @@ namespace MEL{ return true; } + MTLVertexDescriptor* Shader::CreateVertexDescriptor(const BufferLayout &layout){ + MTLVertexDescriptor* vertexDescriptor=[MTLVertexDescriptor vertexDescriptor]; + + uint32_t attributeIndex=0; + uint32_t bufferIndex=0; + + for(const auto& element:layout){ + MTLVertexAttributeDescriptor* attribute=vertexDescriptor.attributes[attributeIndex]; + + attribute.format=ShaderDataTypeToMetal(element.Type); + attribute.offset=element.Offset; + attribute.bufferIndex=bufferIndex; + + MEL_CORE_INFO("Vertex Attribute[{}]:{} at buffer {},offset {},format {}", + attributeIndex,element.Name,bufferIndex,element.Offset,(int)attribute.format); + attributeIndex++; + } + + MTLVertexBufferLayoutDescriptor* bufferLayout=vertexDescriptor.layouts[bufferIndex]; + bufferLayout.stride=layout.GetStride(); + bufferLayout.stepRate=1; + bufferLayout.stepFunction=MTLVertexStepFunctionPerVertex; + + return vertexDescriptor; + } + void Shader::Bind(){ auto renderer=Application::Get().GetRenderer(); renderer->SetCurrentPipelineState(m_PipelineState);