metal: implement storage texture support for shaders.

Fixes #1783
This commit is contained in:
slime
2022-10-10 13:47:17 -03:00
parent d966c9e531
commit 99ad576668
8 changed files with 189 additions and 133 deletions
+35
View File
@@ -1098,6 +1098,41 @@ bool Shader::validateBuffer(const UniformInfo *info, Buffer *buffer, bool intern
return true; return true;
} }
bool Shader::fillUniformReflectionData(UniformInfo &u)
{
const auto &r = validationReflection;
if (u.baseType == UNIFORM_STORAGETEXTURE)
{
const auto reflectionit = r.storageTextures.find(u.name);
if (reflectionit != r.storageTextures.end())
{
u.storageTextureFormat = reflectionit->second.format;
u.access = reflectionit->second.access;
return true;
}
// No reflection info - maybe glslang was better at detecting dead code
// than the driver's compiler?
return false;
}
else if (u.baseType == UNIFORM_STORAGEBUFFER)
{
const auto reflectionit = r.storageBuffers.find(u.name);
if (reflectionit != r.storageBuffers.end())
{
u.bufferStride = reflectionit->second.stride;
u.bufferMemberCount = reflectionit->second.memberCount;
u.access = reflectionit->second.access;
return true;
}
return false;
}
return true;
}
bool Shader::initialize() bool Shader::initialize()
{ {
return glslang::InitializeProcess(); return glslang::InitializeProcess();
+2
View File
@@ -289,6 +289,8 @@ protected:
bool usesPointSize; bool usesPointSize;
}; };
bool fillUniformReflectionData(UniformInfo &u);
static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, ValidationReflection &reflection); static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, ValidationReflection &reflection);
static DataBaseType getDataBaseType(PixelFormat format); static DataBaseType getDataBaseType(PixelFormat format);
static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b); static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b);
+1 -1
View File
@@ -211,7 +211,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, love::graphics::Shader *shader); bool applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::graphics::Shader *shader);
void applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, love::graphics::Shader *shader, Texture *maintex); void applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, love::graphics::Shader *shader, Texture *maintex);
id<MTLCommandQueue> commandQueue; id<MTLCommandQueue> commandQueue;
+16 -2
View File
@@ -947,7 +947,7 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const Verte
dirtyRenderState = 0; dirtyRenderState = 0;
} }
void Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::graphics::Shader *shader) bool Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::graphics::Shader *shader)
{ {
Shader *s = (Shader *)shader; Shader *s = (Shader *)shader;
@@ -982,6 +982,8 @@ void Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::g
uniformBufferOffset += alignUp(size, alignment); uniformBufferOffset += alignUp(size, alignment);
bool allWritableVariablesSet = true;
for (const Shader::TextureBinding &b : s->getTextureBindings()) for (const Shader::TextureBinding &b : s->getTextureBindings())
{ {
id<MTLTexture> texture = b.texture; id<MTLTexture> texture = b.texture;
@@ -991,7 +993,12 @@ void Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::g
uint8 sampindex = b.samplerStages[SHADERSTAGE_COMPUTE]; uint8 sampindex = b.samplerStages[SHADERSTAGE_COMPUTE];
if (texindex != LOVE_UINT8_MAX) if (texindex != LOVE_UINT8_MAX)
{
setTexture(encoder, bindings, texindex, texture); setTexture(encoder, bindings, texindex, texture);
if ((b.access & Shader::ACCESS_WRITE) != 0 && texture == nil)
allWritableVariablesSet = false;
}
if (sampindex != LOVE_UINT8_MAX) if (sampindex != LOVE_UINT8_MAX)
setSampler(encoder, bindings, sampindex, samplertex); setSampler(encoder, bindings, sampindex, samplertex);
} }
@@ -1000,10 +1007,16 @@ void Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::g
{ {
uint8 index = b.stages[SHADERSTAGE_COMPUTE]; uint8 index = b.stages[SHADERSTAGE_COMPUTE];
if (index != LOVE_UINT8_MAX) if (index != LOVE_UINT8_MAX)
{
setBuffer(encoder, bindings, index, b.buffer, 0); setBuffer(encoder, bindings, index, b.buffer, 0);
if ((b.access & Shader::ACCESS_WRITE) != 0 && b.buffer == nil)
allWritableVariablesSet = false;
} }
} }
return allWritableVariablesSet;
}
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;
@@ -1299,7 +1312,8 @@ bool Graphics::dispatch(int x, int y, int z)
id<MTLComputeCommandEncoder> computeEncoder = useComputeEncoder(); id<MTLComputeCommandEncoder> computeEncoder = useComputeEncoder();
applyShaderUniforms(computeEncoder, shader); if (!applyShaderUniforms(computeEncoder, shader))
return false;
// TODO: track this state? // TODO: track this state?
[computeEncoder setComputePipelineState:pipeline]; [computeEncoder setComputePipelineState:pipeline];
+4
View File
@@ -43,6 +43,7 @@ namespace spirv_cross
{ {
class CompilerMSL; class CompilerMSL;
struct SPIRType; struct SPIRType;
struct Resource;
} }
namespace love namespace love
@@ -86,6 +87,7 @@ public:
Texture *samplerTexture; Texture *samplerTexture;
bool isMainTexture; bool isMainTexture;
Access access;
uint8 textureStages[SHADERSTAGE_MAX_ENUM]; uint8 textureStages[SHADERSTAGE_MAX_ENUM];
uint8 samplerStages[SHADERSTAGE_MAX_ENUM]; uint8 samplerStages[SHADERSTAGE_MAX_ENUM];
@@ -95,6 +97,7 @@ public:
{ {
id<MTLBuffer> buffer; id<MTLBuffer> buffer;
uint8 stages[SHADERSTAGE_MAX_ENUM]; uint8 stages[SHADERSTAGE_MAX_ENUM];
Access access;
}; };
Shader(id<MTLDevice> device, StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]); Shader(id<MTLDevice> device, StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]);
@@ -137,6 +140,7 @@ private:
}; };
void buildLocalUniforms(const spirv_cross::CompilerMSL &msl, const spirv_cross::SPIRType &type, size_t baseoffset, const std::string &basename); void buildLocalUniforms(const spirv_cross::CompilerMSL &msl, const spirv_cross::SPIRType &type, size_t baseoffset, const std::string &basename);
void addImage(const spirv_cross::CompilerMSL &msl, const spirv_cross::Resource &resource, UniformType baseType);
void compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program); void compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program);
id<MTLFunction> functions[SHADERSTAGE_MAX_ENUM]; id<MTLFunction> functions[SHADERSTAGE_MAX_ENUM];
+85 -62
View File
@@ -455,63 +455,28 @@ void Shader::buildLocalUniforms(const spirv_cross::CompilerMSL &msl, const spirv
} }
} }
void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program) void Shader::addImage(const spirv_cross::CompilerMSL &msl, const spirv_cross::Resource &resource, UniformType baseType)
{ {
using namespace glslang;
using namespace spirv_cross; using namespace spirv_cross;
auto gfx = Graphics::getInstance();
std::map<std::string, int> varyings;
int nextVaryingLocation = 0;
int metalBufferIndices[SHADERSTAGE_MAX_ENUM];
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
metalBufferIndices[i] = getUniformBufferBinding() + 1;
metalBufferIndices[SHADERSTAGE_VERTEX] = DEFAULT_VERTEX_BUFFER_BINDING + 1;
for (int stageindex = 0; stageindex < SHADERSTAGE_MAX_ENUM; stageindex++)
{
auto glslangstage = getGLSLangStage((ShaderStageType) stageindex);
auto intermediate = program.getIntermediate(glslangstage);
if (intermediate == nullptr)
continue;
spv::SpvBuildLogger logger;
glslang::SpvOptions opt;
opt.validate = true;
std::vector<unsigned int> spirv;
GlslangToSpv(*intermediate, spirv, &logger, &opt);
std::string msgs = logger.getAllMessages();
// printf("spirv length: %ld, messages:\n%s\n", spirv.size(), msgs.c_str());
try
{
// printf("GLSL INPUT SOURCE:\n\n%s\n\n", pixel->getSource().c_str());
CompilerMSL msl(std::move(spirv));
auto interfacevars = msl.get_active_interface_variables();
msl.set_enabled_interface_variables(interfacevars);
ShaderResources resources = msl.get_shader_resources();
for (const auto &resource : resources.sampled_images)
{
const SPIRType &basetype = msl.get_type(resource.base_type_id); const SPIRType &basetype = msl.get_type(resource.base_type_id);
const SPIRType &type = msl.get_type(resource.type_id); const SPIRType &type = msl.get_type(resource.type_id);
const SPIRType &imagetype = msl.get_type(basetype.image.type); const SPIRType &imagetype = msl.get_type(basetype.image.type);
UniformInfo u = {}; UniformInfo u = {};
u.baseType = UNIFORM_SAMPLER; u.baseType = baseType;
u.name = resource.name; u.name = resource.name;
u.count = type.array.empty() ? 1 : type.array[0]; u.count = type.array.empty() ? 1 : type.array[0];
u.isDepthSampler = type.image.depth; u.isDepthSampler = type.image.depth;
u.components = 1; u.components = 1;
auto it = uniforms.find(u.name);
if (it != uniforms.end())
return;
if (!fillUniformReflectionData(u))
return;
switch (imagetype.basetype) switch (imagetype.basetype)
{ {
case SPIRType::Float: case SPIRType::Float:
@@ -559,7 +524,7 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
if (u.baseType == UNIFORM_SAMPLER) if (u.baseType == UNIFORM_SAMPLER)
{ {
auto tex = gfx->getDefaultTexture(u.textureType); auto tex = Graphics::getInstance()->getDefaultTexture(u.textureType);
for (int i = 0; i < u.count; i++) for (int i = 0; i < u.count; i++)
{ {
tex->retain(); tex->retain();
@@ -571,6 +536,11 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
for (int i = 0; i < u.count; i++) for (int i = 0; i < u.count; i++)
u.buffers[i] = nullptr; // TODO u.buffers[i] = nullptr; // TODO
} }
else if (u.baseType == UNIFORM_STORAGETEXTURE)
{
for (int i = 0; i < u.count; i++)
u.textures[i] = nullptr;
}
uniforms[u.name] = u; uniforms[u.name] = u;
@@ -579,6 +549,58 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
builtinUniformInfo[builtin] = &uniforms[u.name]; builtinUniformInfo[builtin] = &uniforms[u.name];
} }
void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program)
{
using namespace glslang;
using namespace spirv_cross;
std::map<std::string, int> varyings;
int nextVaryingLocation = 0;
int metalBufferIndices[SHADERSTAGE_MAX_ENUM];
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
metalBufferIndices[i] = getUniformBufferBinding() + 1;
metalBufferIndices[SHADERSTAGE_VERTEX] = DEFAULT_VERTEX_BUFFER_BINDING + 1;
for (int stageindex = 0; stageindex < SHADERSTAGE_MAX_ENUM; stageindex++)
{
auto glslangstage = getGLSLangStage((ShaderStageType) stageindex);
auto intermediate = program.getIntermediate(glslangstage);
if (intermediate == nullptr)
continue;
spv::SpvBuildLogger logger;
glslang::SpvOptions opt;
opt.validate = true;
std::vector<unsigned int> spirv;
GlslangToSpv(*intermediate, spirv, &logger, &opt);
std::string msgs = logger.getAllMessages();
// printf("spirv length: %ld, messages:\n%s\n", spirv.size(), msgs.c_str());
try
{
// printf("GLSL INPUT SOURCE:\n\n%s\n\n", pixel->getSource().c_str());
CompilerMSL msl(std::move(spirv));
auto interfacevars = msl.get_active_interface_variables();
msl.set_enabled_interface_variables(interfacevars);
ShaderResources resources = msl.get_shader_resources();
for (const auto &resource : resources.storage_images)
{
addImage(msl, resource, UNIFORM_STORAGETEXTURE);
}
for (const auto &resource : resources.sampled_images)
{
addImage(msl, resource, UNIFORM_SAMPLER);
}
for (const auto &resource : resources.uniform_buffers) for (const auto &resource : resources.uniform_buffers)
{ {
MSLResourceBinding binding; MSLResourceBinding binding;
@@ -639,19 +661,8 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
u.name = resource.name; u.name = resource.name;
u.count = type.array.empty() ? 1 : type.array[0]; u.count = type.array.empty() ? 1 : type.array[0];
const auto reflectionit = validationReflection.storageBuffers.find(u.name); if (!fillUniformReflectionData(u))
if (reflectionit != validationReflection.storageBuffers.end())
{
u.bufferStride = reflectionit->second.stride;
u.bufferMemberCount = reflectionit->second.memberCount;
u.access = reflectionit->second.access;
}
else
{
// No reflection info - maybe glslang was better at detecting
// dead code than the driver's compiler?
continue; continue;
}
u.buffers = new love::graphics::Buffer*[u.count]; u.buffers = new love::graphics::Buffer*[u.count];
u.dataSize = sizeof(int) * u.count; u.dataSize = sizeof(int) * u.count;
@@ -741,11 +752,11 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
functions[stageindex] = [library newFunctionWithName:library.functionNames[0]]; functions[stageindex] = [library newFunctionWithName:library.functionNames[0]];
for (const auto &resource : resources.sampled_images) auto setTextureBinding = [this](CompilerMSL &msl, int stageindex, const spirv_cross::Resource &resource) -> void
{ {
auto it = uniforms.find(resource.name); auto it = uniforms.find(resource.name);
if (it == uniforms.end()) if (it == uniforms.end())
continue; return;
UniformInfo &u = it->second; UniformInfo &u = it->second;
@@ -753,7 +764,7 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
uint32 samplerbinding = msl.get_automatic_msl_resource_binding_secondary(resource.id); uint32 samplerbinding = msl.get_automatic_msl_resource_binding_secondary(resource.id);
if (texturebinding == (uint32)-1) if (texturebinding == (uint32)-1)
continue; return;
for (int i = 0; i < u.count; i++) for (int i = 0; i < u.count; i++)
{ {
@@ -761,6 +772,7 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
{ {
u.ints[i] = (int)textureBindings.size(); u.ints[i] = (int)textureBindings.size();
TextureBinding b = {}; TextureBinding b = {};
b.access = u.access;
if (u.baseType == UNIFORM_TEXELBUFFER) if (u.baseType == UNIFORM_TEXELBUFFER)
{ {
@@ -788,6 +800,16 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
b.textureStages[stageindex] = (uint8) texturebinding; b.textureStages[stageindex] = (uint8) texturebinding;
b.samplerStages[stageindex] = (uint8) samplerbinding; b.samplerStages[stageindex] = (uint8) samplerbinding;
} }
};
for (const auto &resource : resources.sampled_images)
{
setTextureBinding(msl, stageindex, resource);
}
for (const auto &resource : resources.storage_images)
{
setTextureBinding(msl, stageindex, resource);
} }
for (const auto &resource : resources.storage_buffers) for (const auto &resource : resources.storage_buffers)
@@ -808,6 +830,7 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
{ {
u.ints[i] = (int)bufferBindings.size(); u.ints[i] = (int)bufferBindings.size();
BufferBinding b = {}; BufferBinding b = {};
b.access = u.access;
for (uint8 &stagebinding : b.stages) for (uint8 &stagebinding : b.stages)
stagebinding = LOVE_UINT8_MAX; stagebinding = LOVE_UINT8_MAX;
@@ -841,7 +864,7 @@ Shader::~Shader()
for (const auto &it : uniforms) for (const auto &it : uniforms)
{ {
const auto &u = it.second; const auto &u = it.second;
if (u.baseType == UNIFORM_SAMPLER) if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE)
{ {
free(u.data); free(u.data);
for (int i = 0; i < u.count; i++) for (int i = 0; i < u.count; i++)
@@ -940,7 +963,7 @@ void Shader::updateUniform(const UniformInfo *info, int count)
void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count)
{ @autoreleasepool { { @autoreleasepool {
if (info->baseType != UNIFORM_SAMPLER) if (info->baseType != UNIFORM_SAMPLER && info->baseType != UNIFORM_STORAGETEXTURE)
return; return;
if (current == this) if (current == this)
+4 -25
View File
@@ -139,6 +139,9 @@ void Shader::mapActiveUniforms()
if (u.location == -1) if (u.location == -1)
continue; continue;
if (!fillUniformReflectionData(u))
continue;;
if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER) if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER)
{ {
TextureUnit unit; TextureUnit unit;
@@ -161,19 +164,6 @@ void Shader::mapActiveUniforms()
} }
else if (u.baseType == UNIFORM_STORAGETEXTURE) else if (u.baseType == UNIFORM_STORAGETEXTURE)
{ {
const auto reflectionit = validationReflection.storageTextures.find(u.name);
if (reflectionit != validationReflection.storageTextures.end())
{
u.storageTextureFormat = reflectionit->second.format;
u.access = reflectionit->second.access;
}
else
{
// No reflection info - maybe glslang was better at detecting
// dead code than the driver's compiler?
continue;
}
StorageTextureBinding binding = {}; StorageTextureBinding binding = {};
binding.gltexture = gl.getDefaultTexture(u.textureType, u.dataBaseType); binding.gltexture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
binding.type = u.textureType; binding.type = u.textureType;
@@ -373,19 +363,8 @@ void Shader::mapActiveUniforms()
u.name = std::string(namebuffer, namelength); u.name = std::string(namebuffer, namelength);
u.count = 1; u.count = 1;
const auto reflectionit = validationReflection.storageBuffers.find(u.name); if (!fillUniformReflectionData(u))
if (reflectionit != validationReflection.storageBuffers.end())
{
u.bufferStride = reflectionit->second.stride;
u.bufferMemberCount = reflectionit->second.memberCount;
u.access = reflectionit->second.access;
}
else
{
// No reflection info - maybe glslang was better at detecting
// dead code than the driver's compiler?
continue; continue;
}
// Make sure previously set uniform data is preserved, and shader- // Make sure previously set uniform data is preserved, and shader-
// initialized values are retrieved. // initialized values are retrieved.
+8 -9
View File
@@ -872,18 +872,11 @@ void Shader::compileShaders()
u.components = 1; u.components = 1;
u.name = r.name; u.name = r.name;
u.count = type.array.empty() ? 1 : type.array[0]; u.count = type.array.empty() ? 1 : type.array[0];
u.location = comp.get_decoration(r.id, spv::DecorationBinding);
const auto reflectionit = validationReflection.storageBuffers.find(u.name); if (!fillUniformReflectionData(u))
if (reflectionit != validationReflection.storageBuffers.end())
{
u.bufferStride = reflectionit->second.stride;
u.bufferMemberCount = reflectionit->second.memberCount;
u.access = reflectionit->second.access;
}
else
continue; continue;
u.location = comp.get_decoration(r.id, spv::DecorationBinding);
u.buffers = new love::graphics::Buffer *[u.count]; u.buffers = new love::graphics::Buffer *[u.count];
for (int i = 0; i < u.count; i++) for (int i = 0; i < u.count; i++)
@@ -901,6 +894,10 @@ void Shader::compileShaders()
u.components = 1; u.components = 1;
u.name = r.name; u.name = r.name;
u.count = type.array.empty() ? 1 : type.array[0]; u.count = type.array.empty() ? 1 : type.array[0];
if (!fillUniformReflectionData(u))
continue;
u.textures = new love::graphics::Texture *[u.count]; u.textures = new love::graphics::Texture *[u.count];
u.location = comp.get_decoration(r.id, spv::DecorationBinding); u.location = comp.get_decoration(r.id, spv::DecorationBinding);
@@ -913,6 +910,7 @@ void Shader::compileShaders()
} }
if (shaderStage == SHADERSTAGE_VERTEX) if (shaderStage == SHADERSTAGE_VERTEX)
{
for (const auto &r : shaderResources.stage_inputs) for (const auto &r : shaderResources.stage_inputs)
{ {
const auto &name = r.name; const auto &name = r.name;
@@ -920,6 +918,7 @@ void Shader::compileShaders()
attributes[name] = attributeLocation; attributes[name] = attributeLocation;
} }
} }
}
delete program; delete program;
for (auto shader : glslangShaders) for (auto shader : glslangShaders)