mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
The bones of what will become a Metal backend.
This isn't anywhere close to usable (not even a blank screen will work), let alone complete. It's about 20% done.
This commit is contained in:
@@ -133,10 +133,10 @@ bool Shader::validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err)
|
||||
glslang::TProgram program;
|
||||
|
||||
if (vertex != nullptr)
|
||||
program.addShader(vertex->getGLSLangShader());
|
||||
program.addShader(vertex->getGLSLangValidationShader());
|
||||
|
||||
if (pixel != nullptr)
|
||||
program.addShader(pixel->getGLSLangShader());
|
||||
program.addShader(pixel->getGLSLangValidationShader());
|
||||
|
||||
if (!program.link(EShMsgDefault))
|
||||
{
|
||||
|
||||
@@ -140,7 +140,7 @@ ShaderStage::ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl
|
||||
: stageType(stage)
|
||||
, source(glsl)
|
||||
, cacheKey(cachekey)
|
||||
, glslangShader(nullptr)
|
||||
, glslangValidationShader(nullptr)
|
||||
{
|
||||
EShLanguage glslangStage = EShLangCount;
|
||||
if (stage == STAGE_VERTEX)
|
||||
@@ -150,7 +150,7 @@ ShaderStage::ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl
|
||||
else
|
||||
throw love::Exception("Cannot compile shader stage: unknown stage type.");
|
||||
|
||||
glslangShader = new glslang::TShader(glslangStage);
|
||||
auto glslangShader = new glslang::TShader(glslangStage);
|
||||
|
||||
bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
|
||||
int defaultversion = gles ? 100 : 120;
|
||||
@@ -178,6 +178,8 @@ ShaderStage::ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl
|
||||
delete glslangShader;
|
||||
throw love::Exception("%s", err.c_str());
|
||||
}
|
||||
|
||||
glslangValidationShader = glslangShader;
|
||||
}
|
||||
|
||||
ShaderStage::~ShaderStage()
|
||||
@@ -189,7 +191,7 @@ ShaderStage::~ShaderStage()
|
||||
gfx->cleanupCachedShaderStage(stageType, cacheKey);
|
||||
}
|
||||
|
||||
delete glslangShader;
|
||||
delete glslangValidationShader;
|
||||
}
|
||||
|
||||
bool ShaderStage::getConstant(const char *in, StageType &out)
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include "common/Object.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "Volatile.h"
|
||||
#include "Resource.h"
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -40,7 +39,7 @@ namespace graphics
|
||||
|
||||
class Graphics;
|
||||
|
||||
class ShaderStage : public love::Object, public Volatile, public Resource
|
||||
class ShaderStage : public love::Object
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -54,10 +53,12 @@ public:
|
||||
ShaderStage(Graphics *gfx, StageType stage, const std::string &glsl, bool gles, const std::string &cachekey);
|
||||
virtual ~ShaderStage();
|
||||
|
||||
virtual ptrdiff_t getHandle() const = 0;
|
||||
|
||||
StageType getStageType() const { return stageType; }
|
||||
const std::string &getSource() const { return source; }
|
||||
const std::string &getWarnings() const { return warnings; }
|
||||
glslang::TShader *getGLSLangShader() const { return glslangShader; }
|
||||
glslang::TShader *getGLSLangValidationShader() const { return glslangValidationShader; }
|
||||
|
||||
static bool getConstant(const char *in, StageType &out);
|
||||
static bool getConstant(StageType in, const char *&out);
|
||||
@@ -71,7 +72,7 @@ private:
|
||||
StageType stageType;
|
||||
std::string source;
|
||||
std::string cacheKey;
|
||||
glslang::TShader *glslangShader;
|
||||
glslang::TShader *glslangValidationShader;
|
||||
|
||||
static StringMap<StageType, STAGE_MAX_ENUM>::Entry stageNameEntries[];
|
||||
static StringMap<StageType, STAGE_MAX_ENUM> stageNames;
|
||||
@@ -85,12 +86,8 @@ public:
|
||||
ShaderStageForValidation(Graphics *gfx, StageType stage, const std::string &glsl, bool gles)
|
||||
: ShaderStage(gfx, stage, glsl, gles, "")
|
||||
{}
|
||||
|
||||
virtual ~ShaderStageForValidation() {}
|
||||
|
||||
ptrdiff_t getHandle() const override { return 0; }
|
||||
bool loadVolatile() override { return true; }
|
||||
void unloadVolatile() override { }
|
||||
|
||||
}; // ShaderStageForValidation
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "graphics/Buffer.h"
|
||||
#include "Metal.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
class Buffer final : public love::graphics::Buffer
|
||||
{
|
||||
public:
|
||||
|
||||
Buffer(id<MTLDevice> device, size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags);
|
||||
virtual ~Buffer();
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
void setMappedRangeModified(size_t offset, size_t size) override;
|
||||
void fill(size_t offset, size_t size, const void *data) override;
|
||||
ptrdiff_t getHandle() const override { return (ptrdiff_t) buffer; }
|
||||
|
||||
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
|
||||
|
||||
private:
|
||||
|
||||
id<MTLBuffer> buffer;
|
||||
char *memoryMap;
|
||||
|
||||
NSRange mappedRange;
|
||||
|
||||
}; // Buffer
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#import "Buffer.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
Buffer::Buffer(id<MTLDevice> device, size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
|
||||
: love::graphics::Buffer(size, type, usage, mapflags)
|
||||
, mappedRange()
|
||||
{ @autoreleasepool {
|
||||
MTLResourceOptions opts = MTLResourceStorageModeManaged;
|
||||
buffer = [device newBufferWithLength:size options:opts];
|
||||
|
||||
// TODO: synchronization etc
|
||||
memoryMap = (char *) buffer.contents;
|
||||
|
||||
if (data != nullptr)
|
||||
{
|
||||
memcpy(map(), data, size);
|
||||
unmap();
|
||||
}
|
||||
}}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{ @autoreleasepool {
|
||||
buffer = nil;
|
||||
}}
|
||||
|
||||
void *Buffer::map()
|
||||
{
|
||||
return memoryMap;
|
||||
}
|
||||
|
||||
void Buffer::unmap()
|
||||
{ @autoreleasepool {
|
||||
[buffer didModifyRange:{0, size}];
|
||||
}}
|
||||
|
||||
void Buffer::setMappedRangeModified(size_t offset, size_t size)
|
||||
{
|
||||
mappedRange = NSIntersectionRange(mappedRange, {offset, size});
|
||||
}
|
||||
|
||||
void Buffer::fill(size_t offset, size_t size, const void *data)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/Color.h"
|
||||
#include "common/int.h"
|
||||
#include "graphics/Canvas.h"
|
||||
#include "Metal.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
class Canvas final : public love::graphics::Canvas
|
||||
{
|
||||
public:
|
||||
|
||||
Canvas(id<MTLDevice> device, const Settings &settings);
|
||||
virtual ~Canvas();
|
||||
|
||||
// Implements Texture.
|
||||
void setFilter(const Texture::Filter &f) override;
|
||||
bool setWrap(const Texture::Wrap &w) override;
|
||||
bool setMipmapSharpness(float sharpness) override;
|
||||
void setDepthSampleMode(Optional<CompareMode> mode) override;
|
||||
ptrdiff_t getHandle() const override { return (ptrdiff_t) texture; }
|
||||
|
||||
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
|
||||
void generateMipmaps() override;
|
||||
|
||||
int getMSAA() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptrdiff_t getRenderTargetHandle() const override
|
||||
{
|
||||
return (ptrdiff_t) texture;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
id<MTLTexture> texture;
|
||||
id<MTLTexture> resolveTexture;
|
||||
|
||||
}; // Canvas
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Canvas.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
Canvas::Canvas(id<MTLDevice> device, const Settings &settings)
|
||||
: love::graphics::Canvas(settings)
|
||||
{ @autoreleasepool {
|
||||
MTLTextureDescriptor *desc = [MTLTextureDescriptor new];
|
||||
|
||||
// TODO: sampleCount validation
|
||||
desc.sampleCount = getRequestedMSAA();
|
||||
|
||||
desc.width = pixelWidth;
|
||||
desc.height = pixelHeight;
|
||||
desc.depth = depth;
|
||||
desc.arrayLength = layers;
|
||||
desc.mipmapLevelCount = mipmapCount;
|
||||
desc.textureType = Metal::getTextureType(texType, getRequestedMSAA());
|
||||
|
||||
bool sRGB = false;
|
||||
desc.pixelFormat = Metal::convertPixelFormat(format, sRGB);
|
||||
|
||||
desc.storageMode = MTLStorageModePrivate;
|
||||
desc.usage = MTLTextureUsageRenderTarget;
|
||||
|
||||
if (isReadable())
|
||||
desc.usage |= MTLTextureUsageShaderRead;
|
||||
|
||||
texture = [device newTextureWithDescriptor:desc];
|
||||
|
||||
if (texture == nil)
|
||||
throw love::Exception("Out of graphics memory.");
|
||||
|
||||
// TODO: initialize texture to transparent black.
|
||||
}}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{ @autoreleasepool {
|
||||
texture = nil;
|
||||
}}
|
||||
|
||||
void Canvas::generateMipmaps()
|
||||
{ @autoreleasepool {
|
||||
id<MTLBlitCommandEncoder> encoder = Graphics::getInstance()->useBlitEncoder();
|
||||
[encoder generateMipmapsForTexture:texture];
|
||||
}}
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "graphics/Graphics.h"
|
||||
#include "Metal.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
class Graphics final : public love::graphics::Graphics
|
||||
{
|
||||
public:
|
||||
|
||||
Graphics();
|
||||
virtual ~Graphics();
|
||||
|
||||
// Implements Module.
|
||||
const char *getName() const override { return "love.graphics.metal"; }
|
||||
|
||||
love::graphics::Image *newImage(const Image::Slices &data, const Image::Settings &settings) override;
|
||||
love::graphics::Image *newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings) override;
|
||||
love::graphics::Canvas *newCanvas(const Canvas::Settings &settings) override;
|
||||
love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
|
||||
|
||||
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
|
||||
bool setMode(int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;
|
||||
void unSetMode() override;
|
||||
|
||||
void setActive(bool active) override;
|
||||
|
||||
void draw(const DrawCommand &cmd) override;
|
||||
void draw(const DrawIndexedCommand &cmd) override;
|
||||
void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, Texture *texture) override;
|
||||
|
||||
void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override;
|
||||
void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) override;
|
||||
|
||||
void discard(const std::vector<bool> &colorbuffers, bool depthstencil) override;
|
||||
|
||||
void present(void *screenshotCallbackData) override;
|
||||
|
||||
void setColor(Colorf c) override;
|
||||
|
||||
void setScissor(const Rect &rect) override;
|
||||
void setScissor() override;
|
||||
|
||||
void drawToStencilBuffer(StencilAction action, int value) override;
|
||||
void stopDrawToStencilBuffer() override;
|
||||
|
||||
void setStencilTest(CompareMode compare, int value) override;
|
||||
|
||||
void setDepthMode(CompareMode compare, bool write) override;
|
||||
|
||||
void setFrontFaceWinding(vertex::Winding winding) override;
|
||||
|
||||
void setColorMask(ColorChannelMask mask) override;
|
||||
|
||||
void setBlendState(const BlendState &state) override;
|
||||
|
||||
void setPointSize(float size) override;
|
||||
|
||||
void setWireframe(bool enable) override;
|
||||
|
||||
bool isCanvasFormatSupported(PixelFormat format) const override;
|
||||
bool isCanvasFormatSupported(PixelFormat format, bool readable) const override;
|
||||
bool isImageFormatSupported(PixelFormat format, bool sRGB) const override;
|
||||
Renderer getRenderer() const override;
|
||||
RendererInfo getRendererInfo() const override;
|
||||
|
||||
Shader::Language getShaderLanguageTarget() const override;
|
||||
|
||||
id<MTLCommandBuffer> useCommandBuffer();
|
||||
id<MTLCommandBuffer> getCommandBuffer() const { return commandBuffer; }
|
||||
void submitCommandBuffer();
|
||||
|
||||
id<MTLRenderCommandEncoder> useRenderEncoder();
|
||||
id<MTLRenderCommandEncoder> getRenderEncoder() const { return renderEncoder; }
|
||||
void submitRenderEncoder();
|
||||
|
||||
id<MTLBlitCommandEncoder> useBlitEncoder();
|
||||
id<MTLBlitCommandEncoder> getBlitEncoder() const { return blitEncoder; }
|
||||
void submitBlitEncoder();
|
||||
|
||||
id<MTLSamplerState> getCachedSampler(const Texture::Filter &f, const Texture::Wrap &w, float maxAnisotropy, Optional<CompareMode> depthSampleMode);
|
||||
|
||||
static Graphics *getInstance() { return Module::getInstance<Graphics>(M_GRAPHICS); }
|
||||
|
||||
id<MTLDevice> device;
|
||||
|
||||
private:
|
||||
|
||||
enum StateType
|
||||
{
|
||||
STATE_BLEND,
|
||||
STATE_VIEWPORT,
|
||||
STATE_SCISSOR,
|
||||
STATE_STENCIL,
|
||||
STATE_DEPTH,
|
||||
STATE_SHADER,
|
||||
STATE_COLORMASK,
|
||||
STATE_CULLMODE,
|
||||
STATE_FACEWINDING,
|
||||
STATE_WIREFRAME,
|
||||
};
|
||||
|
||||
enum StateBit
|
||||
{
|
||||
STATEBIT_BLEND = 1 << STATE_BLEND,
|
||||
STATEBIT_VIEWPORT = 1 << STATE_VIEWPORT,
|
||||
STATEBIT_SCISSOR = 1 << STATE_SCISSOR,
|
||||
STATEBIT_STENCIL = 1 << STATE_STENCIL,
|
||||
STATEBIT_DEPTH = 1 << STATE_DEPTH,
|
||||
STATEBIT_SHADER = 1 << STATE_SHADER,
|
||||
STATEBIT_COLORMASK = 1 << STATE_COLORMASK,
|
||||
STATEBIT_CULLMODE = 1 << STATE_CULLMODE,
|
||||
STATEBIT_FACEWINDING = 1 << STATE_FACEWINDING,
|
||||
STATEBIT_WIREFRAME = 1 << STATE_WIREFRAME,
|
||||
STATEBIT_ALL = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
struct RenderState
|
||||
{
|
||||
Rect viewport = {0, 0, 0, 0};
|
||||
ScissorState scissor;
|
||||
BlendState blend;
|
||||
DepthState depth;
|
||||
StencilState stencil;
|
||||
ColorChannelMask colorChannelMask;
|
||||
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;
|
||||
void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) override;
|
||||
void initCapabilities() override;
|
||||
void getAPIStats(int &shaderswitches) const override;
|
||||
|
||||
void endPass();
|
||||
|
||||
id<MTLRenderPipelineState> getCachedRenderPipelineState(const PipelineState &state);
|
||||
id<MTLDepthStencilState> getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil);
|
||||
void applyRenderState(id<MTLRenderCommandEncoder> renderEncoder);
|
||||
|
||||
id<MTLCommandQueue> commandQueue;
|
||||
|
||||
id<MTLCommandBuffer> commandBuffer;
|
||||
id<MTLRenderCommandEncoder> renderEncoder;
|
||||
id<MTLBlitCommandEncoder> blitEncoder;
|
||||
|
||||
MTLRenderPassDescriptor *passDesc;
|
||||
|
||||
uint32 dirtyRenderState;
|
||||
bool windowHasStencil;
|
||||
|
||||
}; // Graphics
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,940 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Graphics.h"
|
||||
#include "StreamBuffer.h"
|
||||
#include "Buffer.h"
|
||||
#include "Canvas.h"
|
||||
#include "Image.h"
|
||||
#include "Shader.h"
|
||||
#include "window/Window.h"
|
||||
#include "image/Image.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
static MTLSamplerMinMagFilter getMTLSamplerFilter(Texture::FilterMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case Texture::FILTER_NONE: return MTLSamplerMinMagFilterLinear;
|
||||
case Texture::FILTER_LINEAR: return MTLSamplerMinMagFilterLinear;
|
||||
case Texture::FILTER_NEAREST: return MTLSamplerMinMagFilterNearest;
|
||||
case Texture::FILTER_MAX_ENUM: return MTLSamplerMinMagFilterLinear;
|
||||
}
|
||||
return MTLSamplerMinMagFilterLinear;
|
||||
}
|
||||
|
||||
static MTLSamplerMipFilter getMTLSamplerMipFilter(Texture::FilterMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case Texture::FILTER_NONE: return MTLSamplerMipFilterNotMipmapped;
|
||||
case Texture::FILTER_LINEAR: return MTLSamplerMipFilterLinear;
|
||||
case Texture::FILTER_NEAREST: return MTLSamplerMipFilterNearest;
|
||||
case Texture::FILTER_MAX_ENUM: return MTLSamplerMipFilterNotMipmapped;
|
||||
}
|
||||
return MTLSamplerMipFilterNotMipmapped;
|
||||
}
|
||||
|
||||
static MTLSamplerAddressMode getMTLSamplerAddressMode(Texture::WrapMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case Texture::WRAP_CLAMP: return MTLSamplerAddressModeClampToEdge;
|
||||
case Texture::WRAP_CLAMP_ZERO: return MTLSamplerAddressModeClampToZero;
|
||||
#ifdef LOVE_MACOS
|
||||
case Texture::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToBorderColor;
|
||||
#else
|
||||
case Texture::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToZero;
|
||||
#endif
|
||||
case Texture::WRAP_REPEAT: return MTLSamplerAddressModeRepeat;
|
||||
case Texture::WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat;
|
||||
case Texture::WRAP_MAX_ENUM: return MTLSamplerAddressModeClampToEdge;
|
||||
}
|
||||
return MTLSamplerAddressModeClampToEdge;
|
||||
}
|
||||
|
||||
static MTLCompareFunction getMTLCompareFunction(CompareMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case COMPARE_LESS: return MTLCompareFunctionLess;
|
||||
case COMPARE_LEQUAL: return MTLCompareFunctionLessEqual;
|
||||
case COMPARE_EQUAL: return MTLCompareFunctionEqual;
|
||||
case COMPARE_GEQUAL: return MTLCompareFunctionGreaterEqual;
|
||||
case COMPARE_GREATER: return MTLCompareFunctionGreater;
|
||||
case COMPARE_NOTEQUAL: return MTLCompareFunctionNotEqual;
|
||||
case COMPARE_ALWAYS: return MTLCompareFunctionAlways;
|
||||
case COMPARE_NEVER: return MTLCompareFunctionNever;
|
||||
case COMPARE_MAX_ENUM: return MTLCompareFunctionNever;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
Graphics::Graphics()
|
||||
: device(nil)
|
||||
, commandQueue(nil)
|
||||
, commandBuffer(nil)
|
||||
, renderEncoder(nil)
|
||||
, blitEncoder(nil)
|
||||
, passDesc(nil)
|
||||
, dirtyRenderState(STATEBIT_ALL)
|
||||
, windowHasStencil(false)
|
||||
{ @autoreleasepool {
|
||||
device = MTLCreateSystemDefaultDevice();
|
||||
if (device == nil)
|
||||
throw love::Exception("Metal is not supported on this system.");
|
||||
|
||||
commandQueue = [device newCommandQueue];
|
||||
passDesc = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
|
||||
initCapabilities();
|
||||
|
||||
auto window = Module::getInstance<love::window::Window>(M_WINDOW);
|
||||
|
||||
if (window != nullptr)
|
||||
{
|
||||
window->setGraphics(this);
|
||||
|
||||
if (window->isOpen())
|
||||
{
|
||||
int w, h;
|
||||
love::window::WindowSettings settings;
|
||||
window->getWindow(w, h, settings);
|
||||
|
||||
double dpiW = w;
|
||||
double dpiH = h;
|
||||
window->windowToDPICoords(&dpiW, &dpiH);
|
||||
|
||||
setMode((int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), settings.stencil);
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
Graphics::~Graphics()
|
||||
{ @autoreleasepool {
|
||||
submitCommandBuffer();
|
||||
passDesc = nil;
|
||||
commandQueue = nil;
|
||||
device = nil;
|
||||
}}
|
||||
|
||||
love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferType type, size_t size)
|
||||
{
|
||||
return CreateStreamBuffer(device, type, size);
|
||||
}
|
||||
|
||||
love::graphics::Image *Graphics::newImage(const Image::Slices &data, const Image::Settings &settings)
|
||||
{
|
||||
return new Image(device, data, settings);
|
||||
}
|
||||
|
||||
love::graphics::Image *Graphics::newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings)
|
||||
{
|
||||
return new Image(device, textype, format, width, height, slices, settings);
|
||||
}
|
||||
|
||||
love::graphics::Canvas *Graphics::newCanvas(const Canvas::Settings &settings)
|
||||
{
|
||||
return new Canvas(device, 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);
|
||||
}
|
||||
|
||||
love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
|
||||
{
|
||||
return new Shader(vertex, pixel);
|
||||
}
|
||||
|
||||
love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
|
||||
{
|
||||
return new Buffer(device, size, data, type, usage, mapflags);
|
||||
}
|
||||
|
||||
void Graphics::initCapabilities()
|
||||
{
|
||||
int msaa = 1;
|
||||
const int checkmsaa[] = {32, 16, 8, 4, 2};
|
||||
for (int samples : checkmsaa)
|
||||
{
|
||||
if ([device supportsTextureSampleCount:samples])
|
||||
{
|
||||
msaa = samples;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
capabilities.features[FEATURE_MULTI_CANVAS_FORMATS] = true;
|
||||
capabilities.features[FEATURE_CLAMP_ZERO] = true;
|
||||
capabilities.features[FEATURE_BLENDMINMAX] = true;
|
||||
capabilities.features[FEATURE_LIGHTEN] = true;
|
||||
capabilities.features[FEATURE_FULL_NPOT] = true;
|
||||
capabilities.features[FEATURE_PIXEL_SHADER_HIGHP] = true;
|
||||
capabilities.features[FEATURE_SHADER_DERIVATIVES] = true;
|
||||
capabilities.features[FEATURE_GLSL3] = true;
|
||||
capabilities.features[FEATURE_GLSL4] = true;
|
||||
capabilities.features[FEATURE_INSTANCING] = true;
|
||||
static_assert(FEATURE_MAX_ENUM == 10, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
|
||||
|
||||
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||
capabilities.limits[LIMIT_POINT_SIZE] = 511;
|
||||
capabilities.limits[LIMIT_TEXTURE_SIZE] = 16384; // TODO
|
||||
capabilities.limits[LIMIT_TEXTURE_LAYERS] = 2048;
|
||||
capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = 2048;
|
||||
capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = 16384; // TODO
|
||||
capabilities.limits[LIMIT_MULTI_CANVAS] = 8; // TODO
|
||||
capabilities.limits[LIMIT_CANVAS_MSAA] = msaa;
|
||||
capabilities.limits[LIMIT_ANISOTROPY] = 16.0f;
|
||||
static_assert(LIMIT_MAX_ENUM == 8, "Graphics::initCapabilities must be updated when adding a new system limit!");
|
||||
|
||||
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||
capabilities.textureTypes[i] = true;
|
||||
}
|
||||
|
||||
void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->pixelWidth = pixelwidth;
|
||||
this->pixelHeight = pixelheight;
|
||||
|
||||
if (!isCanvasActive())
|
||||
{
|
||||
dirtyRenderState |= STATEBIT_VIEWPORT | STATEBIT_SCISSOR;
|
||||
|
||||
// Set up the projection matrix
|
||||
projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f);
|
||||
}
|
||||
}
|
||||
|
||||
bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
|
||||
this->windowHasStencil = windowhasstencil;
|
||||
|
||||
setViewportSize(width, height, pixelwidth, pixelheight);
|
||||
|
||||
created = true;
|
||||
|
||||
if (streamBufferState.vb[0] == nullptr)
|
||||
{
|
||||
// Initial sizes that should be good enough for most cases. It will
|
||||
// resize to fit if needed, later.
|
||||
streamBufferState.vb[0] = CreateStreamBuffer(device, BUFFER_VERTEX, 1024 * 1024 * 1);
|
||||
streamBufferState.vb[1] = CreateStreamBuffer(device, BUFFER_VERTEX, 256 * 1024 * 1);
|
||||
streamBufferState.indexBuffer = CreateStreamBuffer(device, BUFFER_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
|
||||
}
|
||||
|
||||
createQuadIndexBuffer();
|
||||
|
||||
// Restore the graphics state.
|
||||
restoreState(states.back());
|
||||
|
||||
int gammacorrect = isGammaCorrect() ? 1 : 0;
|
||||
Shader::Language target = getShaderLanguageTarget();
|
||||
|
||||
// We always need a default shader.
|
||||
for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
void Graphics::unSetMode()
|
||||
{
|
||||
if (!isCreated())
|
||||
return;
|
||||
|
||||
flushStreamDraws();
|
||||
|
||||
submitCommandBuffer();
|
||||
|
||||
for (auto temp : temporaryCanvases)
|
||||
temp.canvas->release();
|
||||
|
||||
temporaryCanvases.clear();
|
||||
|
||||
created = false;
|
||||
}
|
||||
|
||||
void Graphics::setActive(bool enable)
|
||||
{
|
||||
flushStreamDraws();
|
||||
active = enable;
|
||||
}
|
||||
|
||||
id<MTLCommandBuffer> Graphics::useCommandBuffer()
|
||||
{
|
||||
if (commandBuffer == nil)
|
||||
commandBuffer = [commandQueue commandBuffer];
|
||||
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
void Graphics::submitCommandBuffer()
|
||||
{
|
||||
submitRenderEncoder();
|
||||
submitBlitEncoder();
|
||||
|
||||
if (commandBuffer != nil)
|
||||
{
|
||||
[commandBuffer commit];
|
||||
commandBuffer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
id<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
|
||||
{
|
||||
if (renderEncoder == nil)
|
||||
{
|
||||
submitBlitEncoder();
|
||||
renderEncoder = [useCommandBuffer() renderCommandEncoderWithDescriptor:passDesc];
|
||||
dirtyRenderState = STATEBIT_ALL;
|
||||
}
|
||||
|
||||
return renderEncoder;
|
||||
}
|
||||
|
||||
void Graphics::submitRenderEncoder()
|
||||
{
|
||||
if (renderEncoder != nil)
|
||||
{
|
||||
[renderEncoder endEncoding];
|
||||
renderEncoder = nil;
|
||||
}
|
||||
}
|
||||
|
||||
id<MTLBlitCommandEncoder> Graphics::useBlitEncoder()
|
||||
{
|
||||
if (blitEncoder == nil)
|
||||
{
|
||||
submitRenderEncoder();
|
||||
blitEncoder = [useCommandBuffer() blitCommandEncoder];
|
||||
}
|
||||
|
||||
return blitEncoder;
|
||||
}
|
||||
|
||||
void Graphics::submitBlitEncoder()
|
||||
{
|
||||
if (blitEncoder != nil)
|
||||
{
|
||||
[blitEncoder endEncoding];
|
||||
blitEncoder = nil;
|
||||
}
|
||||
}
|
||||
|
||||
id<MTLSamplerState> Graphics::getCachedSampler(const Texture::Filter &f, const Texture::Wrap &w, float maxAnisotropy, Optional<CompareMode> depthSampleMode)
|
||||
{ @autoreleasepool {
|
||||
id<MTLSamplerState> sampler = nil;
|
||||
|
||||
{
|
||||
MTLSamplerDescriptor *desc = [MTLSamplerDescriptor new];
|
||||
|
||||
desc.minFilter = getMTLSamplerFilter(f.min);
|
||||
desc.magFilter = getMTLSamplerFilter(f.mag);
|
||||
desc.mipFilter = getMTLSamplerMipFilter(f.mipmap);
|
||||
desc.maxAnisotropy = std::max(1.0f, std::min(maxAnisotropy, 16.0f));
|
||||
|
||||
desc.sAddressMode = getMTLSamplerAddressMode(w.s);
|
||||
desc.tAddressMode = getMTLSamplerAddressMode(w.t);
|
||||
desc.rAddressMode = getMTLSamplerAddressMode(w.r);
|
||||
|
||||
#ifdef LOVE_MACOS
|
||||
desc.borderColor = MTLSamplerBorderColorOpaqueWhite;
|
||||
#endif
|
||||
|
||||
if (depthSampleMode.hasValue)
|
||||
desc.compareFunction = getMTLCompareFunction(depthSampleMode.value);
|
||||
|
||||
sampler = [device newSamplerStateWithDescriptor:desc];
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
{
|
||||
MTLStencilDescriptor *stencildesc = [MTLStencilDescriptor new];
|
||||
|
||||
stencildesc.stencilCompareFunction = getMTLCompareFunction(stencil.compare);
|
||||
stencildesc.stencilFailureOperation = MTLStencilOperationKeep;
|
||||
stencildesc.depthFailureOperation = MTLStencilOperationKeep;
|
||||
stencildesc.depthStencilPassOperation = MTLStencilOperationKeep; // TODO
|
||||
stencildesc.readMask = stencil.readMask;
|
||||
stencildesc.writeMask = stencil.writeMask;
|
||||
|
||||
MTLDepthStencilDescriptor *desc = [MTLDepthStencilDescriptor new];
|
||||
|
||||
desc.depthCompareFunction = getMTLCompareFunction(depth.compare);
|
||||
desc.depthWriteEnabled = depth.write;
|
||||
desc.frontFaceStencil = stencildesc;
|
||||
desc.backFaceStencil = stencildesc;
|
||||
|
||||
state = [device newDepthStencilStateWithDescriptor:desc];
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder)
|
||||
{
|
||||
const uint32 pipelineStateBits = STATEBIT_SHADER | STATEBIT_BLEND | STATEBIT_COLORMASK;
|
||||
|
||||
uint32 dirtyState = dirtyRenderState;
|
||||
const auto &state = states.back();
|
||||
|
||||
if (dirtyState & (STATEBIT_VIEWPORT | STATEBIT_SCISSOR))
|
||||
{
|
||||
int rtw = 0;
|
||||
int rth = 0;
|
||||
|
||||
const auto &rt = state.renderTargets.getFirstTarget();
|
||||
if (rt.canvas.get())
|
||||
{
|
||||
rtw = rt.canvas->getPixelWidth();
|
||||
rth = rt.canvas->getPixelHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
rtw = getPixelWidth();
|
||||
rth = getPixelHeight();
|
||||
}
|
||||
|
||||
if (dirtyState & STATEBIT_VIEWPORT)
|
||||
{
|
||||
MTLViewport view;
|
||||
view.originX = 0.0;
|
||||
view.originY = 0.0;
|
||||
view.width = rtw;
|
||||
view.height = rth;
|
||||
view.znear = 0.0;
|
||||
view.zfar = 1.0;
|
||||
[encoder setViewport:view];
|
||||
}
|
||||
|
||||
MTLScissorRect rect = {0, 0, (NSUInteger)rtw, (NSUInteger)rth};
|
||||
|
||||
if (state.scissor)
|
||||
{
|
||||
// TODO: clamping
|
||||
double dpiscale = getCurrentDPIScale();
|
||||
rect.x = (NSUInteger)(state.scissorRect.x*dpiscale);
|
||||
rect.y = (NSUInteger)(state.scissorRect.y*dpiscale);
|
||||
rect.width = (NSUInteger)(state.scissorRect.w*dpiscale);
|
||||
rect.height = (NSUInteger)(state.scissorRect.h*dpiscale);
|
||||
}
|
||||
|
||||
[encoder setScissorRect:rect];
|
||||
}
|
||||
|
||||
if (dirtyState & STATEBIT_FACEWINDING)
|
||||
{
|
||||
auto winding = state.winding == vertex::WINDING_CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
|
||||
[encoder setFrontFacingWinding:winding];
|
||||
}
|
||||
|
||||
if (dirtyState & STATEBIT_WIREFRAME)
|
||||
{
|
||||
auto mode = state.wireframe ? MTLTriangleFillModeLines : MTLTriangleFillModeFill;
|
||||
[encoder setTriangleFillMode:mode];
|
||||
}
|
||||
|
||||
if (dirtyState & STATEBIT_CULLMODE)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
// TODO: attributes
|
||||
if (dirtyState & pipelineStateBits)
|
||||
{
|
||||
if (dirtyState & STATEBIT_BLEND)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (dirtyState & STATEBIT_SHADER)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (dirtyState & STATEBIT_COLORMASK)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (dirtyState & (STATEBIT_DEPTH | STATEBIT_STENCIL))
|
||||
{
|
||||
// id<MTLDepthStencilState> dsstate = getCachedDepthStencilState(<#const DepthState &depth#>, <#const StencilState &stencil#>)
|
||||
}
|
||||
|
||||
dirtyRenderState = 0;
|
||||
}
|
||||
|
||||
void Graphics::draw(const DrawCommand &cmd)
|
||||
{ @autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> encoder = useRenderEncoder();
|
||||
|
||||
applyRenderState(encoder);
|
||||
|
||||
// TODO: vertex attributes
|
||||
|
||||
id<MTLTexture> texture = (__bridge id<MTLTexture>)(void *) cmd.texture->getHandle();
|
||||
[encoder setFragmentTexture:texture atIndex:0];
|
||||
|
||||
[encoder setCullMode:MTLCullModeNone];
|
||||
|
||||
[encoder drawPrimitives:MTLPrimitiveTypeTriangle
|
||||
vertexStart:cmd.vertexStart
|
||||
vertexCount:cmd.vertexCount
|
||||
instanceCount:cmd.instanceCount];
|
||||
}}
|
||||
|
||||
void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
{ @autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> encoder = useRenderEncoder();
|
||||
|
||||
applyRenderState(encoder);
|
||||
|
||||
id<MTLTexture> texture = (__bridge id<MTLTexture>)(void *) cmd.texture->getHandle();
|
||||
[encoder setFragmentTexture:texture atIndex:0];
|
||||
|
||||
[encoder setCullMode:MTLCullModeNone];
|
||||
|
||||
auto indexType = cmd.indexType == INDEX_UINT32 ? MTLIndexTypeUInt32 : MTLIndexTypeUInt16;
|
||||
|
||||
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
|
||||
indexCount:cmd.indexCount
|
||||
indexType:indexType
|
||||
indexBuffer:(__bridge id<MTLBuffer>)(void*)cmd.indexBuffer->getHandle()
|
||||
indexBufferOffset:cmd.indexBufferOffset
|
||||
instanceCount:cmd.instanceCount];
|
||||
}}
|
||||
|
||||
void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, Texture *texture)
|
||||
{ @autoreleasepool {
|
||||
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
|
||||
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = useRenderEncoder();
|
||||
|
||||
applyRenderState(encoder);
|
||||
|
||||
id<MTLTexture> tex = (__bridge id<MTLTexture>)(void *) texture->getHandle();
|
||||
[encoder setFragmentTexture:tex atIndex:0];
|
||||
|
||||
[encoder setCullMode:MTLCullModeNone];
|
||||
|
||||
id<MTLBuffer> ib = (__bridge id<MTLBuffer>)(void *) quadIndexBuffer->getHandle();
|
||||
|
||||
// TODO: Set vertex buffers/attributes
|
||||
// TODO: support for iOS devices that don't support base vertex.
|
||||
|
||||
int basevertex = start * 4;
|
||||
|
||||
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
|
||||
{
|
||||
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
|
||||
|
||||
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
|
||||
indexCount:quadcount * 6
|
||||
indexType:MTLIndexTypeUInt16
|
||||
indexBuffer:ib
|
||||
indexBufferOffset:0
|
||||
instanceCount:1
|
||||
baseVertex:basevertex
|
||||
baseInstance:0];
|
||||
|
||||
++drawCalls;
|
||||
|
||||
basevertex += quadcount * 4;
|
||||
}
|
||||
}}
|
||||
|
||||
void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas)
|
||||
{
|
||||
const DisplayState &state = states.back();
|
||||
// TODO
|
||||
|
||||
flushStreamDraws();
|
||||
endPass();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Graphics::endPass()
|
||||
{
|
||||
auto &rts = states.back().renderTargets;
|
||||
love::graphics::Canvas *depthstencil = rts.depthStencil.canvas.get();
|
||||
|
||||
// Discard the depth/stencil buffer if we're using an internal cached one.
|
||||
if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
|
||||
discard({}, true);
|
||||
|
||||
// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
|
||||
// don't have to worry about resolving to slices.
|
||||
if (rts.colors.size() > 0 && rts.colors[0].canvas->getMSAA() > 1)
|
||||
{
|
||||
int mip = rts.colors[0].mipmap;
|
||||
int w = rts.colors[0].canvas->getPixelWidth(mip);
|
||||
int h = rts.colors[0].canvas->getPixelHeight(mip);
|
||||
|
||||
for (int i = 0; i < (int) rts.colors.size(); i++)
|
||||
{
|
||||
Canvas *c = (Canvas *) rts.colors[i].canvas.get();
|
||||
|
||||
if (!c->isReadable())
|
||||
continue;
|
||||
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
if (depthstencil != nullptr && depthstencil->getMSAA() > 1 && depthstencil->isReadable())
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
for (const auto &rt : rts.colors)
|
||||
{
|
||||
if (rt.canvas->getMipmapMode() == Canvas::MIPMAPS_AUTO && rt.mipmap == 0)
|
||||
rt.canvas->generateMipmaps();
|
||||
}
|
||||
|
||||
int dsmipmap = rts.depthStencil.mipmap;
|
||||
if (depthstencil != nullptr && depthstencil->getMipmapMode() == Canvas::MIPMAPS_AUTO && dsmipmap == 0)
|
||||
depthstencil->generateMipmaps();
|
||||
}
|
||||
|
||||
void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
|
||||
{
|
||||
if (c.hasValue || stencil.hasValue || depth.hasValue)
|
||||
flushStreamDraws();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Graphics::clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth)
|
||||
{
|
||||
if (colors.size() == 0 && !stencil.hasValue && !depth.hasValue)
|
||||
return;
|
||||
|
||||
int ncolorcanvases = (int) states.back().renderTargets.colors.size();
|
||||
int ncolors = (int) colors.size();
|
||||
|
||||
if (ncolors <= 1 && ncolorcanvases <= 1)
|
||||
{
|
||||
clear(ncolors > 0 ? colors[0] : OptionalColorf(), stencil, depth);
|
||||
return;
|
||||
}
|
||||
|
||||
flushStreamDraws();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
|
||||
{
|
||||
flushStreamDraws();
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Graphics::present(void *screenshotCallbackData)
|
||||
{
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
if (isCanvasActive())
|
||||
throw love::Exception("present cannot be called while a Canvas is active.");
|
||||
|
||||
deprecations.draw(this);
|
||||
|
||||
flushStreamDraws();
|
||||
endPass();
|
||||
|
||||
if (!pendingScreenshotCallbacks.empty())
|
||||
{
|
||||
int w = getPixelWidth();
|
||||
int h = getPixelHeight();
|
||||
|
||||
size_t row = 4 * w;
|
||||
size_t size = row * h;
|
||||
|
||||
uint8 *screenshot = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
screenshot = new uint8[size];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
delete[] screenshot;
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
// Replace alpha values with full opacity.
|
||||
for (size_t i = 3; i < size; i += 4)
|
||||
screenshot[i] = 255;
|
||||
|
||||
auto imagemodule = Module::getInstance<love::image::Image>(M_IMAGE);
|
||||
|
||||
for (int i = 0; i < (int) pendingScreenshotCallbacks.size(); i++)
|
||||
{
|
||||
const auto &info = pendingScreenshotCallbacks[i];
|
||||
image::ImageData *img = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
img = imagemodule->newImageData(w, h, PIXELFORMAT_RGBA8_UNORM, screenshot);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
delete[] screenshot;
|
||||
info.callback(&info, nullptr, nullptr);
|
||||
for (int j = i + 1; j < (int) pendingScreenshotCallbacks.size(); j++)
|
||||
{
|
||||
const auto &ninfo = pendingScreenshotCallbacks[j];
|
||||
ninfo.callback(&ninfo, nullptr, nullptr);
|
||||
}
|
||||
pendingScreenshotCallbacks.clear();
|
||||
throw;
|
||||
}
|
||||
|
||||
info.callback(&info, img, screenshotCallbackData);
|
||||
img->release();
|
||||
}
|
||||
|
||||
delete[] screenshot;
|
||||
pendingScreenshotCallbacks.clear();
|
||||
}
|
||||
|
||||
for (StreamBuffer *buffer : streamBufferState.vb)
|
||||
buffer->nextFrame();
|
||||
streamBufferState.indexBuffer->nextFrame();
|
||||
|
||||
submitCommandBuffer();
|
||||
|
||||
auto window = Module::getInstance<love::window::Window>(M_WINDOW);
|
||||
if (window != nullptr)
|
||||
window->swapBuffers();
|
||||
|
||||
// Reset the per-frame stat counts.
|
||||
drawCalls = 0;
|
||||
//gl.stats.shaderSwitches = 0;
|
||||
canvasSwitchCount = 0;
|
||||
drawCallsBatched = 0;
|
||||
|
||||
// This assumes temporary canvases will only be used within a render pass.
|
||||
for (int i = (int) temporaryCanvases.size() - 1; i >= 0; i--)
|
||||
{
|
||||
if (temporaryCanvases[i].framesSinceUse >= MAX_TEMPORARY_CANVAS_UNUSED_FRAMES)
|
||||
{
|
||||
temporaryCanvases[i].canvas->release();
|
||||
temporaryCanvases[i] = temporaryCanvases.back();
|
||||
temporaryCanvases.pop_back();
|
||||
}
|
||||
else
|
||||
temporaryCanvases[i].framesSinceUse++;
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::setScissor(const Rect &rect)
|
||||
{
|
||||
flushStreamDraws();
|
||||
|
||||
DisplayState &state = states.back();
|
||||
state.scissor = true;
|
||||
state.scissorRect = rect;
|
||||
dirtyRenderState |= STATEBIT_SCISSOR;
|
||||
}
|
||||
|
||||
void Graphics::setScissor()
|
||||
{
|
||||
DisplayState &state = states.back();
|
||||
if (state.scissor)
|
||||
{
|
||||
flushStreamDraws();
|
||||
state.scissor = false;
|
||||
dirtyRenderState |= STATEBIT_SCISSOR;
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::drawToStencilBuffer(StencilAction action, int value)
|
||||
{
|
||||
const auto &rts = states.back().renderTargets;
|
||||
love::graphics::Canvas *dscanvas = rts.depthStencil.canvas.get();
|
||||
|
||||
if (!isCanvasActive() && !windowHasStencil)
|
||||
throw love::Exception("The window must have stenciling enabled to draw to the main screen's stencil buffer.");
|
||||
else if (isCanvasActive() && (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) == 0 && (dscanvas == nullptr || !isPixelFormatStencil(dscanvas->getPixelFormat())))
|
||||
throw love::Exception("Drawing to the stencil buffer with a Canvas active requires either stencil=true or a custom stencil-type Canvas to be used, in setCanvas.");
|
||||
|
||||
flushStreamDraws();
|
||||
|
||||
writingToStencil = true;
|
||||
|
||||
dirtyRenderState |= STATEBIT_STENCIL;
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Graphics::stopDrawToStencilBuffer()
|
||||
{
|
||||
if (!writingToStencil)
|
||||
return;
|
||||
|
||||
flushStreamDraws();
|
||||
|
||||
writingToStencil = false;
|
||||
|
||||
const DisplayState &state = states.back();
|
||||
|
||||
// Revert the color write mask.
|
||||
setColorMask(state.colorMask);
|
||||
|
||||
// Use the user-set stencil test state when writes are disabled.
|
||||
setStencilTest(state.stencilCompare, state.stencilTestValue);
|
||||
|
||||
dirtyRenderState |= STATEBIT_STENCIL;
|
||||
}
|
||||
|
||||
void Graphics::setBlendState(const BlendState &blend)
|
||||
{
|
||||
if (!(blend == states.back().blend))
|
||||
{
|
||||
flushStreamDraws();
|
||||
states.back().blend = blend;
|
||||
dirtyRenderState |= STATEBIT_BLEND;
|
||||
}
|
||||
}
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/Color.h"
|
||||
#include "common/int.h"
|
||||
#include "graphics/Image.h"
|
||||
#include "Metal.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
class Image final : public love::graphics::Image
|
||||
{
|
||||
public:
|
||||
|
||||
Image(id<MTLDevice> device, const Slices &data, const Settings &settings);
|
||||
Image(id<MTLDevice> device, TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings);
|
||||
virtual ~Image();
|
||||
|
||||
ptrdiff_t getHandle() const override { return (ptrdiff_t) texture; }
|
||||
|
||||
void setFilter(const Texture::Filter &f) override;
|
||||
bool setWrap(const Texture::Wrap &w) override;
|
||||
|
||||
bool setMipmapSharpness(float sharpness) override;
|
||||
|
||||
private:
|
||||
|
||||
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
|
||||
void generateMipmaps() override;
|
||||
|
||||
void create(id<MTLDevice> device);
|
||||
|
||||
id<MTLTexture> texture;
|
||||
id<MTLSamplerState> sampler;
|
||||
|
||||
}; // Image
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Image.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
Image::Image(id<MTLDevice> device, TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings)
|
||||
: love::graphics::Image(textype, format, width, height, slices, settings)
|
||||
, texture(nil)
|
||||
, sampler(nil)
|
||||
{ @autoreleasepool {
|
||||
create(device);
|
||||
}}
|
||||
|
||||
Image::Image(id<MTLDevice> device, const Slices &slices, const Settings &settings)
|
||||
: love::graphics::Image(slices, settings)
|
||||
, texture(nil)
|
||||
, sampler(nil)
|
||||
{ @autoreleasepool {
|
||||
create(device);
|
||||
}}
|
||||
|
||||
Image::~Image()
|
||||
{ @autoreleasepool {
|
||||
texture = nil;
|
||||
sampler = nil;
|
||||
}}
|
||||
|
||||
void Image::create(id<MTLDevice> device)
|
||||
{
|
||||
MTLTextureDescriptor *desc = [MTLTextureDescriptor new];
|
||||
|
||||
desc.width = pixelWidth;
|
||||
desc.height = pixelHeight;
|
||||
desc.depth = depth;
|
||||
desc.arrayLength = layers;
|
||||
desc.mipmapLevelCount = mipmapCount;
|
||||
desc.textureType = Metal::getTextureType(texType, 1);
|
||||
desc.pixelFormat = Metal::convertPixelFormat(format, sRGB);
|
||||
desc.usage = MTLTextureUsageShaderRead;
|
||||
desc.storageMode = MTLStorageModePrivate;
|
||||
|
||||
texture = [device newTextureWithDescriptor:desc];
|
||||
|
||||
if (texture == nil)
|
||||
throw love::Exception("Out of graphics memory.");
|
||||
|
||||
// TODO: upload
|
||||
|
||||
if (mipmapsType == MIPMAPS_GENERATED)
|
||||
generateMipmaps();
|
||||
}
|
||||
|
||||
void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r)
|
||||
{ @autoreleasepool {
|
||||
auto gfx = Graphics::getInstance();
|
||||
id<MTLBuffer> buffer = [gfx->device newBufferWithBytes:data
|
||||
length:size
|
||||
options:MTLResourceStorageModeShared];
|
||||
|
||||
memcpy(buffer.contents, data, size);
|
||||
|
||||
id<MTLBlitCommandEncoder> encoder = gfx->useBlitEncoder();
|
||||
|
||||
int z = 0;
|
||||
if (texType == TEXTURE_VOLUME)
|
||||
{
|
||||
z = slice;
|
||||
slice = 0;
|
||||
}
|
||||
|
||||
MTLBlitOption options = MTLBlitOptionNone;
|
||||
|
||||
switch (pixelformat)
|
||||
{
|
||||
#ifdef LOVE_IOS
|
||||
case PIXELFORMAT_PVR1_RGB2_UNORM:
|
||||
case PIXELFORMAT_PVR1_RGB4_UNORM:
|
||||
case PIXELFORMAT_PVR1_RGBA2_UNORM:
|
||||
case PIXELFORMAT_PVR1_RGBA4_UNORM:
|
||||
options |= MTLBlitOptionRowLinearPVRTC;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
[encoder copyFromBuffer:buffer
|
||||
sourceOffset:0
|
||||
sourceBytesPerRow:getPixelFormatRowStride(pixelformat, r.w)
|
||||
sourceBytesPerImage:0 // TODO?
|
||||
sourceSize:MTLSizeMake(r.w, r.h, 1)
|
||||
toTexture:texture
|
||||
destinationSlice:slice
|
||||
destinationLevel:level
|
||||
destinationOrigin:MTLOriginMake(r.x, r.y, z)
|
||||
options:options];
|
||||
}}
|
||||
|
||||
void Image::generateMipmaps()
|
||||
{ @autoreleasepool {
|
||||
id<MTLBlitCommandEncoder> encoder = Graphics::getInstance()->useBlitEncoder();
|
||||
[encoder generateMipmapsForTexture:texture];
|
||||
}}
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "graphics/Texture.h"
|
||||
#include "common/pixelformat.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
class Metal
|
||||
{
|
||||
public:
|
||||
|
||||
static MTLTextureType getTextureType(TextureType type, int msaa);
|
||||
static MTLPixelFormat convertPixelFormat(PixelFormat format, bool &isSRGB);
|
||||
|
||||
}; // Metal
|
||||
|
||||
extern Metal metal;
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,298 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Metal.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
MTLTextureType Metal::getTextureType(TextureType type, int msaa)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TEXTURE_2D: return msaa > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
|
||||
case TEXTURE_VOLUME: return MTLTextureType3D;
|
||||
case TEXTURE_2D_ARRAY: return MTLTextureType2DArray;
|
||||
case TEXTURE_CUBE: return MTLTextureTypeCube;
|
||||
case TEXTURE_MAX_ENUM: return MTLTextureType2D;
|
||||
}
|
||||
return MTLTextureType2D;
|
||||
}
|
||||
|
||||
MTLPixelFormat Metal::convertPixelFormat(PixelFormat format, bool &isSRGB)
|
||||
{
|
||||
MTLPixelFormat mtlformat = MTLPixelFormatRGBA8Unorm;
|
||||
|
||||
if (format == PIXELFORMAT_RGBA8_UNORM && isSRGB)
|
||||
format = PIXELFORMAT_sRGBA8_UNORM;
|
||||
|
||||
if (!isPixelFormatCompressed(format) && format != PIXELFORMAT_sRGBA8_UNORM)
|
||||
isSRGB = false;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case PIXELFORMAT_R8_UNORM:
|
||||
mtlformat = MTLPixelFormatR8Unorm;
|
||||
break;
|
||||
case PIXELFORMAT_RG8_UNORM:
|
||||
mtlformat = MTLPixelFormatRG8Unorm;
|
||||
break;
|
||||
case PIXELFORMAT_RGBA8_UNORM:
|
||||
mtlformat = MTLPixelFormatRGBA8Unorm;
|
||||
break;
|
||||
case PIXELFORMAT_sRGBA8_UNORM:
|
||||
mtlformat = MTLPixelFormatRGBA8Unorm_sRGB;
|
||||
break;
|
||||
case PIXELFORMAT_R16_UNORM:
|
||||
mtlformat = MTLPixelFormatR16Unorm;
|
||||
break;
|
||||
case PIXELFORMAT_RG16_UNORM:
|
||||
mtlformat = MTLPixelFormatRG16Unorm;
|
||||
break;
|
||||
case PIXELFORMAT_RGBA16_UNORM:
|
||||
mtlformat = MTLPixelFormatRGBA16Unorm;
|
||||
break;
|
||||
case PIXELFORMAT_R16_FLOAT:
|
||||
mtlformat = MTLPixelFormatR16Float;
|
||||
break;
|
||||
case PIXELFORMAT_RG16_FLOAT:
|
||||
mtlformat = MTLPixelFormatRG16Float;
|
||||
break;
|
||||
case PIXELFORMAT_RGBA16_FLOAT:
|
||||
mtlformat = MTLPixelFormatRGBA16Float;
|
||||
break;
|
||||
case PIXELFORMAT_R32_FLOAT:
|
||||
mtlformat = MTLPixelFormatR32Float;
|
||||
break;
|
||||
case PIXELFORMAT_RG32_FLOAT:
|
||||
mtlformat = MTLPixelFormatRG32Float;
|
||||
break;
|
||||
case PIXELFORMAT_RGBA32_FLOAT:
|
||||
mtlformat = MTLPixelFormatRGBA32Float;
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_LA8_UNORM:
|
||||
mtlformat = MTLPixelFormatRGBA8Unorm;
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_RGBA4_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = MTLPixelFormatABGR4Unorm;
|
||||
#else
|
||||
mtlformat = MTLPixelFormatRGBA8Unorm;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_RGB5A1_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = MTLPixelFormatA1BGR5Unorm;
|
||||
#else
|
||||
mtlformat = MTLPixelFormatRGBA8Unorm;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_RGB565_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = MTLPixelFormatB5G6R5Unorm;
|
||||
#else
|
||||
mtlformat = MTLPixelFormatRGBA8Unorm;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_RGB10A2_UNORM:
|
||||
mtlformat = MTLPixelFormatRGB10A2Unorm;
|
||||
break;
|
||||
case PIXELFORMAT_RG11B10_FLOAT:
|
||||
mtlformat = MTLPixelFormatRG11B10Float;
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_STENCIL8:
|
||||
mtlformat = MTLPixelFormatStencil8;
|
||||
break;
|
||||
case PIXELFORMAT_DEPTH16_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = MTLPixelFormatDepth32Float;
|
||||
#else
|
||||
mtlformat = MTLPixelFormatDepth16Unorm;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_DEPTH24_UNORM:
|
||||
mtlformat = MTLPixelFormatDepth32Float;
|
||||
break;
|
||||
case PIXELFORMAT_DEPTH32_FLOAT:
|
||||
mtlformat = MTLPixelFormatDepth32Float;
|
||||
break;
|
||||
case PIXELFORMAT_DEPTH24_UNORM_STENCIL8:
|
||||
mtlformat = MTLPixelFormatDepth24Unorm_Stencil8;
|
||||
break;
|
||||
case PIXELFORMAT_DEPTH32_FLOAT_STENCIL8:
|
||||
mtlformat = MTLPixelFormatDepth32Float_Stencil8;
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_DXT1_UNORM:
|
||||
#ifndef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_DXT3_UNORM:
|
||||
#ifndef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_DXT5_UNORM:
|
||||
#ifndef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_BC4_UNORM:
|
||||
#ifndef LOVE_IOS
|
||||
isSRGB = false;
|
||||
mtlformat = MTLPixelFormatBC4_RUnorm;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_BC4_SNORM:
|
||||
#ifndef LOVE_IOS
|
||||
isSRGB = false;
|
||||
mtlformat = MTLPixelFormatBC4_RSnorm;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_BC5_UNORM:
|
||||
#ifndef LOVE_IOS
|
||||
isSRGB = false;
|
||||
mtlformat = MTLPixelFormatBC5_RGUnorm;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_BC5_SNORM:
|
||||
#ifndef LOVE_IOS
|
||||
isSRGB = false;
|
||||
mtlformat = MTLPixelFormatBC5_RGSnorm;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_BC6H_UFLOAT:
|
||||
#ifndef LOVE_IOS
|
||||
isSRGB = false;
|
||||
mtlformat = MTLPixelFormatBC6H_RGBUfloat;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_BC6H_FLOAT:
|
||||
#ifndef LOVE_IOS
|
||||
isSRGB = false;
|
||||
mtlformat = MTLPixelFormatBC6H_RGBFloat;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_BC7_UNORM:
|
||||
#ifndef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_PVR1_RGB2_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatPVRTC_RGB_2BPP_sRGB : MTLPixelFormatPVRTC_RGB_2BPP;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_PVR1_RGB4_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatPVRTC_RGB_4BPP_sRGB : MTLPixelFormatPVRTC_RGB_4BPP;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_PVR1_RGBA2_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatPVRTC_RGB_2BPP_sRGB : MTLPixelFormatPVRTC_RGBA_2BPP;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_PVR1_RGBA4_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatPVRTC_RGB_4BPP_sRGB : MTLPixelFormatPVRTC_RGBA_4BPP;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_ETC1_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_ETC2_RGB_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_ETC2_RGBA_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_ETC2_RGBA1_UNORM:
|
||||
#ifdef LOVE_IOS
|
||||
mtlformat = isSRGB ? MTLPixelFormatETC2_RGBA1_sRGB : MTLPixelFormatETC2_RGBA1;
|
||||
#endif
|
||||
break;
|
||||
case PIXELFORMAT_EAC_R_UNORM:
|
||||
break;
|
||||
case PIXELFORMAT_EAC_R_SNORM:
|
||||
break;
|
||||
case PIXELFORMAT_EAC_RG_UNORM:
|
||||
break;
|
||||
case PIXELFORMAT_EAC_RG_SNORM:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_4x4:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_5x4:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_5x5:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_6x5:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_6x6:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_8x5:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_8x6:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_8x8:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_10x5:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_10x6:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_10x8:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_10x10:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_12x10:
|
||||
break;
|
||||
case PIXELFORMAT_ASTC_12x12:
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_UNKNOWN:
|
||||
case PIXELFORMAT_NORMAL:
|
||||
case PIXELFORMAT_HDR:
|
||||
case PIXELFORMAT_MAX_ENUM:
|
||||
break;
|
||||
}
|
||||
|
||||
return mtlformat;
|
||||
}
|
||||
|
||||
Metal metal;
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "graphics/Shader.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "Metal.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
class Shader final : public love::graphics::Shader
|
||||
{
|
||||
public:
|
||||
|
||||
Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel);
|
||||
virtual ~Shader();
|
||||
|
||||
// Implements Shader.
|
||||
void attach() override;
|
||||
std::string getWarnings() const override;
|
||||
int getVertexAttributeIndex(const std::string &name) override;
|
||||
const UniformInfo *getUniformInfo(const std::string &name) const override;
|
||||
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
|
||||
void updateUniform(const UniformInfo *info, int count) override;
|
||||
void sendTextures(const UniformInfo *info, Texture **textures, int count) override;
|
||||
bool hasUniform(const std::string &name) const override;
|
||||
ptrdiff_t getHandle() const override;
|
||||
void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) override;
|
||||
|
||||
private:
|
||||
|
||||
id<MTLLibrary> library;
|
||||
|
||||
}; // Metal
|
||||
|
||||
extern Metal metal;
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Shader.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
// glslang
|
||||
#include "libraries/glslang/glslang/Public/ShaderLang.h"
|
||||
#include "libraries/glslang/SPIRV/GlslangToSpv.h"
|
||||
#include "libraries/spirv_cross/spirv_msl.hpp"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
|
||||
: love::graphics::Shader(vertex, pixel)
|
||||
{ @autoreleasepool {
|
||||
auto gfx = Graphics::getInstance();
|
||||
|
||||
using namespace glslang;
|
||||
using namespace spirv_cross;
|
||||
|
||||
// TODO: can this be done in ShaderStage (no linking)?
|
||||
|
||||
glslang::TProgram program;
|
||||
|
||||
if (vertex != nullptr)
|
||||
program.addShader((TShader *) vertex->getHandle());
|
||||
|
||||
if (pixel != nullptr)
|
||||
program.addShader((TShader *) pixel->getHandle());
|
||||
|
||||
if (!program.link(EShMsgDefault))
|
||||
{
|
||||
//err = "Cannot compile shader:\n\n" + std::string(program.getInfoLog()) + "\n" + std::string(program.getInfoDebugLog());
|
||||
}
|
||||
|
||||
for (int i = 0; i < EShLangCount; i++)
|
||||
{
|
||||
auto intermediate = program.getIntermediate((EShLanguage)i);
|
||||
if (intermediate == nullptr)
|
||||
continue;
|
||||
|
||||
spv::SpvBuildLogger logger;
|
||||
glslang::SpvOptions opt;
|
||||
opt.validate = true;
|
||||
|
||||
std::vector<unsigned int> spirv;
|
||||
|
||||
{
|
||||
// timer::ScopedTimer("shader stage");
|
||||
GlslangToSpv(*intermediate, spirv, &logger, &opt);
|
||||
}
|
||||
|
||||
std::string msgs = logger.getAllMessages();
|
||||
// printf("spirv length: %ld, messages:\n%s\n", spirv.size(), msgs.c_str());
|
||||
|
||||
// Compile to GLSL, ready to give to GL driver.
|
||||
try
|
||||
{
|
||||
|
||||
// printf("GLSL INPUT SOURCE:\n\n%s\n\n", pixel->getSource().c_str());
|
||||
|
||||
CompilerMSL msl(std::move(spirv));
|
||||
|
||||
CompilerMSL::Options options;
|
||||
|
||||
#ifdef LOVE_IOS
|
||||
options.platform = CompilerMSL::Options::iOS;
|
||||
#else
|
||||
options.platform = CompilerMSL::Options::macOS;
|
||||
#endif
|
||||
|
||||
msl.set_msl_options(options);
|
||||
|
||||
std::string source = msl.compile();
|
||||
// printf("MSL SOURCE:\n\n%s\n\n", source.c_str());
|
||||
|
||||
NSString *nssource = [[NSString alloc] initWithBytes:source.c_str()
|
||||
length:source.length()
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *err = nil;
|
||||
id<MTLLibrary> library = [gfx->device newLibraryWithSource:nssource options:nil error:&err];
|
||||
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
printf("Error parsing SPIR-V shader source: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
Shader::~Shader()
|
||||
{ @autoreleasepool {
|
||||
|
||||
}}
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "graphics/ShaderStage.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
class ShaderStage final : public love::graphics::ShaderStage
|
||||
{
|
||||
public:
|
||||
|
||||
ShaderStage(love::graphics::Graphics *gfx, StageType stage, const std::string &source, bool gles, const std::string &cachekey);
|
||||
virtual ~ShaderStage();
|
||||
|
||||
ptrdiff_t getHandle() const override { return (ptrdiff_t) glslangShader; }
|
||||
|
||||
private:
|
||||
|
||||
glslang::TShader *glslangShader;
|
||||
|
||||
}; // ShaderStage
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "ShaderStage.h"
|
||||
|
||||
#include "libraries/glslang/glslang/Public/ShaderLang.h"
|
||||
|
||||
// TODO: Use love.graphics to determine actual limits?
|
||||
static const TBuiltInResource defaultTBuiltInResource = {
|
||||
/* .MaxLights = */ 32,
|
||||
/* .MaxClipPlanes = */ 6,
|
||||
/* .MaxTextureUnits = */ 32,
|
||||
/* .MaxTextureCoords = */ 32,
|
||||
/* .MaxVertexAttribs = */ 64,
|
||||
/* .MaxVertexUniformComponents = */ 16384,
|
||||
/* .MaxVaryingFloats = */ 128,
|
||||
/* .MaxVertexTextureImageUnits = */ 32,
|
||||
/* .MaxCombinedTextureImageUnits = */ 80,
|
||||
/* .MaxTextureImageUnits = */ 32,
|
||||
/* .MaxFragmentUniformComponents = */ 16384,
|
||||
/* .MaxDrawBuffers = */ 8,
|
||||
/* .MaxVertexUniformVectors = */ 4096,
|
||||
/* .MaxVaryingVectors = */ 32,
|
||||
/* .MaxFragmentUniformVectors = */ 4096,
|
||||
/* .MaxVertexOutputVectors = */ 32,
|
||||
/* .MaxFragmentInputVectors = */ 31,
|
||||
/* .MinProgramTexelOffset = */ -8,
|
||||
/* .MaxProgramTexelOffset = */ 7,
|
||||
/* .MaxClipDistances = */ 8,
|
||||
/* .MaxComputeWorkGroupCountX = */ 65535,
|
||||
/* .MaxComputeWorkGroupCountY = */ 65535,
|
||||
/* .MaxComputeWorkGroupCountZ = */ 65535,
|
||||
/* .MaxComputeWorkGroupSizeX = */ 1024,
|
||||
/* .MaxComputeWorkGroupSizeY = */ 1024,
|
||||
/* .MaxComputeWorkGroupSizeZ = */ 64,
|
||||
/* .MaxComputeUniformComponents = */ 1024,
|
||||
/* .MaxComputeTextureImageUnits = */ 32,
|
||||
/* .MaxComputeImageUniforms = */ 16,
|
||||
/* .MaxComputeAtomicCounters = */ 4096,
|
||||
/* .MaxComputeAtomicCounterBuffers = */ 8,
|
||||
/* .MaxVaryingComponents = */ 128,
|
||||
/* .MaxVertexOutputComponents = */ 128,
|
||||
/* .MaxGeometryInputComponents = */ 128,
|
||||
/* .MaxGeometryOutputComponents = */ 128,
|
||||
/* .MaxFragmentInputComponents = */ 128,
|
||||
/* .MaxImageUnits = */ 192,
|
||||
/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144,
|
||||
/* .MaxCombinedShaderOutputResources = */ 144,
|
||||
/* .MaxImageSamples = */ 32,
|
||||
/* .MaxVertexImageUniforms = */ 16,
|
||||
/* .MaxTessControlImageUniforms = */ 16,
|
||||
/* .MaxTessEvaluationImageUniforms = */ 16,
|
||||
/* .MaxGeometryImageUniforms = */ 16,
|
||||
/* .MaxFragmentImageUniforms = */ 16,
|
||||
/* .MaxCombinedImageUniforms = */ 80,
|
||||
/* .MaxGeometryTextureImageUnits = */ 16,
|
||||
/* .MaxGeometryOutputVertices = */ 256,
|
||||
/* .MaxGeometryTotalOutputComponents = */ 1024,
|
||||
/* .MaxGeometryUniformComponents = */ 1024,
|
||||
/* .MaxGeometryVaryingComponents = */ 64,
|
||||
/* .MaxTessControlInputComponents = */ 128,
|
||||
/* .MaxTessControlOutputComponents = */ 128,
|
||||
/* .MaxTessControlTextureImageUnits = */ 16,
|
||||
/* .MaxTessControlUniformComponents = */ 1024,
|
||||
/* .MaxTessControlTotalOutputComponents = */ 4096,
|
||||
/* .MaxTessEvaluationInputComponents = */ 128,
|
||||
/* .MaxTessEvaluationOutputComponents = */ 128,
|
||||
/* .MaxTessEvaluationTextureImageUnits = */ 16,
|
||||
/* .MaxTessEvaluationUniformComponents = */ 1024,
|
||||
/* .MaxTessPatchComponents = */ 120,
|
||||
/* .MaxPatchVertices = */ 32,
|
||||
/* .MaxTessGenLevel = */ 64,
|
||||
/* .MaxViewports = */ 16,
|
||||
/* .MaxVertexAtomicCounters = */ 4096,
|
||||
/* .MaxTessControlAtomicCounters = */ 4096,
|
||||
/* .MaxTessEvaluationAtomicCounters = */ 4096,
|
||||
/* .MaxGeometryAtomicCounters = */ 4096,
|
||||
/* .MaxFragmentAtomicCounters = */ 4096,
|
||||
/* .MaxCombinedAtomicCounters = */ 4096,
|
||||
/* .MaxAtomicCounterBindings = */ 8,
|
||||
/* .MaxVertexAtomicCounterBuffers = */ 8,
|
||||
/* .MaxTessControlAtomicCounterBuffers = */ 8,
|
||||
/* .MaxTessEvaluationAtomicCounterBuffers = */ 8,
|
||||
/* .MaxGeometryAtomicCounterBuffers = */ 8,
|
||||
/* .MaxFragmentAtomicCounterBuffers = */ 8,
|
||||
/* .MaxCombinedAtomicCounterBuffers = */ 8,
|
||||
/* .MaxAtomicCounterBufferSize = */ 16384,
|
||||
/* .MaxTransformFeedbackBuffers = */ 4,
|
||||
/* .MaxTransformFeedbackInterleavedComponents = */ 64,
|
||||
/* .MaxCullDistances = */ 8,
|
||||
/* .MaxCombinedClipAndCullDistances = */ 8,
|
||||
/* .MaxSamples = */ 32,
|
||||
/* .maxMeshOutputVerticesNV = */ 256,
|
||||
/* .maxMeshOutputPrimitivesNV = */ 512,
|
||||
/* .maxMeshWorkGroupSizeX_NV = */ 32,
|
||||
/* .maxMeshWorkGroupSizeY_NV = */ 1,
|
||||
/* .maxMeshWorkGroupSizeZ_NV = */ 1,
|
||||
/* .maxTaskWorkGroupSizeX_NV = */ 32,
|
||||
/* .maxTaskWorkGroupSizeY_NV = */ 1,
|
||||
/* .maxTaskWorkGroupSizeZ_NV = */ 1,
|
||||
/* .maxMeshViewCountNV = */ 4,
|
||||
/* .limits = */ {
|
||||
/* .nonInductiveForLoops = */ 1,
|
||||
/* .whileLoops = */ 1,
|
||||
/* .doWhileLoops = */ 1,
|
||||
/* .generalUniformIndexing = */ 1,
|
||||
/* .generalAttributeMatrixVectorIndexing = */ 1,
|
||||
/* .generalVaryingIndexing = */ 1,
|
||||
/* .generalSamplerIndexing = */ 1,
|
||||
/* .generalVariableIndexing = */ 1,
|
||||
/* .generalConstantMatrixVectorIndexing = */ 1,
|
||||
}
|
||||
};
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
ShaderStage::ShaderStage(love::graphics::Graphics *gfx, StageType stage, const std::string &source, bool gles, const std::string &cachekey)
|
||||
: love::graphics::ShaderStage(gfx, stage, source, gles, cachekey)
|
||||
{
|
||||
using namespace glslang;
|
||||
|
||||
EShLanguage glslangStage = EShLangCount;
|
||||
if (stage == STAGE_VERTEX)
|
||||
glslangStage = EShLangVertex;
|
||||
else if (stage == STAGE_PIXEL)
|
||||
glslangStage = EShLangFragment;
|
||||
else
|
||||
throw love::Exception("Cannot compile shader stage: unknown stage type.");
|
||||
|
||||
glslangShader = new TShader(glslangStage);
|
||||
|
||||
// We can't reuse the validation glslang shader object in the base class,
|
||||
// because we need these options set (and the language set to >= 300).
|
||||
glslangShader->setEnvInput(EShSourceGlsl, glslangStage, EShClientNone, 0);
|
||||
glslangShader->setEnvClient(EShClientOpenGL, EShTargetOpenGL_450);
|
||||
glslangShader->setEnvTarget(EShTargetSpv, EShTargetSpv_1_0);
|
||||
glslangShader->setAutoMapLocations(true);
|
||||
glslangShader->setAutoMapBindings(true);
|
||||
|
||||
const char *csrc = source.c_str();
|
||||
int srclen = (int) source.length();
|
||||
glslangShader->setStringsWithLengths(&csrc, &srclen, 1);
|
||||
|
||||
int defaultversion = gles ? 300 : 330;
|
||||
EProfile defaultprofile = ENoProfile;
|
||||
bool forcedefault = false;
|
||||
bool forwardcompat = true;
|
||||
|
||||
if (!glslangShader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
|
||||
{
|
||||
const char *stagename = "unknown";
|
||||
getConstant(stage, stagename);
|
||||
|
||||
std::string err = "Error parsing " + std::string(stagename) + " shader:\n\n"
|
||||
+ std::string(glslangShader->getInfoLog()) + "\n"
|
||||
+ std::string(glslangShader->getInfoDebugLog());
|
||||
|
||||
delete glslangShader;
|
||||
throw love::Exception("%s", err.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ShaderStage::~ShaderStage()
|
||||
{
|
||||
delete glslangShader;
|
||||
}
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "graphics/StreamBuffer.h"
|
||||
#include "Metal.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
love::graphics::StreamBuffer *CreateStreamBuffer(id<MTLDevice> device, BufferType mode, size_t size);
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2020 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "StreamBuffer.h"
|
||||
#include "Metal.h"
|
||||
#include "Graphics.h"
|
||||
#include "common/int.h"
|
||||
|
||||
#include <dispatch/semaphore.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace metal
|
||||
{
|
||||
|
||||
static const int BUFFER_FRAMES = 3;
|
||||
|
||||
class StreamBuffer final : public love::graphics::StreamBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
StreamBuffer(id<MTLDevice> device, BufferType mode, size_t size)
|
||||
: love::graphics::StreamBuffer(mode, size)
|
||||
, frameIndex(0)
|
||||
, mappedFrames()
|
||||
{ @autoreleasepool {
|
||||
MTLResourceOptions opts = MTLResourceStorageModeShared;
|
||||
buffer = [device newBufferWithLength:size * BUFFER_FRAMES options:opts];
|
||||
if (buffer == nil)
|
||||
throw love::Exception("Out of graphics memory.");
|
||||
|
||||
data = (uint8 *) buffer.contents;
|
||||
|
||||
for (int i = 0; i < BUFFER_FRAMES; i++)
|
||||
frameSemaphores[i] = dispatch_semaphore_create(0);
|
||||
}}
|
||||
|
||||
virtual ~StreamBuffer()
|
||||
{ @autoreleasepool {
|
||||
// TODO
|
||||
buffer = nil;
|
||||
for (int i = 0; i < 3; i++)
|
||||
dispatch_release(frameSemaphores[i]);
|
||||
}}
|
||||
|
||||
MapInfo map(size_t /*minsize*/) override
|
||||
{
|
||||
// Make sure this frame's section of the buffer is done being used.
|
||||
if (!mappedFrames[frameIndex])
|
||||
{
|
||||
dispatch_semaphore_wait(frameSemaphores[frameIndex], DISPATCH_TIME_FOREVER);
|
||||
mappedFrames[frameIndex] = true;
|
||||
}
|
||||
|
||||
MapInfo info;
|
||||
info.size = bufferSize - frameGPUReadOffset;
|
||||
info.data = data + (frameIndex * bufferSize) + frameGPUReadOffset;
|
||||
return info;
|
||||
}
|
||||
|
||||
size_t unmap(size_t /*usedsize*/) override
|
||||
{
|
||||
size_t offset = (frameIndex * bufferSize) + frameGPUReadOffset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
void nextFrame() override
|
||||
{
|
||||
id<MTLCommandBuffer> cmd = Graphics::getInstance()->getCommandBuffer();
|
||||
|
||||
// Insert a GPU fence for this frame's section of the data, we'll wait
|
||||
// for it when we try to map that data for writing in subsequent frames.
|
||||
if (mappedFrames[frameIndex])
|
||||
{
|
||||
/*__weak*/ dispatch_semaphore_t semaphore = frameSemaphores[frameIndex];
|
||||
[cmd addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull)
|
||||
{
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
}
|
||||
|
||||
mappedFrames[frameIndex] = false;
|
||||
frameIndex = (frameIndex + 1) % BUFFER_FRAMES;
|
||||
frameGPUReadOffset = 0;
|
||||
}
|
||||
|
||||
void markUsed(size_t usedsize) override
|
||||
{
|
||||
// We insert a fence for all data from this frame at the end of the
|
||||
// frame (in nextFrame), rather than doing anything more fine-grained.
|
||||
frameGPUReadOffset += usedsize;
|
||||
}
|
||||
|
||||
ptrdiff_t getHandle() const override { return (ptrdiff_t)buffer; }
|
||||
|
||||
private:
|
||||
|
||||
id<MTLBuffer> buffer;
|
||||
uint8 *data;
|
||||
|
||||
int frameIndex;
|
||||
dispatch_semaphore_t frameSemaphores[BUFFER_FRAMES];
|
||||
bool mappedFrames[BUFFER_FRAMES];
|
||||
|
||||
}; // StreamBuffer
|
||||
|
||||
love::graphics::StreamBuffer *CreateStreamBuffer(id<MTLDevice> device, BufferType mode, size_t size)
|
||||
{
|
||||
return new StreamBuffer(device, mode, size);
|
||||
}
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "common/config.h"
|
||||
|
||||
#include "Shader.h"
|
||||
#include "ShaderStage.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
// C++
|
||||
@@ -304,7 +305,7 @@ bool Shader::loadVolatile()
|
||||
for (const auto &stage : stages)
|
||||
{
|
||||
if (stage.get() != nullptr)
|
||||
stage->loadVolatile();
|
||||
((ShaderStage*)stage.get())->loadVolatile();
|
||||
}
|
||||
|
||||
program = glCreateProgram();
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class ShaderStage final : public love::graphics::ShaderStage
|
||||
class ShaderStage final : public love::graphics::ShaderStage, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user