|
@@ -145,6 +145,57 @@ static inline id<MTLBuffer> getMTLBuffer(love::graphics::Resource *res)
|
|
|
return res ? (__bridge id<MTLBuffer>)(void *) res->getHandle() : nil;
|
|
|
}
|
|
|
|
|
|
+static inline void setBuffer(id<MTLRenderCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, ShaderStage::StageType stage, int index, id<MTLBuffer> 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<MTLRenderCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, ShaderStage::StageType stage, int index, id<MTLTexture> 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<MTLRenderCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, ShaderStage::StageType stage, int index, id<MTLSamplerState> 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<std::string> 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<love::window::Window>(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<std::string> 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<MTLRenderCommandEncoder> 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<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
|
|
|
passDesc.stencilAttachment.resolveTexture = nil;
|
|
|
|
|
|
id<MTLBuffer> 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<MTLRenderCommandEncoder> renderEncoder, lo
|
|
|
id<MTLBuffer> 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<MTLRenderCommandEncoder> 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<MTLRenderCommandEncoder> encoder, const BufferBindings *buffers)
|
|
|
+static void setVertexBuffers(id<MTLRenderCommandEncoder> encoder, const BufferBindings *buffers, Graphics::RenderEncoderBindings &bindings)
|
|
|
{
|
|
|
uint32 allbits = buffers->useBits;
|
|
|
uint32 i = 0;
|
|
@@ -903,7 +962,7 @@ static void setVertexBuffers(id<MTLRenderCommandEncoder> encoder, const BufferBi
|
|
|
{
|
|
|
auto b = buffers->info[i];
|
|
|
id<MTLBuffer> 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<MTLBuffer> ib = getMTLBuffer(quadIndexBuffer);
|
|
|
|