From 9765485222d8079421624b2b492fa9d906de13a7 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 3 May 2020 12:48:43 -0300 Subject: [PATCH] metal: partial implementation of pipeline states. Add BGRA8 pixel format. --- src/common/pixelformat.cpp | 33 ++-- src/common/pixelformat.h | 9 +- src/modules/graphics/Graphics.cpp | 4 +- src/modules/graphics/Texture.cpp | 2 +- src/modules/graphics/metal/Graphics.h | 9 -- src/modules/graphics/metal/Graphics.mm | 134 ++++------------ src/modules/graphics/metal/Metal.mm | 14 +- src/modules/graphics/metal/Shader.h | 44 ++++- src/modules/graphics/metal/Shader.mm | 187 +++++++++++++++++++++- src/modules/graphics/metal/ShaderStage.mm | 2 +- src/modules/graphics/opengl/Graphics.cpp | 4 +- src/modules/graphics/opengl/OpenGL.cpp | 14 +- src/modules/graphics/renderstate.h | 6 +- 13 files changed, 318 insertions(+), 144 deletions(-) diff --git a/src/common/pixelformat.cpp b/src/common/pixelformat.cpp index 53426ceb3..aaf647ffb 100644 --- a/src/common/pixelformat.cpp +++ b/src/common/pixelformat.cpp @@ -44,7 +44,9 @@ static PixelFormatInfo formatInfo[] = { 2, 1, 1, 8, true, false, false, false }, // PIXELFORMAT_RG32_FLOAT { 4, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_RGBA8_UNORM - { 4, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_sRGBA8_UNORM + { 4, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_RGBA8_UNORM_sRGB + { 4, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_BGRA8_UNORM + { 4, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_BGRA8_UNORM_sRGB { 4, 1, 1, 8, true, false, false, false }, // PIXELFORMAT_RGBA16_UNORM { 4, 1, 1, 8, true, false, false, false }, // PIXELFORMAT_RGBA16_FLOAT { 4, 1, 1, 16, true, false, false, false }, // PIXELFORMAT_RGBA32_FLOAT @@ -123,11 +125,13 @@ static StringMap::Entry formatEntries[] = { "rg16f", PIXELFORMAT_RG16_FLOAT }, { "rg32f", PIXELFORMAT_RG32_FLOAT }, - { "rgba8", PIXELFORMAT_RGBA8_UNORM }, - { "srgba8", PIXELFORMAT_sRGBA8_UNORM }, - { "rgba16", PIXELFORMAT_RGBA16_UNORM }, - { "rgba16f", PIXELFORMAT_RGBA16_FLOAT }, - { "rgba32f", PIXELFORMAT_RGBA32_FLOAT }, + { "rgba8", PIXELFORMAT_RGBA8_UNORM }, + { "srgba8", PIXELFORMAT_RGBA8_UNORM_sRGB }, + { "bgra8", PIXELFORMAT_RGBA8_UNORM }, + { "bgra8srgb", PIXELFORMAT_RGBA8_UNORM_sRGB }, + { "rgba16", PIXELFORMAT_RGBA16_UNORM }, + { "rgba16f", PIXELFORMAT_RGBA16_FLOAT }, + { "rgba32f", PIXELFORMAT_RGBA32_FLOAT }, { "rgba4", PIXELFORMAT_RGBA4_UNORM }, { "rgb5a1", PIXELFORMAT_RGB5A1_UNORM }, @@ -220,17 +224,26 @@ bool isPixelFormatStencil(PixelFormat format) return formatInfo[format].stencil; } +bool isPixelFormatSRGB(PixelFormat format) +{ + return format == PIXELFORMAT_RGBA8_UNORM_sRGB || format == PIXELFORMAT_BGRA8_UNORM_sRGB; +} + PixelFormat getSRGBPixelFormat(PixelFormat format) { if (format == PIXELFORMAT_RGBA8_UNORM) - return PIXELFORMAT_sRGBA8_UNORM; + return PIXELFORMAT_RGBA8_UNORM_sRGB; + else if (format == PIXELFORMAT_BGRA8_UNORM) + return PIXELFORMAT_BGRA8_UNORM_sRGB; return format; } PixelFormat getLinearPixelFormat(PixelFormat format) { - if (format == PIXELFORMAT_sRGBA8_UNORM) + if (format == PIXELFORMAT_RGBA8_UNORM_sRGB) return PIXELFORMAT_RGBA8_UNORM; + else if (format == PIXELFORMAT_BGRA8_UNORM_sRGB) + return PIXELFORMAT_BGRA8_UNORM; return format; } @@ -285,7 +298,9 @@ size_t getPixelFormatRowStride(PixelFormat format, int width) case PIXELFORMAT_DEPTH16_UNORM: return 2 * width; case PIXELFORMAT_RGBA8_UNORM: - case PIXELFORMAT_sRGBA8_UNORM: + case PIXELFORMAT_RGBA8_UNORM_sRGB: + case PIXELFORMAT_BGRA8_UNORM: + case PIXELFORMAT_BGRA8_UNORM_sRGB: case PIXELFORMAT_RG16_UNORM: case PIXELFORMAT_RG16_FLOAT: case PIXELFORMAT_R32_FLOAT: diff --git a/src/common/pixelformat.h b/src/common/pixelformat.h index a1a4b1ddb..71c8d9e37 100644 --- a/src/common/pixelformat.h +++ b/src/common/pixelformat.h @@ -48,7 +48,9 @@ enum PixelFormat // 4-channel normal formats PIXELFORMAT_RGBA8_UNORM, - PIXELFORMAT_sRGBA8_UNORM, + PIXELFORMAT_RGBA8_UNORM_sRGB, + PIXELFORMAT_BGRA8_UNORM, + PIXELFORMAT_BGRA8_UNORM_sRGB, PIXELFORMAT_RGBA16_UNORM, PIXELFORMAT_RGBA16_FLOAT, PIXELFORMAT_RGBA32_FLOAT, @@ -146,6 +148,11 @@ bool isPixelFormatDepth(PixelFormat format); **/ bool isPixelFormatStencil(PixelFormat format); +/** + * Gets whether the specified color pixel format is sRGB-encoded. + **/ +bool isPixelFormatSRGB(PixelFormat format); + /** * Gets the sRGB version of a linear pixel format, if applicable. **/ diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 0b8009994..c0a6b3e09 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -657,7 +657,7 @@ void Graphics::setRenderTargets(const RenderTargets &rts) if (!firsttex->isValidSlice(firsttarget.slice)) throw love::Exception("Invalid slice index: %d.", firsttarget.slice + 1); - bool hasSRGBtexture = firstcolorformat == PIXELFORMAT_sRGBA8_UNORM; + bool hasSRGBtexture = isPixelFormatSRGB(firstcolorformat); int pixelw = firsttex->getPixelWidth(firsttarget.mipmap); int pixelh = firsttex->getPixelHeight(firsttarget.mipmap); int reqmsaa = firsttex->getRequestedMSAA(); @@ -690,7 +690,7 @@ void Graphics::setRenderTargets(const RenderTargets &rts) if (isPixelFormatDepthStencil(format)) throw love::Exception("Depth/stencil format textures must be used with the 'depthstencil' field of the table passed into setRenderTargets."); - if (format == PIXELFORMAT_sRGBA8_UNORM) + if (isPixelFormatSRGB(format)) hasSRGBtexture = true; } diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index 0200e58ef..b20b2b7d9 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -558,7 +558,7 @@ bool Texture::isCompressed() const bool Texture::isFormatLinear() const { - return isGammaCorrect() && !sRGB && format != PIXELFORMAT_sRGBA8_UNORM; + return isGammaCorrect() && !sRGB && !isPixelFormatSRGB(format); } bool Texture::isValidSlice(int slice) const diff --git a/src/modules/graphics/metal/Graphics.h b/src/modules/graphics/metal/Graphics.h index a6f77639a..021e98703 100644 --- a/src/modules/graphics/metal/Graphics.h +++ b/src/modules/graphics/metal/Graphics.h @@ -155,14 +155,6 @@ private: Shader *shader; }; - struct PipelineState - { - vertex::Attributes vertexAttributes; - BlendState blend; - ColorChannelMask colorChannelMask; - Shader *shader; - }; - love::graphics::ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) override; love::graphics::Shader *newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) override; love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override; @@ -172,7 +164,6 @@ private: void endPass(); - id getCachedRenderPipelineState(const PipelineState &state); id getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil); void applyRenderState(id renderEncoder); diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index dfd0e20fc..b8e928ce8 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -23,6 +23,7 @@ #include "Buffer.h" #include "Texture.h" #include "Shader.h" +#include "ShaderStage.h" #include "window/Window.h" #include "image/Image.h" @@ -93,46 +94,6 @@ static MTLCompareFunction getMTLCompareFunction(CompareMode mode) return MTLCompareFunctionNever; } -static MTLVertexFormat getMTLVertexFormat(vertex::DataType type, int components) -{ - // TODO - return MTLVertexFormatFloat4; -} - -static MTLBlendOperation getMTLBlendOperation(BlendOperation op) -{ - switch (op) - { - case BLENDOP_ADD: return MTLBlendOperationAdd; - case BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; - case BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; - case BLENDOP_MIN: return MTLBlendOperationMin; - case BLENDOP_MAX: return MTLBlendOperationMax; - case BLENDOP_MAX_ENUM: return MTLBlendOperationAdd; - } - return MTLBlendOperationAdd; -} - -static MTLBlendFactor getMTLBlendFactor(BlendFactor factor) -{ - switch (factor) - { - case BLENDFACTOR_ZERO: return MTLBlendFactorZero; - case BLENDFACTOR_ONE: return MTLBlendFactorOne; - case BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; - case BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; - case BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; - case BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; - case BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; - case BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; - case BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; - case BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; - case BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; - case BLENDFACTOR_MAX_ENUM: return MTLBlendFactorZero; - } - return MTLBlendFactorZero; -} - love::graphics::Graphics *createInstance() { love::graphics::Graphics *instance = nullptr; @@ -215,7 +176,7 @@ love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings, love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) { - return nullptr; // TODO: new ShaderStage(this, stage, source, gles, cachekey); + return new ShaderStage(this, stage, source, gles, cachekey); } love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) @@ -253,6 +214,10 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int this->windowHasStencil = windowhasstencil; metalLayer.device = device; + metalLayer.pixelFormat = isGammaCorrect() ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm; + + // TODO: set to NO when we have pending screen captures + metalLayer.framebufferOnly = YES; setViewportSize(width, height, pixelwidth, pixelheight); @@ -281,14 +246,14 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int if (!Shader::standardShaders[i]) { const auto &code = defaultShaderCode[i][target][gammacorrect]; -// Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]); + Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]); } } // 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(); + if (!Shader::current) + Shader::standardShaders[Shader::STANDARD_DEFAULT]->attach(); return true; }} @@ -347,7 +312,7 @@ id Graphics::useRenderEncoder() const auto &rts = states.back().renderTargets; if (rts.getFirstTarget().texture.get() != nullptr) { - + // TODO } else { @@ -431,50 +396,6 @@ id Graphics::getCachedSampler(const SamplerState &s) return sampler; }} -id Graphics::getCachedRenderPipelineState(const PipelineState &state) -{ - MTLRenderPipelineDescriptor *pipedesc = [MTLRenderPipelineDescriptor new]; - - MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor]; - - const auto &attributes = state.vertexAttributes; - uint32 allbits = attributes.enableBits; - uint32 i = 0; - while (allbits) - { - uint32 bit = 1u << i; - - if (attributes.enableBits & bit) - { - const auto &attrib = attributes.attribs[i]; - - vertdesc.attributes[i].format = getMTLVertexFormat(attrib.type, attrib.components); - vertdesc.attributes[i].offset = attrib.offsetFromVertex; - vertdesc.attributes[i].bufferIndex = attrib.bufferIndex; - - const auto &layout = attributes.bufferLayouts[attrib.bufferIndex]; - - bool instanced = attributes.instanceBits & (1u << attrib.bufferIndex); - auto step = instanced ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex; - - vertdesc.layouts[attrib.bufferIndex].stride = layout.stride; - vertdesc.layouts[attrib.bufferIndex].stepFunction = step; - } - - i++; - allbits >>= 1; - } - - pipedesc.vertexDescriptor = vertdesc; - -// pipedesc. - - NSError *err = nil; - id pipestate = [device newRenderPipelineStateWithDescriptor:pipedesc error:&err]; - - return pipestate; -} - id Graphics::getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil) { id state = nil; @@ -573,20 +494,31 @@ void Graphics::applyRenderState(id encoder) // TODO: attributes if (dirtyState & pipelineStateBits) { - if (dirtyState & STATEBIT_BLEND) - { +// Shader *shader = (Shader *) state.shader.get(); + Shader *shader = (Shader *) Shader::current; + id pipeline = nil; + if (shader) + { + Shader::RenderPipelineKey key; + + key.blend = state.blend; + key.colorChannelMask = state.colorMask; + + const auto &rts = state.renderTargets.colors; + + for (size_t i = 0; i < rts.size(); i++) + key.colorRenderTargetFormats |= (rts[i].texture->getPixelFormat()) << (8 * i); + + if (state.renderTargets.getFirstTarget().texture.get() == nullptr) + key.colorRenderTargetFormats = isGammaCorrect() ? PIXELFORMAT_BGRA8_UNORM_sRGB : PIXELFORMAT_BGRA8_UNORM; + + // TODO: depth/stencil + + pipeline = shader->getCachedRenderPipeline(key); } - if (dirtyState & STATEBIT_SHADER) - { - - } - - if (dirtyState & STATEBIT_COLORMASK) - { - - } + [encoder setRenderPipelineState:pipeline]; } if (dirtyState & (STATEBIT_DEPTH | STATEBIT_STENCIL)) @@ -1072,7 +1004,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool /*rendertarget*/, { case PIXELFORMAT_NORMAL: if (isGammaCorrect()) - return PIXELFORMAT_sRGBA8_UNORM; + return PIXELFORMAT_RGBA8_UNORM_sRGB; else return PIXELFORMAT_RGBA8_UNORM; case PIXELFORMAT_HDR: diff --git a/src/modules/graphics/metal/Metal.mm b/src/modules/graphics/metal/Metal.mm index 0bc8a9ba9..f6b0427ec 100644 --- a/src/modules/graphics/metal/Metal.mm +++ b/src/modules/graphics/metal/Metal.mm @@ -32,10 +32,10 @@ MTLPixelFormat Metal::convertPixelFormat(PixelFormat format, bool &isSRGB) { MTLPixelFormat mtlformat = MTLPixelFormatRGBA8Unorm; - if (format == PIXELFORMAT_RGBA8_UNORM && isSRGB) - format = PIXELFORMAT_sRGBA8_UNORM; + if (isSRGB) + format = getSRGBPixelFormat(format); - if (!isPixelFormatCompressed(format) && format != PIXELFORMAT_sRGBA8_UNORM) + if (!isPixelFormatCompressed(format) && !isPixelFormatSRGB(format)) isSRGB = false; switch (format) @@ -49,9 +49,15 @@ MTLPixelFormat Metal::convertPixelFormat(PixelFormat format, bool &isSRGB) case PIXELFORMAT_RGBA8_UNORM: mtlformat = MTLPixelFormatRGBA8Unorm; break; - case PIXELFORMAT_sRGBA8_UNORM: + case PIXELFORMAT_RGBA8_UNORM_sRGB: mtlformat = MTLPixelFormatRGBA8Unorm_sRGB; break; + case PIXELFORMAT_BGRA8_UNORM: + mtlformat = MTLPixelFormatBGRA8Unorm; + break; + case PIXELFORMAT_BGRA8_UNORM_sRGB: + mtlformat = MTLPixelFormatBGRA8Unorm_sRGB; + break; case PIXELFORMAT_R16_UNORM: mtlformat = MTLPixelFormatR16Unorm; break; diff --git a/src/modules/graphics/metal/Shader.h b/src/modules/graphics/metal/Shader.h index 2b22d2132..f18cfed94 100644 --- a/src/modules/graphics/metal/Shader.h +++ b/src/modules/graphics/metal/Shader.h @@ -20,10 +20,18 @@ #pragma once +#include "libraries/xxHash/xxhash.h" #include "graphics/Shader.h" #include "graphics/Graphics.h" +#include "graphics/renderstate.h" +#include "graphics/vertex.h" #include "Metal.h" +#import + +#include +#include + namespace love { namespace graphics @@ -35,11 +43,31 @@ class Shader final : public love::graphics::Shader { public: + struct RenderPipelineKey + { + vertex::Attributes vertexAttributes; + BlendState blend; + uint64 colorRenderTargetFormats; + uint32 depthStencilFormat; + ColorChannelMask colorChannelMask; + uint8 msaa; + + RenderPipelineKey() + { + memset(this, 0, sizeof(RenderPipelineKey)); + } + + bool operator == (const RenderPipelineKey &other) const + { + return memcmp(this, &other, sizeof(RenderPipelineKey)) == 0; + } + }; + Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel); virtual ~Shader(); // Implements Shader. - void attach() override {} + void attach() override; std::string getWarnings() const override { return ""; } int getVertexAttributeIndex(const std::string &name) override { return -1; } const UniformInfo *getUniformInfo(const std::string &name) const override { return nullptr; } @@ -50,9 +78,21 @@ public: ptrdiff_t getHandle() const override { return 0; } void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override {} + id getCachedRenderPipeline(const RenderPipelineKey &key); + private: - id library; + struct RenderPipelineHasher + { + size_t operator() (const RenderPipelineKey &key) const + { + return XXH32(&key, sizeof(RenderPipelineKey), 0); + } + }; + + id functions[ShaderStage::STAGE_MAX_ENUM]; + + std::unordered_map cachedRenderPipelines; }; // Metal diff --git a/src/modules/graphics/metal/Shader.mm b/src/modules/graphics/metal/Shader.mm index 0d689e4dd..e1a004bc5 100644 --- a/src/modules/graphics/metal/Shader.mm +++ b/src/modules/graphics/metal/Shader.mm @@ -25,6 +25,9 @@ #include "libraries/glslang/glslang/Public/ShaderLang.h" #include "libraries/glslang/SPIRV/GlslangToSpv.h" #include "libraries/spirv_cross/spirv_msl.hpp" +#include "libraries/spirv_cross/spirv_reflect.hpp" + +#include namespace love { @@ -33,8 +36,62 @@ namespace graphics namespace metal { +static_assert(MAX_COLOR_RENDER_TARGETS <= 8, "Metal pipeline cache key only stores 8 render target pixel formats."); + +static MTLVertexFormat getMTLVertexFormat(vertex::DataType type, int components) +{ + // TODO + return MTLVertexFormatFloat4; +} + +static MTLBlendOperation getMTLBlendOperation(BlendOperation op) +{ + switch (op) + { + case BLENDOP_ADD: return MTLBlendOperationAdd; + case BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; + case BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; + case BLENDOP_MIN: return MTLBlendOperationMin; + case BLENDOP_MAX: return MTLBlendOperationMax; + case BLENDOP_MAX_ENUM: return MTLBlendOperationAdd; + } + return MTLBlendOperationAdd; +} + +static MTLBlendFactor getMTLBlendFactor(BlendFactor factor) +{ + switch (factor) + { + case BLENDFACTOR_ZERO: return MTLBlendFactorZero; + case BLENDFACTOR_ONE: return MTLBlendFactorOne; + case BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; + case BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; + case BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; + case BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; + case BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; + case BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; + case BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; + case BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; + case BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; + case BLENDFACTOR_MAX_ENUM: return MTLBlendFactorZero; + } + return MTLBlendFactorZero; +} + +static EShLanguage getGLSLangStage(ShaderStage::StageType stage) +{ + switch (stage) + { + case ShaderStage::STAGE_VERTEX: return EShLangVertex; + case ShaderStage::STAGE_PIXEL: return EShLangFragment; + case ShaderStage::STAGE_MAX_ENUM: return EShLangCount; + } + return EShLangCount; +} + Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) : love::graphics::Shader(vertex, pixel) + , functions() { @autoreleasepool { auto gfx = Graphics::getInstance(); @@ -54,11 +111,13 @@ Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage if (!program.link(EShMsgDefault)) { //err = "Cannot compile shader:\n\n" + std::string(program.getInfoLog()) + "\n" + std::string(program.getInfoDebugLog()); + throw love::Exception("link failed!\n"); } - for (int i = 0; i < EShLangCount; i++) + for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++) { - auto intermediate = program.getIntermediate((EShLanguage)i); + auto glslangstage = getGLSLangStage((ShaderStage::StageType) i); + auto intermediate = program.getIntermediate(glslangstage); if (intermediate == nullptr) continue; @@ -103,7 +162,13 @@ Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage NSError *err = nil; id library = [gfx->device newLibraryWithSource:nssource options:nil error:&err]; + if (library == nil && err != nil) + { + NSLog(@"errors: %@", err); + throw love::Exception("Error compiling converted Metal shader code"); + } + functions[i] = [library newFunctionWithName:library.functionNames[0]]; } catch (std::exception &e) { @@ -114,9 +179,127 @@ Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage Shader::~Shader() { @autoreleasepool { + for (int i = 0; i < ShaderStage::STAGE_MAX_ENUM; i++) + functions[i] = nil; + for (const auto &kvp : cachedRenderPipelines) + CFBridgingRelease(kvp.second); + + cachedRenderPipelines.clear(); }} +void Shader::attach() +{ + if (current != this) + { + Graphics::flushBatchedDrawsGlobal(); + current = this; + } +} + +id Shader::getCachedRenderPipeline(const RenderPipelineKey &key) +{ + auto it = cachedRenderPipelines.find(key); + + if (it != cachedRenderPipelines.end()) + return (__bridge id) it->second; + + id device = Graphics::getInstance()->device; + + MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new]; + + desc.vertexFunction = functions[ShaderStage::STAGE_VERTEX]; + desc.fragmentFunction = functions[ShaderStage::STAGE_PIXEL]; + + desc.rasterSampleCount = std::max((int) key.msaa, 1); + + for (int i = 0; i < MAX_COLOR_RENDER_TARGETS; i++) + { + PixelFormat format = (PixelFormat)((key.colorRenderTargetFormats >> (i * 8)) & 0xFF); + if (format == PIXELFORMAT_UNKNOWN) + continue; + + MTLRenderPipelineColorAttachmentDescriptor *attachment = desc.colorAttachments[i]; + + bool isSRGB = false; + MTLPixelFormat metalformat = Metal::convertPixelFormat(format, isSRGB); + + attachment.pixelFormat = metalformat; + + if (key.blend.enable) + { + attachment.blendingEnabled = YES; + attachment.sourceRGBBlendFactor = getMTLBlendFactor(key.blend.srcFactorRGB); + attachment.destinationRGBBlendFactor = getMTLBlendFactor(key.blend.dstFactorRGB); + attachment.rgbBlendOperation = getMTLBlendOperation(key.blend.operationRGB); + attachment.sourceAlphaBlendFactor = getMTLBlendFactor(key.blend.srcFactorA); + attachment.destinationAlphaBlendFactor = getMTLBlendFactor(key.blend.dstFactorA); + attachment.alphaBlendOperation = getMTLBlendOperation(key.blend.operationA); + } + + MTLColorWriteMask writeMask = MTLColorWriteMaskNone; + if (key.colorChannelMask.r) + writeMask |= MTLColorWriteMaskRed; + if (key.colorChannelMask.g) + writeMask |= MTLColorWriteMaskGreen; + if (key.colorChannelMask.b) + writeMask |= MTLColorWriteMaskBlue; + if (key.colorChannelMask.a) + writeMask |= MTLColorWriteMaskAlpha; + + attachment.writeMask = writeMask; + + desc.colorAttachments[i] = attachment; + } + + { + MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor]; + + const auto &attributes = key.vertexAttributes; + uint32 allbits = attributes.enableBits; + uint32 i = 0; + while (allbits) + { + uint32 bit = 1u << i; + + if (attributes.enableBits & bit) + { + const auto &attrib = attributes.attribs[i]; + + vertdesc.attributes[i].format = getMTLVertexFormat(attrib.type, attrib.components); + vertdesc.attributes[i].offset = attrib.offsetFromVertex; + vertdesc.attributes[i].bufferIndex = attrib.bufferIndex; + + const auto &layout = attributes.bufferLayouts[attrib.bufferIndex]; + + bool instanced = attributes.instanceBits & (1u << attrib.bufferIndex); + auto step = instanced ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex; + + vertdesc.layouts[attrib.bufferIndex].stride = layout.stride; + vertdesc.layouts[attrib.bufferIndex].stepFunction = step; + } + + i++; + allbits >>= 1; + } + + desc.vertexDescriptor = vertdesc; + } + + NSError *err = nil; + id pipeline = [device newRenderPipelineStateWithDescriptor:desc error:&err]; + + if (err != nil) + { + NSLog(@"Error creating render pipeline: %@", err); + return nil; + } + + cachedRenderPipelines[key] = CFBridgingRetain(pipeline); + + return pipeline; +} + } // metal } // graphics } // love diff --git a/src/modules/graphics/metal/ShaderStage.mm b/src/modules/graphics/metal/ShaderStage.mm index 146763280..9dcbbe7f7 100644 --- a/src/modules/graphics/metal/ShaderStage.mm +++ b/src/modules/graphics/metal/ShaderStage.mm @@ -164,7 +164,7 @@ ShaderStage::ShaderStage(love::graphics::Graphics *gfx, StageType stage, const s glslangShader->setStringsWithLengths(&csrc, &srclen, 1); int defaultversion = gles ? 300 : 330; - EProfile defaultprofile = ENoProfile; + EProfile defaultprofile = gles ? EEsProfile : ECoreProfile; bool forcedefault = false; bool forwardcompat = true; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index c7128ae69..fc4b21ddf 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1408,7 +1408,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool { case PIXELFORMAT_NORMAL: if (isGammaCorrect()) - return PIXELFORMAT_sRGBA8_UNORM; + return PIXELFORMAT_RGBA8_UNORM_sRGB; else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8_UNORM, rendertarget, readable, sRGB)) // 32-bit render targets don't have guaranteed support on GLES2. return PIXELFORMAT_RGBA4_UNORM; @@ -1425,7 +1425,7 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo { if (sRGB && format == PIXELFORMAT_RGBA8_UNORM) { - format = PIXELFORMAT_sRGBA8_UNORM; + format = PIXELFORMAT_RGBA8_UNORM_sRGB; sRGB = false; } diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index c098a67ae..3eb8576e8 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -1400,8 +1400,8 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r f.framebufferAttachments[0] = GL_COLOR_ATTACHMENT0; f.framebufferAttachments[1] = GL_NONE; - if (pixelformat == PIXELFORMAT_RGBA8_UNORM && isSRGB) - pixelformat = PIXELFORMAT_sRGBA8_UNORM; + if (isSRGB) + pixelformat = getSRGBPixelFormat(pixelformat); else if (pixelformat == PIXELFORMAT_ETC1_UNORM) { // The ETC2 format can load ETC1 textures. @@ -1435,7 +1435,7 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r f.externalformat = GL_RGBA; f.type = GL_UNSIGNED_BYTE; break; - case PIXELFORMAT_sRGBA8_UNORM: + case PIXELFORMAT_RGBA8_UNORM_sRGB: f.internalformat = GL_SRGB8_ALPHA8; f.type = GL_UNSIGNED_BYTE; if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0) @@ -1755,7 +1755,7 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r f.internalformat = f.externalformat; } - if (pixelformat != PIXELFORMAT_sRGBA8_UNORM) + if (!isPixelFormatSRGB(pixelformat)) isSRGB = false; } @@ -1767,8 +1767,8 @@ bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget, if (rendertarget && isPixelFormatCompressed(pixelformat)) return false; - if (pixelformat == PIXELFORMAT_RGBA8_UNORM && isSRGB) - pixelformat = PIXELFORMAT_sRGBA8_UNORM; + if (isSRGB) + pixelformat = getSRGBPixelFormat(pixelformat); switch (pixelformat) { @@ -1785,7 +1785,7 @@ bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget, return GLAD_VERSION_1_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8; else return true; - case PIXELFORMAT_sRGBA8_UNORM: + case PIXELFORMAT_RGBA8_UNORM_sRGB: if (rendertarget) { if (GLAD_VERSION_1_0) diff --git a/src/modules/graphics/renderstate.h b/src/modules/graphics/renderstate.h index eafd3760c..1e7712e96 100644 --- a/src/modules/graphics/renderstate.h +++ b/src/modules/graphics/renderstate.h @@ -110,24 +110,24 @@ enum CompareMode struct BlendState { - bool enable = false; BlendOperation operationRGB = BLENDOP_ADD; BlendOperation operationA = BLENDOP_ADD; BlendFactor srcFactorRGB = BLENDFACTOR_ONE; BlendFactor srcFactorA = BLENDFACTOR_ONE; BlendFactor dstFactorRGB = BLENDFACTOR_ZERO; BlendFactor dstFactorA = BLENDFACTOR_ZERO; + bool enable = false; BlendState() {} BlendState(BlendOperation opRGB, BlendOperation opA, BlendFactor srcRGB, BlendFactor srcA, BlendFactor dstRGB, BlendFactor dstA) - : enable(true) - , operationRGB(opRGB) + : operationRGB(opRGB) , operationA(opA) , srcFactorRGB(srcRGB) , srcFactorA(srcA) , dstFactorRGB(dstRGB) , dstFactorA(dstA) + , enable(true) {} bool operator == (const BlendState &b) const