First Metal cleanup & formatting pass

This commit is contained in:
wheremyfoodat 2024-11-09 13:09:12 +02:00
parent 4cc62d4870
commit 49b65242b9
17 changed files with 1084 additions and 1115 deletions

View file

@ -19,8 +19,6 @@ template <typename SurfaceType, size_t capacity, bool evictOnOverflow = false>
class SurfaceCache { class SurfaceCache {
// Vanilla std::optional can't hold actual references // Vanilla std::optional can't hold actual references
using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>; using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>;
//static_assert(std::is_same<SurfaceType, ColourBuffer>() || std::is_same<SurfaceType, DepthBuffer>() ||
// std::is_same<SurfaceType, Texture>(), "Invalid surface type");
size_t size; size_t size;
size_t evictionIndex; size_t evictionIndex;

View file

@ -7,7 +7,6 @@
using namespace PICA; using namespace PICA;
namespace Metal { namespace Metal {
struct BlitPipelineHash { struct BlitPipelineHash {
// Formats // Formats
ColorFmt colorFmt; ColorFmt colorFmt;
@ -71,5 +70,4 @@ private:
MTL::Function* vertexFunction; MTL::Function* vertexFunction;
MTL::Function* fragmentFunction; MTL::Function* fragmentFunction;
}; };
} // namespace Metal } // namespace Metal

View file

@ -3,7 +3,6 @@
#include <Metal/Metal.hpp> #include <Metal/Metal.hpp>
namespace Metal { namespace Metal {
struct RenderState { struct RenderState {
MTL::RenderPipelineState* renderPipelineState = nullptr; MTL::RenderPipelineState* renderPipelineState = nullptr;
MTL::DepthStencilState* depthStencilState = nullptr; MTL::DepthStencilState* depthStencilState = nullptr;
@ -54,5 +53,4 @@ private:
RenderState renderState; RenderState renderState;
}; };
} // namespace Metal } // namespace Metal

View file

@ -7,28 +7,24 @@
using namespace PICA; using namespace PICA;
namespace Metal { namespace Metal {
struct DepthStencilHash { struct DepthStencilHash {
bool depthStencilWrite;
u8 depthFunc;
u32 stencilConfig; u32 stencilConfig;
u16 stencilOpConfig; u16 stencilOpConfig;
bool depthStencilWrite;
u8 depthFunc;
}; };
class DepthStencilCache { class DepthStencilCache {
public: public:
DepthStencilCache() = default; DepthStencilCache() = default;
~DepthStencilCache() { ~DepthStencilCache() { reset(); }
reset();
}
void set(MTL::Device* dev) { void set(MTL::Device* dev) { device = dev; }
device = dev;
}
MTL::DepthStencilState* get(DepthStencilHash hash) { MTL::DepthStencilState* get(DepthStencilHash hash) {
u64 intHash = ((u64)hash.depthStencilWrite << 56) | ((u64)hash.depthFunc << 48) | ((u64)hash.stencilConfig << 16) | (u64)hash.stencilOpConfig; u64 intHash =
((u64)hash.depthStencilWrite << 56) | ((u64)hash.depthFunc << 48) | ((u64)hash.stencilConfig << 16) | (u64)hash.stencilOpConfig;
auto& depthStencilState = depthStencilCache[intHash]; auto& depthStencilState = depthStencilCache[intHash];
if (!depthStencilState) { if (!depthStencilState) {
MTL::DepthStencilDescriptor* desc = MTL::DepthStencilDescriptor::alloc()->init(); MTL::DepthStencilDescriptor* desc = MTL::DepthStencilDescriptor::alloc()->init();
@ -79,8 +75,6 @@ public:
private: private:
std::map<u64, MTL::DepthStencilState*> depthStencilCache; std::map<u64, MTL::DepthStencilState*> depthStencilCache;
MTL::Device* device; MTL::Device* device;
}; };
} // namespace Metal } // namespace Metal

View file

@ -7,7 +7,6 @@
using namespace PICA; using namespace PICA;
namespace Metal { namespace Metal {
struct DrawFragmentFunctionHash { struct DrawFragmentFunctionHash {
bool lightingEnabled; // 1 bit bool lightingEnabled; // 1 bit
u8 lightingNumLights; // 3 bits u8 lightingNumLights; // 3 bits
@ -16,11 +15,6 @@ struct DrawFragmentFunctionHash {
u16 alphaControl; // 12 bits (mask: 11111111 0111 0001) u16 alphaControl; // 12 bits (mask: 11111111 0111 0001)
}; };
//bool operator==(const DrawFragmentFunctionHash& l, const DrawFragmentFunctionHash& r) {
// return ((l.lightingEnabled == r.lightingEnabled) && (l.lightingNumLights == r.lightingNumLights) &&
// (l.lightingConfig1 == r.lightingConfig1) && (l.alphaControl == r.alphaControl));
//}
inline bool operator<(const DrawFragmentFunctionHash& l, const DrawFragmentFunctionHash& r) { inline bool operator<(const DrawFragmentFunctionHash& l, const DrawFragmentFunctionHash& r) {
if (!l.lightingEnabled && r.lightingEnabled) return true; if (!l.lightingEnabled && r.lightingEnabled) return true;
if (l.lightingNumLights < r.lightingNumLights) return true; if (l.lightingNumLights < r.lightingNumLights) return true;
@ -44,12 +38,6 @@ struct DrawPipelineHash { // 56 bits
DrawFragmentFunctionHash fragHash; DrawFragmentFunctionHash fragHash;
}; };
//bool operator==(const DrawPipelineHash& l, const DrawPipelineHash& r) {
// return (((u32)l.colorFmt == (u32)r.colorFmt) && ((u32)l.depthFmt == (u32)r.depthFmt) &&
// (l.blendEnabled == r.blendEnabled) && (l.blendControl == r.blendControl) &&
// (l.colorWriteMask == r.colorWriteMask) && (l.fragHash == r.fragHash));
//}
inline bool operator<(const DrawPipelineHash& l, const DrawPipelineHash& r) { inline bool operator<(const DrawPipelineHash& l, const DrawPipelineHash& r) {
if ((u32)l.colorFmt < (u32)r.colorFmt) return true; if ((u32)l.colorFmt < (u32)r.colorFmt) return true;
if ((u32)l.depthFmt < (u32)r.depthFmt) return true; if ((u32)l.depthFmt < (u32)r.depthFmt) return true;
@ -61,9 +49,6 @@ inline bool operator<(const DrawPipelineHash& l, const DrawPipelineHash& r) {
return false; return false;
} }
// Bind the vertex buffer to binding 30 so that it doesn't occupy the lower indices
#define VERTEX_BUFFER_BINDING_INDEX 30
// This pipeline only caches the pipeline with all of its color and depth attachment variations // This pipeline only caches the pipeline with all of its color and depth attachment variations
class DrawPipelineCache { class DrawPipelineCache {
public: public:
@ -83,9 +68,8 @@ public:
} }
MTL::RenderPipelineState* get(DrawPipelineHash hash) { MTL::RenderPipelineState* get(DrawPipelineHash hash) {
//u32 fragmentFunctionHash = ((u32)hash.lightingEnabled << 22) | ((u32)hash.lightingNumLights << 19) | ((u32)hash.lightingConfig1 << 12) | ((((u32)hash.alphaControl & 0b1111111100000000) >> 8) << 4) | ((((u32)hash.alphaControl & 0b01110000) >> 4) << 1) | ((u32)hash.alphaControl & 0b0001);
//u64 pipelineHash = ((u64)hash.colorFmt << 53) | ((u64)hash.depthFmt << 50) | ((u64)hash.blendEnabled << 49) | ((u64)hash.colorWriteMask << 45) | ((((u64)hash.blendControl & 0b11111111111111110000000000000000) >> 16) << 29) | ((((u64)hash.blendControl & 0b0000011100000000) >> 8) << 26) | (((u64)hash.blendControl & 0b00000111) << 23) | fragmentFunctionHash;
auto& pipeline = pipelineCache[hash]; auto& pipeline = pipelineCache[hash];
if (!pipeline) { if (!pipeline) {
auto& fragmentFunction = fragmentFunctionCache[hash.fragHash]; auto& fragmentFunction = fragmentFunctionCache[hash.fragHash];
if (!fragmentFunction) { if (!fragmentFunction) {
@ -137,8 +121,7 @@ public:
MTL::PixelFormat depthFormat = toMTLPixelFormatDepth(hash.depthFmt); MTL::PixelFormat depthFormat = toMTLPixelFormatDepth(hash.depthFmt);
desc->setDepthAttachmentPixelFormat(depthFormat); desc->setDepthAttachmentPixelFormat(depthFormat);
if (hash.depthFmt == DepthFmt::Depth24Stencil8) if (hash.depthFmt == DepthFmt::Depth24Stencil8) desc->setStencilAttachmentPixelFormat(depthFormat);
desc->setStencilAttachmentPixelFormat(depthFormat);
NS::Error* error = nullptr; NS::Error* error = nullptr;
desc->setLabel(toNSString("Draw pipeline")); desc->setLabel(toNSString("Draw pipeline"));
@ -158,6 +141,7 @@ public:
pair.second->release(); pair.second->release();
} }
pipelineCache.clear(); pipelineCache.clear();
for (auto& pair : fragmentFunctionCache) { for (auto& pair : fragmentFunctionCache) {
pair.second->release(); pair.second->release();
} }

View file

@ -8,17 +8,12 @@ class LutTexture {
public: public:
LutTexture(MTL::Device* device, MTL::TextureType type, MTL::PixelFormat pixelFormat, u32 width, u32 height, const char* name); LutTexture(MTL::Device* device, MTL::TextureType type, MTL::PixelFormat pixelFormat, u32 width, u32 height, const char* name);
~LutTexture(); ~LutTexture();
u32 getNextIndex(); u32 getNextIndex();
// Getters
MTL::Texture* getTexture() { return texture; } MTL::Texture* getTexture() { return texture; }
u32 getCurrentIndex() { return currentIndex; } u32 getCurrentIndex() { return currentIndex; }
private: private:
MTL::Texture* texture; MTL::Texture* texture;
u32 currentIndex = 0; u32 currentIndex = 0;
}; };

View file

@ -1,19 +1,19 @@
#pragma once #pragma once
#include <Metal/Metal.hpp>
#include <array> #include <array>
#include <string> #include <string>
#include <Metal/Metal.hpp>
#include "boost/icl/interval.hpp" #include "boost/icl/interval.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "math_util.hpp" #include "math_util.hpp"
#include "objc_helper.hpp"
#include "opengl.hpp" #include "opengl.hpp"
#include "pica_to_mtl.hpp" #include "pica_to_mtl.hpp"
#include "objc_helper.hpp"
template <typename T> template <typename T>
using Interval = boost::icl::right_open_interval<T>; using Interval = boost::icl::right_open_interval<T>;
namespace Metal { namespace Metal {
template <typename Format_t> template <typename Format_t>
struct RenderTarget { struct RenderTarget {
MTL::Device* device; MTL::Device* device;
@ -47,8 +47,7 @@ struct RenderTarget {
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match // For 2 textures to "match" we only care about their locations, formats, and dimensions to match
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture // For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
bool matches(RenderTarget& other) { bool matches(RenderTarget& other) {
return location == other.location && format == other.format && return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
size.x() == other.size.x() && size.y() == other.size.y();
} }
void allocate() { void allocate() {
@ -69,7 +68,10 @@ struct RenderTarget {
descriptor->setUsage(MTL::TextureUsageRenderTarget | MTL::TextureUsageShaderRead); descriptor->setUsage(MTL::TextureUsageRenderTarget | MTL::TextureUsageShaderRead);
descriptor->setStorageMode(MTL::StorageModePrivate); descriptor->setStorageMode(MTL::StorageModePrivate);
texture = device->newTexture(descriptor); texture = device->newTexture(descriptor);
texture->setLabel(toNSString(std::string(std::is_same<Format_t, PICA::ColorFmt>::value ? "Color" : "Depth") + " render target " + std::to_string(size.u()) + "x" + std::to_string(size.v()))); texture->setLabel(toNSString(
std::string(std::is_same<Format_t, PICA::ColorFmt>::value ? "Color" : "Depth") + " render target " + std::to_string(size.u()) + "x" +
std::to_string(size.v())
));
descriptor->release(); descriptor->release();
} }
@ -81,12 +83,9 @@ struct RenderTarget {
} }
} }
u64 sizeInBytes() { u64 sizeInBytes() { return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format); }
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
}
}; };
typedef RenderTarget<PICA::ColorFmt> ColorRenderTarget; using ColorRenderTarget = RenderTarget<PICA::ColorFmt>;
typedef RenderTarget<PICA::DepthFmt> DepthStencilRenderTarget; using DepthStencilRenderTarget = RenderTarget<PICA::DepthFmt>;
} // namespace Metal } // namespace Metal

View file

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <Metal/Metal.hpp>
#include <array> #include <array>
#include <string> #include <string>
#include <Metal/Metal.hpp>
#include "PICA/regs.hpp" #include "PICA/regs.hpp"
#include "boost/icl/interval.hpp" #include "boost/icl/interval.hpp"
#include "helpers.hpp" #include "helpers.hpp"
@ -10,11 +11,11 @@
#include "opengl.hpp" #include "opengl.hpp"
#include "renderer_mtl/pica_to_mtl.hpp" #include "renderer_mtl/pica_to_mtl.hpp"
template <typename T> template <typename T>
using Interval = boost::icl::right_open_interval<T>; using Interval = boost::icl::right_open_interval<T>;
namespace Metal { namespace Metal {
struct Texture { struct Texture {
MTL::Device* device; MTL::Device* device;
@ -35,7 +36,6 @@ struct Texture {
Texture(MTL::Device* dev, u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true) Texture(MTL::Device* dev, u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
: device(dev), location(loc), format(format), size({x, y}), config(config), valid(valid) { : device(dev), location(loc), format(format), size({x, y}), config(config), valid(valid) {
u64 endLoc = (u64)loc + sizeInBytes(); u64 endLoc = (u64)loc + sizeInBytes();
// Check if start and end are valid here // Check if start and end are valid here
range = Interval<u32>(loc, (u32)endLoc); range = Interval<u32>(loc, (u32)endLoc);
@ -44,8 +44,7 @@ struct Texture {
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match // For 2 textures to "match" we only care about their locations, formats, and dimensions to match
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture // For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
bool matches(Texture& other) { bool matches(Texture& other) {
return location == other.location && format == other.format && return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
size.x() == other.size.x() && size.y() == other.size.y();
} }
void allocate(); void allocate();
@ -65,14 +64,11 @@ struct Texture {
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width); static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
// Returns the format of this texture as a string // Returns the format of this texture as a string
std::string_view formatToString() { std::string_view formatToString() { return PICA::textureFormatToString(format); }
return PICA::textureFormatToString(format);
}
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture // Returns the texel at coordinates (u, v) of an ETC1(A4) texture
// TODO: Make hasAlpha a template parameter // TODO: Make hasAlpha a template parameter
u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data); u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data);
u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData); u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData);
}; };
} // namespace Metal } // namespace Metal

View file

@ -5,16 +5,15 @@
using namespace PICA; using namespace PICA;
namespace Metal { namespace Metal {
struct BufferHandle { struct BufferHandle {
MTL::Buffer* buffer; MTL::Buffer* buffer;
size_t offset; size_t offset;
}; };
// 128MB buffer for caching vertex data
#define CACHE_BUFFER_SIZE 128 * 1024 * 1024
class VertexBufferCache { class VertexBufferCache {
// 128MB buffer for caching vertex data
static constexpr usize CACHE_BUFFER_SIZE = 128 * 1024 * 1024;
public: public:
VertexBufferCache() = default; VertexBufferCache() = default;
@ -76,5 +75,4 @@ private:
buffer->setLabel(toNSString("Shared vertex buffer")); buffer->setLabel(toNSString("Shared vertex buffer"));
} }
}; };
} // namespace Metal } // namespace Metal

View file

@ -5,12 +5,8 @@
#include "mtl_common.hpp" #include "mtl_common.hpp"
namespace Metal { namespace Metal {
dispatch_data_t createDispatchData(const void* data, size_t size); dispatch_data_t createDispatchData(const void* data, size_t size);
} // namespace Metal } // namespace Metal
// Cast from std::string to NS::String* // Cast from std::string to NS::String*
inline NS::String* toNSString(const std::string& str) { inline NS::String* toNSString(const std::string& str) { return NS::String::string(str.c_str(), NS::ASCIIStringEncoding); }
return NS::String::string(str.c_str(), NS::ASCIIStringEncoding);
}

View file

@ -1,10 +1,11 @@
#pragma once #pragma once
#include <Metal/Metal.hpp> #include <Metal/Metal.hpp>
#include "PICA/regs.hpp" #include "PICA/regs.hpp"
namespace PICA {
namespace PICA {
struct PixelFormatInfo { struct PixelFormatInfo {
MTL::PixelFormat pixelFormat; MTL::PixelFormat pixelFormat;
size_t bytesPerTexel; size_t bytesPerTexel;
@ -27,9 +28,7 @@ constexpr PixelFormatInfo pixelFormatInfos[14] = {
{MTL::PixelFormatRGBA8Unorm, 4}, // ETC1A4 {MTL::PixelFormatRGBA8Unorm, 4}, // ETC1A4
}; };
inline PixelFormatInfo getPixelFormatInfo(TextureFmt format) { inline PixelFormatInfo getPixelFormatInfo(TextureFmt format) { return pixelFormatInfos[static_cast<int>(format)]; }
return pixelFormatInfos[static_cast<int>(format)];
}
inline MTL::PixelFormat toMTLPixelFormatColor(ColorFmt format) { inline MTL::PixelFormat toMTLPixelFormatColor(ColorFmt format) {
switch (format) { switch (format) {
@ -45,7 +44,8 @@ inline MTL::PixelFormat toMTLPixelFormatDepth(DepthFmt format) {
switch (format) { switch (format) {
case DepthFmt::Depth16: return MTL::PixelFormatDepth16Unorm; case DepthFmt::Depth16: return MTL::PixelFormatDepth16Unorm;
case DepthFmt::Unknown1: return MTL::PixelFormatInvalid; case DepthFmt::Unknown1: return MTL::PixelFormatInvalid;
case DepthFmt::Depth24: return MTL::PixelFormatDepth32Float; // Metal does not support 24-bit depth formats case DepthFmt::Depth24:
return MTL::PixelFormatDepth32Float; // Metal does not support 24-bit depth formats
// Apple sillicon doesn't support 24-bit depth buffers, so we use 32-bit instead // Apple sillicon doesn't support 24-bit depth buffers, so we use 32-bit instead
case DepthFmt::Depth24Stencil8: return MTL::PixelFormatDepth32Float_Stencil8; case DepthFmt::Depth24Stencil8: return MTL::PixelFormatDepth32Float_Stencil8;
} }
@ -151,5 +151,4 @@ inline MTL::SamplerAddressMode toMTLSamplerAddressMode(u8 addrMode) {
return MTL::SamplerAddressModeClampToEdge; return MTL::SamplerAddressModeClampToEdge;
} }
} // namespace PICA } // namespace PICA

View file

@ -3,15 +3,16 @@
#include <Metal/Metal.hpp> #include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp> #include <QuartzCore/QuartzCore.hpp>
#include "renderer.hpp"
#include "mtl_texture.hpp"
#include "mtl_render_target.hpp"
#include "mtl_blit_pipeline_cache.hpp" #include "mtl_blit_pipeline_cache.hpp"
#include "mtl_draw_pipeline_cache.hpp"
#include "mtl_depth_stencil_cache.hpp"
#include "mtl_vertex_buffer_cache.hpp"
#include "mtl_lut_texture.hpp"
#include "mtl_command_encoder.hpp" #include "mtl_command_encoder.hpp"
#include "mtl_depth_stencil_cache.hpp"
#include "mtl_draw_pipeline_cache.hpp"
#include "mtl_lut_texture.hpp"
#include "mtl_render_target.hpp"
#include "mtl_texture.hpp"
#include "mtl_vertex_buffer_cache.hpp"
#include "renderer.hpp"
// HACK: use the OpenGL cache // HACK: use the OpenGL cache
#include "../renderer_gl/surface_cache.hpp" #include "../renderer_gl/surface_cache.hpp"
@ -101,7 +102,9 @@ class RendererMTL final : public Renderer {
} }
} }
void beginRenderPassIfNeeded(MTL::RenderPassDescriptor* renderPassDescriptor, bool doesClears, MTL::Texture* colorTexture, MTL::Texture* depthTexture = nullptr); void beginRenderPassIfNeeded(
MTL::RenderPassDescriptor* renderPassDescriptor, bool doesClears, MTL::Texture* colorTexture, MTL::Texture* depthTexture = nullptr
);
void commitCommandBuffer() { void commitCommandBuffer() {
if (renderCommandEncoder) { if (renderCommandEncoder) {
@ -119,7 +122,10 @@ class RendererMTL final : public Renderer {
} }
template <typename AttachmentT, typename ClearDataT, typename GetAttachmentT, typename SetClearDataT> template <typename AttachmentT, typename ClearDataT, typename GetAttachmentT, typename SetClearDataT>
inline void clearAttachment(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture, ClearDataT clearData, GetAttachmentT getAttachment, SetClearDataT setClearData) { inline void clearAttachment(
MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture, ClearDataT clearData, GetAttachmentT getAttachment,
SetClearDataT setClearData
) {
bool beginRenderPass = (renderPassDescriptor == nullptr); bool beginRenderPass = (renderPassDescriptor == nullptr);
if (!renderPassDescriptor) { if (!renderPassDescriptor) {
renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
@ -140,7 +146,10 @@ class RendererMTL final : public Renderer {
} }
template <typename AttachmentT, typename ClearDataT, typename GetAttachmentT, typename SetClearDataT> template <typename AttachmentT, typename ClearDataT, typename GetAttachmentT, typename SetClearDataT>
inline bool clearAttachment(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture, std::map<MTL::Texture*, ClearDataT>& clearOps, GetAttachmentT getAttachment, SetClearDataT setClearData) { inline bool clearAttachment(
MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture, std::map<MTL::Texture*, ClearDataT>& clearOps,
GetAttachmentT getAttachment, SetClearDataT setClearData
) {
auto it = clearOps.find(texture); auto it = clearOps.find(texture);
if (it != clearOps.end()) { if (it != clearOps.end()) {
clearAttachment<AttachmentT>(renderPassDescriptor, texture, it->second, getAttachment, setClearData); clearAttachment<AttachmentT>(renderPassDescriptor, texture, it->second, getAttachment, setClearData);
@ -159,29 +168,40 @@ class RendererMTL final : public Renderer {
} }
bool clearColor(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) { bool clearColor(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) {
return clearAttachment<MTL::RenderPassColorAttachmentDescriptor, Color4>(renderPassDescriptor, texture, colorClearOps, [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->colorAttachments()->object(0); }, [](auto attachment, auto& color) { return clearAttachment<MTL::RenderPassColorAttachmentDescriptor, Color4>(
attachment->setClearColor(MTL::ClearColor(color.r, color.g, color.b, color.a)); renderPassDescriptor, texture, colorClearOps,
}); [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->colorAttachments()->object(0); },
[](auto attachment, auto& color) { attachment->setClearColor(MTL::ClearColor(color.r, color.g, color.b, color.a)); }
);
} }
bool clearDepth(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) { bool clearDepth(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) {
return clearAttachment<MTL::RenderPassDepthAttachmentDescriptor, float>(renderPassDescriptor, texture, depthClearOps, [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->depthAttachment(); }, [](auto attachment, auto& depth) { return clearAttachment<MTL::RenderPassDepthAttachmentDescriptor, float>(
attachment->setClearDepth(depth); renderPassDescriptor, texture, depthClearOps,
}); [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->depthAttachment(); },
[](auto attachment, auto& depth) { attachment->setClearDepth(depth); }
);
} }
bool clearStencil(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) { bool clearStencil(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) {
return clearAttachment<MTL::RenderPassStencilAttachmentDescriptor, u8>(renderPassDescriptor, texture, stencilClearOps, [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->stencilAttachment(); }, [](auto attachment, auto& stencil) { return clearAttachment<MTL::RenderPassStencilAttachmentDescriptor, u8>(
attachment->setClearStencil(stencil); renderPassDescriptor, texture, stencilClearOps,
}); [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->stencilAttachment(); },
[](auto attachment, auto& stencil) { attachment->setClearStencil(stencil); }
);
} }
std::optional<Metal::ColorRenderTarget> getColorRenderTarget(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true); std::optional<Metal::ColorRenderTarget> getColorRenderTarget(
u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true
);
Metal::DepthStencilRenderTarget& getDepthRenderTarget(); Metal::DepthStencilRenderTarget& getDepthRenderTarget();
Metal::Texture& getTexture(Metal::Texture& tex); Metal::Texture& getTexture(Metal::Texture& tex);
void setupTextureEnvState(MTL::RenderCommandEncoder* encoder); void setupTextureEnvState(MTL::RenderCommandEncoder* encoder);
void bindTexturesToSlots(); void bindTexturesToSlots();
void updateLightingLUT(MTL::RenderCommandEncoder* encoder); void updateLightingLUT(MTL::RenderCommandEncoder* encoder);
void updateFogLUT(MTL::RenderCommandEncoder* encoder); void updateFogLUT(MTL::RenderCommandEncoder* encoder);
void textureCopyImpl(Metal::ColorRenderTarget& srcFramebuffer, Metal::ColorRenderTarget& destFramebuffer, const Math::Rect<u32>& srcRect, const Math::Rect<u32>& destRect); void textureCopyImpl(
Metal::ColorRenderTarget& srcFramebuffer, Metal::ColorRenderTarget& destFramebuffer, const Math::Rect<u32>& srcRect,
const Math::Rect<u32>& destRect
);
}; };

View file

@ -12,8 +12,9 @@ static constexpr u32 signExtend3To32(u32 val) {
u32 Texture::getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data) { u32 Texture::getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data) {
// Pixel offset of the 8x8 tile based on u, v and the width of the texture // Pixel offset of the 8x8 tile based on u, v and the width of the texture
u32 offs = ((u & ~7) * 8) + ((v & ~7) * width); u32 offs = ((u & ~7) * 8) + ((v & ~7) * width);
if (!hasAlpha) if (!hasAlpha) {
offs >>= 1; offs >>= 1;
}
// In-tile offsets for u/v // In-tile offsets for u/v
u &= 7; u &= 7;

View file

@ -1,12 +1,13 @@
#include <algorithm> #include <algorithm>
#include "colour.hpp" #include "colour.hpp"
#include "renderer_mtl/renderer_mtl.hpp"
#include "renderer_mtl/mtl_texture.hpp" #include "renderer_mtl/mtl_texture.hpp"
#include "renderer_mtl/renderer_mtl.hpp"
using namespace Helpers; using namespace Helpers;
namespace Metal { namespace Metal {
static constexpr u32 signExtend3To32(u32 val) { static constexpr u32 signExtend3To32(u32 val) {
return (u32)(s32(val) << 29 >> 29); return (u32)(s32(val) << 29 >> 29);
} }
@ -14,8 +15,9 @@ static constexpr u32 signExtend3To32(u32 val) {
u32 Texture::getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data) { u32 Texture::getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data) {
// Pixel offset of the 8x8 tile based on u, v and the width of the texture // Pixel offset of the 8x8 tile based on u, v and the width of the texture
u32 offs = ((u & ~7) * 8) + ((v & ~7) * width); u32 offs = ((u & ~7) * 8) + ((v & ~7) * width);
if (!hasAlpha) if (!hasAlpha) {
offs >>= 1; offs >>= 1;
}
// In-tile offsets for u/v // In-tile offsets for u/v
u &= 7; u &= 7;
@ -38,8 +40,7 @@ u32 Texture::getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const
// First 64 bits of the 4x4 subtile are alpha data // First 64 bits of the 4x4 subtile are alpha data
const u64 alphaData = *ptr++; const u64 alphaData = *ptr++;
alpha = Colour::convert4To8Bit((alphaData >> (4 * (u * 4 + v))) & 0xf); alpha = Colour::convert4To8Bit((alphaData >> (4 * (u * 4 + v))) & 0xf);
} } else {
else {
alpha = 0xff; // ETC1 without alpha uses ff for every pixel alpha = 0xff; // ETC1 without alpha uses ff for every pixel
} }
@ -50,14 +51,7 @@ u32 Texture::getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const
u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) { u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) {
static constexpr u32 modifiers[8][2] = { static constexpr u32 modifiers[8][2] = {
{ 2, 8 }, {2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183},
{ 5, 17 },
{ 9, 29 },
{ 13, 42 },
{ 18, 60 },
{ 24, 80 },
{ 33, 106 },
{ 47, 183 },
}; };
// Parse colour data for 4x4 block // Parse colour data for 4x4 block
@ -71,8 +65,7 @@ u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) {
const u32 tableIndex2 = getBits<34, 3, u32>(colourData); const u32 tableIndex2 = getBits<34, 3, u32>(colourData);
const u32 texelIndex = u * 4 + v; // Index of the texel in the block const u32 texelIndex = u * 4 + v; // Index of the texel in the block
if (flip) if (flip) std::swap(u, v);
std::swap(u, v);
s32 r, g, b; s32 r, g, b;
if (diffMode) { if (diffMode) {
@ -120,5 +113,4 @@ u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) {
return (alpha << 24) | (u32(b) << 16) | (u32(g) << 8) | u32(r); return (alpha << 24) | (u32(b) << 16) | (u32(g) << 8) | u32(r);
} }
} // namespace Metal } // namespace Metal

View file

@ -1,8 +1,7 @@
#include "renderer_mtl/renderer_mtl.hpp" #include "renderer_mtl/renderer_mtl.hpp"
namespace Metal { namespace Metal {
static constexpr u32 LAYER_COUNT = 1024;
constexpr u32 LAYER_COUNT = 1024;
LutTexture::LutTexture(MTL::Device* device, MTL::TextureType type, MTL::PixelFormat pixelFormat, u32 width, u32 height, const char* name) { LutTexture::LutTexture(MTL::Device* device, MTL::TextureType type, MTL::PixelFormat pixelFormat, u32 width, u32 height, const char* name) {
MTL::TextureDescriptor* desc = MTL::TextureDescriptor::alloc()->init(); MTL::TextureDescriptor* desc = MTL::TextureDescriptor::alloc()->init();
@ -19,14 +18,10 @@ LutTexture::LutTexture(MTL::Device* device, MTL::TextureType type, MTL::PixelFor
desc->release(); desc->release();
} }
LutTexture::~LutTexture() { LutTexture::~LutTexture() { texture->release(); }
texture->release();
}
u32 LutTexture::getNextIndex() { u32 LutTexture::getNextIndex() {
currentIndex = (currentIndex + 1) % LAYER_COUNT; currentIndex = (currentIndex + 1) % LAYER_COUNT;
return currentIndex; return currentIndex;
} }
} // namespace Metal } // namespace Metal

View file

@ -1,12 +1,14 @@
#include "renderer_mtl/mtl_texture.hpp" #include "renderer_mtl/mtl_texture.hpp"
#include "renderer_mtl/objc_helper.hpp"
#include "colour.hpp"
#include <array> #include <array>
#include "colour.hpp"
#include "renderer_mtl/objc_helper.hpp"
using namespace Helpers; using namespace Helpers;
namespace Metal { namespace Metal {
void Texture::allocate() { void Texture::allocate() {
formatInfo = PICA::getPixelFormatInfo(format); formatInfo = PICA::getPixelFormatInfo(format);
@ -18,7 +20,9 @@ void Texture::allocate() {
descriptor->setUsage(MTL::TextureUsageShaderRead); descriptor->setUsage(MTL::TextureUsageShaderRead);
descriptor->setStorageMode(MTL::StorageModeShared); // TODO: use private + staging buffers? descriptor->setStorageMode(MTL::StorageModeShared); // TODO: use private + staging buffers?
texture = device->newTexture(descriptor); texture = device->newTexture(descriptor);
texture->setLabel(toNSString("Texture " + std::string(PICA::textureFormatToString(format)) + " " + std::to_string(size.u()) + "x" + std::to_string(size.v()))); texture->setLabel(toNSString(
"Texture " + std::string(PICA::textureFormatToString(format)) + " " + std::to_string(size.u()) + "x" + std::to_string(size.v())
));
descriptor->release(); descriptor->release();
setNewConfig(config); setNewConfig(config);
@ -73,17 +77,14 @@ u64 Texture::sizeInBytes() {
case PICA::TextureFmt::RGB565: case PICA::TextureFmt::RGB565:
case PICA::TextureFmt::RGBA4: case PICA::TextureFmt::RGBA4:
case PICA::TextureFmt::RG8: case PICA::TextureFmt::RG8:
case PICA::TextureFmt::IA8: case PICA::TextureFmt::IA8: return pixelCount * 2;
return pixelCount * 2;
case PICA::TextureFmt::A8: // 1 byte per pixel case PICA::TextureFmt::A8: // 1 byte per pixel
case PICA::TextureFmt::I8: case PICA::TextureFmt::I8:
case PICA::TextureFmt::IA4: case PICA::TextureFmt::IA4: return pixelCount;
return pixelCount;
case PICA::TextureFmt::I4: // 4 bits per pixel case PICA::TextureFmt::I4: // 4 bits per pixel
case PICA::TextureFmt::A4: case PICA::TextureFmt::A4: return pixelCount / 2;
return pixelCount / 2;
case PICA::TextureFmt::ETC1: // Compressed formats case PICA::TextureFmt::ETC1: // Compressed formats
case PICA::TextureFmt::ETC1A4: { case PICA::TextureFmt::ETC1A4: {
@ -94,8 +95,7 @@ u64 Texture::sizeInBytes() {
return tileCount * tileSize; return tileCount * tileSize;
} }
default: default: Helpers::panic("[PICA] Attempted to get size of invalid texture type");
Helpers::panic("[PICA] Attempted to get size of invalid texture type");
} }
} }
@ -149,8 +149,7 @@ u8 Texture::decodeTexelU8(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8
return alpha; return alpha;
} }
default: default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
} }
} }
@ -225,8 +224,7 @@ u16 Texture::decodeTexelU16(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const
return (intensity << 12) | (intensity << 8) | (intensity << 4) | 0xff; return (intensity << 12) | (intensity << 8) | (intensity << 4) | 0xff;
} }
default: default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
} }
} }
@ -275,8 +273,7 @@ u32 Texture::decodeTexelU32(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const
case PICA::TextureFmt::ETC1: return getTexelETC(false, u, v, size.u(), data); case PICA::TextureFmt::ETC1: return getTexelETC(false, u, v, size.u(), data);
case PICA::TextureFmt::ETC1A4: return getTexelETC(true, u, v, size.u(), data); case PICA::TextureFmt::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
default: default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
} }
} }
@ -308,5 +305,4 @@ void Texture::decodeTexture(std::span<const u8> data) {
texture->replaceRegion(MTL::Region(0, 0, size.u(), size.v()), 0, 0, decoded.data(), formatInfo.bytesPerTexel * size.u(), 0); texture->replaceRegion(MTL::Region(0, 0, size.u(), size.v()), 0, 0, decoded.data(), formatInfo.bytesPerTexel * size.u(), 0);
} }
} // namespace Metal } // namespace Metal

View file

@ -2,9 +2,10 @@
#include <cmrc/cmrc.hpp> #include <cmrc/cmrc.hpp>
#include <cstddef> #include <cstddef>
#include "renderer_mtl/mtl_lut_texture.hpp" #include "renderer_mtl/mtl_lut_texture.hpp"
// HACK // Hack: Apple annoyingly defines a global "NO" macro which ends up conflicting with our own code...
#undef NO #undef NO
#include "PICA/gpu.hpp" #include "PICA/gpu.hpp"
@ -14,8 +15,10 @@ using namespace PICA;
CMRC_DECLARE(RendererMTL); CMRC_DECLARE(RendererMTL);
const u16 LIGHTING_LUT_TEXTURE_WIDTH = 256; static constexpr u16 LIGHTING_LUT_TEXTURE_WIDTH = 256;
const u32 FOG_LUT_TEXTURE_WIDTH = 128; static constexpr u32 FOG_LUT_TEXTURE_WIDTH = 128;
// Bind the vertex buffer to binding 30 so that it doesn't occupy the lower indices
static constexpr uint VERTEX_BUFFER_BINDING_INDEX = 30;
// HACK: redefinition... // HACK: redefinition...
PICA::ColorFmt ToColorFormat(u32 format) { PICA::ColorFmt ToColorFormat(u32 format) {
@ -40,6 +43,7 @@ MTL::Library* loadLibrary(MTL::Device* device, const cmrc::file& shaderSource) {
RendererMTL::RendererMTL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs) RendererMTL::RendererMTL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
: Renderer(gpu, internalRegs, externalRegs) {} : Renderer(gpu, internalRegs, externalRegs) {}
RendererMTL::~RendererMTL() {} RendererMTL::~RendererMTL() {}
void RendererMTL::reset() { void RendererMTL::reset() {
@ -78,7 +82,7 @@ void RendererMTL::display() {
clearColor(nullptr, bottomScreen->get().texture); clearColor(nullptr, bottomScreen->get().texture);
} }
// -------- Draw -------- // Draw
commandBuffer->pushDebugGroup(toNSString("Display")); commandBuffer->pushDebugGroup(toNSString("Display"));
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
@ -130,8 +134,6 @@ void RendererMTL::initGraphicsContext(SDL_Window* window) {
metalLayer->setDevice(device); metalLayer->setDevice(device);
commandQueue = device->newCommandQueue(); commandQueue = device->newCommandQueue();
// -------- Objects --------
// Textures // Textures
MTL::TextureDescriptor* textureDescriptor = MTL::TextureDescriptor::alloc()->init(); MTL::TextureDescriptor* textureDescriptor = MTL::TextureDescriptor::alloc()->init();
textureDescriptor->setTextureType(MTL::TextureType2D); textureDescriptor->setTextureType(MTL::TextureType2D);
@ -157,7 +159,9 @@ void RendererMTL::initGraphicsContext(SDL_Window* window) {
samplerDescriptor->release(); samplerDescriptor->release();
lutLightingTexture = new Metal::LutTexture(device, MTL::TextureType2DArray, MTL::PixelFormatR16Unorm, LIGHTING_LUT_TEXTURE_WIDTH, Lights::LUT_Count, "Lighting LUT texture"); lutLightingTexture = new Metal::LutTexture(
device, MTL::TextureType2DArray, MTL::PixelFormatR16Unorm, LIGHTING_LUT_TEXTURE_WIDTH, Lights::LUT_Count, "Lighting LUT texture"
);
lutFogTexture = new Metal::LutTexture(device, MTL::TextureType1DArray, MTL::PixelFormatRG32Float, FOG_LUT_TEXTURE_WIDTH, 1, "Fog LUT texture"); lutFogTexture = new Metal::LutTexture(device, MTL::TextureType1DArray, MTL::PixelFormatRG32Float, FOG_LUT_TEXTURE_WIDTH, 1, "Fog LUT texture");
// -------- Pipelines -------- // -------- Pipelines --------
@ -295,7 +299,6 @@ void RendererMTL::initGraphicsContext(SDL_Window* window) {
defaultDepthStencilState = device->newDepthStencilState(depthStencilDescriptor); defaultDepthStencilState = device->newDepthStencilState(depthStencilDescriptor);
depthStencilDescriptor->release(); depthStencilDescriptor->release();
// Release
blitLibrary->release(); blitLibrary->release();
// copyToLutTextureLibrary->release(); // copyToLutTextureLibrary->release();
} }
@ -592,7 +595,6 @@ void RendererMTL::deinitGraphicsContext() {
delete lutLightingTexture; delete lutLightingTexture;
delete lutFogTexture; delete lutFogTexture;
// Release
// copyToLutTexturePipeline->release(); // copyToLutTexturePipeline->release();
displayPipeline->release(); displayPipeline->release();
defaultDepthStencilState->release(); defaultDepthStencilState->release();
@ -736,7 +738,9 @@ void RendererMTL::updateLightingLUT(MTL::RenderCommandEncoder* encoder) {
} }
u32 index = lutLightingTexture->getNextIndex(); u32 index = lutLightingTexture->getNextIndex();
lutLightingTexture->getTexture()->replaceRegion(MTL::Region(0, 0, LIGHTING_LUT_TEXTURE_WIDTH, Lights::LUT_Count), 0, index, lightingLut.data(), LIGHTING_LUT_TEXTURE_WIDTH * 2, 0); lutLightingTexture->getTexture()->replaceRegion(
MTL::Region(0, 0, LIGHTING_LUT_TEXTURE_WIDTH, Lights::LUT_Count), 0, index, lightingLut.data(), LIGHTING_LUT_TEXTURE_WIDTH * 2, 0
);
/* /*
endRenderPass(); endRenderPass();
@ -807,7 +811,8 @@ void RendererMTL::textureCopyImpl(
) { ) {
nextRenderPassName = "Texture copy"; nextRenderPassName = "Texture copy";
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
// TODO: clearColor sets the load action to load if it didn't find any clear, but that is unnecessary if we are doing a copy to the whole texture // TODO: clearColor sets the load action to load if it didn't find any clear, but that is unnecessary if we are doing a copy to the whole
// texture
bool doesClear = clearColor(renderPassDescriptor, destFramebuffer.texture); bool doesClear = clearColor(renderPassDescriptor, destFramebuffer.texture);
beginRenderPassIfNeeded(renderPassDescriptor, doesClear, destFramebuffer.texture); beginRenderPassIfNeeded(renderPassDescriptor, doesClear, destFramebuffer.texture);
@ -819,11 +824,13 @@ void RendererMTL::textureCopyImpl(
// Viewport // Viewport
renderCommandEncoder->setViewport(MTL::Viewport{ renderCommandEncoder->setViewport(MTL::Viewport{
double(destRect.left), double(destRect.bottom), double(destRect.right - destRect.left), double(destRect.top - destRect.bottom), 0.0, 1.0 double(destRect.left), double(destRect.bottom), double(destRect.right - destRect.left), double(destRect.top - destRect.bottom), 0.0, 1.0});
});
float srcRectNDC[4] = { float srcRectNDC[4] = {
srcRect.left / (float)srcFramebuffer.size.u(), srcRect.bottom / (float)srcFramebuffer.size.v(), srcRect.left / (float)srcFramebuffer.size.u(),
(srcRect.right - srcRect.left) / (float)srcFramebuffer.size.u(), (srcRect.top - srcRect.bottom) / (float)srcFramebuffer.size.v() srcRect.bottom / (float)srcFramebuffer.size.v(),
(srcRect.right - srcRect.left) / (float)srcFramebuffer.size.u(),
(srcRect.top - srcRect.bottom) / (float)srcFramebuffer.size.v(),
}; };
// Bind resources // Bind resources
@ -834,10 +841,13 @@ void RendererMTL::textureCopyImpl(
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));
} }
void RendererMTL::beginRenderPassIfNeeded(MTL::RenderPassDescriptor* renderPassDescriptor, bool doesClears, MTL::Texture* colorTexture, MTL::Texture* depthTexture) { void RendererMTL::beginRenderPassIfNeeded(
MTL::RenderPassDescriptor* renderPassDescriptor, bool doesClears, MTL::Texture* colorTexture, MTL::Texture* depthTexture
) {
createCommandBufferIfNeeded(); createCommandBufferIfNeeded();
if (doesClears || !renderCommandEncoder || colorTexture != lastColorTexture || (depthTexture != lastDepthTexture && !(lastDepthTexture && !depthTexture))) { if (doesClears || !renderCommandEncoder || colorTexture != lastColorTexture ||
(depthTexture != lastDepthTexture && !(lastDepthTexture && !depthTexture))) {
endRenderPass(); endRenderPass();
renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor); renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor);