mirror of
https://github.com/love2d/love.git
synced 2026-08-17 19:23:38 +02:00
metal: initial love.graphics.dispatchThreadgroups implementation
This commit is contained in:
@@ -203,6 +203,7 @@ private:
|
|||||||
|
|
||||||
id<MTLDepthStencilState> getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil);
|
id<MTLDepthStencilState> getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil);
|
||||||
void applyRenderState(id<MTLRenderCommandEncoder> renderEncoder, const VertexAttributes &attributes);
|
void applyRenderState(id<MTLRenderCommandEncoder> renderEncoder, const VertexAttributes &attributes);
|
||||||
|
void applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, Shader *shader);
|
||||||
void applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, Shader *shader, Texture *maintex);
|
void applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, Shader *shader, Texture *maintex);
|
||||||
|
|
||||||
id<MTLCommandQueue> commandQueue;
|
id<MTLCommandQueue> commandQueue;
|
||||||
|
|||||||
@@ -168,6 +168,23 @@ static inline void setBuffer(id<MTLRenderCommandEncoder> encoder, Graphics::Rend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void setBuffer(id<MTLComputeCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, int index, id<MTLBuffer> buffer, size_t offset)
|
||||||
|
{
|
||||||
|
void *b = (__bridge void *)buffer;
|
||||||
|
auto &binding = bindings.buffers[index][SHADERSTAGE_COMPUTE];
|
||||||
|
if (binding.buffer != b)
|
||||||
|
{
|
||||||
|
binding.buffer = b;
|
||||||
|
binding.offset = offset;
|
||||||
|
[encoder setBuffer:buffer offset:offset atIndex:index];
|
||||||
|
}
|
||||||
|
else if (binding.offset != offset)
|
||||||
|
{
|
||||||
|
binding.offset = offset;
|
||||||
|
[encoder setBufferOffset:offset atIndex:index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void setTexture(id<MTLRenderCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, ShaderStageType stage, int index, id<MTLTexture> texture)
|
static inline void setTexture(id<MTLRenderCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, ShaderStageType stage, int index, id<MTLTexture> texture)
|
||||||
{
|
{
|
||||||
void *t = (__bridge void *)texture;
|
void *t = (__bridge void *)texture;
|
||||||
@@ -182,6 +199,17 @@ static inline void setTexture(id<MTLRenderCommandEncoder> encoder, Graphics::Ren
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void setTexture(id<MTLComputeCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, int index, id<MTLTexture> texture)
|
||||||
|
{
|
||||||
|
void *t = (__bridge void *)texture;
|
||||||
|
auto &binding = bindings.textures[index][SHADERSTAGE_COMPUTE];
|
||||||
|
if (binding != t)
|
||||||
|
{
|
||||||
|
binding = t;
|
||||||
|
[encoder setTexture:texture atIndex:index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void setSampler(id<MTLRenderCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, ShaderStageType stage, int index, id<MTLSamplerState> sampler)
|
static inline void setSampler(id<MTLRenderCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, ShaderStageType stage, int index, id<MTLSamplerState> sampler)
|
||||||
{
|
{
|
||||||
void *s = (__bridge void *)sampler;
|
void *s = (__bridge void *)sampler;
|
||||||
@@ -196,6 +224,17 @@ static inline void setSampler(id<MTLRenderCommandEncoder> encoder, Graphics::Ren
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void setSampler(id<MTLComputeCommandEncoder> encoder, Graphics::RenderEncoderBindings &bindings, int index, id<MTLSamplerState> sampler)
|
||||||
|
{
|
||||||
|
void *s = (__bridge void *)sampler;
|
||||||
|
auto &binding = bindings.samplers[index][SHADERSTAGE_COMPUTE];
|
||||||
|
if (binding != s)
|
||||||
|
{
|
||||||
|
binding = s;
|
||||||
|
[encoder setSamplerState:sampler atIndex:index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
love::graphics::Graphics *createInstance()
|
love::graphics::Graphics *createInstance()
|
||||||
{
|
{
|
||||||
love::graphics::Graphics *instance = nullptr;
|
love::graphics::Graphics *instance = nullptr;
|
||||||
@@ -662,6 +701,7 @@ id<MTLComputeCommandEncoder> Graphics::useComputeEncoder()
|
|||||||
submitRenderEncoder(SUBMIT_STORE);
|
submitRenderEncoder(SUBMIT_STORE);
|
||||||
submitBlitEncoder();
|
submitBlitEncoder();
|
||||||
computeEncoder = [useCommandBuffer() computeCommandEncoder];
|
computeEncoder = [useCommandBuffer() computeCommandEncoder];
|
||||||
|
renderBindings = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return computeEncoder;
|
return computeEncoder;
|
||||||
@@ -895,6 +935,63 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const Verte
|
|||||||
dirtyRenderState = 0;
|
dirtyRenderState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::graphics::Shader *shader)
|
||||||
|
{
|
||||||
|
Shader *s = (Shader *)shader;
|
||||||
|
|
||||||
|
#ifdef LOVE_MACOS
|
||||||
|
size_t alignment = 256;
|
||||||
|
#else
|
||||||
|
size_t alignment = 16;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t size = s->getLocalUniformBufferSize();
|
||||||
|
uint8 *bufferdata = s->getLocalUniformBufferData();
|
||||||
|
|
||||||
|
if (uniformBuffer->getSize() < uniformBufferOffset + size)
|
||||||
|
{
|
||||||
|
size_t newsize = uniformBuffer->getSize() * 2;
|
||||||
|
uniformBuffer->release();
|
||||||
|
uniformBuffer = CreateStreamBuffer(device, BUFFERUSAGE_VERTEX, newsize);
|
||||||
|
uniformBufferData = {};
|
||||||
|
uniformBufferOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniformBufferData.data == nullptr)
|
||||||
|
uniformBufferData = uniformBuffer->map(uniformBuffer->getSize());
|
||||||
|
|
||||||
|
memcpy(uniformBufferData.data + uniformBufferOffset, bufferdata, size);
|
||||||
|
|
||||||
|
id<MTLBuffer> buffer = getMTLBuffer(uniformBuffer);
|
||||||
|
int uniformindex = Shader::getUniformBufferBinding();
|
||||||
|
|
||||||
|
auto &bindings = renderBindings;
|
||||||
|
setBuffer(encoder, bindings, uniformindex, buffer, uniformBufferOffset);
|
||||||
|
|
||||||
|
uniformBufferOffset += alignUp(size, alignment);
|
||||||
|
|
||||||
|
for (const Shader::TextureBinding &b : s->getTextureBindings())
|
||||||
|
{
|
||||||
|
id<MTLTexture> texture = b.texture;
|
||||||
|
id<MTLSamplerState> sampler = b.sampler;
|
||||||
|
|
||||||
|
uint8 texindex = b.textureStages[SHADERSTAGE_COMPUTE];
|
||||||
|
uint8 sampindex = b.samplerStages[SHADERSTAGE_COMPUTE];
|
||||||
|
|
||||||
|
if (texindex != LOVE_UINT8_MAX)
|
||||||
|
setTexture(encoder, bindings, texindex, texture);
|
||||||
|
if (sampindex != LOVE_UINT8_MAX)
|
||||||
|
setSampler(encoder, bindings, sampindex, sampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Shader::BufferBinding &b : s->getBufferBindings())
|
||||||
|
{
|
||||||
|
uint8 index = b.stages[SHADERSTAGE_COMPUTE];
|
||||||
|
if (index != LOVE_UINT8_MAX)
|
||||||
|
setBuffer(encoder, bindings, index, b.buffer, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, love::graphics::Shader *shader, love::graphics::Texture *maintex)
|
void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, love::graphics::Shader *shader, love::graphics::Texture *maintex)
|
||||||
{
|
{
|
||||||
Shader *s = (Shader *)shader;
|
Shader *s = (Shader *)shader;
|
||||||
@@ -1178,8 +1275,27 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute
|
|||||||
|
|
||||||
bool Graphics::dispatch(int x, int y, int z)
|
bool Graphics::dispatch(int x, int y, int z)
|
||||||
{ @autoreleasepool {
|
{ @autoreleasepool {
|
||||||
// TODO
|
// Set by higher level code before calling dispatch(x, y, z).
|
||||||
return false;
|
auto shader = (Shader *) Shader::current;
|
||||||
|
|
||||||
|
int tX, tY, tZ;
|
||||||
|
shader->getLocalThreadgroupSize(&tX, &tY, &tZ);
|
||||||
|
|
||||||
|
id<MTLComputePipelineState> pipeline = shader->getComputePipeline();
|
||||||
|
if (pipeline == nil)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
id<MTLComputeCommandEncoder> computeEncoder = useComputeEncoder();
|
||||||
|
|
||||||
|
applyShaderUniforms(computeEncoder, shader);
|
||||||
|
|
||||||
|
// TODO: track this state?
|
||||||
|
[computeEncoder setComputePipelineState:pipeline];
|
||||||
|
|
||||||
|
[computeEncoder dispatchThreadgroups:MTLSizeMake(x, y, z)
|
||||||
|
threadsPerThreadgroup:MTLSizeMake(tX, tY, tZ)];
|
||||||
|
|
||||||
|
return true;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int /*pixelw*/, int /*pixelh*/, bool /*hasSRGBtexture*/)
|
void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int /*pixelw*/, int /*pixelh*/, bool /*hasSRGBtexture*/)
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ public:
|
|||||||
void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
|
void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
|
||||||
|
|
||||||
id<MTLRenderPipelineState> getCachedRenderPipeline(const RenderPipelineKey &key);
|
id<MTLRenderPipelineState> getCachedRenderPipeline(const RenderPipelineKey &key);
|
||||||
|
id<MTLComputePipelineState> getComputePipeline() const { return computePipeline; }
|
||||||
|
|
||||||
static int getUniformBufferBinding();
|
static int getUniformBufferBinding();
|
||||||
const std::vector<TextureBinding> &getTextureBindings() const { return textureBindings; }
|
const std::vector<TextureBinding> &getTextureBindings() const { return textureBindings; }
|
||||||
@@ -144,6 +145,7 @@ private:
|
|||||||
std::vector<BufferBinding> bufferBindings;
|
std::vector<BufferBinding> bufferBindings;
|
||||||
|
|
||||||
std::unordered_map<RenderPipelineKey, const void *, RenderPipelineHasher> cachedRenderPipelines;
|
std::unordered_map<RenderPipelineKey, const void *, RenderPipelineHasher> cachedRenderPipelines;
|
||||||
|
id<MTLComputePipelineState> computePipeline;
|
||||||
|
|
||||||
}; // Metal
|
}; // Metal
|
||||||
|
|
||||||
|
|||||||
@@ -347,6 +347,27 @@ Shader::Shader(id<MTLDevice> device, StrongRef<love::graphics::ShaderStage> stag
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
||||||
|
if (functions[SHADERSTAGE_COMPUTE] != nil)
|
||||||
|
{
|
||||||
|
MTLComputePipelineDescriptor *desc = [MTLComputePipelineDescriptor new];
|
||||||
|
desc.computeFunction = functions[SHADERSTAGE_COMPUTE];
|
||||||
|
|
||||||
|
// TODO: threadGroupSizeIsMultipleOfThreadExecutionWidth
|
||||||
|
|
||||||
|
NSError *err = nil;
|
||||||
|
computePipeline = [device newComputePipelineStateWithDescriptor:desc
|
||||||
|
options:MTLPipelineOptionNone
|
||||||
|
reflection:nil
|
||||||
|
error:&err];
|
||||||
|
if (computePipeline == nil)
|
||||||
|
{
|
||||||
|
if (err != nil)
|
||||||
|
throw love::Exception("Error creating compute shader pipeline: %s", err.localizedDescription.UTF8String);
|
||||||
|
else
|
||||||
|
throw love::Exception("Error creating compute shader pipeline.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program)
|
void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program)
|
||||||
|
|||||||
Reference in New Issue
Block a user