mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
metal: partial implementation of pipeline states.
Add BGRA8 pixel format.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<MTLRenderPipelineState> getCachedRenderPipelineState(const PipelineState &state);
|
||||
id<MTLDepthStencilState> getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil);
|
||||
void applyRenderState(id<MTLRenderCommandEncoder> renderEncoder);
|
||||
|
||||
|
||||
@@ -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<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
|
||||
const auto &rts = states.back().renderTargets;
|
||||
if (rts.getFirstTarget().texture.get() != nullptr)
|
||||
{
|
||||
|
||||
// TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -431,50 +396,6 @@ id<MTLSamplerState> Graphics::getCachedSampler(const SamplerState &s)
|
||||
return sampler;
|
||||
}}
|
||||
|
||||
id<MTLRenderPipelineState> 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<MTLRenderPipelineState> pipestate = [device newRenderPipelineStateWithDescriptor:pipedesc error:&err];
|
||||
|
||||
return pipestate;
|
||||
}
|
||||
|
||||
id<MTLDepthStencilState> Graphics::getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil)
|
||||
{
|
||||
id<MTLDepthStencilState> state = nil;
|
||||
@@ -573,20 +494,31 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder)
|
||||
// TODO: attributes
|
||||
if (dirtyState & pipelineStateBits)
|
||||
{
|
||||
if (dirtyState & STATEBIT_BLEND)
|
||||
{
|
||||
// Shader *shader = (Shader *) state.shader.get();
|
||||
Shader *shader = (Shader *) Shader::current;
|
||||
id<MTLRenderPipelineState> 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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <Metal/MTLRenderPipeline.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
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<MTLRenderPipelineState> getCachedRenderPipeline(const RenderPipelineKey &key);
|
||||
|
||||
private:
|
||||
|
||||
id<MTLLibrary> library;
|
||||
struct RenderPipelineHasher
|
||||
{
|
||||
size_t operator() (const RenderPipelineKey &key) const
|
||||
{
|
||||
return XXH32(&key, sizeof(RenderPipelineKey), 0);
|
||||
}
|
||||
};
|
||||
|
||||
id<MTLFunction> functions[ShaderStage::STAGE_MAX_ENUM];
|
||||
|
||||
std::unordered_map<RenderPipelineKey, const void *, RenderPipelineHasher> cachedRenderPipelines;
|
||||
|
||||
}; // Metal
|
||||
|
||||
|
||||
@@ -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 <algorithm>
|
||||
|
||||
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<MTLLibrary> 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<MTLRenderPipelineState> Shader::getCachedRenderPipeline(const RenderPipelineKey &key)
|
||||
{
|
||||
auto it = cachedRenderPipelines.find(key);
|
||||
|
||||
if (it != cachedRenderPipelines.end())
|
||||
return (__bridge id<MTLRenderPipelineState>) it->second;
|
||||
|
||||
id<MTLDevice> 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<MTLRenderPipelineState> 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user