604 lines
20 KiB
C++
604 lines
20 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <Cocoa/Cocoa.h>
|
|
#include <Metal/Metal.h>
|
|
#include <QuartzCore/QuartzCore.h>
|
|
|
|
#include "BLI_assert.h"
|
|
#include "MEM_guardedalloc.h"
|
|
#include "gpu_texture_private.hh"
|
|
|
|
#include "BLI_map.hh"
|
|
#include "GPU_texture.h"
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
@class CAMetalLayer;
|
|
@class MTLCommandQueue;
|
|
@class MTLRenderPipelineState;
|
|
|
|
struct GPUFrameBuffer;
|
|
|
|
/* Texture Update system structs. */
|
|
struct TextureUpdateRoutineSpecialisation {
|
|
|
|
/* The METAL type of data in input array, e.g. half, float, short, int */
|
|
std::string input_data_type;
|
|
|
|
/* The type of the texture data texture2d<T,..>, e.g. T=float, half, int etc. */
|
|
std::string output_data_type;
|
|
|
|
/* Number of image channels provided in input texture data array (min=1, max=4). */
|
|
int component_count_input;
|
|
|
|
/* Number of channels the destination texture has (min=1, max=4). */
|
|
int component_count_output;
|
|
|
|
bool operator==(const TextureUpdateRoutineSpecialisation &other) const
|
|
{
|
|
return ((input_data_type == other.input_data_type) &&
|
|
(output_data_type == other.output_data_type) &&
|
|
(component_count_input == other.component_count_input) &&
|
|
(component_count_output == other.component_count_output));
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
blender::DefaultHash<std::string> string_hasher;
|
|
return (uint64_t)string_hasher(
|
|
this->input_data_type + this->output_data_type +
|
|
std::to_string((this->component_count_input << 8) + this->component_count_output));
|
|
}
|
|
};
|
|
|
|
/* Type of data is being written to the depth target:
|
|
* 0 = floating point (0.0 - 1.0)
|
|
* 1 = 24 bit integer (0 - 2^24)
|
|
* 2 = 32 bit integer (0 - 2^32) */
|
|
|
|
typedef enum {
|
|
MTL_DEPTH_UPDATE_MODE_FLOAT = 0,
|
|
MTL_DEPTH_UPDATE_MODE_INT24 = 1,
|
|
MTL_DEPTH_UPDATE_MODE_INT32 = 2
|
|
} DepthTextureUpdateMode;
|
|
|
|
struct DepthTextureUpdateRoutineSpecialisation {
|
|
DepthTextureUpdateMode data_mode;
|
|
|
|
bool operator==(const DepthTextureUpdateRoutineSpecialisation &other) const
|
|
{
|
|
return ((data_mode == other.data_mode));
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
return (uint64_t)(this->data_mode);
|
|
}
|
|
};
|
|
|
|
/* Texture Read system structs. */
|
|
struct TextureReadRoutineSpecialisation {
|
|
std::string input_data_type;
|
|
std::string output_data_type;
|
|
int component_count_input;
|
|
int component_count_output;
|
|
|
|
/* Format for depth data.
|
|
* 0 = Not a Depth format,
|
|
* 1 = FLOAT DEPTH,
|
|
* 2 = 24Bit Integer Depth,
|
|
* 4 = 32bit Unsigned-Integer Depth. */
|
|
int depth_format_mode;
|
|
|
|
bool operator==(const TextureReadRoutineSpecialisation &other) const
|
|
{
|
|
return ((input_data_type == other.input_data_type) &&
|
|
(output_data_type == other.output_data_type) &&
|
|
(component_count_input == other.component_count_input) &&
|
|
(component_count_output == other.component_count_output) &&
|
|
(depth_format_mode == other.depth_format_mode));
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
blender::DefaultHash<std::string> string_hasher;
|
|
return uint64_t(string_hasher(this->input_data_type + this->output_data_type +
|
|
std::to_string((this->component_count_input << 8) +
|
|
this->component_count_output +
|
|
(this->depth_format_mode << 28))));
|
|
}
|
|
};
|
|
|
|
namespace blender::gpu {
|
|
|
|
class MTLContext;
|
|
class MTLVertBuf;
|
|
|
|
/* Metal Texture internal implementation. */
|
|
static const int MTL_MAX_MIPMAP_COUNT = 15; /* Max: 16384x16384 */
|
|
static const int MTL_MAX_FBO_ATTACHED = 16;
|
|
|
|
/* Samplers */
|
|
struct MTLSamplerState {
|
|
eGPUSamplerState state;
|
|
|
|
/* Mip min and mip max on sampler state always the same.
|
|
* Level range now controlled with textureView to be consistent with GL baseLevel. */
|
|
bool operator==(const MTLSamplerState &other) const
|
|
{
|
|
/* Add other parameters as needed. */
|
|
return (this->state == other.state);
|
|
}
|
|
|
|
operator uint() const
|
|
{
|
|
return uint(state);
|
|
}
|
|
|
|
operator uint64_t() const
|
|
{
|
|
return uint64_t(state);
|
|
}
|
|
};
|
|
|
|
const MTLSamplerState DEFAULT_SAMPLER_STATE = {GPU_SAMPLER_DEFAULT /*, 0, 9999*/};
|
|
|
|
class MTLTexture : public Texture {
|
|
friend class MTLContext;
|
|
friend class MTLStateManager;
|
|
friend class MTLFrameBuffer;
|
|
|
|
private:
|
|
/* Where the textures data comes from. */
|
|
enum {
|
|
MTL_TEXTURE_MODE_DEFAULT, /* Texture is self-initialized (Standard). */
|
|
MTL_TEXTURE_MODE_EXTERNAL, /* Texture source from external id<MTLTexture> handle */
|
|
MTL_TEXTURE_MODE_VBO, /* Texture source initialized from VBO */
|
|
MTL_TEXTURE_MODE_TEXTURE_VIEW /* Texture is a view into an existing texture. */
|
|
} resource_mode_;
|
|
|
|
/* 'baking' refers to the generation of GPU-backed resources. This flag ensures GPU resources are
|
|
* ready. Baking is generally deferred until as late as possible, to ensure all associated
|
|
* resource state has been specified up-front. */
|
|
bool is_baked_ = false;
|
|
MTLTextureDescriptor *texture_descriptor_ = nullptr;
|
|
id<MTLTexture> texture_ = nil;
|
|
MTLTextureUsage usage_;
|
|
|
|
/* Texture Storage. */
|
|
id<MTLBuffer> texture_buffer_ = nil;
|
|
uint aligned_w_ = 0;
|
|
|
|
/* Blit Frame-buffer. */
|
|
GPUFrameBuffer *blit_fb_ = nullptr;
|
|
uint blit_fb_slice_ = 0;
|
|
uint blit_fb_mip_ = 0;
|
|
|
|
/* Non-SRGB texture view, used for when a framebuffer is bound with SRGB disabled. */
|
|
id<MTLTexture> texture_no_srgb_ = nil;
|
|
|
|
/* Texture view properties */
|
|
/* In Metal, we use texture views to either limit mipmap ranges,
|
|
* , apply a swizzle mask, or both.
|
|
*
|
|
* We apply the mip limit in the view rather than in the sampler, as
|
|
* certain effects and functionality such as textureSize rely on the base level
|
|
* being modified.
|
|
*
|
|
* Texture views can also point to external textures, rather than the owned
|
|
* texture if MTL_TEXTURE_MODE_TEXTURE_VIEW is used.
|
|
* If this mode is used, source_texture points to a GPUTexture from which
|
|
* we pull their texture handle as a root.
|
|
*/
|
|
const GPUTexture *source_texture_ = nullptr;
|
|
|
|
enum TextureViewDirtyState {
|
|
TEXTURE_VIEW_NOT_DIRTY = 0,
|
|
TEXTURE_VIEW_SWIZZLE_DIRTY = (1 << 0),
|
|
TEXTURE_VIEW_MIP_DIRTY = (1 << 1)
|
|
};
|
|
id<MTLTexture> mip_swizzle_view_ = nil;
|
|
char tex_swizzle_mask_[4];
|
|
MTLTextureSwizzleChannels mtl_swizzle_mask_;
|
|
bool mip_range_dirty_ = false;
|
|
|
|
int mip_texture_base_level_ = 0;
|
|
int mip_texture_max_level_ = 1000;
|
|
int mip_texture_base_layer_ = 0;
|
|
int texture_view_dirty_flags_ = TEXTURE_VIEW_NOT_DIRTY;
|
|
|
|
/* Max mip-maps for currently allocated texture resource. */
|
|
int mtl_max_mips_ = 1;
|
|
bool has_generated_mips_ = false;
|
|
|
|
/* VBO. */
|
|
MTLVertBuf *vert_buffer_;
|
|
id<MTLBuffer> vert_buffer_mtl_;
|
|
|
|
/* Whether the texture's properties or state has changed (e.g. mipmap range), and re-baking of
|
|
* GPU resource is required. */
|
|
bool is_dirty_;
|
|
bool is_bound_;
|
|
|
|
public:
|
|
MTLTexture(const char *name);
|
|
MTLTexture(const char *name,
|
|
eGPUTextureFormat format,
|
|
eGPUTextureType type,
|
|
id<MTLTexture> metal_texture);
|
|
~MTLTexture();
|
|
|
|
void update_sub(
|
|
int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data) override;
|
|
void update_sub(int offset[3],
|
|
int extent[3],
|
|
eGPUDataFormat format,
|
|
GPUPixelBuffer *pixbuf) override;
|
|
|
|
void generate_mipmap() override;
|
|
void copy_to(Texture *dst) override;
|
|
void clear(eGPUDataFormat format, const void *data) override;
|
|
void swizzle_set(const char swizzle_mask[4]) override;
|
|
void stencil_texture_mode_set(bool use_stencil) override{
|
|
/* TODO(Metal): implement. */
|
|
};
|
|
void mip_range_set(int min, int max) override;
|
|
void *read(int mip, eGPUDataFormat type) override;
|
|
|
|
/* Remove once no longer required -- will just return 0 for now in MTL path. */
|
|
uint gl_bindcode_get() const override;
|
|
|
|
bool is_format_srgb();
|
|
bool texture_is_baked();
|
|
const char *get_name()
|
|
{
|
|
return name_;
|
|
}
|
|
|
|
id<MTLBuffer> get_vertex_buffer() const
|
|
{
|
|
if (resource_mode_ == MTL_TEXTURE_MODE_VBO) {
|
|
return vert_buffer_mtl_;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
protected:
|
|
bool init_internal() override;
|
|
bool init_internal(GPUVertBuf *vbo) override;
|
|
bool init_internal(const GPUTexture *src,
|
|
int mip_offset,
|
|
int layer_offset) override; /* Texture View */
|
|
|
|
private:
|
|
/* Common Constructor, default initialization. */
|
|
void mtl_texture_init();
|
|
|
|
/* Post-construction and member initialization, prior to baking.
|
|
* Called during init_internal */
|
|
void prepare_internal();
|
|
|
|
/* Generate Metal GPU resources and upload data if needed */
|
|
void ensure_baked();
|
|
|
|
/* Delete associated Metal GPU resources. */
|
|
void reset();
|
|
void ensure_mipmaps(int miplvl);
|
|
|
|
/* Flags a given mip level as being used. */
|
|
void add_subresource(uint level);
|
|
|
|
void read_internal(int mip,
|
|
int x_off,
|
|
int y_off,
|
|
int z_off,
|
|
int width,
|
|
int height,
|
|
int depth,
|
|
eGPUDataFormat desired_output_format,
|
|
int num_output_components,
|
|
int debug_data_size,
|
|
void *r_data);
|
|
void bake_mip_swizzle_view();
|
|
|
|
id<MTLTexture> get_metal_handle();
|
|
id<MTLTexture> get_metal_handle_base();
|
|
id<MTLTexture> get_non_srgb_handle();
|
|
MTLSamplerState get_sampler_state();
|
|
void blit(id<MTLBlitCommandEncoder> blit_encoder,
|
|
uint src_x_offset,
|
|
uint src_y_offset,
|
|
uint src_z_offset,
|
|
uint src_slice,
|
|
uint src_mip,
|
|
gpu::MTLTexture *dest,
|
|
uint dst_x_offset,
|
|
uint dst_y_offset,
|
|
uint dst_z_offset,
|
|
uint dst_slice,
|
|
uint dst_mip,
|
|
uint width,
|
|
uint height,
|
|
uint depth);
|
|
void blit(gpu::MTLTexture *dest,
|
|
uint src_x_offset,
|
|
uint src_y_offset,
|
|
uint dst_x_offset,
|
|
uint dst_y_offset,
|
|
uint src_mip,
|
|
uint dst_mip,
|
|
uint dst_slice,
|
|
int width,
|
|
int height);
|
|
GPUFrameBuffer *get_blit_framebuffer(uint dst_slice, uint dst_mip);
|
|
|
|
/* Texture Update function Utilities. */
|
|
/* Metal texture updating does not provide the same range of functionality for type conversion
|
|
* and format compatibility as are available in OpenGL. To achieve the same level of
|
|
* functionality, we need to instead use compute kernels to perform texture data conversions
|
|
* where appropriate.
|
|
* There are a number of different inputs which affect permutations and thus require different
|
|
* shaders and PSOs, such as:
|
|
* - Texture format
|
|
* - Texture type (e.g. 2D, 3D, 2D Array, Depth etc;)
|
|
* - Source data format and component count (e.g. floating point)
|
|
*
|
|
* MECHANISM:
|
|
*
|
|
* blender::map<INPUT DEFINES STRUCT, compute PSO> update_2d_array_kernel_psos;
|
|
* - Generate compute shader with configured kernel below with variable parameters depending
|
|
* on input/output format configurations. Do not need to keep source or descriptors around,
|
|
* just PSO, as same input defines will always generate the same code.
|
|
*
|
|
* - IF datatype IS an exact match e.g. :
|
|
* - Per-component size matches (e.g. GPU_DATA_UBYTE)
|
|
* OR GPU_DATA_10_11_11_REV && GPU_R11G11B10 (equiv)
|
|
* OR D24S8 and GPU_DATA_UINT_24_8
|
|
* We can use BLIT ENCODER.
|
|
*
|
|
* OTHERWISE TRIGGER COMPUTE:
|
|
* - Compute sizes will vary. Threads per grid WILL match 'extent'.
|
|
* Dimensions will vary depending on texture type.
|
|
* - Will use setBytes with 'TextureUpdateParams' struct to pass in useful member params.
|
|
*/
|
|
struct TextureUpdateParams {
|
|
int mip_index;
|
|
int extent[3]; /* Width, Height, Slice on 2D Array tex. */
|
|
int offset[3]; /* Width, Height, Slice on 2D Array tex. */
|
|
uint unpack_row_length; /* Number of pixels between bytes in input data. */
|
|
};
|
|
|
|
id<MTLComputePipelineState> texture_update_1d_get_kernel(
|
|
TextureUpdateRoutineSpecialisation specialization);
|
|
id<MTLComputePipelineState> texture_update_1d_array_get_kernel(
|
|
TextureUpdateRoutineSpecialisation specialization);
|
|
id<MTLComputePipelineState> texture_update_2d_get_kernel(
|
|
TextureUpdateRoutineSpecialisation specialization);
|
|
id<MTLComputePipelineState> texture_update_2d_array_get_kernel(
|
|
TextureUpdateRoutineSpecialisation specialization);
|
|
id<MTLComputePipelineState> texture_update_3d_get_kernel(
|
|
TextureUpdateRoutineSpecialisation specialization);
|
|
|
|
id<MTLComputePipelineState> mtl_texture_update_impl(
|
|
TextureUpdateRoutineSpecialisation specialization_params,
|
|
blender::Map<TextureUpdateRoutineSpecialisation, id<MTLComputePipelineState>>
|
|
&specialization_cache,
|
|
eGPUTextureType texture_type);
|
|
|
|
/* Depth Update Utilities */
|
|
/* Depth texture updates are not directly supported with Blit operations, similarly, we cannot
|
|
* use a compute shader to write to depth, so we must instead render to a depth target.
|
|
* These processes use vertex/fragment shaders to render texture data from an intermediate
|
|
* source, in order to prime the depth buffer. */
|
|
GPUShader *depth_2d_update_sh_get(DepthTextureUpdateRoutineSpecialisation specialization);
|
|
|
|
void update_sub_depth_2d(
|
|
int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data);
|
|
|
|
/* Texture Read function utilities -- Follows a similar mechanism to the updating routines */
|
|
struct TextureReadParams {
|
|
int mip_index;
|
|
int extent[3]; /* Width, Height, Slice on 2D Array tex. */
|
|
int offset[3]; /* Width, Height, Slice on 2D Array tex. */
|
|
};
|
|
|
|
id<MTLComputePipelineState> texture_read_1d_get_kernel(
|
|
TextureReadRoutineSpecialisation specialization);
|
|
id<MTLComputePipelineState> texture_read_1d_array_get_kernel(
|
|
TextureReadRoutineSpecialisation specialization);
|
|
id<MTLComputePipelineState> texture_read_2d_get_kernel(
|
|
TextureReadRoutineSpecialisation specialization);
|
|
id<MTLComputePipelineState> texture_read_2d_array_get_kernel(
|
|
TextureReadRoutineSpecialisation specialization);
|
|
id<MTLComputePipelineState> texture_read_3d_get_kernel(
|
|
TextureReadRoutineSpecialisation specialization);
|
|
|
|
id<MTLComputePipelineState> mtl_texture_read_impl(
|
|
TextureReadRoutineSpecialisation specialization_params,
|
|
blender::Map<TextureReadRoutineSpecialisation, id<MTLComputePipelineState>>
|
|
&specialization_cache,
|
|
eGPUTextureType texture_type);
|
|
|
|
/* fullscreen blit utilities. */
|
|
GPUShader *fullscreen_blit_sh_get();
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("MTLTexture")
|
|
};
|
|
|
|
class MTLPixelBuffer : public PixelBuffer {
|
|
private:
|
|
id<MTLBuffer> buffer_ = nil;
|
|
|
|
public:
|
|
MTLPixelBuffer(uint size);
|
|
~MTLPixelBuffer();
|
|
|
|
void *map() override;
|
|
void unmap() override;
|
|
int64_t get_native_handle() override;
|
|
uint get_size() override;
|
|
|
|
id<MTLBuffer> get_metal_buffer();
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("MTLPixelBuffer")
|
|
};
|
|
|
|
/* Utility */
|
|
MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format);
|
|
int get_mtl_format_bytesize(MTLPixelFormat tex_format);
|
|
int get_mtl_format_num_components(MTLPixelFormat tex_format);
|
|
bool mtl_format_supports_blending(MTLPixelFormat format);
|
|
|
|
/* The type used to define the per-component data in the input buffer. */
|
|
inline std::string tex_data_format_to_msl_type_str(eGPUDataFormat type)
|
|
{
|
|
switch (type) {
|
|
case GPU_DATA_FLOAT:
|
|
return "float";
|
|
case GPU_DATA_HALF_FLOAT:
|
|
return "half";
|
|
case GPU_DATA_INT:
|
|
return "int";
|
|
case GPU_DATA_UINT:
|
|
return "uint";
|
|
case GPU_DATA_UBYTE:
|
|
return "uchar";
|
|
case GPU_DATA_UINT_24_8:
|
|
return "uint"; /* Problematic type - but will match alignment. */
|
|
case GPU_DATA_10_11_11_REV:
|
|
return "float"; /* Problematic type - each component will be read as a float. */
|
|
default:
|
|
BLI_assert(false);
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/* The type T which goes into texture2d<T, access>. */
|
|
inline std::string tex_data_format_to_msl_texture_template_type(eGPUDataFormat type)
|
|
{
|
|
switch (type) {
|
|
case GPU_DATA_FLOAT:
|
|
return "float";
|
|
case GPU_DATA_HALF_FLOAT:
|
|
return "half";
|
|
case GPU_DATA_INT:
|
|
return "int";
|
|
case GPU_DATA_UINT:
|
|
return "uint";
|
|
case GPU_DATA_UBYTE:
|
|
return "ushort";
|
|
case GPU_DATA_UINT_24_8:
|
|
return "uint"; /* Problematic type. */
|
|
case GPU_DATA_10_11_11_REV:
|
|
return "float"; /* Problematic type. */
|
|
default:
|
|
BLI_assert(false);
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/* Determine whether format is writable or not. Use mtl_format_get_writeable_view_format(..) for
|
|
* these. */
|
|
inline bool mtl_format_is_writable(MTLPixelFormat format)
|
|
{
|
|
switch (format) {
|
|
case MTLPixelFormatRGBA8Unorm_sRGB:
|
|
case MTLPixelFormatBGRA8Unorm_sRGB:
|
|
case MTLPixelFormatDepth16Unorm:
|
|
case MTLPixelFormatDepth32Float:
|
|
case MTLPixelFormatDepth32Float_Stencil8:
|
|
case MTLPixelFormatBGR10A2Unorm:
|
|
case MTLPixelFormatDepth24Unorm_Stencil8:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* For the cases where a texture format is unwritable, we can create a texture view of a similar
|
|
* format */
|
|
inline MTLPixelFormat mtl_format_get_writeable_view_format(MTLPixelFormat format)
|
|
{
|
|
switch (format) {
|
|
case MTLPixelFormatRGBA8Unorm_sRGB:
|
|
return MTLPixelFormatRGBA8Unorm;
|
|
case MTLPixelFormatBGRA8Unorm_sRGB:
|
|
return MTLPixelFormatBGRA8Unorm;
|
|
case MTLPixelFormatDepth16Unorm:
|
|
return MTLPixelFormatR16Unorm;
|
|
case MTLPixelFormatDepth32Float:
|
|
return MTLPixelFormatR32Float;
|
|
case MTLPixelFormatDepth32Float_Stencil8:
|
|
/* return MTLPixelFormatRG32Float; */
|
|
/* No alternative mirror format. This should not be used for
|
|
* manual data upload */
|
|
return MTLPixelFormatInvalid;
|
|
case MTLPixelFormatBGR10A2Unorm:
|
|
/* return MTLPixelFormatBGRA8Unorm; */
|
|
/* No alternative mirror format. This should not be used for
|
|
* manual data upload */
|
|
return MTLPixelFormatInvalid;
|
|
case MTLPixelFormatDepth24Unorm_Stencil8:
|
|
/* No direct format, but we'll just mirror the bytes -- Uint
|
|
* should ensure bytes are not re-normalized or manipulated */
|
|
/* return MTLPixelFormatR32Uint; */
|
|
return MTLPixelFormatInvalid;
|
|
default:
|
|
return format;
|
|
}
|
|
return format;
|
|
}
|
|
|
|
inline MTLTextureUsage mtl_usage_from_gpu(eGPUTextureUsage usage)
|
|
{
|
|
MTLTextureUsage mtl_usage = MTLTextureUsageUnknown;
|
|
if (usage == GPU_TEXTURE_USAGE_GENERAL) {
|
|
return MTLTextureUsageUnknown;
|
|
}
|
|
if (usage & GPU_TEXTURE_USAGE_SHADER_READ) {
|
|
mtl_usage = mtl_usage | MTLTextureUsageShaderRead;
|
|
}
|
|
if (usage & GPU_TEXTURE_USAGE_SHADER_WRITE) {
|
|
mtl_usage = mtl_usage | MTLTextureUsageShaderWrite;
|
|
}
|
|
if (usage & GPU_TEXTURE_USAGE_ATTACHMENT) {
|
|
mtl_usage = mtl_usage | MTLTextureUsageRenderTarget;
|
|
}
|
|
if (usage & GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW) {
|
|
mtl_usage = mtl_usage | MTLTextureUsagePixelFormatView;
|
|
}
|
|
return mtl_usage;
|
|
}
|
|
|
|
inline eGPUTextureUsage gpu_usage_from_mtl(MTLTextureUsage mtl_usage)
|
|
{
|
|
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
|
|
if (mtl_usage == MTLTextureUsageUnknown) {
|
|
return GPU_TEXTURE_USAGE_GENERAL;
|
|
}
|
|
if (mtl_usage & MTLTextureUsageShaderRead) {
|
|
usage = usage | GPU_TEXTURE_USAGE_SHADER_READ;
|
|
}
|
|
if (mtl_usage & MTLTextureUsageShaderWrite) {
|
|
usage = usage | GPU_TEXTURE_USAGE_SHADER_WRITE;
|
|
}
|
|
if (mtl_usage & MTLTextureUsageRenderTarget) {
|
|
usage = usage | GPU_TEXTURE_USAGE_ATTACHMENT;
|
|
}
|
|
if (mtl_usage & MTLTextureUsagePixelFormatView) {
|
|
usage = usage | GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW;
|
|
}
|
|
return usage;
|
|
}
|
|
|
|
} // namespace blender::gpu
|