diff --git a/src/modules/graphics/metal/Graphics.h b/src/modules/graphics/metal/Graphics.h index 9492f9077..e939ae353 100644 --- a/src/modules/graphics/metal/Graphics.h +++ b/src/modules/graphics/metal/Graphics.h @@ -39,6 +39,17 @@ class Graphics final : public love::graphics::Graphics { public: + struct RenderEncoderBindings + { + void *textures[32][ShaderStage::STAGE_MAX_ENUM]; + void *samplers[32][ShaderStage::STAGE_MAX_ENUM]; + struct + { + void *buffer; + size_t offset; + } buffers[32][ShaderStage::STAGE_MAX_ENUM]; + }; + Graphics(); virtual ~Graphics(); @@ -153,17 +164,6 @@ private: STATEBIT_ALL = 0xFFFFFFFF }; - struct RenderState - { - Rect viewport = {0, 0, 0, 0}; - ScissorState scissor; - BlendState blend; - DepthState depth; - StencilState stencil; - ColorChannelMask colorChannelMask; - Shader *shader; - }; - struct DeviceFamilies { // All arrays are 1-indexed for convenience @@ -213,6 +213,8 @@ private: AttachmentStoreActions attachmentStoreActions; + RenderEncoderBindings renderBindings; + StreamBuffer *uniformBuffer; StreamBuffer::MapInfo uniformBufferData; size_t uniformBufferOffset; diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index 161499bbc..697ef9399 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -145,6 +145,57 @@ static inline id getMTLBuffer(love::graphics::Resource *res) return res ? (__bridge id)(void *) res->getHandle() : nil; } +static inline void setBuffer(id encoder, Graphics::RenderEncoderBindings &bindings, ShaderStage::StageType stage, int index, id buffer, size_t offset) +{ + void *b = (__bridge void *)buffer; + auto &binding = bindings.buffers[index][stage]; + if (binding.buffer != b) + { + binding.buffer = b; + binding.offset = offset; + if (stage == ShaderStage::STAGE_VERTEX) + [encoder setVertexBuffer:buffer offset:offset atIndex:index]; + else if (stage == ShaderStage::STAGE_PIXEL) + [encoder setFragmentBuffer:buffer offset:offset atIndex:index]; + } + else if (binding.offset != offset) + { + binding.offset = offset; + if (stage == ShaderStage::STAGE_VERTEX) + [encoder setVertexBufferOffset:offset atIndex:index]; + else if (stage == ShaderStage::STAGE_PIXEL) + [encoder setFragmentBufferOffset:offset atIndex:index]; + } +} + +static inline void setTexture(id encoder, Graphics::RenderEncoderBindings &bindings, ShaderStage::StageType stage, int index, id texture) +{ + void *t = (__bridge void *)texture; + auto &binding = bindings.textures[index][stage]; + if (binding != t) + { + binding = t; + if (stage == ShaderStage::STAGE_VERTEX) + [encoder setVertexTexture:texture atIndex:index]; + else if (stage == ShaderStage::STAGE_PIXEL) + [encoder setFragmentTexture:texture atIndex:index]; + } +} + +static inline void setSampler(id encoder, Graphics::RenderEncoderBindings &bindings, ShaderStage::StageType stage, int index, id sampler) +{ + void *s = (__bridge void *)sampler; + auto &binding = bindings.samplers[index][stage]; + if (binding != s) + { + binding = s; + if (stage == ShaderStage::STAGE_VERTEX) + [encoder setVertexSamplerState:sampler atIndex:index]; + else if (stage == ShaderStage::STAGE_PIXEL) + [encoder setFragmentSamplerState:sampler atIndex:index]; + } +} + love::graphics::Graphics *createInstance() { love::graphics::Graphics *instance = nullptr; @@ -182,6 +233,7 @@ Graphics::Graphics() , windowHasStencil(false) , requestedBackbufferMSAA(0) , attachmentStoreActions() + , renderBindings() , uniformBufferOffset(0) , defaultAttributesBuffer(nullptr) , defaultTextures() @@ -226,6 +278,35 @@ Graphics::Graphics() defaultTextures[i]->replacePixels(defaultpixel, sizeof(defaultpixel), 0, 0, r, false); } + if (batchedDrawState.vb[0] == nullptr) + { + // Initial sizes that should be good enough for most cases. It will + // resize to fit if needed, later. + batchedDrawState.vb[0] = CreateStreamBuffer(device, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1); + batchedDrawState.vb[1] = CreateStreamBuffer(device, BUFFERUSAGE_VERTEX, 256 * 1024 * 1); + batchedDrawState.indexBuffer = CreateStreamBuffer(device, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); + } + + createQuadIndexBuffer(); + + // We always need a default shader. + for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) + { + auto stype = (Shader::StandardShader) i; + if (!Shader::standardShaders[i]) + { + std::vector stages; + stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_VERTEX)); + stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_PIXEL)); + Shader::standardShaders[i] = newShader(stages); + } + } + + // A shader should always be active, but the default shader shouldn't be + // returned by getShader(), so we don't do setShader(defaultShader). + if (!Shader::current) + Shader::standardShaders[Shader::STANDARD_DEFAULT]->attach(); + auto window = Module::getInstance(M_WINDOW); if (window != nullptr) @@ -347,38 +428,9 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int created = true; - if (batchedDrawState.vb[0] == nullptr) - { - // Initial sizes that should be good enough for most cases. It will - // resize to fit if needed, later. - batchedDrawState.vb[0] = CreateStreamBuffer(device, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1); - batchedDrawState.vb[1] = CreateStreamBuffer(device, BUFFERUSAGE_VERTEX, 256 * 1024 * 1); - batchedDrawState.indexBuffer = CreateStreamBuffer(device, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); - } - - createQuadIndexBuffer(); - // Restore the graphics state. restoreState(states.back()); - // We always need a default shader. - for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) - { - auto stype = (Shader::StandardShader) i; - if (!Shader::standardShaders[i]) - { - std::vector stages; - stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_VERTEX)); - stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_PIXEL)); - Shader::standardShaders[i] = newShader(stages); - } - } - - // A shader should always be active, but the default shader shouldn't be - // returned by getShader(), so we don't do setShader(defaultShader). - if (!Shader::current) - Shader::standardShaders[Shader::STANDARD_DEFAULT]->attach(); - return true; }} @@ -441,7 +493,7 @@ void Graphics::submitCommandBuffer() } } -static inline void setAttachment(const Graphics::RenderTarget &rt, MTLRenderPassAttachmentDescriptor *desc, MTLStoreAction &storeaction) +static inline void setAttachment(const Graphics::RenderTarget &rt, MTLRenderPassAttachmentDescriptor *desc, MTLStoreAction &storeaction, bool setload = true) { bool isvolume = rt.texture->getTextureType() == TEXTURE_VOLUME; @@ -450,8 +502,12 @@ static inline void setAttachment(const Graphics::RenderTarget &rt, MTLRenderPass desc.slice = isvolume ? 0 : rt.slice; desc.depthPlane = isvolume ? rt.slice : 0; - // Default to load until clear or discard is called. - desc.loadAction = MTLLoadActionLoad; + if (setload) + { + // Default to load until clear or discard is called. + desc.loadAction = MTLLoadActionLoad; + } + desc.storeAction = MTLStoreActionUnknown; storeaction = MTLStoreActionStore; @@ -504,14 +560,16 @@ id Graphics::useRenderEncoder() passDesc.colorAttachments[0].depthPlane = 0; RenderTarget rt(backbufferDepthStencil); - setAttachment(rt, passDesc.depthAttachment, attachmentStoreActions.depth); - setAttachment(rt, passDesc.stencilAttachment, attachmentStoreActions.stencil); + setAttachment(rt, passDesc.depthAttachment, attachmentStoreActions.depth, false); + setAttachment(rt, passDesc.stencilAttachment, attachmentStoreActions.stencil, false); attachmentStoreActions.depth = MTLStoreActionDontCare; attachmentStoreActions.stencil = MTLStoreActionDontCare; } renderEncoder = [useCommandBuffer() renderCommandEncoderWithDescriptor:passDesc]; + renderBindings = {}; + for (int i = 0; i < MAX_COLOR_RENDER_TARGETS; i++) { passDesc.colorAttachments[0].texture = nil; @@ -524,7 +582,7 @@ id Graphics::useRenderEncoder() passDesc.stencilAttachment.resolveTexture = nil; id defaultbuffer = getMTLBuffer(defaultAttributesBuffer); - [renderEncoder setVertexBuffer:defaultbuffer offset:0 atIndex:DEFAULT_VERTEX_BUFFER_BINDING]; + setBuffer(renderEncoder, renderBindings, ShaderStage::STAGE_VERTEX, DEFAULT_VERTEX_BUFFER_BINDING, defaultbuffer, 0); dirtyRenderState = STATEBIT_ALL; } @@ -850,8 +908,9 @@ void Graphics::applyShaderUniforms(id renderEncoder, lo id buffer = getMTLBuffer(uniformBuffer); int uniformindex = Shader::getUniformBufferBinding(); - [renderEncoder setVertexBuffer:buffer offset:uniformBufferOffset atIndex:uniformindex]; - [renderEncoder setFragmentBuffer:buffer offset:uniformBufferOffset atIndex:uniformindex]; + auto &bindings = renderBindings; + setBuffer(renderEncoder, bindings, ShaderStage::STAGE_VERTEX, uniformindex, buffer, uniformBufferOffset); + setBuffer(renderEncoder, bindings, ShaderStage::STAGE_PIXEL, uniformindex, buffer, uniformBufferOffset); uniformBufferOffset += alignUp(size, alignment); @@ -873,25 +932,25 @@ void Graphics::applyShaderUniforms(id renderEncoder, lo sampler = getMTLSampler(maintex); } - uint8 texindex = b.texturestages[ShaderStage::STAGE_VERTEX]; - uint8 sampindex = b.samplerstages[ShaderStage::STAGE_VERTEX]; + uint8 texindex = b.textureStages[ShaderStage::STAGE_VERTEX]; + uint8 sampindex = b.samplerStages[ShaderStage::STAGE_VERTEX]; if (texindex != LOVE_UINT8_MAX) - [renderEncoder setVertexTexture:texture atIndex:texindex]; + setTexture(renderEncoder, bindings, ShaderStage::STAGE_VERTEX, texindex, texture); if (sampindex != LOVE_UINT8_MAX) - [renderEncoder setVertexSamplerState:sampler atIndex:sampindex]; + setSampler(renderEncoder, bindings, ShaderStage::STAGE_VERTEX, sampindex, sampler); - texindex = b.texturestages[ShaderStage::STAGE_PIXEL]; - sampindex = b.samplerstages[ShaderStage::STAGE_PIXEL]; + texindex = b.textureStages[ShaderStage::STAGE_PIXEL]; + sampindex = b.samplerStages[ShaderStage::STAGE_PIXEL]; if (texindex != LOVE_UINT8_MAX) - [renderEncoder setFragmentTexture:texture atIndex:texindex]; + setTexture(renderEncoder, bindings, ShaderStage::STAGE_PIXEL, texindex, texture); if (sampindex != LOVE_UINT8_MAX) - [renderEncoder setFragmentSamplerState:sampler atIndex:sampindex]; + setSampler(renderEncoder, bindings, ShaderStage::STAGE_PIXEL, sampindex, sampler); } } -static void setVertexBuffers(id encoder, const BufferBindings *buffers) +static void setVertexBuffers(id encoder, const BufferBindings *buffers, Graphics::RenderEncoderBindings &bindings) { uint32 allbits = buffers->useBits; uint32 i = 0; @@ -903,7 +962,7 @@ static void setVertexBuffers(id encoder, const BufferBi { auto b = buffers->info[i]; id buffer = getMTLBuffer(b.buffer); - [encoder setVertexBuffer:buffer offset:b.offset atIndex:i + VERTEX_BUFFER_BINDING_START]; + setBuffer(encoder, bindings, ShaderStage::STAGE_VERTEX, i + VERTEX_BUFFER_BINDING_START, buffer, b.offset); } i++; @@ -920,7 +979,7 @@ void Graphics::draw(const DrawCommand &cmd) [encoder setCullMode:MTLCullModeNone]; - setVertexBuffers(encoder, cmd.buffers); + setVertexBuffers(encoder, cmd.buffers, renderBindings); [encoder drawPrimitives:getMTLPrimitiveType(cmd.primitiveType) vertexStart:cmd.vertexStart @@ -937,7 +996,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd) [encoder setCullMode:MTLCullModeNone]; - setVertexBuffers(encoder, cmd.buffers); + setVertexBuffers(encoder, cmd.buffers, renderBindings); auto indexType = cmd.indexType == INDEX_UINT32 ? MTLIndexTypeUInt32 : MTLIndexTypeUInt16; @@ -961,7 +1020,7 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute [encoder setCullMode:MTLCullModeNone]; - setVertexBuffers(encoder, &buffers); + setVertexBuffers(encoder, &buffers, renderBindings); id ib = getMTLBuffer(quadIndexBuffer); diff --git a/src/modules/graphics/metal/Metal.h b/src/modules/graphics/metal/Metal.h index 087cb2595..dd959d58d 100644 --- a/src/modules/graphics/metal/Metal.h +++ b/src/modules/graphics/metal/Metal.h @@ -47,8 +47,6 @@ public: }; // Metal -extern Metal metal; - } // metal } // graphics } // love diff --git a/src/modules/graphics/metal/Metal.mm b/src/modules/graphics/metal/Metal.mm index a15e3d442..5e0eb6a0f 100644 --- a/src/modules/graphics/metal/Metal.mm +++ b/src/modules/graphics/metal/Metal.mm @@ -346,8 +346,6 @@ Metal::PixelFormatDesc Metal::convertPixelFormat(PixelFormat format, bool &isSRG return desc; } -Metal metal; - } // metal } // graphics } // love diff --git a/src/modules/graphics/metal/Shader.h b/src/modules/graphics/metal/Shader.h index a7d9a6f77..c0f10e175 100644 --- a/src/modules/graphics/metal/Shader.h +++ b/src/modules/graphics/metal/Shader.h @@ -80,8 +80,8 @@ public: bool isMainTexture; - uint8 texturestages[ShaderStage::STAGE_MAX_ENUM]; - uint8 samplerstages[ShaderStage::STAGE_MAX_ENUM]; + uint8 textureStages[ShaderStage::STAGE_MAX_ENUM]; + uint8 samplerStages[ShaderStage::STAGE_MAX_ENUM]; }; struct BufferBinding diff --git a/src/modules/graphics/metal/Shader.mm b/src/modules/graphics/metal/Shader.mm index bcce0927f..7bb08b44d 100644 --- a/src/modules/graphics/metal/Shader.mm +++ b/src/modules/graphics/metal/Shader.mm @@ -672,17 +672,17 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p b.isMainTexture = true; } - for (uint8 &stagebinding : b.texturestages) + for (uint8 &stagebinding : b.textureStages) stagebinding = LOVE_UINT8_MAX; - for (uint8 &stagebinding : b.samplerstages) + for (uint8 &stagebinding : b.samplerStages) stagebinding = LOVE_UINT8_MAX; textureBindings.push_back(b); } auto &b = textureBindings[u.ints[i]]; - b.texturestages[stageindex] = (uint8) texturebinding; - b.samplerstages[stageindex] = (uint8) samplerbinding; + b.textureStages[stageindex] = (uint8) texturebinding; + b.samplerStages[stageindex] = (uint8) samplerbinding; } } }