Commit fe26bae4 by Le Quyen Committed by Commit Bot

Metal backend implementation pt 2

This is without Metal specific shader translator implemented yet. Bug: angleproject:2634 Change-Id: I95d589442251c9ba111bd05a2dc379a36739046c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1855069Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 076fae01
...@@ -31,6 +31,7 @@ enum class FeatureCategory ...@@ -31,6 +31,7 @@ enum class FeatureCategory
D3DCompilerWorkarounds, D3DCompilerWorkarounds,
VulkanWorkarounds, VulkanWorkarounds,
VulkanFeatures, VulkanFeatures,
MetalFeatures,
}; };
constexpr char kFeatureCategoryFrontendWorkarounds[] = "Frontend workarounds"; constexpr char kFeatureCategoryFrontendWorkarounds[] = "Frontend workarounds";
...@@ -39,6 +40,7 @@ constexpr char kFeatureCategoryD3DWorkarounds[] = "D3D workarounds"; ...@@ -39,6 +40,7 @@ constexpr char kFeatureCategoryD3DWorkarounds[] = "D3D workarounds";
constexpr char kFeatureCategoryD3DCompilerWorkarounds[] = "D3D compiler workarounds"; constexpr char kFeatureCategoryD3DCompilerWorkarounds[] = "D3D compiler workarounds";
constexpr char kFeatureCategoryVulkanWorkarounds[] = "Vulkan workarounds"; constexpr char kFeatureCategoryVulkanWorkarounds[] = "Vulkan workarounds";
constexpr char kFeatureCategoryVulkanFeatures[] = "Vulkan features"; constexpr char kFeatureCategoryVulkanFeatures[] = "Vulkan features";
constexpr char kFeatureCategoryMetalFeatures[] = "Metal features";
constexpr char kFeatureCategoryUnknown[] = "Unknown"; constexpr char kFeatureCategoryUnknown[] = "Unknown";
inline const char *FeatureCategoryToString(const FeatureCategory &fc) inline const char *FeatureCategoryToString(const FeatureCategory &fc)
...@@ -69,6 +71,10 @@ inline const char *FeatureCategoryToString(const FeatureCategory &fc) ...@@ -69,6 +71,10 @@ inline const char *FeatureCategoryToString(const FeatureCategory &fc)
return kFeatureCategoryVulkanFeatures; return kFeatureCategoryVulkanFeatures;
break; break;
case FeatureCategory::MetalFeatures:
return kFeatureCategoryMetalFeatures;
break;
default: default:
return kFeatureCategoryUnknown; return kFeatureCategoryUnknown;
break; break;
......
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// FeaturesMtl.h: Optional features for the Metal renderer.
//
#ifndef ANGLE_PLATFORM_FEATURESMTL_H_
#define ANGLE_PLATFORM_FEATURESMTL_H_
#include "platform/Feature.h"
namespace angle
{
struct FeaturesMtl : FeatureSetBase
{
// BaseVertex/Instanced draw support:
Feature hasBaseVertexInstancedDraw = {
"has_base_vertex_instanced_draw", FeatureCategory::MetalFeatures,
"The renderer supports base vertex instanced draw", &members};
// Non-uniform compute shader dispatch support, i.e. Group size is not necessarily to be fixed:
Feature hasNonUniformDispatch = {
"has_non_uniform_dispatch", FeatureCategory::MetalFeatures,
"The renderer supports non uniform compute shader dispatch's group size", &members};
// Texture swizzle support:
Feature hasTextureSwizzle = {"has_texture_swizzle", FeatureCategory::MetalFeatures,
"The renderer supports texture swizzle", &members};
// On macos, separate depth & stencil buffers are not supproted. However, on iOS devices,
// they are supproted:
Feature allowSeparatedDepthStencilBuffers = {
"allow_separate_depth_stencil_buffers", FeatureCategory::MetalFeatures,
"Some Apple platforms such as iOS allows separate depth & stencil buffers, "
"whereas others such as macOS don't",
&members};
};
} // namespace angle
#endif // ANGLE_PLATFORM_FEATURESMTL_H_
...@@ -37,6 +37,7 @@ namespace angle ...@@ -37,6 +37,7 @@ namespace angle
{ {
struct FeaturesD3D; struct FeaturesD3D;
struct FeaturesVk; struct FeaturesVk;
struct FeaturesMtl;
using TraceEventHandle = uint64_t; using TraceEventHandle = uint64_t;
using EGLDisplayType = void *; using EGLDisplayType = void *;
struct PlatformMethods; struct PlatformMethods;
...@@ -225,6 +226,11 @@ using OverrideFeaturesVkFunc = void (*)(PlatformMethods *platform, ...@@ -225,6 +226,11 @@ using OverrideFeaturesVkFunc = void (*)(PlatformMethods *platform,
inline void DefaultOverrideFeaturesVk(PlatformMethods *platform, angle::FeaturesVk *featuresVulkan) inline void DefaultOverrideFeaturesVk(PlatformMethods *platform, angle::FeaturesVk *featuresVulkan)
{} {}
using OverrideFeaturesMtlFunc = void (*)(PlatformMethods *platform,
angle::FeaturesMtl *featuresMetal);
inline void DefaultOverrideFeaturesMtl(PlatformMethods *platform, angle::FeaturesMtl *featuresMetal)
{}
// Callback on a successful program link with the program binary. Can be used to store // Callback on a successful program link with the program binary. Can be used to store
// shaders to disk. Keys are a 160-bit SHA-1 hash. // shaders to disk. Keys are a 160-bit SHA-1 hash.
using ProgramKeyType = std::array<uint8_t, 20>; using ProgramKeyType = std::array<uint8_t, 20>;
...@@ -254,6 +260,7 @@ inline void DefaultCacheProgram(PlatformMethods *platform, ...@@ -254,6 +260,7 @@ inline void DefaultCacheProgram(PlatformMethods *platform,
OP(histogramBoolean, HistogramBoolean) \ OP(histogramBoolean, HistogramBoolean) \
OP(overrideWorkaroundsD3D, OverrideWorkaroundsD3D) \ OP(overrideWorkaroundsD3D, OverrideWorkaroundsD3D) \
OP(overrideFeaturesVk, OverrideFeaturesVk) \ OP(overrideFeaturesVk, OverrideFeaturesVk) \
OP(overrideFeaturesMtl, OverrideFeaturesMtl) \
OP(cacheProgram, CacheProgram) OP(cacheProgram, CacheProgram)
#define ANGLE_PLATFORM_METHOD_DEF(Name, CapsName) CapsName##Func Name = Default##CapsName; #define ANGLE_PLATFORM_METHOD_DEF(Name, CapsName) CapsName##Func Name = Default##CapsName;
......
{ {
"src/libANGLE/renderer/metal/shaders/blit.metal":
"86cad2bdbbed1fe7ec94f4ce01ede0c1",
"src/libANGLE/renderer/metal/shaders/clear.metal":
"1c231afc6100433a79fce49046aa5965",
"src/libANGLE/renderer/metal/shaders/common.h":
"d44903e10552301d7ec89c1a0e6e0862",
"src/libANGLE/renderer/metal/shaders/compiled/mtl_default_shaders.inc": "src/libANGLE/renderer/metal/shaders/compiled/mtl_default_shaders.inc":
"a60a4682f4aa7cea2d35e551af938e3b", "ae02216e4b3588d28b0e87a54deddabb",
"src/libANGLE/renderer/metal/shaders/gen_indices.metal":
"002511e2b980a7fca7e80cbda6a82712",
"src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py": "src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py":
"5d8832978de07470b2d6dbf18104ba76", "8de75752bb966cdbe575defc04fa7a7a",
"src/libANGLE/renderer/metal/shaders/master_source.metal": "src/libANGLE/renderer/metal/shaders/master_source.metal":
"fbe6f4bfb49a48ae87791a4cff5fab0a", "fbe6f4bfb49a48ae87791a4cff5fab0a",
"src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc": "src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc":
"e3546ea73c88d12f8d59b2b6a286b4bf" "492ddcfd51acfc95d02de2658cf999cd"
} }
\ No newline at end of file
{ {
"src/libANGLE/renderer/metal/gen_mtl_format_table.py": "src/libANGLE/renderer/metal/gen_mtl_format_table.py":
"5dd54a352213e303ba517fa105bca8fb", "afa19b351ae9615be0ec962790d81bd3",
"src/libANGLE/renderer/metal/mtl_format_map.json": "src/libANGLE/renderer/metal/mtl_format_map.json":
"c6f5b6dda11e456cfbcaeec53eb46fa0", "c6f5b6dda11e456cfbcaeec53eb46fa0",
"src/libANGLE/renderer/metal/mtl_format_table_autogen.mm": "src/libANGLE/renderer/metal/mtl_format_table_autogen.mm":
"9ccbbb1c6d4f84ea2f8a22c126040ec9" "706e07c2bf5b50fa031678a5c2372465"
} }
\ No newline at end of file
...@@ -186,7 +186,7 @@ IGNORED_INCLUDES = { ...@@ -186,7 +186,7 @@ IGNORED_INCLUDES = {
b'libANGLE/renderer/gl/egl/ozone/DisplayOzone.h', b'libANGLE/renderer/gl/egl/ozone/DisplayOzone.h',
b'libANGLE/renderer/gl/egl/android/DisplayAndroid.h', b'libANGLE/renderer/gl/egl/android/DisplayAndroid.h',
b'libANGLE/renderer/gl/wgl/DisplayWGL.h', b'libANGLE/renderer/gl/wgl/DisplayWGL.h',
b'libANGLE/renderer/metal/DisplayMtl.h', b'libANGLE/renderer/metal/DisplayMtl_api.h',
b'libANGLE/renderer/null/DisplayNULL.h', b'libANGLE/renderer/null/DisplayNULL.h',
b'libANGLE/renderer/vulkan/android/DisplayVkAndroid.h', b'libANGLE/renderer/vulkan/android/DisplayVkAndroid.h',
b'libANGLE/renderer/vulkan/fuchsia/DisplayVkFuchsia.h', b'libANGLE/renderer/vulkan/fuchsia/DisplayVkFuchsia.h',
......
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// apple_platform_utils.h: Common utilities for Apple platforms.
#ifndef COMMON_APPLE_PLATFORM_UTILS_H_
#define COMMON_APPLE_PLATFORM_UTILS_H_
#include <TargetConditionals.h>
// These are macros for substitution of Apple specific directive @available:
// TARGET_OS_MACCATALYST only available in MacSDK 10.15
// ANGLE_APPLE_AVAILABLE_XCI: check if either of the 3 platforms (OSX/Catalyst/iOS) min verions is
// available:
#if TARGET_OS_MACCATALYST
# define ANGLE_APPLE_AVAILABLE_XCI(macVer, macCatalystVer, iOSVer) \
@available(macOS macVer, macCatalyst macCatalystVer, iOS iOSVer, *)
// ANGLE_APPLE_AVAILABLE_XC: check if either of the 2 platforms (OSX/Catalyst) min verions is
// available:
# define ANGLE_APPLE_AVAILABLE_XC(macVer, macCatalystVer) \
@available(macOS macVer, macCatalyst macCatalystVer, *)
#else
# define ANGLE_APPLE_AVAILABLE_XCI(macVer, macCatalystVer, iOSVer) \
ANGLE_APPLE_AVAILABLE_XI(macVer, iOSVer)
// ANGLE_APPLE_AVAILABLE_XC: check if either of the 2 platforms (OSX/Catalyst) min verions is
// available:
# define ANGLE_APPLE_AVAILABLE_XC(macVer, macCatalystVer) @available(macOS macVer, *)
#endif
// ANGLE_APPLE_AVAILABLE_XI: check if either of the 2 platforms (OSX/iOS) min verions is available:
#define ANGLE_APPLE_AVAILABLE_XI(macVer, iOSVer) @available(macOS macVer, iOS iOSVer, *)
#endif
...@@ -629,6 +629,24 @@ bool IsTriangleMode(PrimitiveMode drawMode) ...@@ -629,6 +629,24 @@ bool IsTriangleMode(PrimitiveMode drawMode)
return false; return false;
} }
bool IsPolygonMode(PrimitiveMode mode)
{
switch (mode)
{
case PrimitiveMode::Points:
case PrimitiveMode::Lines:
case PrimitiveMode::LineStrip:
case PrimitiveMode::LineLoop:
case PrimitiveMode::LinesAdjacency:
case PrimitiveMode::LineStripAdjacency:
return false;
default:
break;
}
return true;
}
namespace priv namespace priv
{ {
const angle::PackedEnumMap<PrimitiveMode, bool> gLineModes = { const angle::PackedEnumMap<PrimitiveMode, bool> gLineModes = {
......
...@@ -96,6 +96,7 @@ static_assert(GetPrimitiveRestartIndexFromType<uint32_t>() == 0xFFFFFFFF, ...@@ -96,6 +96,7 @@ static_assert(GetPrimitiveRestartIndexFromType<uint32_t>() == 0xFFFFFFFF,
"verify restart index for uint8_t values"); "verify restart index for uint8_t values");
bool IsTriangleMode(PrimitiveMode drawMode); bool IsTriangleMode(PrimitiveMode drawMode);
bool IsPolygonMode(PrimitiveMode mode);
namespace priv namespace priv
{ {
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
#endif // defined(ANGLE_ENABLE_VULKAN) #endif // defined(ANGLE_ENABLE_VULKAN)
#if defined(ANGLE_ENABLE_METAL) #if defined(ANGLE_ENABLE_METAL)
# include "libANGLE/renderer/metal/DisplayMtl.h" # include "libANGLE/renderer/metal/DisplayMtl_api.h"
#endif // defined(ANGLE_ENABLE_METAL) #endif // defined(ANGLE_ENABLE_METAL)
namespace egl namespace egl
...@@ -183,7 +183,7 @@ EGLAttrib GetDisplayTypeFromEnvironment() ...@@ -183,7 +183,7 @@ EGLAttrib GetDisplayTypeFromEnvironment()
#endif #endif
#if defined(ANGLE_ENABLE_METAL) #if defined(ANGLE_ENABLE_METAL)
if (rx::DisplayMtl::IsMetalAvailable()) if (rx::IsMetalDisplayAvailable())
{ {
return EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE; return EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE;
} }
...@@ -202,6 +202,12 @@ EGLAttrib GetDisplayTypeFromEnvironment() ...@@ -202,6 +202,12 @@ EGLAttrib GetDisplayTypeFromEnvironment()
# else # else
return EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE; return EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
# endif # endif
#elif defined(ANGLE_ENABLE_METAL)
// If we reach this point, it means rx::IsMetalDisplayAvailable() return false
// and ANGLE_ENABLE_OPENGL is not defined.
// Use default type as a fallback. Just to please the compiler.
// CreateDisplayFromAttribs() will fail regardless.
return EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
#elif defined(ANGLE_ENABLE_VULKAN) #elif defined(ANGLE_ENABLE_VULKAN)
return EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; return EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
#elif defined(ANGLE_ENABLE_NULL) #elif defined(ANGLE_ENABLE_NULL)
...@@ -298,9 +304,9 @@ rx::DisplayImpl *CreateDisplayFromAttribs(const AttributeMap &attribMap, const D ...@@ -298,9 +304,9 @@ rx::DisplayImpl *CreateDisplayFromAttribs(const AttributeMap &attribMap, const D
break; break;
case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE: case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
#if defined(ANGLE_ENABLE_METAL) #if defined(ANGLE_ENABLE_METAL)
if (rx::DisplayMtl::IsMetalAvailable()) if (rx::IsMetalDisplayAvailable())
{ {
impl = new rx::DisplayMtl(state); impl = rx::CreateMetalDisplay(state);
break; break;
} }
#endif #endif
......
...@@ -17,6 +17,7 @@ _metal_backend_sources = [ ...@@ -17,6 +17,7 @@ _metal_backend_sources = [
"ContextMtl.h", "ContextMtl.h",
"ContextMtl.mm", "ContextMtl.mm",
"DisplayMtl.h", "DisplayMtl.h",
"DisplayMtl_api.h",
"DisplayMtl.mm", "DisplayMtl.mm",
"FrameBufferMtl.h", "FrameBufferMtl.h",
"FrameBufferMtl.mm", "FrameBufferMtl.mm",
...@@ -24,8 +25,8 @@ _metal_backend_sources = [ ...@@ -24,8 +25,8 @@ _metal_backend_sources = [
"ProgramMtl.mm", "ProgramMtl.mm",
"RenderBufferMtl.h", "RenderBufferMtl.h",
"RenderBufferMtl.mm", "RenderBufferMtl.mm",
"RendererMtl.h", "RenderTargetMtl.h",
"RendererMtl.mm", "RenderTargetMtl.mm",
"ShaderMtl.h", "ShaderMtl.h",
"ShaderMtl.mm", "ShaderMtl.mm",
"SurfaceMtl.h", "SurfaceMtl.h",
...@@ -34,8 +35,27 @@ _metal_backend_sources = [ ...@@ -34,8 +35,27 @@ _metal_backend_sources = [
"TextureMtl.mm", "TextureMtl.mm",
"VertexArrayMtl.h", "VertexArrayMtl.h",
"VertexArrayMtl.mm", "VertexArrayMtl.mm",
"mtl_buffer_pool.h",
"mtl_buffer_pool.mm",
"mtl_command_buffer.h",
"mtl_command_buffer.mm",
"mtl_common.h", "mtl_common.h",
"mtl_common.mm", "mtl_common.mm",
"mtl_format_table_autogen.mm",
"mtl_format_utils.h",
"mtl_format_utils.mm",
"mtl_glslang_utils.h",
"mtl_glslang_utils.mm",
"mtl_render_utils.h",
"mtl_render_utils.mm",
"mtl_resources.h",
"mtl_resources.mm",
"mtl_state_cache.h",
"mtl_state_cache.mm",
"mtl_utils.h",
"mtl_utils.mm",
"shaders/mtl_default_shaders_src_autogen.inc",
"shaders/compiled/mtl_default_shaders.inc",
] ]
config("angle_metal_backend_config") { config("angle_metal_backend_config") {
...@@ -59,6 +79,7 @@ angle_source_set("angle_metal_backend") { ...@@ -59,6 +79,7 @@ angle_source_set("angle_metal_backend") {
libs = [] libs = []
public_deps = [ public_deps = [
"${angle_root}:angle_glslang_wrapper",
"${angle_root}:libANGLE_headers", "${angle_root}:libANGLE_headers",
] ]
......
...@@ -18,11 +18,63 @@ ...@@ -18,11 +18,63 @@
#include "libANGLE/Observer.h" #include "libANGLE/Observer.h"
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#include "libANGLE/renderer/BufferImpl.h" #include "libANGLE/renderer/BufferImpl.h"
#include "libANGLE/renderer/Format.h"
#include "libANGLE/renderer/metal/mtl_buffer_pool.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
namespace rx namespace rx
{ {
class BufferMtl : public BufferImpl // Conversion buffers hold translated index and vertex data.
struct ConversionBufferMtl
{
ConversionBufferMtl(const gl::Context *context, size_t initialSize, size_t alignment);
~ConversionBufferMtl();
// One state value determines if we need to re-stream vertex data.
bool dirty;
// The conversion is stored in a dynamic buffer.
mtl::BufferPool data;
};
struct IndexConversionBufferMtl : public ConversionBufferMtl
{
IndexConversionBufferMtl(const gl::Context *context,
gl::DrawElementsType type,
size_t offsetIn);
const gl::DrawElementsType type;
const size_t offset;
// These properties are to be filled by user of this buffer conversion
mtl::BufferRef convertedBuffer;
size_t convertedOffset;
};
class BufferHolderMtl
{
public:
virtual ~BufferHolderMtl() = default;
// Due to the complication of synchronizing accesses between CPU and GPU,
// a mtl::Buffer might be under used by GPU but CPU wants to modify its content through
// map() method, this could lead to GPU stalling. The more efficient method is maintain
// a queue of mtl::Buffer and only let CPU modifies a free mtl::Buffer.
// So, in order to let GPU use the most recent modified content, one must call this method
// right before the draw call to retrieved the most up-to-date mtl::Buffer.
mtl::BufferRef getCurrentBuffer(const gl::Context *context)
{
return mIsWeak ? mBufferWeakRef.lock() : mBuffer;
}
protected:
mtl::BufferRef mBuffer;
mtl::BufferWeakRef mBufferWeakRef;
bool mIsWeak = false;
};
class BufferMtl : public BufferImpl, public BufferHolderMtl
{ {
public: public:
BufferMtl(const gl::BufferState &state); BufferMtl(const gl::BufferState &state);
...@@ -58,6 +110,67 @@ class BufferMtl : public BufferImpl ...@@ -58,6 +110,67 @@ class BufferMtl : public BufferImpl
size_t count, size_t count,
bool primitiveRestartEnabled, bool primitiveRestartEnabled,
gl::IndexRange *outRange) override; gl::IndexRange *outRange) override;
angle::Result getFirstLastIndices(const gl::Context *context,
gl::DrawElementsType type,
size_t offset,
size_t count,
std::pair<uint32_t, uint32_t> *outIndices) const;
const uint8_t *getClientShadowCopyData(const gl::Context *context);
ConversionBufferMtl *getVertexConversionBuffer(const gl::Context *context,
angle::FormatID formatID,
GLuint stride,
size_t offset);
IndexConversionBufferMtl *getIndexConversionBuffer(const gl::Context *context,
gl::DrawElementsType type,
size_t offset);
size_t size() const { return mState.getSize(); }
private:
angle::Result setSubDataImpl(const gl::Context *context,
const void *data,
size_t size,
size_t offset);
angle::Result commitShadowCopy(const gl::Context *context);
void markConversionBuffersDirty();
// Client side shadow buffer
angle::MemoryBuffer mShadowCopy;
// GPU side buffers pool
mtl::BufferPool mBufferPool;
struct VertexConversionBuffer : public ConversionBufferMtl
{
VertexConversionBuffer(const gl::Context *context,
angle::FormatID formatIDIn,
GLuint strideIn,
size_t offsetIn);
// The conversion is identified by the triple of {format, stride, offset}.
angle::FormatID formatID;
GLuint stride;
size_t offset;
};
// A cache of converted vertex data.
std::vector<VertexConversionBuffer> mVertexConversionBuffers;
std::vector<IndexConversionBufferMtl> mIndexConversionBuffers;
};
class SimpleWeakBufferHolderMtl : public BufferHolderMtl
{
public:
SimpleWeakBufferHolderMtl();
void set(const mtl::BufferRef &buffer) { mBufferWeakRef = buffer; }
}; };
} // namespace rx } // namespace rx
......
...@@ -15,11 +15,15 @@ ...@@ -15,11 +15,15 @@
#include "common/Optional.h" #include "common/Optional.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/renderer/ContextImpl.h" #include "libANGLE/renderer/ContextImpl.h"
#include "libANGLE/renderer/metal/mtl_common.h" #include "libANGLE/renderer/metal/mtl_buffer_pool.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
#include "libANGLE/renderer/metal/mtl_utils.h"
namespace rx namespace rx
{ {
class RendererMtl; class DisplayMtl;
class FramebufferMtl; class FramebufferMtl;
class VertexArrayMtl; class VertexArrayMtl;
class ProgramMtl; class ProgramMtl;
...@@ -27,7 +31,7 @@ class ProgramMtl; ...@@ -27,7 +31,7 @@ class ProgramMtl;
class ContextMtl : public ContextImpl, public mtl::Context class ContextMtl : public ContextImpl, public mtl::Context
{ {
public: public:
ContextMtl(const gl::State &state, gl::ErrorSet *errorSet, RendererMtl *renderer); ContextMtl(const gl::State &state, gl::ErrorSet *errorSet, DisplayMtl *display);
~ContextMtl() override; ~ContextMtl() override;
angle::Result initialize() override; angle::Result initialize() override;
...@@ -185,13 +189,204 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -185,13 +189,204 @@ class ContextMtl : public ContextImpl, public mtl::Context
const char *file, const char *file,
const char *function, const char *function,
unsigned int line) override; unsigned int line) override;
void handleError(NSError *_Nullable error,
const char *file,
const char *function,
unsigned int line) override;
using ContextImpl::handleError; using ContextImpl::handleError;
void invalidateState(const gl::Context *context);
void invalidateDefaultAttribute(size_t attribIndex);
void invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask);
void invalidateCurrentTextures();
void invalidateDriverUniforms();
void invalidateRenderPipeline();
// Call this to notify ContextMtl whenever FramebufferMtl's state changed
void onDrawFrameBufferChange(const gl::Context *context, FramebufferMtl *framebuffer);
const MTLClearColor &getClearColorValue() const;
MTLColorWriteMask getColorMask() const;
float getClearDepthValue() const;
uint32_t getClearStencilValue() const;
// Return front facing stencil write mask
uint32_t getStencilMask() const;
bool isDepthWriteEnabled() const;
const mtl::Format &getPixelFormat(angle::FormatID angleFormatId) const;
// See mtl::FormatTable::getVertexFormat()
const mtl::VertexFormat &getVertexFormat(angle::FormatID angleFormatId,
bool tightlyPacked) const;
// Recommended to call these methods to end encoding instead of invoking the encoder's
// endEncoding() directly.
void endEncoding(mtl::RenderCommandEncoder *encoder);
// Ends any active command encoder
void endEncoding(bool forceSaveRenderPassContent);
void flushCommandBufer();
void present(const gl::Context *context, id<CAMetalDrawable> presentationDrawable);
angle::Result finishCommandBuffer();
// Check whether compatible render pass has been started.
bool hasStartedRenderPass(const mtl::RenderPassDesc &desc);
bool hasStartedRenderPass(FramebufferMtl *framebuffer);
// Get current render encoder. May be nullptr if no render pass has been started.
mtl::RenderCommandEncoder *getRenderCommandEncoder();
mtl::RenderCommandEncoder *getCurrentFramebufferRenderCommandEncoder();
// Will end current command encoder if it is valid, then start new encoder.
// Unless hasStartedRenderPass(desc) returns true.
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::RenderPassDesc &desc);
// Utilities to quickly create render command enconder to a specific texture:
// The previous content of texture will be loaded if clearColor is not provided
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::TextureRef &textureTarget,
const gl::ImageIndex &index,
const Optional<MTLClearColor> &clearColor);
// The previous content of texture will be loaded
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::TextureRef &textureTarget,
const gl::ImageIndex &index);
// Will end current command encoder and start new blit command encoder. Unless a blit comamnd
// encoder is already started.
mtl::BlitCommandEncoder *getBlitCommandEncoder();
// Will end current command encoder and start new compute command encoder. Unless a compute
// command encoder is already started.
mtl::ComputeCommandEncoder *getComputeCommandEncoder();
private: private:
gl::TextureCapsMap mNativeTextureCaps; void ensureCommandBufferValid();
gl::Extensions mNativeExtensions; angle::Result setupDraw(const gl::Context *context,
gl::Caps mNativeCaps; gl::PrimitiveMode mode,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrNone,
const void *indices);
angle::Result genLineLoopLastSegment(const gl::Context *context,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrNone,
const void *indices,
mtl::BufferRef *lastSegmentIndexBufferOut);
angle::Result drawTriFanArrays(const gl::Context *context, GLint first, GLsizei count);
angle::Result drawTriFanArraysWithBaseVertex(const gl::Context *context,
GLint first,
GLsizei count);
angle::Result drawTriFanArraysLegacy(const gl::Context *context, GLint first, GLsizei count);
angle::Result drawTriFanElements(const gl::Context *context,
GLsizei count,
gl::DrawElementsType type,
const void *indices);
void updateViewport(FramebufferMtl *framebufferMtl,
const gl::Rectangle &viewport,
float nearPlane,
float farPlane);
void updateDepthRange(float nearPlane, float farPlane);
void updateScissor(const gl::State &glState);
void updateCullMode(const gl::State &glState);
void updateFrontFace(const gl::State &glState);
void updateDepthBias(const gl::State &glState);
void updateDrawFrameBufferBinding(const gl::Context *context);
void updateProgramExecutable(const gl::Context *context);
void updateVertexArray(const gl::Context *context);
angle::Result updateDefaultAttribute(size_t attribIndex);
angle::Result handleDirtyDefaultAttribs(const gl::Context *context);
angle::Result handleDirtyDriverUniforms(const gl::Context *context);
angle::Result handleDirtyDepthStencilState(const gl::Context *context);
angle::Result handleDirtyDepthBias(const gl::Context *context);
angle::Result checkIfPipelineChanged(const gl::Context *context,
gl::PrimitiveMode primitiveMode,
Optional<mtl::RenderPipelineDesc> *changedPipelineDesc);
// Dirty bits.
enum DirtyBitType : size_t
{
DIRTY_BIT_DEFAULT_ATTRIBS,
DIRTY_BIT_TEXTURES,
DIRTY_BIT_DRIVER_UNIFORMS,
DIRTY_BIT_DEPTH_STENCIL_DESC,
DIRTY_BIT_DEPTH_BIAS,
DIRTY_BIT_STENCIL_REF,
DIRTY_BIT_BLEND_COLOR,
DIRTY_BIT_VIEWPORT,
DIRTY_BIT_SCISSOR,
DIRTY_BIT_DRAW_FRAMEBUFFER,
DIRTY_BIT_CULL_MODE,
DIRTY_BIT_WINDING,
DIRTY_BIT_RENDER_PIPELINE,
DIRTY_BIT_MAX,
};
// See compiler/translator/TranslatorVulkan.cpp: AddDriverUniformsToShader()
struct DriverUniforms
{
float viewport[4];
float halfRenderAreaHeight;
float viewportYScale;
float negViewportYScale;
// NOTE(hqle): Transform feedsback is not supported yet.
uint32_t xfbActiveUnpaused;
int32_t xfbBufferOffsets[4];
uint32_t acbBufferOffsets[4];
// We'll use x, y, z for near / far / diff respectively.
float depthRange[4];
};
struct DefaultAttribute
{
// NOTE(hqle): Support integer default attributes in ES 3.0
float values[4];
};
mtl::CommandBuffer mCmdBuffer;
mtl::RenderCommandEncoder mRenderEncoder;
mtl::BlitCommandEncoder mBlitEncoder;
mtl::ComputeCommandEncoder mComputeEncoder;
// Cached back-end objects
FramebufferMtl *mDrawFramebuffer = nullptr;
VertexArrayMtl *mVertexArray = nullptr;
ProgramMtl *mProgram = nullptr;
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
gl::AttributesMask mDirtyDefaultAttribsMask;
DirtyBits mDirtyBits;
// State
mtl::RenderPipelineDesc mRenderPipelineDesc;
mtl::DepthStencilDesc mDepthStencilDesc;
mtl::BlendDesc mBlendDesc;
MTLClearColor mClearColor;
MTLViewport mViewport;
MTLScissorRect mScissorRect;
MTLWinding mWinding;
MTLCullMode mCullMode;
bool mCullAllPolygons = false;
// Lineloop and TriFan index buffer
mtl::BufferPool mLineLoopIndexBuffer;
mtl::BufferPool mTriFanIndexBuffer;
// one buffer can be reused for any starting vertex in DrawArrays()
mtl::BufferRef mTriFanArraysIndexBuffer;
DriverUniforms mDriverUniforms;
DefaultAttribute mDefaultAttributes[mtl::kMaxVertexAttribs];
}; };
} // namespace rx } // namespace rx
......
...@@ -10,7 +10,15 @@ ...@@ -10,7 +10,15 @@
#ifndef LIBANGLE_RENDERER_METAL_DISPLAYMTL_H_ #ifndef LIBANGLE_RENDERER_METAL_DISPLAYMTL_H_
#define LIBANGLE_RENDERER_METAL_DISPLAYMTL_H_ #define LIBANGLE_RENDERER_METAL_DISPLAYMTL_H_
#include "common/PackedEnums.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/DisplayImpl.h" #include "libANGLE/renderer/DisplayImpl.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
#include "libANGLE/renderer/metal/mtl_render_utils.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
#include "libANGLE/renderer/metal/mtl_utils.h"
#include "platform/FeaturesMtl.h"
namespace egl namespace egl
{ {
...@@ -19,15 +27,11 @@ class Surface; ...@@ -19,15 +27,11 @@ class Surface;
namespace rx namespace rx
{ {
class ContextMtl;
class RendererMtl;
class DisplayMtl : public DisplayImpl class DisplayMtl : public DisplayImpl
{ {
public: public:
// Check whether minimum required Metal version is available on the host platform.
static bool IsMetalAvailable();
DisplayMtl(const egl::DisplayState &state); DisplayMtl(const egl::DisplayState &state);
~DisplayMtl() override; ~DisplayMtl() override;
...@@ -86,14 +90,72 @@ class DisplayMtl : public DisplayImpl ...@@ -86,14 +90,72 @@ class DisplayMtl : public DisplayImpl
egl::ConfigSet generateConfigs() override; egl::ConfigSet generateConfigs() override;
RendererMtl *getRenderer() { return mRenderer.get(); } std::string getRendererDescription() const;
gl::Caps getNativeCaps() const;
const gl::TextureCapsMap &getNativeTextureCaps() const;
const gl::Extensions &getNativeExtensions() const;
const gl::Limitations &getNativeLimitations() const { return mNativeLimitations; }
const angle::FeaturesMtl &getFeatures() const { return mFeatures; }
id<MTLDevice> getMetalDevice() const { return mMetalDevice; }
mtl::CommandQueue &cmdQueue() { return mCmdQueue; }
const mtl::FormatTable &getFormatTable() const { return mFormatTable; }
mtl::RenderUtils &getUtils() { return mUtils; }
mtl::StateCache &getStateCache() { return mStateCache; }
id<MTLDepthStencilState> getDepthStencilState(const mtl::DepthStencilDesc &desc)
{
return mStateCache.getDepthStencilState(getMetalDevice(), desc);
}
id<MTLSamplerState> getSamplerState(const mtl::SamplerDesc &desc)
{
return mStateCache.getSamplerState(getMetalDevice(), desc);
}
const mtl::TextureRef &getNullTexture(const gl::Context *context, gl::TextureType type);
const mtl::Format &getPixelFormat(angle::FormatID angleFormatId) const
{
return mFormatTable.getPixelFormat(angleFormatId);
}
// See mtl::FormatTable::getVertexFormat()
const mtl::VertexFormat &getVertexFormat(angle::FormatID angleFormatId,
bool tightlyPacked) const
{
return mFormatTable.getVertexFormat(angleFormatId, tightlyPacked);
}
protected: protected:
void generateExtensions(egl::DisplayExtensions *outExtensions) const override; void generateExtensions(egl::DisplayExtensions *outExtensions) const override;
void generateCaps(egl::Caps *outCaps) const override; void generateCaps(egl::Caps *outCaps) const override;
private: private:
std::unique_ptr<RendererMtl> mRenderer; angle::Result initializeImpl(egl::Display *display);
void ensureCapsInitialized() const;
void initializeCaps() const;
void initializeExtensions() const;
void initializeTextureCaps() const;
void initializeFeatures();
mtl::AutoObjCPtr<id<MTLDevice>> mMetalDevice = nil;
mtl::CommandQueue mCmdQueue;
mtl::FormatTable mFormatTable;
mtl::StateCache mStateCache;
mtl::RenderUtils mUtils;
angle::PackedEnumMap<gl::TextureType, mtl::TextureRef> mNullTextures;
mutable bool mCapsInitialized;
mutable gl::TextureCapsMap mNativeTextureCaps;
mutable gl::Extensions mNativeExtensions;
mutable gl::Caps mNativeCaps;
mutable gl::Limitations mNativeLimitations;
angle::FeaturesMtl mFeatures;
}; };
} // namespace rx } // namespace rx
......
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayMtl_api.h:
// Defines the Metal Display APIs to be used by the code outside metal module.
//
#ifndef LIBANGLE_RENDERER_METAL_DISPLAYMTL_API_H_
#define LIBANGLE_RENDERER_METAL_DISPLAYMTL_API_H_
#include "libANGLE/renderer/DisplayImpl.h"
namespace rx
{
// Check whether minimum required Metal version is available on the host platform.
bool IsMetalDisplayAvailable();
DisplayImpl *CreateMetalDisplay(const egl::DisplayState &state);
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_DISPLAYMTL_API_H_ */
...@@ -13,14 +13,20 @@ ...@@ -13,14 +13,20 @@
#import <Metal/Metal.h> #import <Metal/Metal.h>
#include "libANGLE/renderer/FramebufferImpl.h" #include "libANGLE/renderer/FramebufferImpl.h"
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
#include "libANGLE/renderer/metal/mtl_render_utils.h"
namespace rx namespace rx
{ {
class ContextMtl;
class SurfaceMtl;
class FramebufferMtl : public FramebufferImpl class FramebufferMtl : public FramebufferImpl
{ {
public: public:
explicit FramebufferMtl(const gl::FramebufferState &state); explicit FramebufferMtl(const gl::FramebufferState &state,
bool flipY,
bool alwaysDiscardDepthStencil);
~FramebufferMtl() override; ~FramebufferMtl() override;
void destroy(const gl::Context *context) override; void destroy(const gl::Context *context) override;
...@@ -76,6 +82,67 @@ class FramebufferMtl : public FramebufferImpl ...@@ -76,6 +82,67 @@ class FramebufferMtl : public FramebufferImpl
angle::Result getSamplePosition(const gl::Context *context, angle::Result getSamplePosition(const gl::Context *context,
size_t index, size_t index,
GLfloat *xy) const override; GLfloat *xy) const override;
RenderTargetMtl *getColorReadRenderTarget() const;
bool flipY() const { return mFlipY; }
gl::Rectangle getCompleteRenderArea() const;
const mtl::RenderPassDesc &getRenderPassDesc(ContextMtl *context);
// Call this to notify FramebufferMtl whenever its render pass has ended.
void onFinishedDrawingToFrameBuffer(const gl::Context *context,
mtl::RenderCommandEncoder *encoder);
angle::Result readPixelsImpl(const gl::Context *context,
const gl::Rectangle &area,
const PackPixelsParams &packPixelsParams,
RenderTargetMtl *renderTarget,
uint8_t *pixels);
private:
void reset();
angle::Result invalidateImpl(ContextMtl *contextMtl, size_t count, const GLenum *attachments);
angle::Result clearImpl(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
mtl::ClearRectParams *clearOpts);
angle::Result clearWithLoadOp(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts);
angle::Result clearWithDraw(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts);
angle::Result prepareRenderPass(const gl::Context *context,
gl::DrawBufferMask drawColorBuffers,
mtl::RenderPassDesc *descOut);
void overrideClearColor(const mtl::TextureRef &texture,
MTLClearColor clearColor,
MTLClearColor *colorOut);
angle::Result updateColorRenderTarget(const gl::Context *context, size_t colorIndexGL);
angle::Result updateDepthRenderTarget(const gl::Context *context);
angle::Result updateStencilRenderTarget(const gl::Context *context);
angle::Result updateCachedRenderTarget(const gl::Context *context,
const gl::FramebufferAttachment *attachment,
RenderTargetMtl **cachedRenderTarget);
// NOTE: we cannot use RenderTargetCache here because it doesn't support separate
// depth & stencil attachments as of now. Separate depth & stencil could be useful to
// save spaces on iOS devices. See doc/PackedDepthStencilSupport.md.
std::array<RenderTargetMtl *, mtl::kMaxRenderTargets> mColorRenderTargets;
std::array<bool, mtl::kMaxRenderTargets> mDiscardColors;
RenderTargetMtl *mDepthRenderTarget = nullptr;
bool mDiscardDepth = false;
RenderTargetMtl *mStencilRenderTarget = nullptr;
bool mDiscardStencil = false;
mtl::RenderPassDesc mRenderPassDesc;
const bool mAlwaysDiscardDepthStencil;
const bool mFlipY = false;
}; };
} // namespace rx } // namespace rx
......
...@@ -12,10 +12,18 @@ ...@@ -12,10 +12,18 @@
#import <Metal/Metal.h> #import <Metal/Metal.h>
#include <array>
#include "common/Optional.h"
#include "common/utilities.h"
#include "libANGLE/renderer/ProgramImpl.h" #include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
namespace rx namespace rx
{ {
class ContextMtl;
class ProgramMtl : public ProgramImpl class ProgramMtl : public ProgramImpl
{ {
...@@ -95,6 +103,13 @@ class ProgramMtl : public ProgramImpl ...@@ -95,6 +103,13 @@ class ProgramMtl : public ProgramImpl
GLint components, GLint components,
const GLfloat *coeffs) override; const GLfloat *coeffs) override;
// Calls this before drawing, changedPipelineDesc is passed when vertex attributes desc and/or
// shader program changed.
angle::Result setupDraw(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
const Optional<mtl::RenderPipelineDesc> &changedPipelineDesc,
bool forceTexturesSetting);
private: private:
template <int cols, int rows> template <int cols, int rows>
void setUniformMatrixfv(GLint location, void setUniformMatrixfv(GLint location,
...@@ -106,6 +121,49 @@ class ProgramMtl : public ProgramImpl ...@@ -106,6 +121,49 @@ class ProgramMtl : public ProgramImpl
template <typename T> template <typename T>
void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType); void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType);
angle::Result initDefaultUniformBlocks(const gl::Context *glContext);
angle::Result commitUniforms(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder);
angle::Result updateTextures(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
bool forceUpdate);
void reset(ContextMtl *context);
void linkResources(const gl::ProgramLinkedResources &resources);
angle::Result linkImpl(const gl::Context *glContext, gl::InfoLog &infoLog);
angle::Result convertToMsl(const gl::Context *glContext,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
std::vector<uint32_t> *sprivCode);
angle::Result createMslShader(const gl::Context *glContext,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
const std::string &translatedSource);
// State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable
{
DefaultUniformBlock();
~DefaultUniformBlock();
// Shadow copies of the shader uniform data.
angle::MemoryBuffer uniformData;
// Since the default blocks are laid out in std140, this tells us where to write on a call
// to a setUniform method. They are arranged in uniform location order.
std::vector<sh::BlockMemberInfo> uniformLayout;
};
gl::ShaderBitSet mDefaultUniformBlocksDirty;
gl::ShaderBitSet mSamplerBindingsDirty;
gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks;
// We keep the translated linked shader sources to use with shader draw call patching.
gl::ShaderMap<std::string> mShaderSource;
mtl::RenderPipelineCache mMetalRenderPipelineCache;
}; };
} // namespace rx } // namespace rx
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#define LIBANGLE_RENDERER_METAL_RENDERBUFFERMTL_H_ #define LIBANGLE_RENDERER_METAL_RENDERBUFFERMTL_H_
#include "libANGLE/renderer/RenderbufferImpl.h" #include "libANGLE/renderer/RenderbufferImpl.h"
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
namespace rx namespace rx
{ {
...@@ -42,6 +44,19 @@ class RenderbufferMtl : public RenderbufferImpl ...@@ -42,6 +44,19 @@ class RenderbufferMtl : public RenderbufferImpl
angle::Result initializeContents(const gl::Context *context, angle::Result initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) override; const gl::ImageIndex &imageIndex) override;
private:
angle::Result setStorageImpl(const gl::Context *context,
size_t samples,
GLenum internalformat,
size_t width,
size_t height);
void releaseTexture();
mtl::Format mFormat;
mtl::TextureRef mTexture;
RenderTargetMtl mRenderTarget;
}; };
} // namespace rx } // namespace rx
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "libANGLE/renderer/metal/RenderBufferMtl.h" #include "libANGLE/renderer/metal/RenderBufferMtl.h"
#include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
#include "libANGLE/renderer/metal/mtl_utils.h"
namespace rx namespace rx
{ {
...@@ -20,7 +22,50 @@ RenderbufferMtl::~RenderbufferMtl() {} ...@@ -20,7 +22,50 @@ RenderbufferMtl::~RenderbufferMtl() {}
void RenderbufferMtl::onDestroy(const gl::Context *context) void RenderbufferMtl::onDestroy(const gl::Context *context)
{ {
UNIMPLEMENTED(); releaseTexture();
}
void RenderbufferMtl::releaseTexture()
{
mTexture = nullptr;
}
angle::Result RenderbufferMtl::setStorageImpl(const gl::Context *context,
size_t samples,
GLenum internalformat,
size_t width,
size_t height)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// NOTE(hqle): Support MSAA
ANGLE_CHECK(contextMtl, samples == 1, "Multisample is not supported atm.", GL_INVALID_VALUE);
if (mTexture != nullptr && mTexture->valid())
{
// Check against the state if we need to recreate the storage.
if (internalformat != mState.getFormat().info->internalFormat ||
static_cast<GLsizei>(width) != mState.getWidth() ||
static_cast<GLsizei>(height) != mState.getHeight())
{
releaseTexture();
}
}
const gl::InternalFormat &internalFormat = gl::GetSizedInternalFormatInfo(internalformat);
angle::FormatID angleFormatId =
angle::Format::InternalFormatToID(internalFormat.sizedInternalFormat);
mFormat = contextMtl->getPixelFormat(angleFormatId);
if ((mTexture == nullptr || !mTexture->valid()) && (width != 0 && height != 0))
{
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mFormat, static_cast<uint32_t>(width),
static_cast<uint32_t>(height), 1, false, &mTexture));
mRenderTarget.set(mTexture, 0, 0, mFormat);
}
return angle::Result::Continue;
} }
angle::Result RenderbufferMtl::setStorage(const gl::Context *context, angle::Result RenderbufferMtl::setStorage(const gl::Context *context,
...@@ -28,8 +73,7 @@ angle::Result RenderbufferMtl::setStorage(const gl::Context *context, ...@@ -28,8 +73,7 @@ angle::Result RenderbufferMtl::setStorage(const gl::Context *context,
size_t width, size_t width,
size_t height) size_t height)
{ {
UNIMPLEMENTED(); return setStorageImpl(context, 1, internalformat, width, height);
return angle::Result::Stop;
} }
angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context, angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context,
...@@ -38,6 +82,7 @@ angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context, ...@@ -38,6 +82,7 @@ angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context,
size_t width, size_t width,
size_t height) size_t height)
{ {
// NOTE(hqle): Support MSAA
UNIMPLEMENTED(); UNIMPLEMENTED();
return angle::Result::Stop; return angle::Result::Stop;
} }
...@@ -45,6 +90,7 @@ angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context, ...@@ -45,6 +90,7 @@ angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context,
angle::Result RenderbufferMtl::setStorageEGLImageTarget(const gl::Context *context, angle::Result RenderbufferMtl::setStorageEGLImageTarget(const gl::Context *context,
egl::Image *image) egl::Image *image)
{ {
// NOTE(hqle): Support EGLimage
UNIMPLEMENTED(); UNIMPLEMENTED();
return angle::Result::Stop; return angle::Result::Stop;
} }
...@@ -55,14 +101,15 @@ angle::Result RenderbufferMtl::getAttachmentRenderTarget(const gl::Context *cont ...@@ -55,14 +101,15 @@ angle::Result RenderbufferMtl::getAttachmentRenderTarget(const gl::Context *cont
GLsizei samples, GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut) FramebufferAttachmentRenderTarget **rtOut)
{ {
UNIMPLEMENTED(); // NOTE(hqle): Support MSAA.
return angle::Result::Stop; ASSERT(mTexture && mTexture->valid());
*rtOut = &mRenderTarget;
return angle::Result::Continue;
} }
angle::Result RenderbufferMtl::initializeContents(const gl::Context *context, angle::Result RenderbufferMtl::initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) const gl::ImageIndex &imageIndex)
{ {
UNIMPLEMENTED(); return mtl::InitializeTextureContents(context, mTexture, mFormat, imageIndex);
return angle::Result::Continue;
} }
} }
\ No newline at end of file
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RenderTargetMtl.h:
// Defines the class interface for RenderTargetMtl.
//
#ifndef LIBANGLE_RENDERER_METAL_RENDERTARGETMTL_H_
#define LIBANGLE_RENDERER_METAL_RENDERTARGETMTL_H_
#import <Metal/Metal.h>
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
namespace rx
{
// This is a very light-weight class that does not own to the resources it points to.
// It's meant only to copy across some information from a FramebufferAttachment to the
// business rendering logic.
class RenderTargetMtl final : public FramebufferAttachmentRenderTarget
{
public:
RenderTargetMtl();
~RenderTargetMtl() override;
// Used in std::vector initialization.
RenderTargetMtl(RenderTargetMtl &&other);
void set(const mtl::TextureRef &texture, size_t level, size_t layer, const mtl::Format &format);
void set(const mtl::TextureRef &texture);
void reset();
const mtl::TextureRef &getTexture() const { return mTexture; }
size_t getLevelIndex() const { return mLevelIndex; }
size_t getLayerIndex() const { return mLayerIndex; }
const mtl::Format *getFormat() const { return mFormat; }
void toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *rpaDescOut) const;
private:
mtl::TextureRef mTexture;
size_t mLevelIndex = 0;
size_t mLayerIndex = 0;
const mtl::Format *mFormat = nullptr;
};
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_RENDERTARGETMTL_H */
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RenderTargetMtl.mm:
// Implements the class methods for RenderTargetMtl.
//
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
namespace rx
{
RenderTargetMtl::RenderTargetMtl() {}
RenderTargetMtl::~RenderTargetMtl()
{
reset();
}
RenderTargetMtl::RenderTargetMtl(RenderTargetMtl &&other)
: mTexture(std::move(other.mTexture)),
mLevelIndex(other.mLevelIndex),
mLayerIndex(other.mLayerIndex)
{}
void RenderTargetMtl::set(const mtl::TextureRef &texture,
size_t level,
size_t layer,
const mtl::Format &format)
{
mTexture = texture;
mLevelIndex = level;
mLayerIndex = layer;
mFormat = &format;
}
void RenderTargetMtl::set(const mtl::TextureRef &texture)
{
mTexture = texture;
}
void RenderTargetMtl::reset()
{
mTexture.reset();
mLevelIndex = 0;
mLayerIndex = 0;
mFormat = nullptr;
}
void RenderTargetMtl::toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *rpaDescOut) const
{
rpaDescOut->texture = mTexture;
rpaDescOut->level = static_cast<uint32_t>(mLevelIndex);
rpaDescOut->slice = static_cast<uint32_t>(mLayerIndex);
}
}
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RendererMtl.h:
// Defines class interface for RendererMtl.
#ifndef LIBANGLE_RENDERER_METAL_RENDERERMTL_H_
#define LIBANGLE_RENDERER_METAL_RENDERERMTL_H_
#import <Metal/Metal.h>
#include "common/PackedEnums.h"
#include "libANGLE/Caps.h"
#include "libANGLE/angletypes.h"
namespace egl
{
class Display;
}
namespace rx
{
class ContextMtl;
class RendererMtl final : angle::NonCopyable
{
public:
RendererMtl();
~RendererMtl();
angle::Result initialize(egl::Display *display);
void onDestroy();
std::string getVendorString() const;
std::string getRendererDescription() const;
const gl::Limitations &getNativeLimitations() const;
id<MTLDevice> getMetalDevice() const { return nil; }
private:
gl::Limitations mNativeLimitations;
};
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_RENDERERMTL_H_ */
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RendererMtl.mm:
// Implements the class methods for RendererMtl.
//
#include "libANGLE/renderer/metal/RendererMtl.h"
#include "libANGLE/renderer/metal/mtl_common.h"
namespace rx
{
RendererMtl::RendererMtl() {}
RendererMtl::~RendererMtl() {}
angle::Result RendererMtl::initialize(egl::Display *display)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
void RendererMtl::onDestroy()
{
UNIMPLEMENTED();
}
std::string RendererMtl::getVendorString() const
{
std::string vendorString = "Google Inc.";
UNIMPLEMENTED();
return vendorString;
}
std::string RendererMtl::getRendererDescription() const
{
std::string desc = "Metal Renderer";
UNIMPLEMENTED();
return desc;
}
const gl::Limitations &RendererMtl::getNativeLimitations() const
{
UNIMPLEMENTED();
return mNativeLimitations;
}
}
...@@ -24,9 +24,17 @@ std::shared_ptr<WaitableCompileEvent> ShaderMtl::compile(const gl::Context *cont ...@@ -24,9 +24,17 @@ std::shared_ptr<WaitableCompileEvent> ShaderMtl::compile(const gl::Context *cont
gl::ShCompilerInstance *compilerInstance, gl::ShCompilerInstance *compilerInstance,
ShCompileOptions options) ShCompileOptions options)
{ {
UNIMPLEMENTED(); ShCompileOptions compileOptions = SH_INITIALIZE_UNINITIALIZED_LOCALS;
return compileImpl(context, compilerInstance, mData.getSource(), options); bool isWebGL = context->getExtensions().webglCompatibility;
if (isWebGL && mData.getShaderType() != gl::ShaderType::Compute)
{
compileOptions |= SH_INIT_OUTPUT_VARIABLES;
}
compileOptions |= SH_CLAMP_POINT_SIZE;
return compileImpl(context, compilerInstance, mData.getSource(), compileOptions | options);
} }
std::string ShaderMtl::getDebugInfo() const std::string ShaderMtl::getDebugInfo() const
......
...@@ -10,19 +10,27 @@ ...@@ -10,19 +10,27 @@
#import <Metal/Metal.h> #import <Metal/Metal.h>
#import <QuartzCore/CALayer.h> #import <QuartzCore/CALayer.h>
#import <QuartzCore/CAMetalLayer.h>
#include "libANGLE/renderer/FramebufferImpl.h"
#include "libANGLE/renderer/SurfaceImpl.h" #include "libANGLE/renderer/SurfaceImpl.h"
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
namespace rx namespace rx
{ {
class DisplayMtl;
class SurfaceMtl : public SurfaceImpl class SurfaceMtl : public SurfaceImpl
{ {
public: public:
SurfaceMtl(const egl::SurfaceState &state, SurfaceMtl(DisplayMtl *display,
const egl::SurfaceState &state,
EGLNativeWindowType window, EGLNativeWindowType window,
EGLint width, const egl::AttributeMap &attribs);
EGLint height);
~SurfaceMtl() override; ~SurfaceMtl() override;
void destroy(const egl::Display *display) override; void destroy(const egl::Display *display) override;
...@@ -62,6 +70,31 @@ class SurfaceMtl : public SurfaceImpl ...@@ -62,6 +70,31 @@ class SurfaceMtl : public SurfaceImpl
const gl::ImageIndex &imageIndex, const gl::ImageIndex &imageIndex,
GLsizei samples, GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut) override; FramebufferAttachmentRenderTarget **rtOut) override;
private:
angle::Result swapImpl(const gl::Context *context);
angle::Result ensureRenderTargetsCreated(const gl::Context *context);
angle::Result obtainNextDrawable(const gl::Context *context);
angle::Result ensureDepthStencilSizeCorrect(const gl::Context *context,
gl::Framebuffer::DirtyBits *fboDirtyBits);
// Check if metal layer has been resized.
void checkIfLayerResized();
mtl::AutoObjCObj<CAMetalLayer> mMetalLayer = nil;
CALayer *mLayer;
mtl::AutoObjCPtr<id<CAMetalDrawable>> mCurrentDrawable = nil;
mtl::TextureRef mDrawableTexture;
mtl::TextureRef mDepthTexture;
mtl::TextureRef mStencilTexture;
bool mUsePackedDepthStencil = false;
mtl::Format mColorFormat;
mtl::Format mDepthFormat;
mtl::Format mStencilFormat;
RenderTargetMtl mColorRenderTarget;
RenderTargetMtl mDepthRenderTarget;
RenderTargetMtl mStencilRenderTarget;
}; };
} // namespace rx } // namespace rx
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
#include "common/PackedEnums.h" #include "common/PackedEnums.h"
#include "libANGLE/renderer/TextureImpl.h" #include "libANGLE/renderer/TextureImpl.h"
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
namespace rx namespace rx
{ {
...@@ -136,6 +139,86 @@ class TextureMtl : public TextureImpl ...@@ -136,6 +139,86 @@ class TextureMtl : public TextureImpl
angle::Result initializeContents(const gl::Context *context, angle::Result initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) override; const gl::ImageIndex &imageIndex) override;
void bindVertexShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex,
int samplerSlotIndex);
void bindFragmentShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex,
int samplerSlotIndex);
const mtl::Format &getFormat() const { return mFormat; }
private:
void releaseTexture();
// If levels = 0, this function will create full mipmaps texture.
angle::Result setStorageImpl(const gl::Context *context,
gl::TextureType type,
size_t levels,
const mtl::Format &mtlFormat,
const gl::Extents &size);
angle::Result redefineImage(const gl::Context *context,
const gl::ImageIndex &index,
const mtl::Format &mtlFormat,
const gl::Extents &size);
angle::Result setImageImpl(const gl::Context *context,
const gl::ImageIndex &index,
const gl::InternalFormat &formatInfo,
const gl::Extents &size,
GLenum type,
const gl::PixelUnpackState &unpack,
const uint8_t *pixels);
angle::Result setSubImageImpl(const gl::Context *context,
const gl::ImageIndex &index,
const gl::Box &area,
const gl::InternalFormat &formatInfo,
GLenum type,
const gl::PixelUnpackState &unpack,
const uint8_t *pixels);
angle::Result copySubImageImpl(const gl::Context *context,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const gl::Rectangle &sourceArea,
const gl::InternalFormat &internalFormat,
gl::Framebuffer *source);
angle::Result copySubImageWithDraw(const gl::Context *context,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const gl::Rectangle &sourceArea,
const gl::InternalFormat &internalFormat,
gl::Framebuffer *source);
angle::Result copySubImageCPU(const gl::Context *context,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const gl::Rectangle &sourceArea,
const gl::InternalFormat &internalFormat,
gl::Framebuffer *source);
// Convert pixels to suported format before uploading to texture
angle::Result convertAndSetSubImage(const gl::Context *context,
const gl::ImageIndex &index,
const MTLRegion &mtlArea,
const gl::InternalFormat &internalFormat,
const angle::Format &pixelsFormat,
size_t pixelsRowPitch,
const uint8_t *pixels);
angle::Result generateMipmapCPU(const gl::Context *context);
mtl::Format mFormat;
mtl::TextureRef mTexture;
id<MTLSamplerState> mMetalSamplerState = nil;
std::vector<RenderTargetMtl> mLayeredRenderTargets;
std::vector<mtl::TextureRef> mLayeredTextureViews;
bool mIsPow2 = false;
}; };
} // namespace rx } // namespace rx
......
...@@ -11,6 +11,11 @@ ...@@ -11,6 +11,11 @@
#define LIBANGLE_RENDERER_METAL_VERTEXARRAYMTL_H_ #define LIBANGLE_RENDERER_METAL_VERTEXARRAYMTL_H_
#include "libANGLE/renderer/VertexArrayImpl.h" #include "libANGLE/renderer/VertexArrayImpl.h"
#include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/mtl_buffer_pool.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
namespace rx namespace rx
{ {
...@@ -19,7 +24,7 @@ class ContextMtl; ...@@ -19,7 +24,7 @@ class ContextMtl;
class VertexArrayMtl : public VertexArrayImpl class VertexArrayMtl : public VertexArrayImpl
{ {
public: public:
VertexArrayMtl(const gl::VertexArrayState &state); VertexArrayMtl(const gl::VertexArrayState &state, ContextMtl *context);
~VertexArrayMtl() override; ~VertexArrayMtl() override;
void destroy(const gl::Context *context) override; void destroy(const gl::Context *context) override;
...@@ -28,6 +33,80 @@ class VertexArrayMtl : public VertexArrayImpl ...@@ -28,6 +33,80 @@ class VertexArrayMtl : public VertexArrayImpl
const gl::VertexArray::DirtyBits &dirtyBits, const gl::VertexArray::DirtyBits &dirtyBits,
gl::VertexArray::DirtyAttribBitsArray *attribBits, gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits) override; gl::VertexArray::DirtyBindingBitsArray *bindingBits) override;
// Feed client side's vertex/index data
angle::Result updateClientAttribs(const gl::Context *context,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices);
// vertexDescChanged is both input and output, the input value if is true, will force new
// mtl::VertexDesc to be returned via vertexDescOut. Otherwise, it is only returned when the
// vertex array is dirty
angle::Result setupDraw(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
bool *vertexDescChanged,
mtl::VertexDesc *vertexDescOut);
angle::Result getIndexBuffer(const gl::Context *glContext,
gl::DrawElementsType indexType,
size_t indexCount,
const void *sourcePointer,
mtl::BufferRef *idxBufferOut,
size_t *idxBufferOffsetOut,
gl::DrawElementsType *indexTypeOut);
private:
angle::Result syncDirtyAttrib(const gl::Context *glContext,
const gl::VertexAttribute &attrib,
const gl::VertexBinding &binding,
size_t attribIndex);
angle::Result convertIndexBuffer(const gl::Context *glContext,
gl::DrawElementsType indexType,
size_t offset,
mtl::BufferRef *idxBufferOut,
size_t *idxBufferOffsetOut);
angle::Result streamIndexBufferFromClient(const gl::Context *glContext,
gl::DrawElementsType indexType,
size_t indexCount,
const void *sourcePointer,
mtl::BufferRef *idxBufferOut,
size_t *idxBufferOffsetOut);
angle::Result convertIndexBufferGPU(const gl::Context *glContext,
gl::DrawElementsType indexType,
BufferMtl *idxBuffer,
size_t offset,
size_t indexCount,
IndexConversionBufferMtl *conversion);
angle::Result convertVertexBuffer(const gl::Context *glContext,
BufferMtl *srcBuffer,
const gl::VertexBinding &binding,
size_t attribIndex,
const mtl::VertexFormat &vertexFormat);
angle::Result convertVertexBufferCPU(const gl::Context *glContext,
BufferMtl *srcBuffer,
const gl::VertexBinding &binding,
size_t attribIndex,
const mtl::VertexFormat &vertexFormat,
ConversionBufferMtl *conversion);
// These can point to real BufferMtl or converted buffer in mConvertedArrayBufferHolders
gl::AttribArray<BufferHolderMtl *> mCurrentArrayBuffers;
gl::AttribArray<SimpleWeakBufferHolderMtl> mConvertedArrayBufferHolders;
gl::AttribArray<size_t> mCurrentArrayBufferOffsets;
gl::AttribArray<GLuint> mCurrentArrayBufferStrides;
gl::AttribArray<MTLVertexFormat> mCurrentArrayBufferFormats;
mtl::BufferPool mDynamicVertexData;
mtl::BufferPool mDynamicIndexData;
bool mVertexArrayDirty = true;
}; };
} // namespace rx } // namespace rx
......
# Packed depth stencil support in Metal
Metal has different runtime behaviors when it comes to packed depth stencil format usage.
On macOS, packed depth24stencil8 format is supported (albeit optionally) and if application
wants to use both depth and stencil attachments in the same render pass, these attachments must
point to the same packed depth stencil texture. In other words, it is not permitted to use separate
depth & stencil textures in a same render pass.
iOS simulators and mac Catalyst platforms have the same restrictions as macOS.
On iOS devices, depth24stencil8 format is not available. The only packed format supported is depth32stencil8 which is a 64 bits format (24 bits unused). However, metal runtime allows separate
depth & stencil textures to be attached to one render pass. So technically, one depth32 texture
and one stencil8 texture can be used together.
\ No newline at end of file
...@@ -32,7 +32,7 @@ template_autogen_inl = """// GENERATED FILE - DO NOT EDIT. ...@@ -32,7 +32,7 @@ template_autogen_inl = """// GENERATED FILE - DO NOT EDIT.
#include <TargetConditionals.h> #include <TargetConditionals.h>
#include "libANGLE/renderer/Format.h" #include "libANGLE/renderer/Format.h"
#include "libANGLE/renderer/metal/RendererMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h" #include "libANGLE/renderer/metal/mtl_format_utils.h"
namespace rx namespace rx
...@@ -40,11 +40,11 @@ namespace rx ...@@ -40,11 +40,11 @@ namespace rx
namespace mtl namespace mtl
{{ {{
void Format::init(const RendererMtl *renderer, angle::FormatID intendedFormatId_) void Format::init(const DisplayMtl *display, angle::FormatID intendedFormatId_)
{{ {{
this->intendedFormatId = intendedFormatId_; this->intendedFormatId = intendedFormatId_;
id<MTLDevice> metalDevice = renderer->getMetalDevice(); id<MTLDevice> metalDevice = display->getMetalDevice();
// Actual conversion // Actual conversion
switch (this->intendedFormatId) switch (this->intendedFormatId)
...@@ -176,11 +176,11 @@ def gen_image_map_switch_string(image_table): ...@@ -176,11 +176,11 @@ def gen_image_map_switch_string(image_table):
return mac_case return mac_case
re = '' re = ''
re += "#if TARGET_OS_OSX\n" re += "#if TARGET_OS_OSX || TARGET_OS_MACCATALYST\n"
re += mac_case re += mac_case
re += "#else // TARGET_OS_OSX\n" re += "#else // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n"
re += non_mac_case re += non_mac_case
re += "#endif // TARGET_OS_OSX\n" re += "#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n"
return re return re
# Common case # Common case
...@@ -190,7 +190,7 @@ def gen_image_map_switch_string(image_table): ...@@ -190,7 +190,7 @@ def gen_image_map_switch_string(image_table):
switch_data += gen_image_map_switch_common_case(angle_format, angle_override[angle_format]) switch_data += gen_image_map_switch_common_case(angle_format, angle_override[angle_format])
# Mac specific # Mac specific
switch_data += "#if TARGET_OS_OSX\n" switch_data += "#if TARGET_OS_OSX || TARGET_OS_MACCATALYST\n"
for angle_format in sorted(mac_specific_map.keys()): for angle_format in sorted(mac_specific_map.keys()):
switch_data += gen_image_map_switch_mac_case(angle_format, angle_format, angle_to_mtl, switch_data += gen_image_map_switch_mac_case(angle_format, angle_format, angle_to_mtl,
mac_specific_map, mac_fallbacks) mac_specific_map, mac_fallbacks)
...@@ -200,7 +200,7 @@ def gen_image_map_switch_string(image_table): ...@@ -200,7 +200,7 @@ def gen_image_map_switch_string(image_table):
angle_to_mtl, mac_specific_map, mac_fallbacks) angle_to_mtl, mac_specific_map, mac_fallbacks)
# iOS specific # iOS specific
switch_data += "#elif TARGET_OS_IOS // TARGET_OS_OSX\n" switch_data += "#elif TARGET_OS_IOS // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n"
for angle_format in sorted(ios_specific_map.keys()): for angle_format in sorted(ios_specific_map.keys()):
switch_data += gen_image_map_switch_simple_case(angle_format, angle_format, switch_data += gen_image_map_switch_simple_case(angle_format, angle_format,
ios_specific_map) ios_specific_map)
...@@ -208,7 +208,7 @@ def gen_image_map_switch_string(image_table): ...@@ -208,7 +208,7 @@ def gen_image_map_switch_string(image_table):
# overide case will always map to a format in common table, i.e. angle_to_mtl # overide case will always map to a format in common table, i.e. angle_to_mtl
switch_data += gen_image_map_switch_simple_case(angle_format, ios_override[angle_format], switch_data += gen_image_map_switch_simple_case(angle_format, ios_override[angle_format],
angle_to_mtl) angle_to_mtl)
switch_data += "#endif // TARGET_OS_OSX\n" switch_data += "#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n"
switch_data += " default:\n" switch_data += " default:\n"
switch_data += " this->metalFormat = MTLPixelFormatInvalid;\n" switch_data += " this->metalFormat = MTLPixelFormatInvalid;\n"
switch_data += " this->actualFormatId = angle::FormatID::NONE;" switch_data += " this->actualFormatId = angle::FormatID::NONE;"
......
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// mtl_buffer_pool.h:
// Defines class interface for BufferPool, managing a pool of mtl::Buffer
//
#ifndef LIBANGLE_RENDERER_METAL_MTL_BUFFER_POOL_H_
#define LIBANGLE_RENDERER_METAL_MTL_BUFFER_POOL_H_
#include "libANGLE/renderer/metal/mtl_resources.h"
namespace rx
{
class ContextMtl;
namespace mtl
{
// A buffer pool is conceptually an infinitely long buffer. Each time you write to the buffer,
// you will always write to a previously unused portion. After a series of writes, you must flush
// the buffer data to the device. Buffer lifetime currently assumes that each new allocation will
// last as long or longer than each prior allocation.
//
// Buffer pool is used to implement a variety of data streaming operations in Metal, such
// as for immediate vertex array and element array data, and other dynamic data.
//
// Internally buffer pool keeps a collection of mtl::Buffer. When we write past the end of a
// currently active mtl::Buffer we keep it until it is no longer in use. We then mark it available
// for future allocations in a free list.
class BufferPool
{
public:
// alwaysAllocNewBuffer=true will always allocate new buffer or reuse free buffer on allocate(),
// regardless of whether current buffer still has unused portion or not.
BufferPool(bool alwaysAllocNewBuffer = false);
~BufferPool();
// Init is called after the buffer creation so that the alignment can be specified later.
void initialize(ContextMtl *contextMtl,
size_t initialSize,
size_t alignment,
size_t maxBuffers = 0);
// This call will allocate a new region at the end of the buffer. It internally may trigger
// a new buffer to be created (which is returned in the optional parameter
// `newBufferAllocatedOut`). The new region will be in the returned buffer at given offset. If
// a memory pointer is given, the buffer will be automatically map()ed.
angle::Result allocate(ContextMtl *contextMtl,
size_t sizeInBytes,
uint8_t **ptrOut = nullptr,
BufferRef *bufferOut = nullptr,
size_t *offsetOut = nullptr,
bool *newBufferAllocatedOut = nullptr);
// After a sequence of writes, call commit to ensure the data is visible to the device.
angle::Result commit(ContextMtl *contextMtl);
// This releases all the buffers that have been allocated since this was last called.
void releaseInFlightBuffers(ContextMtl *contextMtl);
// This frees resources immediately.
void destroy(ContextMtl *contextMtl);
const BufferRef &getCurrentBuffer() { return mBuffer; }
size_t getAlignment() { return mAlignment; }
void updateAlignment(ContextMtl *contextMtl, size_t alignment);
// Set whether allocate() will always allocate new buffer or attempting to append to previous
// buffer or not. Default is false.
void setAlwaysAllocateNewBuffer(bool e) { mAlwaysAllocateNewBuffer = e; }
private:
void reset();
angle::Result allocateNewBuffer(ContextMtl *contextMtl);
void destroyBufferList(ContextMtl *contextMtl, std::vector<BufferRef> *buffers);
size_t mInitialSize;
BufferRef mBuffer;
uint32_t mNextAllocationOffset;
size_t mSize;
size_t mAlignment;
std::vector<BufferRef> mInFlightBuffers;
std::vector<BufferRef> mBufferFreeList;
size_t mBuffersAllocated;
size_t mMaxBuffers;
bool mAlwaysAllocateNewBuffer;
};
} // namespace mtl
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_MTL_BUFFER_POOL_H_ */
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// mtl_buffer_pool.mm:
// Implements the class methods for BufferPool.
//
#include "libANGLE/renderer/metal/mtl_buffer_pool.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
namespace rx
{
namespace mtl
{
// BufferPool implementation.
BufferPool::BufferPool(bool alwaysAllocNewBuffer)
: mInitialSize(0),
mBuffer(nullptr),
mNextAllocationOffset(0),
mSize(0),
mAlignment(1),
mBuffersAllocated(0),
mMaxBuffers(0),
mAlwaysAllocateNewBuffer(alwaysAllocNewBuffer)
{}
void BufferPool::initialize(ContextMtl *contextMtl,
size_t initialSize,
size_t alignment,
size_t maxBuffers)
{
destroy(contextMtl);
mInitialSize = initialSize;
mSize = 0;
mMaxBuffers = maxBuffers;
updateAlignment(contextMtl, alignment);
}
BufferPool::~BufferPool() {}
angle::Result BufferPool::allocateNewBuffer(ContextMtl *contextMtl)
{
if (mMaxBuffers > 0 && mBuffersAllocated >= mMaxBuffers)
{
// We reach the max number of buffers allowed.
// Try to deallocate old and smaller size inflight buffers.
releaseInFlightBuffers(contextMtl);
}
if (mMaxBuffers > 0 && mBuffersAllocated >= mMaxBuffers)
{
// If we reach this point, it means there was no buffer deallocated inside
// releaseInFlightBuffers() thus, the number of buffers allocated still exceeds number
// allowed.
ASSERT(!mBufferFreeList.empty());
// Reuse the buffer in free list:
if (mBufferFreeList.front()->isBeingUsedByGPU(contextMtl))
{
contextMtl->flushCommandBufer();
// Force the GPU to finish its rendering and make the old buffer available.
contextMtl->cmdQueue().ensureResourceReadyForCPU(mBufferFreeList.front());
}
mBuffer = mBufferFreeList.front();
mBufferFreeList.erase(mBufferFreeList.begin());
return angle::Result::Continue;
}
ANGLE_TRY(Buffer::MakeBuffer(contextMtl, mSize, nullptr, &mBuffer));
ASSERT(mBuffer);
mBuffersAllocated++;
return angle::Result::Continue;
}
angle::Result BufferPool::allocate(ContextMtl *contextMtl,
size_t sizeInBytes,
uint8_t **ptrOut,
BufferRef *bufferOut,
size_t *offsetOut,
bool *newBufferAllocatedOut)
{
size_t sizeToAllocate = roundUp(sizeInBytes, mAlignment);
angle::base::CheckedNumeric<size_t> checkedNextWriteOffset = mNextAllocationOffset;
checkedNextWriteOffset += sizeToAllocate;
if (!mBuffer || !checkedNextWriteOffset.IsValid() ||
checkedNextWriteOffset.ValueOrDie() >= mSize || mAlwaysAllocateNewBuffer)
{
if (mBuffer)
{
ANGLE_TRY(commit(contextMtl));
}
if (sizeToAllocate > mSize)
{
mSize = std::max(mInitialSize, sizeToAllocate);
// Clear the free list since the free buffers are now too small.
destroyBufferList(contextMtl, &mBufferFreeList);
}
// The front of the free list should be the oldest. Thus if it is in use the rest of the
// free list should be in use as well.
if (mBufferFreeList.empty() || mBufferFreeList.front()->isBeingUsedByGPU(contextMtl))
{
ANGLE_TRY(allocateNewBuffer(contextMtl));
}
else
{
mBuffer = mBufferFreeList.front();
mBufferFreeList.erase(mBufferFreeList.begin());
}
ASSERT(mBuffer->size() == mSize);
mNextAllocationOffset = 0;
if (newBufferAllocatedOut != nullptr)
{
*newBufferAllocatedOut = true;
}
}
else if (newBufferAllocatedOut != nullptr)
{
*newBufferAllocatedOut = false;
}
ASSERT(mBuffer != nullptr);
if (bufferOut != nullptr)
{
*bufferOut = mBuffer;
}
// Optionally map() the buffer if possible
if (ptrOut)
{
*ptrOut = mBuffer->map(contextMtl) + mNextAllocationOffset;
}
if (offsetOut)
{
*offsetOut = static_cast<size_t>(mNextAllocationOffset);
}
mNextAllocationOffset += static_cast<uint32_t>(sizeToAllocate);
return angle::Result::Continue;
}
angle::Result BufferPool::commit(ContextMtl *contextMtl)
{
if (mBuffer)
{
mBuffer->unmap(contextMtl);
mInFlightBuffers.push_back(mBuffer);
mBuffer = nullptr;
}
mNextAllocationOffset = 0;
return angle::Result::Continue;
}
void BufferPool::releaseInFlightBuffers(ContextMtl *contextMtl)
{
for (auto &toRelease : mInFlightBuffers)
{
// If the dynamic buffer was resized we cannot reuse the retained buffer.
if (toRelease->size() < mSize)
{
toRelease = nullptr;
mBuffersAllocated--;
}
else
{
mBufferFreeList.push_back(toRelease);
}
}
mInFlightBuffers.clear();
}
void BufferPool::destroyBufferList(ContextMtl *contextMtl, std::vector<BufferRef> *buffers)
{
ASSERT(mBuffersAllocated >= buffers->size());
mBuffersAllocated -= buffers->size();
buffers->clear();
}
void BufferPool::destroy(ContextMtl *contextMtl)
{
destroyBufferList(contextMtl, &mInFlightBuffers);
destroyBufferList(contextMtl, &mBufferFreeList);
reset();
if (mBuffer)
{
mBuffer->unmap(contextMtl);
mBuffer = nullptr;
}
}
void BufferPool::updateAlignment(ContextMtl *contextMtl, size_t alignment)
{
ASSERT(alignment > 0);
// NOTE(hqle): May check additional platform limits.
mAlignment = alignment;
}
void BufferPool::reset()
{
mSize = 0;
mNextAllocationOffset = 0;
mMaxBuffers = 0;
mAlwaysAllocateNewBuffer = false;
mBuffersAllocated = 0;
}
}
}
...@@ -13,22 +13,275 @@ ...@@ -13,22 +13,275 @@
#import <Metal/Metal.h> #import <Metal/Metal.h>
#include <TargetConditionals.h>
#include <string> #include <string>
#include "common/Optional.h" #include "common/Optional.h"
#include "common/PackedEnums.h" #include "common/PackedEnums.h"
#include "common/angleutils.h" #include "common/angleutils.h"
#include "common/apple_platform_utils.h"
#include "libANGLE/Constants.h" #include "libANGLE/Constants.h"
#include "libANGLE/Version.h" #include "libANGLE/Version.h"
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#if TARGET_OS_IPHONE
# if !defined(ANGLE_IOS_DEPLOY_TARGET)
# define ANGLE_IOS_DEPLOY_TARGET __IPHONE_11_0
# endif
#endif
#define ANGLE_MTL_OBJC_SCOPE @autoreleasepool
#if !__has_feature(objc_arc)
# define ANGLE_MTL_AUTORELEASE autorelease
#else
# define ANGLE_MTL_AUTORELEASE self
#endif
#define ANGLE_MTL_UNUSED __attribute__((unused))
#if defined(ANGLE_MTL_ENABLE_TRACE)
# define ANGLE_MTL_LOG(...) NSLog(@__VA_ARGS__)
#else
# define ANGLE_MTL_LOG(...) (void)0
#endif
namespace egl
{
class Display;
class Image;
} // namespace egl
#define ANGLE_GL_OBJECTS_X(PROC) \
PROC(Buffer) \
PROC(Context) \
PROC(Framebuffer) \
PROC(MemoryObject) \
PROC(Query) \
PROC(Program) \
PROC(Semaphore) \
PROC(Texture) \
PROC(TransformFeedback) \
PROC(VertexArray)
#define ANGLE_PRE_DECLARE_OBJECT(OBJ) class OBJ;
namespace gl
{
struct Rectangle;
ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_OBJECT)
} // namespace gl
#define ANGLE_PRE_DECLARE_MTL_OBJECT(OBJ) class OBJ##Mtl;
namespace rx namespace rx
{ {
class RendererMtl; class DisplayMtl;
class ContextMtl;
class FramebufferMtl;
class BufferMtl;
class VertexArrayMtl;
class TextureMtl;
class ProgramMtl;
ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_MTL_OBJECT)
namespace mtl namespace mtl
{ {
// NOTE(hqle): support variable max number of vertex attributes
constexpr uint32_t kMaxVertexAttribs = gl::MAX_VERTEX_ATTRIBS;
// NOTE(hqle): support variable max number of render targets
constexpr uint32_t kMaxRenderTargets = 1;
constexpr size_t kDefaultAttributeSize = 4 * sizeof(float);
// Metal limits
constexpr uint32_t kMaxShaderBuffers = 31;
constexpr uint32_t kMaxShaderSamplers = 16;
constexpr size_t kDefaultUniformsMaxSize = 4 * 1024;
constexpr uint32_t kMaxViewports = 1;
constexpr uint32_t kVertexAttribBufferOffsetAlignment = 4;
constexpr uint32_t kVertexAttribBufferStrideAlignment = 4;
// Alignment requirement for offset passed to setVertex|FragmentBuffer
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
constexpr uint32_t kBufferSettingOffsetAlignment = 256;
#else
constexpr uint32_t kBufferSettingOffsetAlignment = 4;
#endif
constexpr uint32_t kIndexBufferOffsetAlignment = 4;
// Binding index start for vertex data buffers:
constexpr uint32_t kVboBindingIndexStart = 0;
// Binding index for default attribute buffer:
constexpr uint32_t kDefaultAttribsBindingIndex = kVboBindingIndexStart + kMaxVertexAttribs;
// Binding index for driver uniforms:
constexpr uint32_t kDriverUniformsBindingIndex = kDefaultAttribsBindingIndex + 1;
// Binding index for default uniforms:
constexpr uint32_t kDefaultUniformsBindingIndex = kDefaultAttribsBindingIndex + 3;
constexpr uint32_t kStencilMaskAll = 0xff; // Only 8 bits stencil is supported
constexpr float kEmulatedAlphaValue = 1.0f;
// NOTE(hqle): Support ES 3.0.
constexpr gl::Version kMaxSupportedGLVersion = gl::Version(2, 0);
template <typename T>
struct ImplTypeHelper;
// clang-format off
#define ANGLE_IMPL_TYPE_HELPER_GL(OBJ) \
template<> \
struct ImplTypeHelper<gl::OBJ> \
{ \
using ImplType = OBJ##Mtl; \
};
// clang-format on
ANGLE_GL_OBJECTS_X(ANGLE_IMPL_TYPE_HELPER_GL)
template <>
struct ImplTypeHelper<egl::Display>
{
using ImplType = DisplayMtl;
};
template <typename T>
using GetImplType = typename ImplTypeHelper<T>::ImplType;
template <typename T>
GetImplType<T> *GetImpl(const T *_Nonnull glObject)
{
return GetImplAs<GetImplType<T>>(glObject);
}
// This class wraps Objective-C pointer inside, it will manage the lifetime of
// the Objective-C pointer. Changing pointer is not supported outside subclass.
template <typename T>
class WrappedObject
{
public:
WrappedObject() = default;
~WrappedObject() { release(); }
bool valid() const { return (mMetalObject != nil); }
T get() const { return mMetalObject; }
inline void reset() { release(); }
operator T() const { return get(); }
protected:
inline void set(T obj) { retainAssign(obj); }
void retainAssign(T obj)
{
T retained = obj;
#if !__has_feature(objc_arc)
[retained retain];
#endif
release();
mMetalObject = obj;
}
private:
void release()
{
#if !__has_feature(objc_arc)
[mMetalObject release];
#endif
mMetalObject = nil;
}
T mMetalObject = nil;
};
// This class is similar to WrappedObject, however, it allows changing the
// internal pointer with public methods.
template <typename T>
class AutoObjCPtr : public WrappedObject<T>
{
public:
using ParentType = WrappedObject<T>;
AutoObjCPtr() {}
AutoObjCPtr(const std::nullptr_t &theNull) {}
AutoObjCPtr(const AutoObjCPtr &src) { this->retainAssign(src.get()); }
AutoObjCPtr(AutoObjCPtr &&src) { this->transfer(std::forward<AutoObjCPtr>(src)); }
// Take ownership of the pointer
AutoObjCPtr(T &&src)
{
this->retainAssign(src);
src = nil;
}
AutoObjCPtr &operator=(const AutoObjCPtr &src)
{
this->retainAssign(src.get());
return *this;
}
AutoObjCPtr &operator=(AutoObjCPtr &&src)
{
this->transfer(std::forward<AutoObjCPtr>(src));
return *this;
}
// Take ownership of the pointer
AutoObjCPtr &operator=(T &&src)
{
this->retainAssign(src);
src = nil;
return *this;
}
AutoObjCPtr &operator=(const std::nullptr_t &theNull)
{
this->set(nil);
return *this;
}
bool operator==(const AutoObjCPtr &rhs) const { return (*this) == rhs.get(); }
bool operator==(T rhs) const { return this->get() == rhs; }
bool operator==(const std::nullptr_t &theNull) const { return this->get(); }
inline operator bool() { return this->get(); }
bool operator!=(const AutoObjCPtr &rhs) const { return (*this) != rhs.get(); }
bool operator!=(T rhs) const { return this->get() != rhs; }
using ParentType::retainAssign;
private:
void transfer(AutoObjCPtr &&src)
{
this->retainAssign(std::move(src.get()));
src.reset();
}
};
template <typename T>
using AutoObjCObj = AutoObjCPtr<T *>;
struct ClearOptions
{
Optional<MTLClearColor> clearColor;
Optional<float> clearDepth;
Optional<uint32_t> clearStencil;
};
class CommandQueue;
class ErrorHandler class ErrorHandler
{ {
public: public:
...@@ -38,24 +291,30 @@ class ErrorHandler ...@@ -38,24 +291,30 @@ class ErrorHandler
const char *file, const char *file,
const char *function, const char *function,
unsigned int line) = 0; unsigned int line) = 0;
virtual void handleError(NSError *_Nullable error,
const char *file,
const char *function,
unsigned int line) = 0;
}; };
class Context : public ErrorHandler class Context : public ErrorHandler
{ {
public: public:
Context(RendererMtl *rendererMtl); Context(DisplayMtl *displayMtl);
_Nullable id<MTLDevice> getMetalDevice() const; _Nullable id<MTLDevice> getMetalDevice() const;
mtl::CommandQueue &cmdQueue();
RendererMtl *getRenderer() const { return mRendererMtl; } DisplayMtl *getDisplay() const { return mDisplay; }
protected: protected:
RendererMtl *mRendererMtl; DisplayMtl *mDisplay;
}; };
#define ANGLE_MTL_CHECK(context, test, error) \ #define ANGLE_MTL_CHECK(context, test, error) \
do \ do \
{ \ { \
if (ANGLE_UNLIKELY(!test)) \ if (ANGLE_UNLIKELY(!(test))) \
{ \ { \
context->handleError(error, __FILE__, ANGLE_FUNCTION, __LINE__); \ context->handleError(error, __FILE__, ANGLE_FUNCTION, __LINE__); \
return angle::Result::Stop; \ return angle::Result::Stop; \
......
...@@ -14,18 +14,23 @@ ...@@ -14,18 +14,23 @@
#include <cstring> #include <cstring>
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#include "libANGLE/renderer/metal/RendererMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h"
namespace rx namespace rx
{ {
namespace mtl namespace mtl
{ {
Context::Context(RendererMtl *rendererMtl) : mRendererMtl(rendererMtl) {} Context::Context(DisplayMtl *display) : mDisplay(display) {}
id<MTLDevice> Context::getMetalDevice() const id<MTLDevice> Context::getMetalDevice() const
{ {
return mRendererMtl->getMetalDevice(); return mDisplay->getMetalDevice();
}
mtl::CommandQueue &Context::cmdQueue()
{
return mDisplay->cmdQueue();
} }
} // namespace mtl } // namespace mtl
......
...@@ -20,13 +20,20 @@ ...@@ -20,13 +20,20 @@
namespace rx namespace rx
{ {
class RendererMtl; class DisplayMtl;
namespace mtl namespace mtl
{ {
struct FormatBase struct FormatBase
{ {
inline bool operator==(const FormatBase &rhs) const
{
return intendedFormatId == rhs.intendedFormatId && actualFormatId == rhs.actualFormatId;
}
inline bool operator!=(const FormatBase &rhs) const { return !((*this) == rhs); }
const angle::Format &actualAngleFormat() const; const angle::Format &actualAngleFormat() const;
const angle::Format &intendedAngleFormat() const; const angle::Format &intendedAngleFormat() const;
...@@ -49,7 +56,7 @@ struct Format : public FormatBase ...@@ -49,7 +56,7 @@ struct Format : public FormatBase
MTLPixelFormat metalFormat = MTLPixelFormatInvalid; MTLPixelFormat metalFormat = MTLPixelFormatInvalid;
private: private:
void init(const RendererMtl *renderer, angle::FormatID intendedFormatId); void init(const DisplayMtl *display, angle::FormatID intendedFormatId);
friend class FormatTable; friend class FormatTable;
}; };
...@@ -75,9 +82,9 @@ class FormatTable final : angle::NonCopyable ...@@ -75,9 +82,9 @@ class FormatTable final : angle::NonCopyable
FormatTable() = default; FormatTable() = default;
~FormatTable() = default; ~FormatTable() = default;
angle::Result initialize(const RendererMtl *renderer); angle::Result initialize(const DisplayMtl *display);
void generateTextureCaps(const RendererMtl *renderer, void generateTextureCaps(const DisplayMtl *display,
gl::TextureCapsMap *capsMapOut, gl::TextureCapsMap *capsMapOut,
std::vector<GLenum> *compressedFormatsOut) const; std::vector<GLenum> *compressedFormatsOut) const;
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/renderer/Format.h" #include "libANGLE/renderer/Format.h"
#include "libANGLE/renderer/metal/RendererMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h"
namespace rx namespace rx
{ {
...@@ -22,11 +22,9 @@ namespace mtl ...@@ -22,11 +22,9 @@ namespace mtl
namespace namespace
{ {
bool OverrideTextureCaps(const RendererMtl *renderer, bool OverrideTextureCaps(const DisplayMtl *display, angle::FormatID formatId, gl::TextureCaps *caps)
angle::FormatID formatId,
gl::TextureCaps *caps)
{ {
// TODO(hqle): Auto generate this. // NOTE(hqle): Auto generate this.
switch (formatId) switch (formatId)
{ {
case angle::FormatID::R8G8_UNORM: case angle::FormatID::R8G8_UNORM:
...@@ -40,13 +38,13 @@ bool OverrideTextureCaps(const RendererMtl *renderer, ...@@ -40,13 +38,13 @@ bool OverrideTextureCaps(const RendererMtl *renderer,
true; true;
return true; return true;
default: default:
// TODO(hqle): Handle more cases // NOTE(hqle): Handle more cases
return false; return false;
} }
} }
void GenerateTextureCapsMap(const FormatTable &formatTable, void GenerateTextureCapsMap(const FormatTable &formatTable,
const RendererMtl *renderer, const DisplayMtl *display,
gl::TextureCapsMap *capsMapOut, gl::TextureCapsMap *capsMapOut,
std::vector<GLenum> *compressedFormatsOut) std::vector<GLenum> *compressedFormatsOut)
{ {
...@@ -64,26 +62,30 @@ void GenerateTextureCapsMap(const FormatTable &formatTable, ...@@ -64,26 +62,30 @@ void GenerateTextureCapsMap(const FormatTable &formatTable,
// Then using that json file to generate a table in C++ file. // Then using that json file to generate a table in C++ file.
gl::Extensions tmpTextureExtensions; gl::Extensions tmpTextureExtensions;
#if TARGET_OS_OSX #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
// Requires depth24Stencil8PixelFormatSupported=YES for these extensions // Requires depth24Stencil8PixelFormatSupported=YES for these extensions
bool packedDepthStencil24Support = bool packedDepthStencil24Support =
renderer->getMetalDevice().depth24Stencil8PixelFormatSupported; display->getMetalDevice().depth24Stencil8PixelFormatSupported;
tmpTextureExtensions.packedDepthStencil = true; // We support this reguardless tmpTextureExtensions.packedDepthStencil = true; // We support this reguardless
tmpTextureExtensions.colorBufferHalfFloat = packedDepthStencil24Support; tmpTextureExtensions.colorBufferHalfFloat = packedDepthStencil24Support;
tmpTextureExtensions.colorBufferFloat = packedDepthStencil24Support; tmpTextureExtensions.colorBufferFloat = packedDepthStencil24Support;
tmpTextureExtensions.colorBufferFloatRGB = packedDepthStencil24Support; tmpTextureExtensions.colorBufferFloatRGB = packedDepthStencil24Support;
tmpTextureExtensions.colorBufferFloatRGBA = packedDepthStencil24Support; tmpTextureExtensions.colorBufferFloatRGBA = packedDepthStencil24Support;
tmpTextureExtensions.textureHalfFloat = packedDepthStencil24Support; tmpTextureExtensions.textureHalfFloat = packedDepthStencil24Support;
tmpTextureExtensions.textureFloat = packedDepthStencil24Support; tmpTextureExtensions.textureFloat = packedDepthStencil24Support;
tmpTextureExtensions.textureHalfFloatLinear = packedDepthStencil24Support; tmpTextureExtensions.textureHalfFloatLinear = packedDepthStencil24Support;
tmpTextureExtensions.textureFloatLinear = packedDepthStencil24Support; tmpTextureExtensions.textureFloatLinear = packedDepthStencil24Support;
tmpTextureExtensions.textureRG = packedDepthStencil24Support; tmpTextureExtensions.textureRG = packedDepthStencil24Support;
tmpTextureExtensions.textureFormatBGRA8888 = packedDepthStencil24Support; tmpTextureExtensions.textureFormatBGRA8888 = packedDepthStencil24Support;
tmpTextureExtensions.textureCompressionDXT1 = true;
tmpTextureExtensions.textureCompressionDXT3 = true; tmpTextureExtensions.textureCompressionDXT3 = true;
tmpTextureExtensions.textureCompressionDXT5 = true; tmpTextureExtensions.textureCompressionDXT5 = true;
tmpTextureExtensions.textureCompressionS3TCsRGB = true;
// We can only fully support DXT1 without alpha using texture swizzle support from MacOs 10.15
tmpTextureExtensions.textureCompressionDXT1 = display->getFeatures().hasTextureSwizzle.enabled;
tmpTextureExtensions.textureCompressionS3TCsRGB = tmpTextureExtensions.textureCompressionDXT1;
#else #else
tmpTextureExtensions.packedDepthStencil = true; // override to D32_FLOAT_S8X24_UINT tmpTextureExtensions.packedDepthStencil = true; // override to D32_FLOAT_S8X24_UINT
tmpTextureExtensions.colorBufferHalfFloat = true; tmpTextureExtensions.colorBufferHalfFloat = true;
...@@ -95,7 +97,7 @@ void GenerateTextureCapsMap(const FormatTable &formatTable, ...@@ -95,7 +97,7 @@ void GenerateTextureCapsMap(const FormatTable &formatTable,
tmpTextureExtensions.textureFloat = true; tmpTextureExtensions.textureFloat = true;
tmpTextureExtensions.textureRG = true; tmpTextureExtensions.textureRG = true;
tmpTextureExtensions.textureFormatBGRA8888 = true; tmpTextureExtensions.textureFormatBGRA8888 = true;
if ([renderer->getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1]) if ([display->getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1])
{ {
tmpTextureExtensions.compressedETC1RGB8Texture = true; tmpTextureExtensions.compressedETC1RGB8Texture = true;
tmpTextureExtensions.compressedETC2RGB8Texture = true; tmpTextureExtensions.compressedETC2RGB8Texture = true;
...@@ -132,14 +134,14 @@ void GenerateTextureCapsMap(const FormatTable &formatTable, ...@@ -132,14 +134,14 @@ void GenerateTextureCapsMap(const FormatTable &formatTable,
const auto &clientVersion = kMaxSupportedGLVersion; const auto &clientVersion = kMaxSupportedGLVersion;
// First let check whether we can determine programmatically. // First let check whether we can determine programmatically.
if (!OverrideTextureCaps(renderer, mtlFormat.intendedFormatId, &textureCaps)) if (!OverrideTextureCaps(display, mtlFormat.intendedFormatId, &textureCaps))
{ {
// Let angle decide based on extensions we enabled above. // Let angle decide based on extensions we enabled above.
textureCaps = gl::GenerateMinimumTextureCaps(internalFormatInfo.sizedInternalFormat, textureCaps = gl::GenerateMinimumTextureCaps(internalFormatInfo.sizedInternalFormat,
clientVersion, tmpTextureExtensions); clientVersion, tmpTextureExtensions);
} }
// TODO(hqle): Support MSAA. // NOTE(hqle): Support MSAA.
textureCaps.sampleCounts.clear(); textureCaps.sampleCounts.clear();
textureCaps.sampleCounts.insert(0); textureCaps.sampleCounts.insert(0);
textureCaps.sampleCounts.insert(1); textureCaps.sampleCounts.insert(1);
...@@ -147,7 +149,7 @@ void GenerateTextureCapsMap(const FormatTable &formatTable, ...@@ -147,7 +149,7 @@ void GenerateTextureCapsMap(const FormatTable &formatTable,
if (textureCaps.filterable && mtlFormat.actualFormatId == angle::FormatID::D32_FLOAT) if (textureCaps.filterable && mtlFormat.actualFormatId == angle::FormatID::D32_FLOAT)
{ {
// Only MacOS support filterable for D32_FLOAT texture // Only MacOS support filterable for D32_FLOAT texture
#if !TARGET_OS_OSX #if !TARGET_OS_OSX || TARGET_OS_MACCATALYST
textureCaps.filterable = false; textureCaps.filterable = false;
#endif #endif
} }
...@@ -210,7 +212,7 @@ bool Format::FormatRenderable(MTLPixelFormat format) ...@@ -210,7 +212,7 @@ bool Format::FormatRenderable(MTLPixelFormat format)
case MTLPixelFormatDepth32Float: case MTLPixelFormatDepth32Float:
case MTLPixelFormatStencil8: case MTLPixelFormatStencil8:
case MTLPixelFormatDepth32Float_Stencil8: case MTLPixelFormatDepth32Float_Stencil8:
#if TARGET_OS_OSX #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
case MTLPixelFormatDepth16Unorm: case MTLPixelFormatDepth16Unorm:
case MTLPixelFormatDepth24Unorm_Stencil8: case MTLPixelFormatDepth24Unorm_Stencil8:
#else #else
...@@ -221,7 +223,7 @@ bool Format::FormatRenderable(MTLPixelFormat format) ...@@ -221,7 +223,7 @@ bool Format::FormatRenderable(MTLPixelFormat format)
case MTLPixelFormatABGR4Unorm: case MTLPixelFormatABGR4Unorm:
case MTLPixelFormatBGR5A1Unorm: case MTLPixelFormatBGR5A1Unorm:
#endif #endif
// TODO(hqle): we may add more formats support here in future. // NOTE(hqle): we may add more formats support here in future.
return true; return true;
default: default:
return false; return false;
...@@ -237,11 +239,11 @@ bool Format::FormatCPUReadable(MTLPixelFormat format) ...@@ -237,11 +239,11 @@ bool Format::FormatCPUReadable(MTLPixelFormat format)
case MTLPixelFormatDepth32Float: case MTLPixelFormatDepth32Float:
case MTLPixelFormatStencil8: case MTLPixelFormatStencil8:
case MTLPixelFormatDepth32Float_Stencil8: case MTLPixelFormatDepth32Float_Stencil8:
#if TARGET_OS_OSX #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
case MTLPixelFormatDepth16Unorm: case MTLPixelFormatDepth16Unorm:
case MTLPixelFormatDepth24Unorm_Stencil8: case MTLPixelFormatDepth24Unorm_Stencil8:
#endif #endif
// TODO(hqle): we may add more formats support here in future. // NOTE(hqle): we may add more formats support here in future.
return false; return false;
default: default:
return true; return true;
...@@ -254,13 +256,13 @@ const gl::InternalFormat &Format::intendedInternalFormat() const ...@@ -254,13 +256,13 @@ const gl::InternalFormat &Format::intendedInternalFormat() const
} }
// FormatTable implementation // FormatTable implementation
angle::Result FormatTable::initialize(const RendererMtl *renderer) angle::Result FormatTable::initialize(const DisplayMtl *display)
{ {
for (size_t i = 0; i < angle::kNumANGLEFormats; ++i) for (size_t i = 0; i < angle::kNumANGLEFormats; ++i)
{ {
const auto formatId = static_cast<angle::FormatID>(i); const auto formatId = static_cast<angle::FormatID>(i);
mPixelFormatTable[i].init(renderer, formatId); mPixelFormatTable[i].init(display, formatId);
mVertexFormatTables[0][i].init(formatId, false); mVertexFormatTables[0][i].init(formatId, false);
mVertexFormatTables[1][i].init(formatId, true); mVertexFormatTables[1][i].init(formatId, true);
} }
...@@ -268,11 +270,11 @@ angle::Result FormatTable::initialize(const RendererMtl *renderer) ...@@ -268,11 +270,11 @@ angle::Result FormatTable::initialize(const RendererMtl *renderer)
return angle::Result::Continue; return angle::Result::Continue;
} }
void FormatTable::generateTextureCaps(const RendererMtl *renderer, void FormatTable::generateTextureCaps(const DisplayMtl *display,
gl::TextureCapsMap *capsMapOut, gl::TextureCapsMap *capsMapOut,
std::vector<GLenum> *compressedFormatsOut) const std::vector<GLenum> *compressedFormatsOut) const
{ {
GenerateTextureCapsMap(*this, renderer, capsMapOut, compressedFormatsOut); GenerateTextureCapsMap(*this, display, capsMapOut, compressedFormatsOut);
} }
const Format &FormatTable::getPixelFormat(angle::FormatID angleFormatId) const const Format &FormatTable::getPixelFormat(angle::FormatID angleFormatId) const
......
//
// Copyright (c) 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// GlslangUtils: Wrapper for Khronos's glslang compiler.
//
#ifndef LIBANGLE_RENDERER_METAL_GLSLANGWRAPPER_H_
#define LIBANGLE_RENDERER_METAL_GLSLANGWRAPPER_H_
#include "libANGLE/Caps.h"
#include "libANGLE/Context.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/metal/mtl_common.h"
namespace rx
{
namespace mtl
{
class GlslangUtils
{
public:
static void GetShaderSource(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut);
static angle::Result GetShaderCode(ErrorHandler *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut);
};
} // namespace mtl
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_GLSLANGWRAPPER_H_ */
//
// Copyright (c) 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// GlslangUtils: Wrapper for Khronos's glslang compiler.
//
#include "libANGLE/renderer/metal/mtl_glslang_utils.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
namespace rx
{
namespace mtl
{
// static
void GlslangUtils::GetShaderSource(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut)
{
UNIMPLEMENTED();
}
// static
angle::Result GlslangUtils::GetShaderCode(ErrorHandler *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
UNIMPLEMENTED();
return angle::Result::Stop;
}
} // namespace mtl
} // namespace rx
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// mtl_render_utils.h:
// Defines the class interface for RenderUtils.
//
#ifndef LIBANGLE_RENDERER_METAL_MTL_RENDER_UTILS_H_
#define LIBANGLE_RENDERER_METAL_MTL_RENDER_UTILS_H_
#import <Metal/Metal.h>
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
namespace rx
{
class BufferMtl;
class ContextMtl;
class DisplayMtl;
namespace mtl
{
struct ClearRectParams : public ClearOptions
{
gl::Rectangle clearArea;
bool flipY = false;
};
struct BlitParams
{
gl::Offset dstOffset;
// Destination texture needs to have viewport Y flipped?
// The difference between this param and unpackFlipY is that unpackFlipY is from
// glCopyImageCHROMIUM(), and dstFlipY controls whether the final viewport needs to be
// flipped when drawing to destination texture.
bool dstFlipY = false;
MTLColorWriteMask dstColorMask = MTLColorWriteMaskAll;
TextureRef src;
uint32_t srcLevel = 0;
gl::Rectangle srcRect;
bool srcYFlipped = false; // source texture has data flipped in Y direction
bool unpackFlipY = false; // flip texture data copying process in Y direction
bool unpackPremultiplyAlpha = false;
bool unpackUnmultiplyAlpha = false;
bool dstLuminance = false;
};
struct TriFanFromArrayParams
{
uint32_t firstVertex;
uint32_t vertexCount;
BufferRef dstBuffer;
// Must be multiples of kBufferSettingOffsetAlignment
uint32_t dstOffset;
};
struct IndexGenerationParams
{
gl::DrawElementsType srcType;
GLsizei indexCount;
const void *indices;
BufferRef dstBuffer;
uint32_t dstOffset;
};
class RenderUtils : public Context, angle::NonCopyable
{
public:
RenderUtils(DisplayMtl *display);
~RenderUtils() override;
angle::Result initialize();
void onDestroy();
// Clear current framebuffer
void clearWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ClearRectParams &params);
// Blit texture data to current framebuffer
void blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
angle::Result convertIndexBuffer(const gl::Context *context,
gl::DrawElementsType srcType,
uint32_t indexCount,
const BufferRef &srcBuffer,
uint32_t srcOffset,
const BufferRef &dstBuffer,
// Must be multiples of kBufferSettingOffsetAlignment
uint32_t dstOffset);
angle::Result generateTriFanBufferFromArrays(const gl::Context *context,
const TriFanFromArrayParams &params);
angle::Result generateTriFanBufferFromElementsArray(const gl::Context *context,
const IndexGenerationParams &params);
angle::Result generateLineLoopLastSegment(const gl::Context *context,
uint32_t firstVertex,
uint32_t lastVertex,
const BufferRef &dstBuffer,
uint32_t dstOffset);
angle::Result generateLineLoopLastSegmentFromElementsArray(const gl::Context *context,
const IndexGenerationParams &params);
angle::Result dispatchCompute(const gl::Context *context,
ComputeCommandEncoder *encoder,
id<MTLComputePipelineState> pipelineState,
size_t numThreads);
private:
// override ErrorHandler
void handleError(GLenum error,
const char *file,
const char *function,
unsigned int line) override;
void handleError(NSError *_Nullable error,
const char *file,
const char *function,
unsigned int line) override;
angle::Result initShaderLibrary();
void initClearResources();
void initBlitResources();
void setupClearWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ClearRectParams &params);
void setupBlitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
id<MTLDepthStencilState> getClearDepthStencilState(const gl::Context *context,
const ClearRectParams &params);
id<MTLRenderPipelineState> getClearRenderPipelineState(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ClearRectParams &params);
id<MTLRenderPipelineState> getBlitRenderPipelineState(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
void setupBlitWithDrawUniformData(RenderCommandEncoder *cmdEncoder, const BlitParams &params);
void setupDrawCommonStates(RenderCommandEncoder *cmdEncoder);
AutoObjCPtr<id<MTLComputePipelineState>> getIndexConversionPipeline(
ContextMtl *context,
gl::DrawElementsType srcType,
uint32_t srcOffset);
AutoObjCPtr<id<MTLComputePipelineState>> getTriFanFromElemArrayGeneratorPipeline(
ContextMtl *context,
gl::DrawElementsType srcType,
uint32_t srcOffset);
angle::Result ensureTriFanFromArrayGeneratorInitialized(ContextMtl *context);
angle::Result generateTriFanBufferFromElementsArrayGPU(
const gl::Context *context,
gl::DrawElementsType srcType,
uint32_t indexCount,
const BufferRef &srcBuffer,
uint32_t srcOffset,
const BufferRef &dstBuffer,
// Must be multiples of kBufferSettingOffsetAlignment
uint32_t dstOffset);
angle::Result generateTriFanBufferFromElementsArrayCPU(const gl::Context *context,
const IndexGenerationParams &params);
angle::Result generateLineLoopLastSegmentFromElementsArrayCPU(
const gl::Context *context,
const IndexGenerationParams &params);
AutoObjCPtr<id<MTLLibrary>> mDefaultShaders = nil;
RenderPipelineCache mClearRenderPipelineCache;
RenderPipelineCache mBlitRenderPipelineCache;
RenderPipelineCache mBlitPremultiplyAlphaRenderPipelineCache;
RenderPipelineCache mBlitUnmultiplyAlphaRenderPipelineCache;
struct IndexConvesionPipelineCacheKey
{
gl::DrawElementsType srcType;
bool srcBufferOffsetAligned;
bool operator==(const IndexConvesionPipelineCacheKey &other) const;
bool operator<(const IndexConvesionPipelineCacheKey &other) const;
};
std::map<IndexConvesionPipelineCacheKey, AutoObjCPtr<id<MTLComputePipelineState>>>
mIndexConversionPipelineCaches;
std::map<IndexConvesionPipelineCacheKey, AutoObjCPtr<id<MTLComputePipelineState>>>
mTriFanFromElemArrayGeneratorPipelineCaches;
AutoObjCPtr<id<MTLComputePipelineState>> mTriFanFromArraysGeneratorPipeline;
};
} // namespace mtl
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_MTL_RENDER_UTILS_H_ */
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// mtl_resources.h:
// Declares wrapper classes for Metal's MTLTexture and MTLBuffer.
//
#ifndef LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_
#define LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_
#import <Metal/Metal.h>
#include <atomic>
#include <memory>
#include "common/FastVector.h"
#include "common/MemoryBuffer.h"
#include "common/angleutils.h"
#include "libANGLE/Error.h"
#include "libANGLE/ImageIndex.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/metal/mtl_common.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
namespace rx
{
class ContextMtl;
namespace mtl
{
class CommandQueue;
class Resource;
class Texture;
class Buffer;
using ResourceRef = std::shared_ptr<Resource>;
using TextureRef = std::shared_ptr<Texture>;
using TextureWeakRef = std::weak_ptr<Texture>;
using BufferRef = std::shared_ptr<Buffer>;
using BufferWeakRef = std::weak_ptr<Buffer>;
class Resource : angle::NonCopyable
{
public:
virtual ~Resource() {}
bool isBeingUsedByGPU(Context *context) const;
void setUsedByCommandBufferWithQueueSerial(uint64_t serial, bool writing);
const std::atomic<uint64_t> &getCommandBufferQueueSerial() const
{
return mUsageRef->cmdBufferQueueSerial;
}
// Flag indicate whether we should synchornize the content to CPU after GPU changed this
// resource's content.
bool isCPUReadMemDirty() const { return mUsageRef->cpuReadMemDirty; }
void resetCPUReadMemDirty() { mUsageRef->cpuReadMemDirty = false; }
protected:
Resource();
// Share the GPU usage ref with other resource
Resource(Resource *other);
private:
struct UsageRef
{
// The id of the last command buffer that is using this resource.
std::atomic<uint64_t> cmdBufferQueueSerial{0};
// NOTE(hqle): resource dirty handle is not threadsafe.
// This flag means the resource was issued to be modified by GPU, if CPU wants to read
// its content, explicit synchornization call must be invoked.
bool cpuReadMemDirty = false;
};
// One resource object might just be a view of another resource. For example, a texture 2d
// object might be a view of one face of a cube texture object. Another example is one texture
// object of size 2x2 might be a mipmap view of a texture object size 4x4. Thus, if one object
// is being used by a command buffer, it means the other object is being used also. In this
// case, the two objects must share the same UsageRef property.
std::shared_ptr<UsageRef> mUsageRef;
};
class Texture final : public Resource,
public WrappedObject<id<MTLTexture>>,
public std::enable_shared_from_this<Texture>
{
public:
static angle::Result Make2DTexture(ContextMtl *context,
const Format &format,
uint32_t width,
uint32_t height,
uint32_t mips /** use zero to create full mipmaps chain */,
bool renderTargetOnly,
TextureRef *refOut);
static angle::Result MakeCubeTexture(ContextMtl *context,
const Format &format,
uint32_t size,
uint32_t mips /** use zero to create full mipmaps chain */,
bool renderTargetOnly,
TextureRef *refOut);
static TextureRef MakeFromMetal(id<MTLTexture> metalTexture);
void replaceRegion(ContextMtl *context,
MTLRegion region,
uint32_t mipmapLevel,
uint32_t slice,
const uint8_t *data,
size_t bytesPerRow);
// read pixel data from slice 0
void getBytes(ContextMtl *context,
size_t bytesPerRow,
MTLRegion region,
uint32_t mipmapLevel,
uint8_t *dataOut);
// Create 2d view of a cube face
TextureRef createFaceView(uint32_t face);
MTLTextureType textureType() const;
MTLPixelFormat pixelFormat() const;
uint32_t mipmapLevels() const;
uint32_t width(uint32_t level = 0) const;
uint32_t height(uint32_t level = 0) const;
gl::Extents size(uint32_t level = 0) const;
gl::Extents size(const gl::ImageIndex &index) const;
// For render target
MTLColorWriteMask getColorWritableMask() const { return mColorWritableMask; }
void setColorWritableMask(MTLColorWriteMask mask) { mColorWritableMask = mask; }
// Change the wrapped metal object. Special case for swapchain image
void set(id<MTLTexture> metalTexture);
private:
using ParentClass = WrappedObject<id<MTLTexture>>;
Texture(id<MTLTexture> metalTexture);
Texture(ContextMtl *context,
MTLTextureDescriptor *desc,
uint32_t mips,
bool renderTargetOnly,
bool supportTextureView);
// Create a texture view
Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, uint32_t slice);
void syncContent(ContextMtl *context);
MTLColorWriteMask mColorWritableMask = MTLColorWriteMaskAll;
};
class Buffer final : public Resource, public WrappedObject<id<MTLBuffer>>
{
public:
static angle::Result MakeBuffer(ContextMtl *context,
size_t size,
const uint8_t *data,
BufferRef *bufferOut);
angle::Result reset(ContextMtl *context, size_t size, const uint8_t *data);
uint8_t *map(ContextMtl *context);
void unmap(ContextMtl *context);
size_t size() const;
private:
Buffer(ContextMtl *context, size_t size, const uint8_t *data);
};
} // namespace mtl
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_ */
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// mtl_utils.h:
// Declares utilities functions that create Metal shaders, convert from angle enums
// to Metal enums and so on.
//
#ifndef LIBANGLE_RENDERER_METAL_MTL_UTILS_H_
#define LIBANGLE_RENDERER_METAL_MTL_UTILS_H_
#import <Metal/Metal.h>
#include "angle_gl.h"
#include "common/PackedEnums.h"
#include "libANGLE/Context.h"
#include "libANGLE/Texture.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
namespace rx
{
namespace mtl
{
NS_ASSUME_NONNULL_BEGIN
angle::Result InitializeTextureContents(const gl::Context *context,
const TextureRef &texture,
const Format &textureObjFormat,
const gl::ImageIndex &index);
MTLViewport GetViewport(const gl::Rectangle &rect, double znear = 0, double zfar = 1);
MTLViewport GetViewportFlipY(const gl::Rectangle &rect,
NSUInteger screenHeight,
double znear = 0,
double zfar = 1);
MTLViewport GetViewport(const gl::Rectangle &rect,
NSUInteger screenHeight,
bool flipY,
double znear = 0,
double zfar = 1);
MTLScissorRect GetScissorRect(const gl::Rectangle &rect,
NSUInteger screenHeight = 0,
bool flipY = false);
AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary(id<MTLDevice> metalDevice,
const std::string &source,
AutoObjCPtr<NSError *> *error);
AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary(id<MTLDevice> metalDevice,
const char *source,
size_t sourceLen,
AutoObjCPtr<NSError *> *error);
AutoObjCPtr<id<MTLLibrary>> CreateShaderLibraryFromBinary(id<MTLDevice> metalDevice,
const uint8_t *binarySource,
size_t binarySourceLen,
AutoObjCPtr<NSError *> *error);
// Need to define invalid enum value since Metal doesn't define it
constexpr MTLTextureType MTLTextureTypeInvalid = static_cast<MTLTextureType>(NSUIntegerMax);
static_assert(sizeof(MTLTextureType) == sizeof(NSUInteger),
"MTLTextureType is supposed to be based on NSUInteger");
constexpr MTLPrimitiveType MTLPrimitiveTypeInvalid = static_cast<MTLPrimitiveType>(NSUIntegerMax);
static_assert(sizeof(MTLPrimitiveType) == sizeof(NSUInteger),
"MTLPrimitiveType is supposed to be based on NSUInteger");
constexpr MTLIndexType MTLIndexTypeInvalid = static_cast<MTLIndexType>(NSUIntegerMax);
static_assert(sizeof(MTLIndexType) == sizeof(NSUInteger),
"MTLIndexType is supposed to be based on NSUInteger");
MTLTextureType GetTextureType(gl::TextureType glType);
MTLSamplerMinMagFilter GetFilter(GLenum filter);
MTLSamplerMipFilter GetMipmapFilter(GLenum filter);
MTLSamplerAddressMode GetSamplerAddressMode(GLenum wrap);
MTLBlendFactor GetBlendFactor(GLenum factor);
MTLBlendOperation GetBlendOp(GLenum op);
MTLCompareFunction GetCompareFunc(GLenum func);
MTLStencilOperation GetStencilOp(GLenum op);
MTLWinding GetFontfaceWinding(GLenum frontFaceMode, bool invert);
PrimitiveTopologyClass GetPrimitiveTopologyClass(gl::PrimitiveMode mode);
MTLPrimitiveType GetPrimitiveType(gl::PrimitiveMode mode);
MTLIndexType GetIndexType(gl::DrawElementsType type);
// Useful to set clear color for texture originally having no alpha in GL, but backend's format
// has alpha channel.
MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask colorMask);
NS_ASSUME_NONNULL_END
} // namespace mtl
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_MTL_UTILS_H_ */
...@@ -14,7 +14,7 @@ struct BlitParams ...@@ -14,7 +14,7 @@ struct BlitParams
float2 srcTexCoords[4]; float2 srcTexCoords[4];
int srcLevel; int srcLevel;
bool srcLuminance; // source texture is luminance texture bool srcLuminance; // source texture is luminance texture
bool dstFlipY; bool dstFlipViewportY;
bool dstLuminance; // destination texture is luminance; bool dstLuminance; // destination texture is luminance;
}; };
...@@ -31,9 +31,11 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]], ...@@ -31,9 +31,11 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
output.position = float4(gCorners[vid], 0.0, 1.0); output.position = float4(gCorners[vid], 0.0, 1.0);
output.texCoords = options.srcTexCoords[gTexcoordsIndices[vid]]; output.texCoords = options.srcTexCoords[gTexcoordsIndices[vid]];
if (options.dstFlipY) if (!options.dstFlipViewportY)
{ {
output.position = -output.position; // If viewport is not flipped, we have to flip Y in normalized device coordinates.
// Since NDC has Y is opposite direction of viewport coodrinates.
output.position.y = -output.position.y;
} }
return output; return output;
...@@ -61,7 +63,7 @@ float4 blitOutput(float4 color, constant BlitParams &options) ...@@ -61,7 +63,7 @@ float4 blitOutput(float4 color, constant BlitParams &options)
if (options.dstLuminance) if (options.dstLuminance)
{ {
ret.r = ret.g = ret.b = (color.r * 0.3) + (color.g * 0.59) + (color.b * 0.11); ret.r = ret.g = ret.b = color.r;
} }
return ret; return ret;
......
...@@ -13,6 +13,12 @@ ...@@ -13,6 +13,12 @@
# include <metal_stdlib> # include <metal_stdlib>
#endif #endif
#define ANGLE_KERNEL_GUARD(IDX, MAX_COUNT) \
if (IDX >= MAX_COUNT) \
{ \
return; \
}
using namespace metal; using namespace metal;
// Full screen quad's vertices // Full screen quad's vertices
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -6,17 +6,51 @@ ...@@ -6,17 +6,51 @@
#include "common.h" #include "common.h"
constant bool kSourceBufferAligned[[function_constant(0)]];
constant bool kSourceIndexIsU8[[function_constant(1)]];
constant bool kSourceIndexIsU16[[function_constant(2)]];
constant bool kSourceIndexIsU32[[function_constant(3)]];
constant bool kSourceBufferUnaligned = !kSourceBufferAligned;
constant bool kUseSourceBufferU8 = kSourceIndexIsU8 || kSourceBufferUnaligned;
constant bool kUseSourceBufferU16 = kSourceIndexIsU16 && kSourceBufferAligned;
constant bool kUseSourceBufferU32 = kSourceIndexIsU32 && kSourceBufferAligned;
struct IndexConversionParams struct IndexConversionParams
{ {
uint32_t srcOffset; // offset in bytes uint32_t srcOffset; // offset in bytes
uint32_t indexCount; uint32_t indexCount;
}; };
#define ANGLE_IDX_CONVERSION_GUARD(IDX, OPTS) \ #define ANGLE_IDX_CONVERSION_GUARD(IDX, OPTS) ANGLE_KERNEL_GUARD(IDX, OPTS.indexCount)
if (IDX >= OPTS.indexCount) \
{ \ inline ushort getIndexAligned(constant ushort *inputAligned, uint offset, uint idx)
return; \ {
} return inputAligned[offset / 2 + idx];
}
inline uint getIndexAligned(constant uint *inputAligned, uint offset, uint idx)
{
return inputAligned[offset / 4 + idx];
}
inline uchar getIndexAligned(constant uchar *input, uint offset, uint idx)
{
return input[offset + idx];
}
inline ushort getIndexUnalignedU16(constant uchar *input, uint offset, uint idx)
{
ushort inputLo = input[offset + 2 * idx];
ushort inputHi = input[offset + 2 * idx + 1];
// Little endian conversion:
return inputLo | (inputHi << 8);
}
inline uint getIndexUnalignedU32(constant uchar *input, uint offset, uint idx)
{
uint input0 = input[offset + 4 * idx];
uint input1 = input[offset + 4 * idx + 1];
uint input2 = input[offset + 4 * idx + 2];
uint input3 = input[offset + 4 * idx + 3];
// Little endian conversion:
return input0 | (input1 << 8) | (input2 << 16) | (input3 << 24);
}
kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]], kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]],
constant IndexConversionParams &options[[buffer(0)]], constant IndexConversionParams &options[[buffer(0)]],
...@@ -24,53 +58,113 @@ kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]], ...@@ -24,53 +58,113 @@ kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]],
device ushort *output[[buffer(2)]]) device ushort *output[[buffer(2)]])
{ {
ANGLE_IDX_CONVERSION_GUARD(idx, options); ANGLE_IDX_CONVERSION_GUARD(idx, options);
output[idx] = input[options.srcOffset + idx]; output[idx] = getIndexAligned(input, options.srcOffset, idx);
} }
kernel void convertIndexU16Unaligned(uint idx[[thread_position_in_grid]], kernel void convertIndexU16(
constant IndexConversionParams &options[[buffer(0)]], uint idx[[thread_position_in_grid]],
constant uchar *input[[buffer(1)]], constant IndexConversionParams &options[[buffer(0)]],
device ushort *output[[buffer(2)]]) constant uchar *input[[ buffer(1), function_constant(kSourceBufferUnaligned) ]],
constant ushort *inputAligned[[ buffer(1), function_constant(kSourceBufferAligned) ]],
device ushort *output[[buffer(2)]])
{ {
ANGLE_IDX_CONVERSION_GUARD(idx, options); ANGLE_IDX_CONVERSION_GUARD(idx, options);
ushort inputLo = input[options.srcOffset + 2 * idx];
ushort inputHi = input[options.srcOffset + 2 * idx + 1]; ushort value;
// Little endian conversion: if (kSourceBufferAligned)
ushort value = inputLo | (inputHi << 8); {
value = getIndexAligned(inputAligned, options.srcOffset, idx);
}
else
{
value = getIndexUnalignedU16(input, options.srcOffset, idx);
}
output[idx] = value; output[idx] = value;
} }
kernel void convertIndexU16Aligned(uint idx[[thread_position_in_grid]], kernel void convertIndexU32(
constant IndexConversionParams &options[[buffer(0)]], uint idx[[thread_position_in_grid]],
constant ushort *input[[buffer(1)]], constant IndexConversionParams &options[[buffer(0)]],
device ushort *output[[buffer(2)]]) constant uchar *input[[ buffer(1), function_constant(kSourceBufferUnaligned) ]],
constant uint *inputAligned[[ buffer(1), function_constant(kSourceBufferAligned) ]],
device uint *output[[buffer(2)]])
{ {
ANGLE_IDX_CONVERSION_GUARD(idx, options); ANGLE_IDX_CONVERSION_GUARD(idx, options);
ushort value = input[options.srcOffset / 2 + idx];
uint value;
if (kSourceBufferAligned)
{
value = getIndexAligned(inputAligned, options.srcOffset, idx);
}
else
{
value = getIndexUnalignedU32(input, options.srcOffset, idx);
}
output[idx] = value; output[idx] = value;
} }
kernel void convertIndexU32Unaligned(uint idx[[thread_position_in_grid]], struct TriFanArrayParams
constant IndexConversionParams &options[[buffer(0)]],
constant uchar *input[[buffer(1)]],
device uint *output[[buffer(2)]])
{ {
ANGLE_IDX_CONVERSION_GUARD(idx, options); uint firstVertex;
uint input0 = input[options.srcOffset + 4 * idx]; uint vertexCountFrom3rd; // vertex count excluding the 1st & 2nd vertices.
uint input1 = input[options.srcOffset + 4 * idx + 1]; };
uint input2 = input[options.srcOffset + 4 * idx + 2]; kernel void genTriFanIndicesFromArray(uint idx[[thread_position_in_grid]],
uint input3 = input[options.srcOffset + 4 * idx + 3]; constant TriFanArrayParams &options[[buffer(0)]],
// Little endian conversion: device uint *output[[buffer(2)]])
uint value = input0 | (input1 << 8) | (input2 << 16) | (input3 << 24); {
output[idx] = value; ANGLE_KERNEL_GUARD(idx, options.vertexCountFrom3rd);
uint vertexIdx = options.firstVertex + 2 + idx;
output[3 * idx] = options.firstVertex;
output[3 * idx + 1] = vertexIdx - 1;
output[3 * idx + 2] = vertexIdx;
} }
kernel void convertIndexU32Aligned(uint idx[[thread_position_in_grid]], inline uint getIndexU32(uint offset,
constant IndexConversionParams &options[[buffer(0)]], uint idx,
constant uint *input[[buffer(1)]], constant uchar *inputU8[[function_constant(kUseSourceBufferU8)]],
device uint *output[[buffer(2)]]) constant ushort *inputU16[[function_constant(kUseSourceBufferU16)]],
constant uint *inputU32[[function_constant(kUseSourceBufferU32)]])
{ {
ANGLE_IDX_CONVERSION_GUARD(idx, options); if (kUseSourceBufferU8)
uint value = input[options.srcOffset / 4 + idx]; {
output[idx] = value; if (kSourceIndexIsU16)
{
return getIndexUnalignedU16(inputU8, offset, idx);
}
else if (kSourceIndexIsU32)
{
return getIndexUnalignedU32(inputU8, offset, idx);
}
return getIndexAligned(inputU8, offset, idx);
}
else if (kUseSourceBufferU16)
{
return getIndexAligned(inputU16, offset, idx);
}
else if (kUseSourceBufferU32)
{
return getIndexAligned(inputU32, offset, idx);
}
return 0;
} }
// Generate triangle fan indices from an indices buffer. indexCount options indicates number
// of indices starting from the 3rd.
kernel void genTriFanIndicesFromElements(
uint idx[[thread_position_in_grid]],
constant IndexConversionParams &options[[buffer(0)]],
constant uchar *inputU8[[ buffer(1), function_constant(kUseSourceBufferU8) ]],
constant ushort *inputU16[[ buffer(1), function_constant(kUseSourceBufferU16) ]],
constant uint *inputU32[[ buffer(1), function_constant(kUseSourceBufferU32) ]],
device uint *output[[buffer(2)]])
{
ANGLE_IDX_CONVERSION_GUARD(idx, options);
uint elemIdx = 2 + idx;
output[3 * idx] = getIndexU32(options.srcOffset, 0, inputU8, inputU16, inputU32);
output[3 * idx + 1] = getIndexU32(options.srcOffset, elemIdx - 1, inputU8, inputU16, inputU32);
output[3 * idx + 2] = getIndexU32(options.srcOffset, elemIdx, inputU8, inputU16, inputU32);
}
\ No newline at end of file
...@@ -25,7 +25,9 @@ template_header_boilerplate = """// GENERATED FILE - DO NOT EDIT. ...@@ -25,7 +25,9 @@ template_header_boilerplate = """// GENERATED FILE - DO NOT EDIT.
def main(): def main():
# auto_script parameters. # auto_script parameters.
if len(sys.argv) > 1: if len(sys.argv) > 1:
inputs = ['master_source.metal'] inputs = [
'master_source.metal', 'blit.metal', 'clear.metal', 'gen_indices.metal', 'common.h'
]
outputs = ['compiled/mtl_default_shaders.inc', 'mtl_default_shaders_src_autogen.inc'] outputs = ['compiled/mtl_default_shaders.inc', 'mtl_default_shaders_src_autogen.inc']
if sys.argv[1] == 'inputs': if sys.argv[1] == 'inputs':
...@@ -47,7 +49,7 @@ def main(): ...@@ -47,7 +49,7 @@ def main():
print('Compiling ios version of default shaders ...') print('Compiling ios version of default shaders ...')
os.system( os.system(
'xcrun -sdk iphoneos metal master_source.metal -mios-version-min=8.0 -c -o compiled/default.ios.air' 'xcrun -sdk iphoneos metal master_source.metal -mios-version-min=11.0 -c -o compiled/default.ios.air'
) )
os.system( os.system(
'xcrun -sdk iphoneos metallib compiled/default.ios.air -o compiled/default.ios.metallib') 'xcrun -sdk iphoneos metallib compiled/default.ios.air -o compiled/default.ios.metallib')
...@@ -69,14 +71,15 @@ def main(): ...@@ -69,14 +71,15 @@ def main():
os.system('echo "#include <TargetConditionals.h>\n\n" >> compiled/mtl_default_shaders.inc') os.system('echo "#include <TargetConditionals.h>\n\n" >> compiled/mtl_default_shaders.inc')
# Mac version # Mac version
os.system('echo "#if TARGET_OS_OSX\n" >> compiled/mtl_default_shaders.inc') os.system(
'echo "#if TARGET_OS_OSX || TARGET_OS_MACCATALYST\n" >> compiled/mtl_default_shaders.inc')
os.system('echo "constexpr" >> compiled/mtl_default_shaders.inc') os.system('echo "constexpr" >> compiled/mtl_default_shaders.inc')
os.system('xxd -i compiled/default.metallib >> compiled/mtl_default_shaders.inc') os.system('xxd -i compiled/default.metallib >> compiled/mtl_default_shaders.inc')
# iOS simulator version # iOS simulator version
os.system( os.system(
'echo "\n#elif TARGET_OS_SIMULATOR // TARGET_OS_OSX\n" >> compiled/mtl_default_shaders.inc' 'echo "\n#elif TARGET_OS_SIMULATOR // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n" >> compiled/mtl_default_shaders.inc'
) )
os.system( os.system(
...@@ -90,7 +93,8 @@ def main(): ...@@ -90,7 +93,8 @@ def main():
# iOS version # iOS version
os.system( os.system(
'echo "\n#elif TARGET_OS_IOS // TARGET_OS_OSX\n" >> compiled/mtl_default_shaders.inc') 'echo "\n#elif TARGET_OS_IOS // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n" >> compiled/mtl_default_shaders.inc'
)
os.system( os.system(
'echo "#define compiled_default_metallib compiled_default_ios_metallib" >> compiled/mtl_default_shaders.inc' 'echo "#define compiled_default_metallib compiled_default_ios_metallib" >> compiled/mtl_default_shaders.inc'
...@@ -101,7 +105,9 @@ def main(): ...@@ -101,7 +105,9 @@ def main():
os.system('echo "constexpr" >> compiled/mtl_default_shaders.inc') os.system('echo "constexpr" >> compiled/mtl_default_shaders.inc')
os.system('xxd -i compiled/default.ios.metallib >> compiled/mtl_default_shaders.inc') os.system('xxd -i compiled/default.ios.metallib >> compiled/mtl_default_shaders.inc')
os.system('echo "#endif // TARGET_OS_OSX\n" >> compiled/mtl_default_shaders.inc') os.system(
'echo "#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n" >> compiled/mtl_default_shaders.inc'
)
# Write full source string for debug purpose # Write full source string for debug purpose
os.system("echo \"{0}\" > mtl_default_shaders_src_autogen.inc".format(boilerplate_code)) os.system("echo \"{0}\" > mtl_default_shaders_src_autogen.inc".format(boilerplate_code))
......
...@@ -38,7 +38,7 @@ constexpr char default_metallib_src[] = R"( ...@@ -38,7 +38,7 @@ constexpr char default_metallib_src[] = R"(
# 1 "./common.h" 1 # 1 "./common.h" 1
# 16 "./common.h" # 22 "./common.h"
using namespace metal; using namespace metal;
...@@ -82,7 +82,7 @@ struct BlitParams ...@@ -82,7 +82,7 @@ struct BlitParams
float2 srcTexCoords[4]; float2 srcTexCoords[4];
int srcLevel; int srcLevel;
bool srcLuminance; bool srcLuminance;
bool dstFlipY; bool dstFlipViewportY;
bool dstLuminance; bool dstLuminance;
}; };
...@@ -99,9 +99,11 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]], ...@@ -99,9 +99,11 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
output.position = float4(gCorners[vid], 0.0, 1.0); output.position = float4(gCorners[vid], 0.0, 1.0);
output.texCoords = options.srcTexCoords[gTexcoordsIndices[vid]]; output.texCoords = options.srcTexCoords[gTexcoordsIndices[vid]];
if (options.dstFlipY) if (!options.dstFlipViewportY)
{ {
output.position = -output.position;
output.position.y = -output.position.y;
} }
return output; return output;
...@@ -129,7 +131,7 @@ float4 blitOutput(float4 color, constant BlitParams &options) ...@@ -129,7 +131,7 @@ float4 blitOutput(float4 color, constant BlitParams &options)
if (options.dstLuminance) if (options.dstLuminance)
{ {
ret.r = ret.g = ret.b = (color.r * 0.3) + (color.g * 0.59) + (color.b * 0.11); ret.r = ret.g = ret.b = color.r;
} }
return ret; return ret;
...@@ -172,6 +174,15 @@ fragment float4 blitUnmultiplyAlphaFS(BlitVSOut input [[stage_in]], ...@@ -172,6 +174,15 @@ fragment float4 blitUnmultiplyAlphaFS(BlitVSOut input [[stage_in]],
constant bool kSourceBufferAligned[[function_constant(0)]];
constant bool kSourceIndexIsU8[[function_constant(1)]];
constant bool kSourceIndexIsU16[[function_constant(2)]];
constant bool kSourceIndexIsU32[[function_constant(3)]];
constant bool kSourceBufferUnaligned = !kSourceBufferAligned;
constant bool kUseSourceBufferU8 = kSourceIndexIsU8 || kSourceBufferUnaligned;
constant bool kUseSourceBufferU16 = kSourceIndexIsU16 && kSourceBufferAligned;
constant bool kUseSourceBufferU32 = kSourceIndexIsU32 && kSourceBufferAligned;
struct IndexConversionParams struct IndexConversionParams
{ {
uint32_t srcOffset; uint32_t srcOffset;
...@@ -180,9 +191,34 @@ struct IndexConversionParams ...@@ -180,9 +191,34 @@ struct IndexConversionParams
inline ushort getIndexAligned(constant ushort *inputAligned, uint offset, uint idx)
{
return inputAligned[offset / 2 + idx];
}
inline uint getIndexAligned(constant uint *inputAligned, uint offset, uint idx)
{
return inputAligned[offset / 4 + idx];
}
inline uchar getIndexAligned(constant uchar *input, uint offset, uint idx)
{
return input[offset + idx];
}
inline ushort getIndexUnalignedU16(constant uchar *input, uint offset, uint idx)
{
ushort inputLo = input[offset + 2 * idx];
ushort inputHi = input[offset + 2 * idx + 1];
return inputLo | (inputHi << 8);
}
inline uint getIndexUnalignedU32(constant uchar *input, uint offset, uint idx)
{
uint input0 = input[offset + 4 * idx];
uint input1 = input[offset + 4 * idx + 1];
uint input2 = input[offset + 4 * idx + 2];
uint input3 = input[offset + 4 * idx + 3];
return input0 | (input1 << 8) | (input2 << 16) | (input3 << 24);
}
kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]], kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]],
constant IndexConversionParams &options[[buffer(0)]], constant IndexConversionParams &options[[buffer(0)]],
...@@ -190,55 +226,115 @@ kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]], ...@@ -190,55 +226,115 @@ kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]],
device ushort *output[[buffer(2)]]) device ushort *output[[buffer(2)]])
{ {
if (idx >= options.indexCount) { return; }; if (idx >= options.indexCount) { return; };
output[idx] = input[options.srcOffset + idx]; output[idx] = getIndexAligned(input, options.srcOffset, idx);
} }
kernel void convertIndexU16Unaligned(uint idx[[thread_position_in_grid]], kernel void convertIndexU16(
constant IndexConversionParams &options[[buffer(0)]], uint idx[[thread_position_in_grid]],
constant uchar *input[[buffer(1)]], constant IndexConversionParams &options[[buffer(0)]],
device ushort *output[[buffer(2)]]) constant uchar *input[[ buffer(1), function_constant(kSourceBufferUnaligned) ]],
constant ushort *inputAligned[[ buffer(1), function_constant(kSourceBufferAligned) ]],
device ushort *output[[buffer(2)]])
{ {
if (idx >= options.indexCount) { return; }; if (idx >= options.indexCount) { return; };
ushort inputLo = input[options.srcOffset + 2 * idx];
ushort inputHi = input[options.srcOffset + 2 * idx + 1];
ushort value = inputLo | (inputHi << 8); ushort value;
if (kSourceBufferAligned)
{
value = getIndexAligned(inputAligned, options.srcOffset, idx);
}
else
{
value = getIndexUnalignedU16(input, options.srcOffset, idx);
}
output[idx] = value; output[idx] = value;
} }
kernel void convertIndexU16Aligned(uint idx[[thread_position_in_grid]], kernel void convertIndexU32(
constant IndexConversionParams &options[[buffer(0)]], uint idx[[thread_position_in_grid]],
constant ushort *input[[buffer(1)]], constant IndexConversionParams &options[[buffer(0)]],
device ushort *output[[buffer(2)]]) constant uchar *input[[ buffer(1), function_constant(kSourceBufferUnaligned) ]],
constant uint *inputAligned[[ buffer(1), function_constant(kSourceBufferAligned) ]],
device uint *output[[buffer(2)]])
{ {
if (idx >= options.indexCount) { return; }; if (idx >= options.indexCount) { return; };
ushort value = input[options.srcOffset / 2 + idx];
uint value;
if (kSourceBufferAligned)
{
value = getIndexAligned(inputAligned, options.srcOffset, idx);
}
else
{
value = getIndexUnalignedU32(input, options.srcOffset, idx);
}
output[idx] = value; output[idx] = value;
} }
kernel void convertIndexU32Unaligned(uint idx[[thread_position_in_grid]], struct TriFanArrayParams
constant IndexConversionParams &options[[buffer(0)]],
constant uchar *input[[buffer(1)]],
device uint *output[[buffer(2)]])
{ {
if (idx >= options.indexCount) { return; }; uint firstVertex;
uint input0 = input[options.srcOffset + 4 * idx]; uint vertexCountFrom3rd;
uint input1 = input[options.srcOffset + 4 * idx + 1]; };
uint input2 = input[options.srcOffset + 4 * idx + 2]; kernel void genTriFanIndicesFromArray(uint idx[[thread_position_in_grid]],
uint input3 = input[options.srcOffset + 4 * idx + 3]; constant TriFanArrayParams &options[[buffer(0)]],
device uint *output[[buffer(2)]])
{
if (idx >= options.vertexCountFrom3rd) { return; };
uint value = input0 | (input1 << 8) | (input2 << 16) | (input3 << 24); uint vertexIdx = options.firstVertex + 2 + idx;
output[idx] = value;
output[3 * idx] = options.firstVertex;
output[3 * idx + 1] = vertexIdx - 1;
output[3 * idx + 2] = vertexIdx;
} }
kernel void convertIndexU32Aligned(uint idx[[thread_position_in_grid]], inline uint getIndexU32(uint offset,
constant IndexConversionParams &options[[buffer(0)]], uint idx,
constant uint *input[[buffer(1)]], constant uchar *inputU8[[function_constant(kUseSourceBufferU8)]],
device uint *output[[buffer(2)]]) constant ushort *inputU16[[function_constant(kUseSourceBufferU16)]],
constant uint *inputU32[[function_constant(kUseSourceBufferU32)]])
{
if (kUseSourceBufferU8)
{
if (kSourceIndexIsU16)
{
return getIndexUnalignedU16(inputU8, offset, idx);
}
else if (kSourceIndexIsU32)
{
return getIndexUnalignedU32(inputU8, offset, idx);
}
return getIndexAligned(inputU8, offset, idx);
}
else if (kUseSourceBufferU16)
{
return getIndexAligned(inputU16, offset, idx);
}
else if (kUseSourceBufferU32)
{
return getIndexAligned(inputU32, offset, idx);
}
return 0;
}
kernel void genTriFanIndicesFromElements(
uint idx[[thread_position_in_grid]],
constant IndexConversionParams &options[[buffer(0)]],
constant uchar *inputU8[[ buffer(1), function_constant(kUseSourceBufferU8) ]],
constant ushort *inputU16[[ buffer(1), function_constant(kUseSourceBufferU16) ]],
constant uint *inputU32[[ buffer(1), function_constant(kUseSourceBufferU32) ]],
device uint *output[[buffer(2)]])
{ {
if (idx >= options.indexCount) { return; }; if (idx >= options.indexCount) { return; };
uint value = input[options.srcOffset / 4 + idx];
output[idx] = value; uint elemIdx = 2 + idx;
output[3 * idx] = getIndexU32(options.srcOffset, 0, inputU8, inputU16, inputU32);
output[3 * idx + 1] = getIndexU32(options.srcOffset, elemIdx - 1, inputU8, inputU16, inputU32);
output[3 * idx + 2] = getIndexU32(options.srcOffset, elemIdx, inputU8, inputU16, inputU32);
} }
# 12 "master_source.metal" 2 # 12 "master_source.metal" 2
......
...@@ -352,6 +352,13 @@ Error ValidatePlatformType(const ClientExtensions &clientExtensions, EGLAttrib p ...@@ -352,6 +352,13 @@ Error ValidatePlatformType(const ClientExtensions &clientExtensions, EGLAttrib p
} }
break; break;
case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
if (!clientExtensions.platformANGLEMetal)
{
return EglBadAttribute() << "Metal platform is unsupported.";
}
break;
default: default:
return EglBadAttribute() << "Unknown platform type."; return EglBadAttribute() << "Unknown platform type.";
} }
......
...@@ -25,6 +25,7 @@ libangle_common_sources = [ ...@@ -25,6 +25,7 @@ libangle_common_sources = [
"src/common/android_util.h", "src/common/android_util.h",
"src/common/angleutils.cpp", "src/common/angleutils.cpp",
"src/common/angleutils.h", "src/common/angleutils.h",
"src/common/apple_platform_utils.h",
"src/common/bitset_utils.h", "src/common/bitset_utils.h",
"src/common/debug.cpp", "src/common/debug.cpp",
"src/common/debug.h", "src/common/debug.h",
...@@ -159,6 +160,7 @@ libangle_includes = [ ...@@ -159,6 +160,7 @@ libangle_includes = [
"include/platform/Feature.h", "include/platform/Feature.h",
"include/platform/FeaturesD3D.h", "include/platform/FeaturesD3D.h",
"include/platform/FeaturesGL.h", "include/platform/FeaturesGL.h",
"include/platform/FeaturesMtl.h",
"include/platform/FeaturesVk.h", "include/platform/FeaturesVk.h",
"include/platform/FrontendFeatures.h", "include/platform/FrontendFeatures.h",
"include/platform/Platform.h", "include/platform/Platform.h",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment