mirror of
https://github.com/love2d/love.git
synced 2026-08-16 16:20:42 +02:00
Update metal backend for Image+Canvas->Texture changes
This commit is contained in:
@@ -21,8 +21,7 @@
|
||||
#include "Graphics.h"
|
||||
#include "StreamBuffer.h"
|
||||
#include "Buffer.h"
|
||||
#include "Canvas.h"
|
||||
#include "Image.h"
|
||||
#include "Texture.h"
|
||||
#include "Shader.h"
|
||||
#include "window/Window.h"
|
||||
#include "image/Image.h"
|
||||
@@ -34,44 +33,43 @@ namespace graphics
|
||||
namespace metal
|
||||
{
|
||||
|
||||
static MTLSamplerMinMagFilter getMTLSamplerFilter(Texture::FilterMode mode)
|
||||
static MTLSamplerMinMagFilter getMTLSamplerFilter(SamplerState::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;
|
||||
case SamplerState::FILTER_LINEAR: return MTLSamplerMinMagFilterLinear;
|
||||
case SamplerState::FILTER_NEAREST: return MTLSamplerMinMagFilterNearest;
|
||||
case SamplerState::FILTER_MAX_ENUM: return MTLSamplerMinMagFilterLinear;
|
||||
}
|
||||
return MTLSamplerMinMagFilterLinear;
|
||||
}
|
||||
|
||||
static MTLSamplerMipFilter getMTLSamplerMipFilter(Texture::FilterMode mode)
|
||||
static MTLSamplerMipFilter getMTLSamplerMipFilter(SamplerState::MipmapFilterMode 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;
|
||||
case SamplerState::MIPMAP_FILTER_NONE: return MTLSamplerMipFilterNotMipmapped;
|
||||
case SamplerState::MIPMAP_FILTER_LINEAR: return MTLSamplerMipFilterLinear;
|
||||
case SamplerState::MIPMAP_FILTER_NEAREST: return MTLSamplerMipFilterNearest;
|
||||
case SamplerState::MIPMAP_FILTER_MAX_ENUM: return MTLSamplerMipFilterNotMipmapped;
|
||||
}
|
||||
return MTLSamplerMipFilterNotMipmapped;
|
||||
}
|
||||
|
||||
static MTLSamplerAddressMode getMTLSamplerAddressMode(Texture::WrapMode mode)
|
||||
static MTLSamplerAddressMode getMTLSamplerAddressMode(SamplerState::WrapMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case Texture::WRAP_CLAMP: return MTLSamplerAddressModeClampToEdge;
|
||||
case Texture::WRAP_CLAMP_ZERO: return MTLSamplerAddressModeClampToZero;
|
||||
case SamplerState::WRAP_CLAMP: return MTLSamplerAddressModeClampToEdge;
|
||||
case SamplerState::WRAP_CLAMP_ZERO: return MTLSamplerAddressModeClampToZero;
|
||||
#ifdef LOVE_MACOS
|
||||
case Texture::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToBorderColor;
|
||||
case SamplerState::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToBorderColor;
|
||||
#else
|
||||
case Texture::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToZero;
|
||||
case SamplerState::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;
|
||||
case SamplerState::WRAP_REPEAT: return MTLSamplerAddressModeRepeat;
|
||||
case SamplerState::WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat;
|
||||
case SamplerState::WRAP_MAX_ENUM: return MTLSamplerAddressModeClampToEdge;
|
||||
}
|
||||
return MTLSamplerAddressModeClampToEdge;
|
||||
}
|
||||
@@ -202,19 +200,9 @@ love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferType type, size_t
|
||||
return CreateStreamBuffer(device, type, size);
|
||||
}
|
||||
|
||||
love::graphics::Image *Graphics::newImage(const Image::Slices &data, const Image::Settings &settings)
|
||||
love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings, const Texture::Slices *data)
|
||||
{
|
||||
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);
|
||||
return new Texture(device, settings, data);
|
||||
}
|
||||
|
||||
love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles)
|
||||
@@ -245,9 +233,9 @@ void Graphics::initCapabilities()
|
||||
}
|
||||
}
|
||||
|
||||
capabilities.features[FEATURE_MULTI_CANVAS_FORMATS] = true;
|
||||
capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = true;
|
||||
capabilities.features[FEATURE_CLAMP_ZERO] = true;
|
||||
capabilities.features[FEATURE_BLENDMINMAX] = true;
|
||||
capabilities.features[FEATURE_BLEND_MINMAX] = true;
|
||||
capabilities.features[FEATURE_LIGHTEN] = true;
|
||||
capabilities.features[FEATURE_FULL_NPOT] = true;
|
||||
capabilities.features[FEATURE_PIXEL_SHADER_HIGHP] = true;
|
||||
@@ -263,8 +251,8 @@ void Graphics::initCapabilities()
|
||||
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_RENDER_TARGETS] = 8; // TODO
|
||||
capabilities.limits[LIMIT_TEXTURE_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!");
|
||||
|
||||
@@ -279,7 +267,7 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
|
||||
this->pixelWidth = pixelwidth;
|
||||
this->pixelHeight = pixelheight;
|
||||
|
||||
if (!isCanvasActive())
|
||||
if (!isRenderTargetActive())
|
||||
{
|
||||
dirtyRenderState |= STATEBIT_VIEWPORT | STATEBIT_SCISSOR;
|
||||
|
||||
@@ -343,10 +331,10 @@ void Graphics::unSetMode()
|
||||
|
||||
submitCommandBuffer();
|
||||
|
||||
for (auto temp : temporaryCanvases)
|
||||
temp.canvas->release();
|
||||
for (auto temp : temporaryTextures)
|
||||
temp.texture->release();
|
||||
|
||||
temporaryCanvases.clear();
|
||||
temporaryTextures.clear();
|
||||
|
||||
created = false;
|
||||
}
|
||||
@@ -418,28 +406,31 @@ void Graphics::submitBlitEncoder()
|
||||
}
|
||||
}
|
||||
|
||||
id<MTLSamplerState> Graphics::getCachedSampler(const Texture::Filter &f, const Texture::Wrap &w, float maxAnisotropy, Optional<CompareMode> depthSampleMode)
|
||||
id<MTLSamplerState> Graphics::getCachedSampler(const SamplerState &s)
|
||||
{ @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.minFilter = getMTLSamplerFilter(s.minFilter);
|
||||
desc.magFilter = getMTLSamplerFilter(s.magFilter);
|
||||
desc.mipFilter = getMTLSamplerMipFilter(s.mipmapFilter);
|
||||
desc.maxAnisotropy = std::max(1.0f, std::min((float)s.maxAnisotropy, 16.0f));
|
||||
|
||||
desc.sAddressMode = getMTLSamplerAddressMode(w.s);
|
||||
desc.tAddressMode = getMTLSamplerAddressMode(w.t);
|
||||
desc.rAddressMode = getMTLSamplerAddressMode(w.r);
|
||||
desc.sAddressMode = getMTLSamplerAddressMode(s.wrapU);
|
||||
desc.tAddressMode = getMTLSamplerAddressMode(s.wrapV);
|
||||
desc.rAddressMode = getMTLSamplerAddressMode(s.wrapW);
|
||||
|
||||
#ifdef LOVE_MACOS
|
||||
desc.borderColor = MTLSamplerBorderColorOpaqueWhite;
|
||||
#endif
|
||||
|
||||
if (depthSampleMode.hasValue)
|
||||
desc.compareFunction = getMTLCompareFunction(depthSampleMode.value);
|
||||
desc.lodMinClamp = s.minLod;
|
||||
desc.lodMaxClamp = s.maxLod;
|
||||
|
||||
if (s.depthSampleMode.hasValue)
|
||||
desc.compareFunction = getMTLCompareFunction(s.depthSampleMode.value);
|
||||
|
||||
sampler = [device newSamplerStateWithDescriptor:desc];
|
||||
}
|
||||
@@ -531,10 +522,10 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder)
|
||||
int rth = 0;
|
||||
|
||||
const auto &rt = state.renderTargets.getFirstTarget();
|
||||
if (rt.canvas.get())
|
||||
if (rt.texture.get())
|
||||
{
|
||||
rtw = rt.canvas->getPixelWidth();
|
||||
rth = rt.canvas->getPixelHeight();
|
||||
rtw = rt.texture->getPixelWidth();
|
||||
rth = rt.texture->getPixelHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -653,7 +644,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
instanceCount:cmd.instanceCount];
|
||||
}}
|
||||
|
||||
void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, Texture *texture)
|
||||
void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture)
|
||||
{ @autoreleasepool {
|
||||
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
|
||||
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
|
||||
@@ -693,7 +684,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu
|
||||
}
|
||||
}}
|
||||
|
||||
void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas)
|
||||
void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas)
|
||||
{
|
||||
const DisplayState &state = states.back();
|
||||
// TODO
|
||||
@@ -707,7 +698,7 @@ void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pix
|
||||
void Graphics::endPass()
|
||||
{
|
||||
auto &rts = states.back().renderTargets;
|
||||
love::graphics::Canvas *depthstencil = rts.depthStencil.canvas.get();
|
||||
love::graphics::Texture *depthstencil = rts.depthStencil.texture.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)
|
||||
@@ -715,15 +706,15 @@ void Graphics::endPass()
|
||||
|
||||
// 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)
|
||||
if (rts.colors.size() > 0 && rts.colors[0].texture->getMSAA() > 1)
|
||||
{
|
||||
int mip = rts.colors[0].mipmap;
|
||||
int w = rts.colors[0].canvas->getPixelWidth(mip);
|
||||
int h = rts.colors[0].canvas->getPixelHeight(mip);
|
||||
int w = rts.colors[0].texture->getPixelWidth(mip);
|
||||
int h = rts.colors[0].texture->getPixelHeight(mip);
|
||||
|
||||
for (int i = 0; i < (int) rts.colors.size(); i++)
|
||||
{
|
||||
Canvas *c = (Canvas *) rts.colors[i].canvas.get();
|
||||
Texture *c = (Texture *) rts.colors[i].texture.get();
|
||||
|
||||
if (!c->isReadable())
|
||||
continue;
|
||||
@@ -739,12 +730,12 @@ void Graphics::endPass()
|
||||
|
||||
for (const auto &rt : rts.colors)
|
||||
{
|
||||
if (rt.canvas->getMipmapMode() == Canvas::MIPMAPS_AUTO && rt.mipmap == 0)
|
||||
rt.canvas->generateMipmaps();
|
||||
if (rt.texture->getMipmapsMode() == Texture::MIPMAPS_AUTO && rt.mipmap == 0)
|
||||
rt.texture->generateMipmaps();
|
||||
}
|
||||
|
||||
int dsmipmap = rts.depthStencil.mipmap;
|
||||
if (depthstencil != nullptr && depthstencil->getMipmapMode() == Canvas::MIPMAPS_AUTO && dsmipmap == 0)
|
||||
if (depthstencil != nullptr && depthstencil->getMipmapsMode() == Texture::MIPMAPS_AUTO && dsmipmap == 0)
|
||||
depthstencil->generateMipmaps();
|
||||
}
|
||||
|
||||
@@ -786,8 +777,8 @@ void Graphics::present(void *screenshotCallbackData)
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
if (isCanvasActive())
|
||||
throw love::Exception("present cannot be called while a Canvas is active.");
|
||||
if (isRenderTargetActive())
|
||||
throw love::Exception("present cannot be called while a render target is active.");
|
||||
|
||||
deprecations.draw(this);
|
||||
|
||||
@@ -865,20 +856,20 @@ void Graphics::present(void *screenshotCallbackData)
|
||||
// Reset the per-frame stat counts.
|
||||
drawCalls = 0;
|
||||
//gl.stats.shaderSwitches = 0;
|
||||
canvasSwitchCount = 0;
|
||||
renderTargetSwitchCount = 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--)
|
||||
for (int i = (int) temporaryTextures.size() - 1; i >= 0; i--)
|
||||
{
|
||||
if (temporaryCanvases[i].framesSinceUse >= MAX_TEMPORARY_CANVAS_UNUSED_FRAMES)
|
||||
if (temporaryTextures[i].framesSinceUse >= MAX_TEMPORARY_TEXTURE_UNUSED_FRAMES)
|
||||
{
|
||||
temporaryCanvases[i].canvas->release();
|
||||
temporaryCanvases[i] = temporaryCanvases.back();
|
||||
temporaryCanvases.pop_back();
|
||||
temporaryTextures[i].texture->release();
|
||||
temporaryTextures[i] = temporaryTextures.back();
|
||||
temporaryTextures.pop_back();
|
||||
}
|
||||
else
|
||||
temporaryCanvases[i].framesSinceUse++;
|
||||
temporaryTextures[i].framesSinceUse++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,11 +897,11 @@ void Graphics::setScissor()
|
||||
void Graphics::drawToStencilBuffer(StencilAction action, int value)
|
||||
{
|
||||
const auto &rts = states.back().renderTargets;
|
||||
love::graphics::Canvas *dscanvas = rts.depthStencil.canvas.get();
|
||||
love::graphics::Texture *dstexture = rts.depthStencil.texture.get();
|
||||
|
||||
if (!isCanvasActive() && !windowHasStencil)
|
||||
if (!isRenderTargetActive() && !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())))
|
||||
else if (isRenderTargetActive() && (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) == 0 && (dstexture == nullptr || !isPixelFormatStencil(dstexture->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();
|
||||
|
||||
Reference in New Issue
Block a user